From 2fd714e52fc41e38f23d4f50078e192f3fc3b9d5 Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 4 Oct 2000 01:14:41 +0000 Subject: [PATCH] Initial revision --- panda/Config.Irix.pp | 49 + panda/Config.Linux.pp | 49 + panda/Config.Win32.pp | 49 + panda/Config.pp | 167 + panda/LocalSetup.pp | 211 + panda/Package.pp | 11 + panda/Sources.pp | 10 + panda/acinclude.m4 | 933 +++ panda/metalibs/Sources.pp | 7 + panda/metalibs/panda/Sources.pp | 25 + panda/metalibs/panda/panda.cxx | 9 + panda/metalibs/pandadx/Sources.pp | 21 + panda/metalibs/pandadx/pandadx.cxx | 9 + panda/metalibs/pandaegg/Sources.pp | 19 + panda/metalibs/pandaegg/pandaegg.cxx | 9 + panda/metalibs/pandaexpress/Sources.pp | 17 + panda/metalibs/pandaexpress/pandaexpress.cxx | 9 + panda/metalibs/pandagl/Sources.pp | 21 + panda/metalibs/pandagl/pandagl.cxx | 9 + panda/metalibs/pandaglut/Sources.pp | 21 + panda/metalibs/pandaglut/pandaglut.cxx | 9 + panda/metalibs/pandaphysics/Sources.pp | 19 + panda/metalibs/pandaphysics/pandaphysics.cxx | 9 + panda/metalibs/pandarib/Sources.pp | 21 + panda/metalibs/pandarib/pandarib.cxx | 9 + panda/src/PACKAGE-DESC | 212 + panda/src/Sources.pp | 4 + panda/src/audio/Sources.pp | 49 + panda/src/audio/audio.h | 15 + panda/src/audio/audio_load_midi.cxx | 75 + panda/src/audio/audio_load_wav.cxx | 71 + panda/src/audio/audio_manager.I | 69 + panda/src/audio/audio_manager.cxx | 95 + panda/src/audio/audio_manager.h | 47 + panda/src/audio/audio_midi.cxx | 233 + panda/src/audio/audio_midi.h | 26 + panda/src/audio/audio_mikmod_traits.cxx | 305 + panda/src/audio/audio_mikmod_traits.h | 102 + panda/src/audio/audio_music.I | 67 + panda/src/audio/audio_music.cxx | 36 + panda/src/audio/audio_music.h | 62 + panda/src/audio/audio_null_traits.I | 13 + panda/src/audio/audio_null_traits.cxx | 70 + panda/src/audio/audio_null_traits.h | 41 + panda/src/audio/audio_pool.I | 141 + panda/src/audio/audio_pool.cxx | 243 + panda/src/audio/audio_pool.h | 63 + panda/src/audio/audio_sample.I | 67 + panda/src/audio/audio_sample.cxx | 45 + panda/src/audio/audio_sample.h | 63 + panda/src/audio/audio_trait.cxx | 48 + panda/src/audio/audio_trait.h | 49 + panda/src/audio/audio_win_traits.I | 28 + panda/src/audio/audio_win_traits.cxx | 660 ++ panda/src/audio/audio_win_traits.h | 78 + panda/src/audio/config_audio.cxx | 41 + panda/src/audio/config_audio.h | 20 + panda/src/audio/test_audio.cxx | 41 + panda/src/builder/Sources.pp | 47 + panda/src/builder/builder.I | 69 + panda/src/builder/builder.cxx | 229 + panda/src/builder/builder.h | 198 + panda/src/builder/builderAttrib.I | 69 + panda/src/builder/builderAttrib.cxx | 12 + panda/src/builder/builderAttrib.h | 59 + panda/src/builder/builderAttribTempl.I | 244 + panda/src/builder/builderAttribTempl.h | 70 + panda/src/builder/builderBucket.I | 117 + panda/src/builder/builderBucket.cxx | 240 + panda/src/builder/builderBucket.h | 104 + panda/src/builder/builderBucketNode.I | 92 + panda/src/builder/builderBucketNode.cxx | 87 + panda/src/builder/builderBucketNode.h | 58 + panda/src/builder/builderFuncs.I | 908 +++ panda/src/builder/builderFuncs.h | 66 + panda/src/builder/builderMisc.cxx | 37 + panda/src/builder/builderMisc.h | 18 + panda/src/builder/builderNormalVisualizer.I | 11 + panda/src/builder/builderNormalVisualizer.cxx | 65 + panda/src/builder/builderNormalVisualizer.h | 51 + panda/src/builder/builderPrim.cxx | 194 + panda/src/builder/builderPrim.h | 119 + panda/src/builder/builderPrimTempl.I | 1025 +++ panda/src/builder/builderPrimTempl.h | 143 + panda/src/builder/builderProperties.cxx | 116 + panda/src/builder/builderProperties.h | 115 + panda/src/builder/builderTypes.cxx | 102 + panda/src/builder/builderTypes.h | 221 + panda/src/builder/builderVertex.I | 265 + panda/src/builder/builderVertex.cxx | 12 + panda/src/builder/builderVertex.h | 119 + panda/src/builder/builderVertexTempl.I | 291 + panda/src/builder/builderVertexTempl.h | 73 + panda/src/builder/config_builder.cxx | 14 + panda/src/builder/config_builder.h | 14 + panda/src/builder/mesher.cxx | 11 + panda/src/builder/mesher.h | 42 + panda/src/builder/mesherEdge.I | 143 + panda/src/builder/mesherEdge.h | 62 + panda/src/builder/mesherFanMaker.I | 312 + panda/src/builder/mesherFanMaker.h | 75 + panda/src/builder/mesherStrip.I | 1625 +++++ panda/src/builder/mesherStrip.h | 133 + panda/src/builder/mesherTempl.I | 661 ++ panda/src/builder/mesherTempl.h | 77 + panda/src/builder/pta_BuilderC.cxx | 11 + panda/src/builder/pta_BuilderC.h | 37 + panda/src/builder/pta_BuilderN.cxx | 11 + panda/src/builder/pta_BuilderN.h | 37 + panda/src/builder/pta_BuilderTC.cxx | 11 + panda/src/builder/pta_BuilderTC.h | 37 + panda/src/builder/pta_BuilderV.cxx | 11 + panda/src/builder/pta_BuilderV.h | 37 + panda/src/builder/test_builder.cxx | 199 + panda/src/builder/test_builder_data.cxx | 705 ++ panda/src/builder/vector_BuilderC.cxx | 11 + panda/src/builder/vector_BuilderC.h | 32 + panda/src/builder/vector_BuilderN.cxx | 11 + panda/src/builder/vector_BuilderN.h | 32 + panda/src/builder/vector_BuilderTC.cxx | 11 + panda/src/builder/vector_BuilderTC.h | 32 + panda/src/builder/vector_BuilderV.cxx | 11 + panda/src/builder/vector_BuilderV.h | 32 + panda/src/chan/Sources.pp | 43 + panda/src/chan/animBundle.I | 53 + panda/src/chan/animBundle.cxx | 86 + panda/src/chan/animBundle.h | 72 + panda/src/chan/animBundleNode.I | 60 + panda/src/chan/animBundleNode.cxx | 102 + panda/src/chan/animBundleNode.h | 68 + panda/src/chan/animChannel.I | 106 + panda/src/chan/animChannel.cxx | 12 + panda/src/chan/animChannel.h | 120 + panda/src/chan/animChannelBase.I | 35 + panda/src/chan/animChannelBase.cxx | 66 + panda/src/chan/animChannelBase.h | 71 + panda/src/chan/animChannelFixed.I | 72 + panda/src/chan/animChannelFixed.h | 57 + panda/src/chan/animChannelMatrixXfmTable.I | 70 + panda/src/chan/animChannelMatrixXfmTable.cxx | 366 + panda/src/chan/animChannelMatrixXfmTable.h | 85 + panda/src/chan/animChannelScalarTable.I | 27 + panda/src/chan/animChannelScalarTable.cxx | 211 + panda/src/chan/animChannelScalarTable.h | 70 + panda/src/chan/animControl.I | 109 + panda/src/chan/animControl.N | 3 + panda/src/chan/animControl.cxx | 647 ++ panda/src/chan/animControl.h | 166 + panda/src/chan/animControlCollection.I | 228 + panda/src/chan/animControlCollection.cxx | 217 + panda/src/chan/animControlCollection.h | 83 + panda/src/chan/animGroup.I | 4 + panda/src/chan/animGroup.cxx | 240 + panda/src/chan/animGroup.h | 97 + panda/src/chan/auto_bind.cxx | 141 + panda/src/chan/auto_bind.h | 30 + panda/src/chan/config_chan.cxx | 69 + panda/src/chan/config_chan.h | 17 + panda/src/chan/movingPart.I | 122 + panda/src/chan/movingPart.h | 69 + panda/src/chan/movingPartBase.I | 18 + panda/src/chan/movingPartBase.cxx | 176 + panda/src/chan/movingPartBase.h | 77 + panda/src/chan/movingPartMatrix.I | 37 + panda/src/chan/movingPartMatrix.cxx | 148 + panda/src/chan/movingPartMatrix.h | 70 + panda/src/chan/movingPartScalar.I | 37 + panda/src/chan/movingPartScalar.cxx | 102 + panda/src/chan/movingPartScalar.h | 69 + panda/src/chan/partBundle.I | 75 + panda/src/chan/partBundle.N | 10 + panda/src/chan/partBundle.cxx | 463 ++ panda/src/chan/partBundle.h | 162 + panda/src/chan/partBundleNode.I | 57 + panda/src/chan/partBundleNode.cxx | 74 + panda/src/chan/partBundleNode.h | 66 + panda/src/chan/partGroup.I | 33 + panda/src/chan/partGroup.cxx | 484 ++ panda/src/chan/partGroup.h | 119 + panda/src/chan/vector_PartGroupStar.cxx | 11 + panda/src/chan/vector_PartGroupStar.h | 33 + panda/src/chancfg/Sources.pp | 36 + panda/src/chancfg/chancfg.I | 4 + panda/src/chancfg/chancfg.N | 2 + panda/src/chancfg/chancfg.cxx | 534 ++ panda/src/chancfg/chancfg.h | 97 + panda/src/chancfg/chanlayout.I | 32 + panda/src/chancfg/chanlayout.cxx | 113 + panda/src/chancfg/chanlayout.h | 45 + panda/src/chancfg/chanparse.I | 98 + panda/src/chancfg/chanparse.cxx | 140 + panda/src/chancfg/chanparse.h | 35 + panda/src/chancfg/chansetup.I | 128 + panda/src/chancfg/chansetup.cxx | 204 + panda/src/chancfg/chansetup.h | 87 + panda/src/chancfg/chanshare.h | 13 + panda/src/chancfg/chanviewport.I | 39 + panda/src/chancfg/chanviewport.h | 31 + panda/src/chancfg/chanwindow.I | 76 + panda/src/chancfg/chanwindow.cxx | 105 + panda/src/chancfg/chanwindow.h | 52 + panda/src/chancfg/layout_db | 217 + panda/src/chancfg/setup_db | 82 + panda/src/chancfg/test_chancfg.cxx | 10 + panda/src/chancfg/window_db | 112 + panda/src/char/Sources.pp | 27 + panda/src/char/character.I | 50 + panda/src/char/character.cxx | 484 ++ panda/src/char/character.h | 116 + panda/src/char/characterJoint.cxx | 351 + panda/src/char/characterJoint.h | 97 + panda/src/char/characterJointBundle.I | 29 + panda/src/char/characterJointBundle.cxx | 65 + panda/src/char/characterJointBundle.h | 59 + panda/src/char/characterSlider.cxx | 87 + panda/src/char/characterSlider.h | 54 + panda/src/char/computedVertices.I | 39 + panda/src/char/computedVertices.cxx | 430 ++ panda/src/char/computedVertices.h | 102 + panda/src/char/computedVerticesMorph.I | 137 + panda/src/char/computedVerticesMorph.cxx | 93 + panda/src/char/computedVerticesMorph.h | 127 + panda/src/char/config_char.cxx | 42 + panda/src/char/config_char.h | 17 + panda/src/char/dynamicVertices.cxx | 101 + panda/src/char/dynamicVertices.h | 60 + panda/src/chat/Sources.pp | 18 + panda/src/chat/chatHelpers.cxx | 45 + panda/src/chat/chatHelpers.h | 16 + panda/src/chat/chatInput.I | 64 + panda/src/chat/chatInput.cxx | 116 + panda/src/chat/chatInput.h | 76 + panda/src/chat/config_chat.cxx | 16 + panda/src/chat/config_chat.h | 14 + panda/src/collide/Sources.pp | 52 + panda/src/collide/collideMask.h | 20 + panda/src/collide/collisionEntry.I | 222 + panda/src/collide/collisionEntry.cxx | 51 + panda/src/collide/collisionEntry.h | 111 + panda/src/collide/collisionHandler.cxx | 42 + panda/src/collide/collisionHandler.h | 53 + panda/src/collide/collisionHandlerEvent.I | 93 + panda/src/collide/collisionHandlerEvent.cxx | 174 + panda/src/collide/collisionHandlerEvent.h | 81 + panda/src/collide/collisionHandlerFloor.I | 28 + panda/src/collide/collisionHandlerFloor.cxx | 99 + panda/src/collide/collisionHandlerFloor.h | 61 + panda/src/collide/collisionHandlerPhysical.I | 26 + .../src/collide/collisionHandlerPhysical.cxx | 189 + panda/src/collide/collisionHandlerPhysical.h | 86 + panda/src/collide/collisionHandlerPusher.I | 24 + panda/src/collide/collisionHandlerPusher.cxx | 95 + panda/src/collide/collisionHandlerPusher.h | 59 + panda/src/collide/collisionHandlerQueue.cxx | 65 + panda/src/collide/collisionHandlerQueue.h | 58 + panda/src/collide/collisionLevelState.I | 162 + panda/src/collide/collisionLevelState.N | 1 + panda/src/collide/collisionLevelState.cxx | 88 + panda/src/collide/collisionLevelState.h | 75 + panda/src/collide/collisionNode.I | 150 + panda/src/collide/collisionNode.cxx | 286 + panda/src/collide/collisionNode.h | 93 + panda/src/collide/collisionPlane.I | 80 + panda/src/collide/collisionPlane.cxx | 291 + panda/src/collide/collisionPlane.h | 92 + panda/src/collide/collisionPolygon.I | 55 + panda/src/collide/collisionPolygon.cxx | 650 ++ panda/src/collide/collisionPolygon.h | 102 + panda/src/collide/collisionRay.I | 133 + panda/src/collide/collisionRay.cxx | 139 + panda/src/collide/collisionRay.h | 85 + panda/src/collide/collisionSolid.I | 50 + panda/src/collide/collisionSolid.cxx | 233 + panda/src/collide/collisionSolid.h | 121 + panda/src/collide/collisionSphere.I | 104 + panda/src/collide/collisionSphere.cxx | 336 + panda/src/collide/collisionSphere.h | 93 + panda/src/collide/collisionTraverser.I | 4 + panda/src/collide/collisionTraverser.cxx | 553 ++ panda/src/collide/collisionTraverser.h | 98 + panda/src/collide/config_collide.cxx | 48 + panda/src/collide/config_collide.h | 14 + panda/src/collide/test_collide.cxx | 62 + panda/src/configfiles/Configrc | 5 + panda/src/configfiles/Sources.pp | 4 + panda/src/configfiles/panda.emacs | 276 + panda/src/configfiles/panda.emacs.Xdefaults | 12 + panda/src/configfiles/panda.init | 24 + panda/src/cull/Sources.pp | 48 + panda/src/cull/config_cull.cxx | 35 + panda/src/cull/config_cull.h | 14 + panda/src/cull/cullLevelState.h | 25 + panda/src/cull/cullState.I | 248 + panda/src/cull/cullState.cxx | 91 + panda/src/cull/cullState.h | 112 + panda/src/cull/cullStateLookup.I | 37 + panda/src/cull/cullStateLookup.cxx | 215 + panda/src/cull/cullStateLookup.h | 72 + panda/src/cull/cullStateSubtree.I | 44 + panda/src/cull/cullStateSubtree.cxx | 69 + panda/src/cull/cullStateSubtree.h | 55 + panda/src/cull/cullTraverser.I | 121 + panda/src/cull/cullTraverser.cxx | 431 ++ panda/src/cull/cullTraverser.h | 127 + panda/src/cull/directRenderTransition.I | 13 + panda/src/cull/directRenderTransition.cxx | 34 + panda/src/cull/directRenderTransition.h | 61 + panda/src/cull/geomBin.I | 90 + panda/src/cull/geomBin.cxx | 108 + panda/src/cull/geomBin.h | 94 + panda/src/cull/geomBinAttribute.I | 66 + panda/src/cull/geomBinAttribute.N | 1 + panda/src/cull/geomBinAttribute.cxx | 98 + panda/src/cull/geomBinAttribute.h | 61 + panda/src/cull/geomBinBackToFront.I | 84 + panda/src/cull/geomBinBackToFront.cxx | 144 + panda/src/cull/geomBinBackToFront.h | 81 + panda/src/cull/geomBinFixed.I | 85 + panda/src/cull/geomBinFixed.cxx | 76 + panda/src/cull/geomBinFixed.h | 78 + panda/src/cull/geomBinGroup.I | 64 + panda/src/cull/geomBinGroup.cxx | 86 + panda/src/cull/geomBinGroup.h | 71 + panda/src/cull/geomBinNormal.cxx | 47 + panda/src/cull/geomBinNormal.h | 49 + panda/src/cull/geomBinTransition.I | 81 + panda/src/cull/geomBinTransition.cxx | 85 + panda/src/cull/geomBinTransition.h | 61 + panda/src/cull/geomBinUnsorted.I | 15 + panda/src/cull/geomBinUnsorted.cxx | 145 + panda/src/cull/geomBinUnsorted.h | 65 + panda/src/cull/test_cull.cxx | 141 + panda/src/device/Sources.pp | 23 + panda/src/device/adinputNode.cxx | 124 + panda/src/device/adinputNode.h | 77 + panda/src/device/analogData.I | 62 + panda/src/device/analogData.cxx | 9 + panda/src/device/analogData.h | 37 + panda/src/device/buttonData.I | 49 + panda/src/device/buttonData.cxx | 9 + panda/src/device/buttonData.h | 29 + panda/src/device/clientBase.cxx | 423 ++ panda/src/device/clientBase.h | 141 + panda/src/device/clients.txt | 52 + panda/src/device/config_device.cxx | 24 + panda/src/device/config_device.h | 16 + panda/src/device/dialData.I | 49 + panda/src/device/dialData.cxx | 9 + panda/src/device/dialData.h | 29 + panda/src/device/mouse.cxx | 119 + panda/src/device/mouse.h | 88 + panda/src/device/trackerData.I | 59 + panda/src/device/trackerData.cxx | 9 + panda/src/device/trackerData.h | 40 + panda/src/device/trackerNode.cxx | 156 + panda/src/device/trackerNode.h | 87 + panda/src/dgraph/Sources.pp | 69 + panda/src/dgraph/buttonEventDataAttribute.I | 76 + panda/src/dgraph/buttonEventDataAttribute.cxx | 94 + panda/src/dgraph/buttonEventDataAttribute.h | 81 + panda/src/dgraph/buttonEventDataTransition.I | 34 + .../src/dgraph/buttonEventDataTransition.cxx | 80 + panda/src/dgraph/buttonEventDataTransition.h | 58 + panda/src/dgraph/config_dgraph.cxx | 46 + panda/src/dgraph/config_dgraph.h | 15 + panda/src/dgraph/dataGraphTraversal.cxx | 99 + panda/src/dgraph/dataGraphTraversal.h | 70 + panda/src/dgraph/dataNode.cxx | 73 + panda/src/dgraph/dataNode.h | 117 + panda/src/dgraph/dataRelation.I | 42 + panda/src/dgraph/dataRelation.N | 1 + panda/src/dgraph/dataRelation.cxx | 21 + panda/src/dgraph/dataRelation.h | 54 + panda/src/dgraph/describe_data_verbose.cxx | 61 + panda/src/dgraph/describe_data_verbose.h | 22 + panda/src/dgraph/doubleDataAttribute.I | 23 + panda/src/dgraph/doubleDataAttribute.cxx | 34 + panda/src/dgraph/doubleDataAttribute.h | 53 + panda/src/dgraph/doubleDataTransition.I | 25 + panda/src/dgraph/doubleDataTransition.cxx | 34 + panda/src/dgraph/doubleDataTransition.h | 53 + panda/src/dgraph/doublePtrDataAttribute.I | 22 + panda/src/dgraph/doublePtrDataAttribute.cxx | 34 + panda/src/dgraph/doublePtrDataAttribute.h | 53 + panda/src/dgraph/doublePtrDataTransition.I | 24 + panda/src/dgraph/doublePtrDataTransition.cxx | 34 + panda/src/dgraph/doublePtrDataTransition.h | 53 + panda/src/dgraph/intDataAttribute.I | 23 + panda/src/dgraph/intDataAttribute.cxx | 34 + panda/src/dgraph/intDataAttribute.h | 53 + panda/src/dgraph/intDataTransition.I | 25 + panda/src/dgraph/intDataTransition.cxx | 34 + panda/src/dgraph/intDataTransition.h | 53 + panda/src/dgraph/matrixDataAttribute.I | 24 + panda/src/dgraph/matrixDataAttribute.cxx | 34 + panda/src/dgraph/matrixDataAttribute.h | 58 + panda/src/dgraph/matrixDataTransition.I | 25 + panda/src/dgraph/matrixDataTransition.cxx | 34 + panda/src/dgraph/matrixDataTransition.h | 58 + .../src/dgraph/modifierButtonDataAttribute.I | 56 + .../dgraph/modifierButtonDataAttribute.cxx | 78 + .../src/dgraph/modifierButtonDataAttribute.h | 68 + .../src/dgraph/modifierButtonDataTransition.I | 34 + .../dgraph/modifierButtonDataTransition.cxx | 80 + .../src/dgraph/modifierButtonDataTransition.h | 54 + panda/src/dgraph/numericDataAttribute.I | 132 + panda/src/dgraph/numericDataAttribute.h | 66 + panda/src/dgraph/numericDataTransition.I | 252 + panda/src/dgraph/numericDataTransition.h | 75 + panda/src/dgraph/pointerDataAttribute.I | 136 + panda/src/dgraph/pointerDataAttribute.h | 70 + panda/src/dgraph/pointerDataTransition.I | 182 + panda/src/dgraph/pointerDataTransition.h | 66 + panda/src/dgraph/test_dgraph.cxx | 186 + panda/src/dgraph/vec3DataAttribute.I | 25 + panda/src/dgraph/vec3DataAttribute.cxx | 34 + panda/src/dgraph/vec3DataAttribute.h | 58 + panda/src/dgraph/vec3DataTransition.I | 25 + panda/src/dgraph/vec3DataTransition.cxx | 34 + panda/src/dgraph/vec3DataTransition.h | 58 + panda/src/dgraph/vec4DataAttribute.I | 24 + panda/src/dgraph/vec4DataAttribute.cxx | 34 + panda/src/dgraph/vec4DataAttribute.h | 58 + panda/src/dgraph/vec4DataTransition.I | 24 + panda/src/dgraph/vec4DataTransition.cxx | 34 + panda/src/dgraph/vec4DataTransition.h | 58 + panda/src/dgraph/vectorDataAttribute.I | 129 + panda/src/dgraph/vectorDataAttribute.h | 67 + panda/src/dgraph/vectorDataTransition.I | 216 + panda/src/dgraph/vectorDataTransition.h | 78 + panda/src/display/Sources.pp | 52 + panda/src/display/config_display.cxx | 109 + panda/src/display/config_display.h | 37 + panda/src/display/displayRegion.I | 135 + panda/src/display/displayRegion.cxx | 176 + panda/src/display/displayRegion.h | 92 + panda/src/display/displayRegionStack.I | 48 + panda/src/display/displayRegionStack.h | 38 + panda/src/display/frameBufferStack.I | 46 + panda/src/display/frameBufferStack.h | 38 + panda/src/display/graphicsChannel.I | 26 + panda/src/display/graphicsChannel.cxx | 218 + panda/src/display/graphicsChannel.h | 94 + panda/src/display/graphicsLayer.I | 41 + panda/src/display/graphicsLayer.cxx | 230 + panda/src/display/graphicsLayer.h | 100 + panda/src/display/graphicsPipe.I | 23 + panda/src/display/graphicsPipe.N | 13 + panda/src/display/graphicsPipe.cxx | 324 + panda/src/display/graphicsPipe.h | 134 + panda/src/display/graphicsStateGuardian.I | 184 + panda/src/display/graphicsStateGuardian.N | 1 + panda/src/display/graphicsStateGuardian.cxx | 435 ++ panda/src/display/graphicsStateGuardian.h | 213 + panda/src/display/graphicsWindow.I | 273 + panda/src/display/graphicsWindow.N | 13 + panda/src/display/graphicsWindow.cxx | 523 ++ panda/src/display/graphicsWindow.h | 268 + panda/src/display/graphicsWindowInputDevice.I | 126 + .../src/display/graphicsWindowInputDevice.cxx | 163 + panda/src/display/graphicsWindowInputDevice.h | 90 + panda/src/display/hardwareChannel.I | 54 + panda/src/display/hardwareChannel.cxx | 55 + panda/src/display/hardwareChannel.h | 72 + panda/src/display/interactiveGraphicsPipe.I | 5 + panda/src/display/interactiveGraphicsPipe.cxx | 47 + panda/src/display/interactiveGraphicsPipe.h | 39 + .../src/display/noninteractiveGraphicsPipe.I | 4 + .../display/noninteractiveGraphicsPipe.cxx | 47 + .../src/display/noninteractiveGraphicsPipe.h | 39 + panda/src/display/pipeSpec.I | 65 + panda/src/display/pipeSpec.cxx | 36 + panda/src/display/pipeSpec.h | 47 + panda/src/display/renderBuffer.h | 48 + panda/src/display/savedFrameBuffer.I | 15 + panda/src/display/savedFrameBuffer.cxx | 8 + panda/src/display/savedFrameBuffer.h | 67 + panda/src/display/test_display.cxx | 10 + panda/src/display/textureContext.I | 16 + panda/src/display/textureContext.cxx | 8 + panda/src/display/textureContext.h | 58 + panda/src/doc/Panda_Whitepaper.doc | Bin 0 -> 581632 bytes panda/src/doc/Sources.pp | 2 + panda/src/doc/channel.flo | Bin 0 -> 16384 bytes panda/src/doc/channel.gif | Bin 0 -> 3671 bytes panda/src/doc/drawable.flo | Bin 0 -> 30208 bytes panda/src/doc/drawable.gif | Bin 0 -> 8714 bytes panda/src/doc/eggSyntax.txt | 1264 ++++ panda/src/doc/graph-ex.flo | Bin 0 -> 29184 bytes panda/src/doc/graph-ex.gif | Bin 0 -> 3659 bytes panda/src/doc/gsg.flo | Bin 0 -> 19456 bytes panda/src/doc/gsg.gif | Bin 0 -> 5882 bytes panda/src/doc/howto.install_panda_on_unix | 74 + panda/src/doc/howto.install_panda_on_windows | 187 + panda/src/doc/image-trinity.flo | Bin 0 -> 22016 bytes panda/src/doc/image-trinity.gif | Bin 0 -> 4951 bytes panda/src/doc/light.flo | Bin 0 -> 19968 bytes panda/src/doc/light.gif | Bin 0 -> 5911 bytes panda/src/doc/node.flo | Bin 0 -> 24064 bytes panda/src/doc/node.gif | Bin 0 -> 6518 bytes panda/src/doc/pipe.flo | Bin 0 -> 26624 bytes panda/src/doc/pipe.gif | Bin 0 -> 8998 bytes panda/src/doc/sampleClass.I | 52 + panda/src/doc/sampleClass.cxx | 63 + panda/src/doc/sampleClass.h | 104 + panda/src/doc/shader.flo | Bin 0 -> 31744 bytes panda/src/doc/shader.gif | Bin 0 -> 9484 bytes panda/src/doc/window.flo | Bin 0 -> 19456 bytes panda/src/doc/window.gif | Bin 0 -> 5044 bytes panda/src/doc/windowing.flo | Bin 0 -> 26624 bytes panda/src/doc/windowing.gif | Bin 0 -> 7572 bytes panda/src/downloader/Sources.pp | 30 + panda/src/downloader/asyncUtility.I | 24 + panda/src/downloader/asyncUtility.cxx | 136 + panda/src/downloader/asyncUtility.h | 53 + panda/src/downloader/config_downloader.cxx | 45 + panda/src/downloader/config_downloader.h | 26 + panda/src/downloader/decompressor.cxx | 319 + panda/src/downloader/decompressor.h | 52 + panda/src/downloader/downloadDb.I | 251 + panda/src/downloader/downloadDb.cxx | 920 +++ panda/src/downloader/downloadDb.h | 206 + panda/src/downloader/download_utils.cxx | 62 + panda/src/downloader/download_utils.h | 17 + panda/src/downloader/downloader.I | 48 + panda/src/downloader/downloader.cxx | 629 ++ panda/src/downloader/downloader.h | 105 + panda/src/downloader/extractor.cxx | 243 + panda/src/downloader/extractor.h | 46 + panda/src/downloader/patcher.cxx | 195 + panda/src/downloader/patcher.h | 46 + panda/src/downloader/zcompressor.I | 82 + panda/src/downloader/zcompressor.cxx | 215 + panda/src/downloader/zcompressor.h | 78 + panda/src/downloadertools/Sources.pp | 61 + panda/src/downloadertools/apply_patch.cxx | 26 + panda/src/downloadertools/build_patch.cxx | 27 + panda/src/downloadertools/check_adler.cxx | 15 + panda/src/downloadertools/check_crc.cxx | 15 + panda/src/downloadertools/multify.cxx | 65 + panda/src/downloadertools/pcompress.cxx | 101 + panda/src/downloadertools/pdecompress.cxx | 20 + panda/src/dxgsg/Sources.pp | 19 + panda/src/dxgsg/config_dxgsg.cxx | 66 + panda/src/dxgsg/config_dxgsg.h | 28 + panda/src/dxgsg/dxGraphicsStateGuardian.I | 888 +++ panda/src/dxgsg/dxGraphicsStateGuardian.cxx | 4460 +++++++++++++ panda/src/dxgsg/dxGraphicsStateGuardian.h | 442 ++ panda/src/dxgsg/dxSavedFrameBuffer.I | 26 + panda/src/dxgsg/dxSavedFrameBuffer.cxx | 8 + panda/src/dxgsg/dxSavedFrameBuffer.h | 51 + panda/src/dxgsg/dxTextureContext.I | 18 + panda/src/dxgsg/dxTextureContext.cxx | 868 +++ panda/src/dxgsg/dxTextureContext.h | 62 + panda/src/effects/Sources.pp | 17 + panda/src/effects/config_effects.cxx | 19 + panda/src/effects/config_effects.h | 14 + panda/src/effects/lensFlareNode.I | 71 + panda/src/effects/lensFlareNode.cxx | 726 ++ panda/src/effects/lensFlareNode.h | 162 + panda/src/egg/Sources.pp | 69 + panda/src/egg/config_egg.cxx | 80 + panda/src/egg/config_egg.h | 18 + panda/src/egg/eggAlphaMode.I | 116 + panda/src/egg/eggAlphaMode.cxx | 129 + panda/src/egg/eggAlphaMode.h | 78 + panda/src/egg/eggAnimData.I | 144 + panda/src/egg/eggAnimData.cxx | 8 + panda/src/egg/eggAnimData.h | 69 + panda/src/egg/eggAttributes.I | 153 + panda/src/egg/eggAttributes.cxx | 149 + panda/src/egg/eggAttributes.h | 85 + panda/src/egg/eggBin.cxx | 63 + panda/src/egg/eggBin.h | 52 + panda/src/egg/eggBinMaker.cxx | 267 + panda/src/egg/eggBinMaker.h | 299 + panda/src/egg/eggComment.I | 82 + panda/src/egg/eggComment.cxx | 26 + panda/src/egg/eggComment.h | 65 + panda/src/egg/eggCoordinateSystem.I | 49 + panda/src/egg/eggCoordinateSystem.cxx | 44 + panda/src/egg/eggCoordinateSystem.h | 58 + panda/src/egg/eggCurve.I | 92 + panda/src/egg/eggCurve.cxx | 53 + panda/src/egg/eggCurve.h | 66 + panda/src/egg/eggData.I | 78 + panda/src/egg/eggData.cxx | 267 + panda/src/egg/eggData.h | 69 + panda/src/egg/eggExternalReference.I | 38 + panda/src/egg/eggExternalReference.cxx | 67 + panda/src/egg/eggExternalReference.h | 53 + panda/src/egg/eggFilenameNode.I | 80 + panda/src/egg/eggFilenameNode.cxx | 18 + panda/src/egg/eggFilenameNode.h | 54 + panda/src/egg/eggGroup.I | 407 ++ panda/src/egg/eggGroup.cxx | 817 +++ panda/src/egg/eggGroup.h | 247 + panda/src/egg/eggGroupNode.I | 113 + panda/src/egg/eggGroupNode.cxx | 466 ++ panda/src/egg/eggGroupNode.h | 142 + panda/src/egg/eggMaterial.I | 52 + panda/src/egg/eggMaterial.cxx | 57 + panda/src/egg/eggMaterial.h | 38 + panda/src/egg/eggMiscFuncs.I | 5 + panda/src/egg/eggMiscFuncs.cxx | 140 + panda/src/egg/eggMiscFuncs.h | 65 + panda/src/egg/eggMorph.I | 103 + panda/src/egg/eggMorph.h | 58 + panda/src/egg/eggMorphList.I | 23 + panda/src/egg/eggMorphList.h | 36 + panda/src/egg/eggNamedObject.I | 37 + panda/src/egg/eggNamedObject.cxx | 31 + panda/src/egg/eggNamedObject.h | 55 + panda/src/egg/eggNode.I | 214 + panda/src/egg/eggNode.cxx | 141 + panda/src/egg/eggNode.h | 110 + panda/src/egg/eggNurbsCurve.I | 131 + panda/src/egg/eggNurbsCurve.cxx | 145 + panda/src/egg/eggNurbsCurve.h | 72 + panda/src/egg/eggNurbsSurface.I | 316 + panda/src/egg/eggNurbsSurface.cxx | 231 + panda/src/egg/eggNurbsSurface.h | 103 + panda/src/egg/eggObject.I | 36 + panda/src/egg/eggObject.cxx | 9 + panda/src/egg/eggObject.h | 49 + panda/src/egg/eggParameters.cxx | 38 + panda/src/egg/eggParameters.h | 54 + panda/src/egg/eggPoint.I | 35 + panda/src/egg/eggPoint.cxx | 39 + panda/src/egg/eggPoint.h | 50 + panda/src/egg/eggPolygon.I | 35 + panda/src/egg/eggPolygon.cxx | 39 + panda/src/egg/eggPolygon.h | 49 + panda/src/egg/eggPrimitive.I | 334 + panda/src/egg/eggPrimitive.cxx | 400 ++ panda/src/egg/eggPrimitive.h | 170 + panda/src/egg/eggSAnimData.I | 64 + panda/src/egg/eggSAnimData.cxx | 86 + panda/src/egg/eggSAnimData.h | 54 + panda/src/egg/eggSurface.I | 97 + panda/src/egg/eggSurface.cxx | 9 + panda/src/egg/eggSurface.h | 55 + panda/src/egg/eggSwitchCondition.cxx | 69 + panda/src/egg/eggSwitchCondition.h | 90 + panda/src/egg/eggTable.I | 61 + panda/src/egg/eggTable.cxx | 77 + panda/src/egg/eggTable.h | 69 + panda/src/egg/eggTexture.I | 327 + panda/src/egg/eggTexture.cxx | 554 ++ panda/src/egg/eggTexture.h | 171 + panda/src/egg/eggTextureCollection.I | 27 + panda/src/egg/eggTextureCollection.cxx | 464 ++ panda/src/egg/eggTextureCollection.h | 95 + panda/src/egg/eggUtilities.I | 121 + panda/src/egg/eggUtilities.cxx | 39 + panda/src/egg/eggUtilities.h | 62 + panda/src/egg/eggVertex.I | 276 + panda/src/egg/eggVertex.cxx | 327 + panda/src/egg/eggVertex.h | 148 + panda/src/egg/eggVertexPool.I | 54 + panda/src/egg/eggVertexPool.cxx | 284 + panda/src/egg/eggVertexPool.h | 126 + panda/src/egg/eggXfmAnimData.I | 186 + panda/src/egg/eggXfmAnimData.cxx | 255 + panda/src/egg/eggXfmAnimData.h | 84 + panda/src/egg/eggXfmSAnim.I | 147 + panda/src/egg/eggXfmSAnim.cxx | 403 ++ panda/src/egg/eggXfmSAnim.h | 87 + panda/src/egg/lexer.lxx | 697 ++ panda/src/egg/lexerDefs.h | 28 + panda/src/egg/parser.yxx | 2234 +++++++ panda/src/egg/parserDefs.h | 45 + panda/src/egg/test_egg.cxx | 29 + panda/src/egg2sg/Sources.pp | 31 + panda/src/egg2sg/animBundleMaker.cxx | 324 + panda/src/egg2sg/animBundleMaker.h | 62 + panda/src/egg2sg/characterMaker.cxx | 361 + panda/src/egg2sg/characterMaker.h | 77 + panda/src/egg2sg/computedVerticesMaker.I | 4 + panda/src/egg2sg/computedVerticesMaker.cxx | 497 ++ panda/src/egg2sg/computedVerticesMaker.h | 132 + .../src/egg2sg/computedVerticesMakerEntity.I | 71 + .../src/egg2sg/computedVerticesMakerEntity.h | 63 + panda/src/egg2sg/config_egg2sg.cxx | 61 + panda/src/egg2sg/config_egg2sg.h | 45 + panda/src/egg2sg/deferredArcProperty.cxx | 90 + panda/src/egg2sg/deferredArcProperty.h | 54 + panda/src/egg2sg/deferredArcTraverser.cxx | 43 + panda/src/egg2sg/deferredArcTraverser.h | 44 + panda/src/egg2sg/eggBinner.cxx | 70 + panda/src/egg2sg/eggBinner.h | 43 + panda/src/egg2sg/eggLoader.cxx | 1613 +++++ panda/src/egg2sg/eggLoader.h | 122 + panda/src/egg2sg/load_egg_file.cxx | 86 + panda/src/egg2sg/load_egg_file.h | 40 + panda/src/egg2sg/loaderFileTypeEgg.cxx | 66 + panda/src/egg2sg/loaderFileTypeEgg.h | 46 + panda/src/egg2sg/test_loader.cxx | 99 + panda/src/event/Sources.pp | 22 + panda/src/event/config_event.cxx | 24 + panda/src/event/config_event.h | 15 + panda/src/event/event.cxx | 128 + panda/src/event/event.h | 68 + panda/src/event/eventHandler.cxx | 146 + panda/src/event/eventHandler.h | 81 + panda/src/event/eventParameter.I | 227 + panda/src/event/eventParameter.cxx | 11 + panda/src/event/eventParameter.h | 119 + panda/src/event/eventQueue.I | 20 + panda/src/event/eventQueue.cxx | 81 + panda/src/event/eventQueue.h | 50 + panda/src/event/eventReceiver.cxx | 8 + panda/src/event/eventReceiver.h | 36 + panda/src/event/pt_Event.cxx | 11 + panda/src/event/pt_Event.h | 33 + panda/src/event/throw_event.I | 48 + panda/src/event/throw_event.h | 29 + panda/src/express/Sources.pp | 44 + panda/src/express/bigEndian.I | 91 + panda/src/express/bigEndian.cxx | 25 + panda/src/express/bigEndian.h | 60 + panda/src/express/buffer.I | 14 + panda/src/express/buffer.cxx | 44 + panda/src/express/buffer.h | 39 + panda/src/express/circBuffer.I | 98 + panda/src/express/circBuffer.h | 41 + panda/src/express/clockObject.I | 180 + panda/src/express/clockObject.cxx | 76 + panda/src/express/clockObject.h | 85 + panda/src/express/config_express.cxx | 50 + panda/src/express/config_express.h | 28 + panda/src/express/datagram.I | 395 ++ panda/src/express/datagram.cxx | 68 + panda/src/express/datagram.h | 111 + panda/src/express/datagramGenerator.I | 15 + panda/src/express/datagramGenerator.cxx | 16 + panda/src/express/datagramGenerator.h | 30 + panda/src/express/datagramIterator.cxx | 518 ++ panda/src/express/datagramIterator.h | 63 + panda/src/express/datagramSink.I | 14 + panda/src/express/datagramSink.cxx | 16 + panda/src/express/datagramSink.h | 29 + panda/src/express/get_config_path.cxx | 52 + panda/src/express/get_config_path.h | 34 + panda/src/express/indent.I | 56 + panda/src/express/indent.cxx | 18 + panda/src/express/indent.h | 42 + panda/src/express/littleEndian.I | 92 + panda/src/express/littleEndian.cxx | 25 + panda/src/express/littleEndian.h | 62 + panda/src/express/memoryUsage.I | 216 + panda/src/express/memoryUsage.cxx | 620 ++ panda/src/express/memoryUsage.h | 147 + panda/src/express/memoryUsagePointers.I | 59 + panda/src/express/memoryUsagePointers.cxx | 154 + panda/src/express/memoryUsagePointers.h | 83 + panda/src/express/multifile.I | 29 + panda/src/express/multifile.cxx | 631 ++ panda/src/express/multifile.h | 116 + panda/src/express/namable.I | 106 + panda/src/express/namable.cxx | 8 + panda/src/express/namable.h | 67 + panda/src/express/numeric_types.h | 73 + panda/src/express/patchfile.I | 41 + panda/src/express/patchfile.cxx | 635 ++ panda/src/express/patchfile.h | 86 + panda/src/express/pointerTo.I | 487 ++ panda/src/express/pointerTo.h | 218 + panda/src/express/referenceCount.I | 384 ++ panda/src/express/referenceCount.cxx | 8 + panda/src/express/referenceCount.h | 129 + panda/src/express/tokenBoard.I | 73 + panda/src/express/tokenBoard.h | 66 + panda/src/express/trueClock.I | 28 + panda/src/express/trueClock.cxx | 190 + panda/src/express/trueClock.h | 39 + panda/src/express/typeHandle.I | 473 ++ panda/src/express/typeHandle.cxx | 669 ++ panda/src/express/typeHandle.h | 571 ++ panda/src/express/typedReferenceCount.I | 39 + panda/src/express/typedReferenceCount.cxx | 7 + panda/src/express/typedReferenceCount.h | 52 + panda/src/express/typedef.h | 29 + panda/src/framework/Sources.pp | 16 + panda/src/framework/config_framework.cxx | 19 + panda/src/framework/config_framework.h | 19 + panda/src/framework/framework.cxx | 1177 ++++ panda/src/framework/framework.h | 44 + panda/src/glgsg/Sources.pp | 22 + panda/src/glgsg/config_glgsg.cxx | 63 + panda/src/glgsg/config_glgsg.h | 27 + panda/src/glgsg/glGraphicsStateGuardian.I | 1305 ++++ panda/src/glgsg/glGraphicsStateGuardian.cxx | 5025 ++++++++++++++ panda/src/glgsg/glGraphicsStateGuardian.h | 352 + panda/src/glgsg/glSavedFrameBuffer.I | 26 + panda/src/glgsg/glSavedFrameBuffer.cxx | 8 + panda/src/glgsg/glSavedFrameBuffer.h | 51 + panda/src/glgsg/glTextureContext.I | 18 + panda/src/glgsg/glTextureContext.cxx | 8 + panda/src/glgsg/glTextureContext.h | 55 + panda/src/glutdisplay/Sources.pp | 39 + panda/src/glutdisplay/config_glutdisplay.cxx | 22 + panda/src/glutdisplay/config_glutdisplay.h | 15 + panda/src/glutdisplay/glutGraphicsPipe.cxx | 70 + panda/src/glutdisplay/glutGraphicsPipe.h | 50 + panda/src/glutdisplay/glutGraphicsWindow.cxx | 307 + panda/src/glutdisplay/glutGraphicsWindow.h | 97 + panda/src/glutdisplay/test_glut.cxx | 68 + panda/src/glutdisplay/test_glut_win.cxx | 25 + panda/src/glxdisplay/Sources.pp | 21 + panda/src/glxdisplay/config_glxdisplay.cxx | 22 + panda/src/glxdisplay/config_glxdisplay.h | 15 + panda/src/glxdisplay/glxGraphicsPipe.cxx | 110 + panda/src/glxdisplay/glxGraphicsPipe.h | 68 + panda/src/glxdisplay/glxGraphicsWindow.I | 30 + panda/src/glxdisplay/glxGraphicsWindow.cxx | 1366 ++++ panda/src/glxdisplay/glxGraphicsWindow.h | 115 + panda/src/gobj/LOD.I | 119 + panda/src/gobj/LOD.cxx | 129 + panda/src/gobj/LOD.h | 104 + panda/src/gobj/Sources.pp | 45 + panda/src/gobj/config_gobj.cxx | 98 + panda/src/gobj/config_gobj.h | 26 + panda/src/gobj/drawable.cxx | 34 + panda/src/gobj/drawable.h | 77 + panda/src/gobj/fog.I | 111 + panda/src/gobj/fog.cxx | 104 + panda/src/gobj/fog.h | 103 + panda/src/gobj/geom.I | 126 + panda/src/gobj/geom.N | 12 + panda/src/gobj/geom.cxx | 750 +++ panda/src/gobj/geom.h | 257 + panda/src/gobj/geomLine.cxx | 97 + panda/src/gobj/geomLine.h | 74 + panda/src/gobj/geomLinestrip.cxx | 109 + panda/src/gobj/geomLinestrip.h | 73 + panda/src/gobj/geomPoint.cxx | 105 + panda/src/gobj/geomPoint.h | 74 + panda/src/gobj/geomPolygon.cxx | 80 + panda/src/gobj/geomPolygon.h | 62 + panda/src/gobj/geomQuad.cxx | 130 + panda/src/gobj/geomQuad.h | 60 + panda/src/gobj/geomSphere.cxx | 66 + panda/src/gobj/geomSphere.h | 75 + panda/src/gobj/geomSprite.I | 107 + panda/src/gobj/geomSprite.cxx | 146 + panda/src/gobj/geomSprite.h | 96 + panda/src/gobj/geomTri.cxx | 245 + panda/src/gobj/geomTri.h | 60 + panda/src/gobj/geomTrifan.cxx | 410 ++ panda/src/gobj/geomTrifan.h | 63 + panda/src/gobj/geomTristrip.cxx | 519 ++ panda/src/gobj/geomTristrip.h | 62 + panda/src/gobj/geomprimitives.h | 20 + panda/src/gobj/imageBuffer.cxx | 71 + panda/src/gobj/imageBuffer.h | 81 + panda/src/gobj/material.I | 145 + panda/src/gobj/material.cxx | 75 + panda/src/gobj/material.h | 94 + panda/src/gobj/orthoProjection.I | 28 + panda/src/gobj/orthoProjection.cxx | 129 + panda/src/gobj/orthoProjection.h | 58 + panda/src/gobj/perspectiveProjection.I | 31 + panda/src/gobj/perspectiveProjection.cxx | 168 + panda/src/gobj/perspectiveProjection.h | 62 + panda/src/gobj/pixelBuffer.I | 379 ++ panda/src/gobj/pixelBuffer.N | 3 + panda/src/gobj/pixelBuffer.cxx | 274 + panda/src/gobj/pixelBuffer.h | 158 + panda/src/gobj/projection.cxx | 52 + panda/src/gobj/projection.h | 55 + panda/src/gobj/test_gobj.cxx | 17 + panda/src/gobj/texture.I | 66 + panda/src/gobj/texture.N | 2 + panda/src/gobj/texture.cxx | 379 ++ panda/src/gobj/texture.h | 152 + panda/src/gobj/texturePool.I | 98 + panda/src/gobj/texturePool.cxx | 117 + panda/src/gobj/texturePool.h | 57 + panda/src/graph/Sources.pp | 100 + panda/src/graph/allAttributesWrapper.I | 231 + panda/src/graph/allAttributesWrapper.cxx | 73 + panda/src/graph/allAttributesWrapper.h | 74 + panda/src/graph/allTransitionsWrapper.I | 422 ++ panda/src/graph/allTransitionsWrapper.cxx | 35 + panda/src/graph/allTransitionsWrapper.h | 131 + panda/src/graph/bitMask32Transition.cxx | 12 + panda/src/graph/bitMask32Transition.h | 35 + panda/src/graph/bitMaskAttribute.I | 103 + panda/src/graph/bitMaskAttribute.h | 64 + panda/src/graph/bitMaskTransition.I | 230 + panda/src/graph/bitMaskTransition.h | 79 + panda/src/graph/boundedObject.I | 112 + panda/src/graph/boundedObject.N | 1 + panda/src/graph/boundedObject.cxx | 57 + panda/src/graph/boundedObject.h | 68 + panda/src/graph/config_graph.cxx | 67 + panda/src/graph/config_graph.h | 20 + panda/src/graph/dftraverser.I | 102 + panda/src/graph/dftraverser.h | 78 + panda/src/graph/graphReducer.cxx | 446 ++ panda/src/graph/graphReducer.h | 57 + panda/src/graph/immediateAttribute.I | 34 + panda/src/graph/immediateAttribute.cxx | 51 + panda/src/graph/immediateAttribute.h | 53 + panda/src/graph/immediateTransition.I | 34 + panda/src/graph/immediateTransition.cxx | 75 + panda/src/graph/immediateTransition.h | 78 + panda/src/graph/lmatrix4fTransition.cxx | 12 + panda/src/graph/lmatrix4fTransition.h | 35 + panda/src/graph/matrixAttribute.I | 103 + panda/src/graph/matrixAttribute.h | 64 + panda/src/graph/matrixTransition.I | 229 + panda/src/graph/matrixTransition.h | 87 + panda/src/graph/multiAttribute.I | 330 + panda/src/graph/multiAttribute.h | 105 + panda/src/graph/multiNodeAttribute.I | 34 + panda/src/graph/multiNodeAttribute.cxx | 8 + panda/src/graph/multiNodeAttribute.h | 58 + panda/src/graph/multiNodeTransition.I | 34 + panda/src/graph/multiNodeTransition.cxx | 8 + panda/src/graph/multiNodeTransition.h | 66 + panda/src/graph/multiTransition.I | 561 ++ panda/src/graph/multiTransition.h | 128 + panda/src/graph/multiTransitionHelpers.I | 436 ++ panda/src/graph/multiTransitionHelpers.h | 127 + panda/src/graph/namedNode.I | 38 + panda/src/graph/namedNode.cxx | 98 + panda/src/graph/namedNode.h | 68 + panda/src/graph/node.I | 23 + panda/src/graph/node.cxx | 509 ++ panda/src/graph/node.h | 176 + panda/src/graph/nodeAttribute.I | 149 + panda/src/graph/nodeAttribute.N | 1 + panda/src/graph/nodeAttribute.cxx | 42 + panda/src/graph/nodeAttribute.h | 103 + panda/src/graph/nodeAttributeWrapper.I | 132 + panda/src/graph/nodeAttributeWrapper.cxx | 60 + panda/src/graph/nodeAttributeWrapper.h | 61 + panda/src/graph/nodeAttributes.I | 125 + panda/src/graph/nodeAttributes.N | 2 + panda/src/graph/nodeAttributes.cxx | 251 + panda/src/graph/nodeAttributes.h | 89 + panda/src/graph/nodeRelation.I | 392 ++ panda/src/graph/nodeRelation.N | 2 + panda/src/graph/nodeRelation.cxx | 757 +++ panda/src/graph/nodeRelation.h | 235 + panda/src/graph/nodeTransition.I | 180 + panda/src/graph/nodeTransition.N | 1 + panda/src/graph/nodeTransition.cxx | 120 + panda/src/graph/nodeTransition.h | 132 + panda/src/graph/nodeTransitionCache.I | 52 + panda/src/graph/nodeTransitionCache.cxx | 495 ++ panda/src/graph/nodeTransitionCache.h | 110 + panda/src/graph/nodeTransitionCacheEntry.I | 343 + panda/src/graph/nodeTransitionCacheEntry.cxx | 33 + panda/src/graph/nodeTransitionCacheEntry.h | 92 + panda/src/graph/nodeTransitionWrapper.I | 266 + panda/src/graph/nodeTransitionWrapper.cxx | 50 + panda/src/graph/nodeTransitionWrapper.h | 96 + panda/src/graph/nodeTransitions.I | 81 + panda/src/graph/nodeTransitions.cxx | 278 + panda/src/graph/nodeTransitions.h | 91 + panda/src/graph/nullAttributeWrapper.I | 97 + panda/src/graph/nullAttributeWrapper.cxx | 6 + panda/src/graph/nullAttributeWrapper.h | 50 + panda/src/graph/nullLevelState.cxx | 6 + panda/src/graph/nullLevelState.h | 25 + panda/src/graph/nullTransitionWrapper.I | 196 + panda/src/graph/nullTransitionWrapper.cxx | 7 + panda/src/graph/nullTransitionWrapper.h | 86 + panda/src/graph/onAttribute.I | 34 + panda/src/graph/onAttribute.cxx | 47 + panda/src/graph/onAttribute.h | 58 + panda/src/graph/onOffAttribute.I | 89 + panda/src/graph/onOffAttribute.cxx | 105 + panda/src/graph/onOffAttribute.h | 67 + panda/src/graph/onOffTransition.I | 117 + panda/src/graph/onOffTransition.cxx | 263 + panda/src/graph/onOffTransition.h | 100 + panda/src/graph/onTransition.I | 34 + panda/src/graph/onTransition.cxx | 120 + panda/src/graph/onTransition.h | 77 + panda/src/graph/pointerNameClass.h | 27 + panda/src/graph/pt_NamedNode.N | 3 + panda/src/graph/pt_NamedNode.cxx | 11 + panda/src/graph/pt_NamedNode.h | 34 + panda/src/graph/pt_Node.N | 3 + panda/src/graph/pt_Node.cxx | 12 + panda/src/graph/pt_Node.h | 37 + panda/src/graph/setTransitionHelpers.I | 1060 +++ panda/src/graph/setTransitionHelpers.h | 125 + panda/src/graph/test_graph.cxx | 746 +++ panda/src/graph/test_graphRead.cxx | 49 + panda/src/graph/test_graphWrite.cxx | 68 + panda/src/graph/transitionDirection.h | 16 + panda/src/graph/traverserVisitor.I | 57 + panda/src/graph/traverserVisitor.h | 41 + panda/src/graph/vector_PT_Node.cxx | 11 + panda/src/graph/vector_PT_Node.h | 33 + panda/src/graph/wrt.I | 532 ++ panda/src/graph/wrt.h | 123 + panda/src/grutil/Sources.pp | 18 + panda/src/grutil/config_grutil.cxx | 15 + panda/src/grutil/config_grutil.h | 16 + panda/src/grutil/lineSegs.I | 225 + panda/src/grutil/lineSegs.cxx | 215 + panda/src/grutil/lineSegs.h | 86 + panda/src/gsgbase/Sources.pp | 28 + panda/src/gsgbase/config_gsgbase.cxx | 15 + panda/src/gsgbase/config_gsgbase.h | 11 + .../src/gsgbase/graphicsStateGuardianBase.cxx | 8 + panda/src/gsgbase/graphicsStateGuardianBase.h | 189 + panda/src/gsgbase/pptmpNGk8yj | 67 + panda/src/gsgbase/test_gsgbase.cxx | 10 + panda/src/gsgmisc/Sources.pp | 15 + panda/src/gsgmisc/geomIssuer.I | 59 + panda/src/gsgmisc/geomIssuer.cxx | 92 + panda/src/gsgmisc/geomIssuer.h | 74 + panda/src/ipc/Sources.pp | 86 + panda/src/ipc/ipc.cxx | 77 + panda/src/ipc/ipc_atomics.h | 25 + panda/src/ipc/ipc_condition.h | 39 + panda/src/ipc/ipc_file.I | 96 + panda/src/ipc/ipc_file.h | 63 + panda/src/ipc/ipc_library.h | 28 + panda/src/ipc/ipc_mach_traits.h | 284 + panda/src/ipc/ipc_mutex.h | 40 + panda/src/ipc/ipc_nspr_traits.h | 331 + panda/src/ipc/ipc_nt_traits.cxx | 17 + panda/src/ipc/ipc_nt_traits.h | 556 ++ panda/src/ipc/ipc_posix_traits.h | 444 ++ panda/src/ipc/ipc_ps2_traits.h | 474 ++ panda/src/ipc/ipc_semaphore.h | 44 + panda/src/ipc/ipc_solaris_traits.h | 236 + panda/src/ipc/ipc_thread.h | 273 + panda/src/ipc/ipc_traits.cxx | 255 + panda/src/ipc/ipc_traits.h | 216 + panda/src/ipc/loom.cxx | 72 + panda/src/ipc/loom.h | 61 + panda/src/ipc/loom_internal.h | 75 + panda/src/ipc/loom_main.cxx | 84 + panda/src/ipc/loom_test1.cxx | 150 + panda/src/ipc/loom_test2.cxx | 41 + panda/src/ipc/test_diners.cxx | 139 + panda/src/ipc/test_file.cxx | 67 + panda/src/ipc/test_priority.cxx | 81 + panda/src/ipc/test_prodcons.cxx | 93 + panda/src/ipc/test_thread.cxx | 90 + panda/src/ipc/test_threaddata.cxx | 44 + panda/src/lerp/Sources.pp | 18 + panda/src/lerp/config_lerp.cxx | 36 + panda/src/lerp/config_lerp.h | 15 + panda/src/lerp/lerp.cxx | 182 + panda/src/lerp/lerp.h | 111 + panda/src/lerp/lerpblend.cxx | 84 + panda/src/lerp/lerpblend.h | 152 + panda/src/lerp/lerpchans.h | 90 + panda/src/lerp/lerpfunctor.cxx | 49 + panda/src/lerp/lerpfunctor.h | 179 + panda/src/light/Sources.pp | 27 + panda/src/light/ambientLight.cxx | 50 + panda/src/light/ambientLight.h | 72 + panda/src/light/config_light.cxx | 28 + panda/src/light/config_light.h | 14 + panda/src/light/directionalLight.I | 24 + panda/src/light/directionalLight.cxx | 52 + panda/src/light/directionalLight.h | 81 + panda/src/light/light.cxx | 14 + panda/src/light/light.h | 69 + panda/src/light/lightAttribute.I | 13 + panda/src/light/lightAttribute.cxx | 79 + panda/src/light/lightAttribute.h | 63 + panda/src/light/lightNameClass.h | 27 + panda/src/light/lightTransition.I | 26 + panda/src/light/lightTransition.cxx | 65 + panda/src/light/lightTransition.h | 69 + panda/src/light/pointLight.I | 84 + panda/src/light/pointLight.cxx | 54 + panda/src/light/pointLight.h | 93 + panda/src/light/pt_Light.cxx | 11 + panda/src/light/pt_Light.h | 34 + panda/src/light/spotlight.I | 104 + panda/src/light/spotlight.cxx | 262 + panda/src/light/spotlight.h | 105 + panda/src/light/vector_PT_Light.cxx | 11 + panda/src/light/vector_PT_Light.h | 32 + panda/src/linmath/Sources.pp | 47 + panda/src/linmath/cast_to_double.I | 49 + panda/src/linmath/cast_to_double.h | 32 + panda/src/linmath/cast_to_float.I | 49 + panda/src/linmath/cast_to_float.h | 32 + panda/src/linmath/cmath.I | 52 + panda/src/linmath/cmath.h | 34 + panda/src/linmath/compose_matrix.I | 77 + panda/src/linmath/compose_matrix.cxx | 398 ++ panda/src/linmath/compose_matrix.h | 95 + panda/src/linmath/config_linmath.cxx | 57 + panda/src/linmath/config_linmath.h | 14 + panda/src/linmath/coordinateSystem.cxx | 94 + panda/src/linmath/coordinateSystem.h | 42 + panda/src/linmath/deg_2_rad.h | 17 + panda/src/linmath/ioPtaDatagramLinMath.I | 45 + panda/src/linmath/ioPtaDatagramLinMath.cxx | 11 + panda/src/linmath/ioPtaDatagramLinMath.h | 62 + panda/src/linmath/lmat_ops.I | 66 + panda/src/linmath/lmat_ops.h | 81 + panda/src/linmath/lmatrix.cxx | 13 + panda/src/linmath/lmatrix.h | 25 + panda/src/linmath/lmatrix3.I | 1109 ++++ panda/src/linmath/lmatrix3.h | 193 + panda/src/linmath/lmatrix4.I | 1506 +++++ panda/src/linmath/lmatrix4.h | 188 + panda/src/linmath/lorientation.I | 134 + panda/src/linmath/lorientation.h | 47 + panda/src/linmath/lpoint2.I | 225 + panda/src/linmath/lpoint2.h | 69 + panda/src/linmath/lpoint3.I | 278 + panda/src/linmath/lpoint3.h | 85 + panda/src/linmath/lpoint4.I | 247 + panda/src/linmath/lpoint4.h | 71 + panda/src/linmath/lquaternion.I | 550 ++ panda/src/linmath/lquaternion.h | 113 + panda/src/linmath/lrotation.I | 152 + panda/src/linmath/lrotation.h | 48 + panda/src/linmath/luse.I | 4 + panda/src/linmath/luse.N | 75 + panda/src/linmath/luse.cxx | 14 + panda/src/linmath/luse.h | 142 + panda/src/linmath/lvec2_ops.I | 91 + panda/src/linmath/lvec2_ops.h | 54 + panda/src/linmath/lvec3_ops.I | 92 + panda/src/linmath/lvec3_ops.h | 64 + panda/src/linmath/lvec4_ops.I | 71 + panda/src/linmath/lvec4_ops.h | 54 + panda/src/linmath/lvecBase2.I | 604 ++ panda/src/linmath/lvecBase2.h | 133 + panda/src/linmath/lvecBase3.I | 680 ++ panda/src/linmath/lvecBase3.h | 139 + panda/src/linmath/lvecBase4.I | 710 ++ panda/src/linmath/lvecBase4.h | 139 + panda/src/linmath/lvector2.I | 262 + panda/src/linmath/lvector2.h | 69 + panda/src/linmath/lvector3.I | 403 ++ panda/src/linmath/lvector3.h | 93 + panda/src/linmath/lvector4.I | 285 + panda/src/linmath/lvector4.h | 70 + panda/src/linmath/mathNumbers.cxx | 9 + panda/src/linmath/mathNumbers.h | 16 + panda/src/linmath/nearly_zero.h | 58 + panda/src/linmath/pta_Colorf.cxx | 11 + panda/src/linmath/pta_Colorf.h | 37 + panda/src/linmath/pta_Normalf.cxx | 11 + panda/src/linmath/pta_Normalf.h | 37 + panda/src/linmath/pta_TexCoordf.cxx | 11 + panda/src/linmath/pta_TexCoordf.h | 37 + panda/src/linmath/pta_Vertexf.cxx | 11 + panda/src/linmath/pta_Vertexf.h | 37 + panda/src/linmath/test_math.cxx | 135 + panda/src/linmath/vector_Colorf.cxx | 11 + panda/src/linmath/vector_Colorf.h | 32 + panda/src/linmath/vector_LPoint2f.cxx | 11 + panda/src/linmath/vector_LPoint2f.h | 32 + panda/src/linmath/vector_Normalf.cxx | 11 + panda/src/linmath/vector_Normalf.h | 32 + panda/src/linmath/vector_TexCoordf.h | 20 + panda/src/linmath/vector_Vertexf.cxx | 11 + panda/src/linmath/vector_Vertexf.h | 32 + panda/src/loader/Sources.pp | 23 + panda/src/loader/bamFile.I | 31 + panda/src/loader/bamFile.cxx | 247 + panda/src/loader/bamFile.h | 53 + panda/src/loader/config_loader.cxx | 42 + panda/src/loader/config_loader.h | 23 + panda/src/loader/loader.I | 17 + panda/src/loader/loader.cxx | 465 ++ panda/src/loader/loader.h | 56 + panda/src/loader/loaderFileType.cxx | 40 + panda/src/loader/loaderFileType.h | 55 + panda/src/loader/loaderFileTypeBam.cxx | 106 + panda/src/loader/loaderFileTypeBam.h | 46 + panda/src/loader/loaderFileTypeRegistry.cxx | 138 + panda/src/loader/loaderFileTypeRegistry.h | 51 + panda/src/loader/modelPool.I | 93 + panda/src/loader/modelPool.cxx | 105 + panda/src/loader/modelPool.h | 65 + panda/src/mathutil/Sources.pp | 41 + panda/src/mathutil/boundingHexahedron.I | 30 + panda/src/mathutil/boundingHexahedron.cxx | 336 + panda/src/mathutil/boundingHexahedron.h | 109 + panda/src/mathutil/boundingLine.I | 33 + panda/src/mathutil/boundingLine.cxx | 114 + panda/src/mathutil/boundingLine.h | 78 + panda/src/mathutil/boundingSphere.I | 33 + panda/src/mathutil/boundingSphere.cxx | 452 ++ panda/src/mathutil/boundingSphere.h | 91 + panda/src/mathutil/boundingVolume.I | 112 + panda/src/mathutil/boundingVolume.cxx | 191 + panda/src/mathutil/boundingVolume.h | 147 + panda/src/mathutil/config_mathutil.cxx | 28 + panda/src/mathutil/config_mathutil.h | 16 + panda/src/mathutil/finiteBoundingVolume.cxx | 8 + panda/src/mathutil/finiteBoundingVolume.h | 63 + panda/src/mathutil/frustum.I | 266 + panda/src/mathutil/frustum.h | 64 + panda/src/mathutil/geometricBoundingVolume.I | 108 + .../src/mathutil/geometricBoundingVolume.cxx | 30 + panda/src/mathutil/geometricBoundingVolume.h | 75 + panda/src/mathutil/look_at.I | 79 + panda/src/mathutil/look_at.cxx | 338 + panda/src/mathutil/look_at.h | 92 + panda/src/mathutil/mathHelpers.I | 14 + panda/src/mathutil/mathHelpers.h | 20 + panda/src/mathutil/mathutil.h | 23 + panda/src/mathutil/omniBoundingVolume.I | 14 + panda/src/mathutil/omniBoundingVolume.cxx | 187 + panda/src/mathutil/omniBoundingVolume.h | 74 + panda/src/mathutil/plane.I | 306 + panda/src/mathutil/plane.N | 2 + panda/src/mathutil/plane.cxx | 14 + panda/src/mathutil/plane.h | 88 + panda/src/mathutil/rotate_to.cxx | 92 + panda/src/mathutil/rotate_to.h | 31 + panda/src/mathutil/test_mathutil.cxx | 53 + panda/src/net/Sources.pp | 88 + panda/src/net/config_net.cxx | 40 + panda/src/net/config_net.h | 20 + panda/src/net/connection.cxx | 150 + panda/src/net/connection.h | 46 + panda/src/net/connectionListener.cxx | 69 + panda/src/net/connectionListener.h | 41 + panda/src/net/connectionManager.N | 1 + panda/src/net/connectionManager.cxx | 316 + panda/src/net/connectionManager.h | 77 + panda/src/net/connectionReader.cxx | 834 +++ panda/src/net/connectionReader.h | 147 + panda/src/net/connectionWriter.cxx | 236 + panda/src/net/connectionWriter.h | 71 + panda/src/net/datagramQueue.cxx | 163 + panda/src/net/datagramQueue.h | 45 + panda/src/net/datagramTCPHeader.cxx | 101 + panda/src/net/datagramTCPHeader.h | 47 + panda/src/net/datagramUDPHeader.cxx | 129 + panda/src/net/datagramUDPHeader.h | 49 + panda/src/net/datagram_ui.cxx | 115 + panda/src/net/datagram_ui.h | 28 + panda/src/net/netAddress.cxx | 168 + panda/src/net/netAddress.h | 47 + panda/src/net/netDatagram.I | 34 + panda/src/net/netDatagram.cxx | 132 + panda/src/net/netDatagram.h | 84 + panda/src/net/pprerror.cxx | 209 + panda/src/net/pprerror.h | 14 + panda/src/net/queuedConnectionListener.I | 34 + panda/src/net/queuedConnectionListener.cxx | 118 + panda/src/net/queuedConnectionListener.h | 62 + panda/src/net/queuedConnectionManager.cxx | 96 + panda/src/net/queuedConnectionManager.h | 45 + panda/src/net/queuedConnectionReader.cxx | 102 + panda/src/net/queuedConnectionReader.h | 44 + panda/src/net/queuedReturn.I | 171 + panda/src/net/queuedReturn.h | 52 + panda/src/net/recentConnectionReader.cxx | 119 + panda/src/net/recentConnectionReader.h | 47 + panda/src/net/test_datagram.cxx | 44 + panda/src/net/test_spam_client.cxx | 106 + panda/src/net/test_spam_server.cxx | 114 + panda/src/net/test_tcp_client.cxx | 89 + panda/src/net/test_tcp_server.cxx | 117 + panda/src/net/test_udp.cxx | 86 + panda/src/pandabase/Sources.pp | 12 + panda/src/pandabase/libpandabase.in | 30 + panda/src/pandabase/pandabase.cxx | 6 + panda/src/pandabase/pandabase.h | 18 + panda/src/pandabase/pandasymbols.h | 129 + panda/src/particlesystem/Sources.pp | 61 + panda/src/particlesystem/baseParticle.I | 36 + panda/src/particlesystem/baseParticle.cxx | 47 + panda/src/particlesystem/baseParticle.h | 56 + .../src/particlesystem/baseParticleEmitter.I | 56 + .../particlesystem/baseParticleEmitter.cxx | 53 + .../src/particlesystem/baseParticleEmitter.h | 47 + .../src/particlesystem/baseParticleFactory.I | 121 + .../particlesystem/baseParticleFactory.cxx | 75 + .../src/particlesystem/baseParticleFactory.h | 61 + .../src/particlesystem/baseParticleRenderer.I | 98 + .../particlesystem/baseParticleRenderer.cxx | 86 + .../src/particlesystem/baseParticleRenderer.h | 98 + panda/src/particlesystem/boxEmitter.I | 26 + panda/src/particlesystem/boxEmitter.cxx | 73 + panda/src/particlesystem/boxEmitter.h | 35 + .../particlesystem/config_particlesystem.cxx | 16 + .../particlesystem/config_particlesystem.h | 16 + panda/src/particlesystem/discEmitter.I | 92 + panda/src/particlesystem/discEmitter.cxx | 97 + panda/src/particlesystem/discEmitter.h | 45 + panda/src/particlesystem/emitters.h | 20 + .../src/particlesystem/geomParticleRenderer.I | 45 + .../particlesystem/geomParticleRenderer.cxx | 178 + .../src/particlesystem/geomParticleRenderer.h | 57 + panda/src/particlesystem/lineEmitter.I | 27 + panda/src/particlesystem/lineEmitter.cxx | 71 + panda/src/particlesystem/lineEmitter.h | 36 + panda/src/particlesystem/orientedParticle.I | 22 + panda/src/particlesystem/orientedParticle.cxx | 75 + panda/src/particlesystem/orientedParticle.h | 36 + .../particlesystem/orientedParticleFactory.I | 40 + .../orientedParticleFactory.cxx | 58 + .../particlesystem/orientedParticleFactory.h | 39 + panda/src/particlesystem/particleSystem.I | 372 ++ panda/src/particlesystem/particleSystem.cxx | 394 ++ panda/src/particlesystem/particleSystem.h | 153 + .../particlesystem/particleSystemManager.I | 45 + .../particlesystem/particleSystemManager.cxx | 106 + .../particlesystem/particleSystemManager.h | 45 + panda/src/particlesystem/particlefactories.h | 13 + panda/src/particlesystem/particles.h | 12 + panda/src/particlesystem/particlesystems.txt | 158 + panda/src/particlesystem/pointEmitter.I | 24 + panda/src/particlesystem/pointEmitter.cxx | 61 + panda/src/particlesystem/pointEmitter.h | 36 + panda/src/particlesystem/pointParticle.cxx | 74 + panda/src/particlesystem/pointParticle.h | 29 + .../particlesystem/pointParticleFactory.cxx | 58 + .../src/particlesystem/pointParticleFactory.h | 27 + .../particlesystem/pointParticleRenderer.I | 105 + .../particlesystem/pointParticleRenderer.cxx | 262 + .../particlesystem/pointParticleRenderer.h | 87 + panda/src/particlesystem/rectangleEmitter.I | 26 + panda/src/particlesystem/rectangleEmitter.cxx | 71 + panda/src/particlesystem/rectangleEmitter.h | 36 + panda/src/particlesystem/ringEmitter.I | 37 + panda/src/particlesystem/ringEmitter.cxx | 75 + panda/src/particlesystem/ringEmitter.h | 38 + .../particlesystem/sparkleParticleRenderer.I | 108 + .../sparkleParticleRenderer.cxx | 250 + .../particlesystem/sparkleParticleRenderer.h | 85 + .../src/particlesystem/sphereSurfaceEmitter.I | 21 + .../particlesystem/sphereSurfaceEmitter.cxx | 65 + .../src/particlesystem/sphereSurfaceEmitter.h | 34 + .../src/particlesystem/sphereVolumeEmitter.I | 21 + .../particlesystem/sphereVolumeEmitter.cxx | 74 + .../src/particlesystem/sphereVolumeEmitter.h | 33 + .../particlesystem/spriteParticleRenderer.I | 131 + .../particlesystem/spriteParticleRenderer.cxx | 280 + .../particlesystem/spriteParticleRenderer.h | 89 + panda/src/particlesystem/tangentRingEmitter.I | 22 + .../src/particlesystem/tangentRingEmitter.cxx | 62 + panda/src/particlesystem/tangentRingEmitter.h | 35 + panda/src/particlesystem/zSpinParticle.I | 32 + panda/src/particlesystem/zSpinParticle.cxx | 101 + panda/src/particlesystem/zSpinParticle.h | 47 + .../src/particlesystem/zSpinParticleFactory.I | 59 + .../particlesystem/zSpinParticleFactory.cxx | 68 + .../src/particlesystem/zSpinParticleFactory.h | 41 + panda/src/physics/Sources.pp | 61 + panda/src/physics/actorNode.I | 22 + panda/src/physics/actorNode.cxx | 102 + panda/src/physics/actorNode.h | 65 + panda/src/physics/angularEulerIntegrator.cxx | 146 + panda/src/physics/angularEulerIntegrator.h | 27 + panda/src/physics/angularForce.cxx | 48 + panda/src/physics/angularForce.h | 47 + panda/src/physics/angularIntegrator.cxx | 40 + panda/src/physics/angularIntegrator.h | 37 + panda/src/physics/angularVectorForce.I | 22 + panda/src/physics/angularVectorForce.cxx | 69 + panda/src/physics/angularVectorForce.h | 52 + panda/src/physics/baseForce.I | 31 + panda/src/physics/baseForce.cxx | 39 + panda/src/physics/baseForce.h | 65 + panda/src/physics/baseIntegrator.I | 22 + panda/src/physics/baseIntegrator.cxx | 130 + panda/src/physics/baseIntegrator.h | 53 + panda/src/physics/config_physics.cxx | 47 + panda/src/physics/config_physics.h | 16 + panda/src/physics/forceNode.I | 43 + panda/src/physics/forceNode.cxx | 92 + panda/src/physics/forceNode.h | 63 + panda/src/physics/forces.h | 20 + panda/src/physics/linearCylinderVortexForce.I | 58 + .../src/physics/linearCylinderVortexForce.cxx | 100 + panda/src/physics/linearCylinderVortexForce.h | 68 + panda/src/physics/linearDistanceForce.I | 80 + panda/src/physics/linearDistanceForce.cxx | 41 + panda/src/physics/linearDistanceForce.h | 70 + panda/src/physics/linearEulerIntegrator.cxx | 168 + panda/src/physics/linearEulerIntegrator.h | 27 + panda/src/physics/linearForce.I | 51 + panda/src/physics/linearForce.cxx | 69 + panda/src/physics/linearForce.h | 66 + panda/src/physics/linearFrictionForce.I | 22 + panda/src/physics/linearFrictionForce.cxx | 73 + panda/src/physics/linearFrictionForce.h | 50 + panda/src/physics/linearIntegrator.cxx | 45 + panda/src/physics/linearIntegrator.h | 38 + panda/src/physics/linearJitterForce.cxx | 57 + panda/src/physics/linearJitterForce.h | 44 + panda/src/physics/linearNoiseForce.I | 75 + panda/src/physics/linearNoiseForce.cxx | 138 + panda/src/physics/linearNoiseForce.h | 63 + panda/src/physics/linearRandomForce.I | 21 + panda/src/physics/linearRandomForce.cxx | 50 + panda/src/physics/linearRandomForce.h | 52 + panda/src/physics/linearSinkForce.cxx | 69 + panda/src/physics/linearSinkForce.h | 45 + panda/src/physics/linearSourceForce.cxx | 69 + panda/src/physics/linearSourceForce.h | 45 + panda/src/physics/linearUserDefinedForce.I | 13 + panda/src/physics/linearUserDefinedForce.cxx | 59 + panda/src/physics/linearUserDefinedForce.h | 57 + panda/src/physics/linearVectorForce.I | 24 + panda/src/physics/linearVectorForce.cxx | 75 + panda/src/physics/linearVectorForce.h | 53 + panda/src/physics/physical.I | 161 + panda/src/physics/physical.cxx | 112 + panda/src/physics/physical.h | 93 + panda/src/physics/physicalNode.I | 43 + panda/src/physics/physicalNode.cxx | 92 + panda/src/physics/physicalNode.h | 64 + panda/src/physics/physics.txt | 42 + panda/src/physics/physicsManager.I | 96 + panda/src/physics/physicsManager.cxx | 119 + panda/src/physics/physicsManager.h | 68 + panda/src/physics/physicsObject.I | 184 + panda/src/physics/physicsObject.cxx | 98 + panda/src/physics/physicsObject.h | 93 + panda/src/physics/test_physics.cxx | 83 + panda/src/pnm/Sources.pp | 17 + panda/src/pnm/bitio.c | 202 + panda/src/pnm/bitio.h | 85 + panda/src/pnm/compile.h | 4 + panda/src/pnm/libpbm.h | 17 + panda/src/pnm/libpbm1.c | 1549 +++++ panda/src/pnm/libpbm2.c | 134 + panda/src/pnm/libpbm3.c | 123 + panda/src/pnm/libpbm4.c | 80 + panda/src/pnm/libpbm5.c | 1090 +++ panda/src/pnm/libpgm.h | 11 + panda/src/pnm/libpgm1.c | 149 + panda/src/pnm/libpgm2.c | 161 + panda/src/pnm/libpnm1.c | 132 + panda/src/pnm/libpnm2.c | 121 + panda/src/pnm/libpnm3.c | 386 ++ panda/src/pnm/libpnm4.c | 453 ++ panda/src/pnm/libppm.h | 11 + panda/src/pnm/libppm1.c | 195 + panda/src/pnm/libppm2.c | 192 + panda/src/pnm/libppm3.c | 262 + panda/src/pnm/libppm4.c | 328 + panda/src/pnm/libppm5.c | 696 ++ panda/src/pnm/pbm.h | 49 + panda/src/pnm/pbmfont.h | 27 + panda/src/pnm/pbmplus.h | 289 + panda/src/pnm/pgm.h | 57 + panda/src/pnm/pnm.h | 48 + panda/src/pnm/ppm.h | 107 + panda/src/pnm/ppmcmap.h | 45 + panda/src/pnm/ppmdraw.h | 103 + panda/src/pnm/rast.h | 111 + panda/src/pnm/version.h | 4 + panda/src/pnmimage/Sources.pp | 21 + panda/src/pnmimage/config_pnmimage.cxx | 16 + panda/src/pnmimage/config_pnmimage.h | 14 + panda/src/pnmimage/pnm-image-alias.cxx | 178 + panda/src/pnmimage/pnm-image-filter-core.cxx | 90 + panda/src/pnmimage/pnm-image-filter.cxx | 592 ++ panda/src/pnmimage/pnm-image-img.cxx | 103 + panda/src/pnmimage/pnm-image-radiance.cxx | 156 + panda/src/pnmimage/pnm-image-readbmp.cxx | 518 ++ panda/src/pnmimage/pnm-image-readsgi.cxx | 434 ++ panda/src/pnmimage/pnm-image-readyuv.cxx | 114 + panda/src/pnmimage/pnm-image-softimage.cxx | 539 ++ panda/src/pnmimage/pnm-image-tiff.cxx | 567 ++ panda/src/pnmimage/pnm-image-writebmp.cxx | 609 ++ panda/src/pnmimage/pnm-image-writesgi.cxx | 310 + panda/src/pnmimage/pnm-image-writeyuv.cxx | 96 + panda/src/pnmimage/pnmFileType.cxx | 154 + panda/src/pnmimage/pnmFileType.h | 69 + panda/src/pnmimage/pnmFileTypeRegistry.cxx | 232 + panda/src/pnmimage/pnmFileTypeRegistry.h | 56 + panda/src/pnmimage/pnmImage.I | 757 +++ panda/src/pnmimage/pnmImage.cxx | 561 ++ panda/src/pnmimage/pnmImage.h | 187 + panda/src/pnmimage/pnmImageHeader.I | 191 + panda/src/pnmimage/pnmImageHeader.cxx | 348 + panda/src/pnmimage/pnmImageHeader.h | 99 + panda/src/pnmimage/pnmReader.I | 40 + panda/src/pnmimage/pnmReader.cxx | 91 + panda/src/pnmimage/pnmReader.h | 48 + panda/src/pnmimage/pnmReaderTypes.h | 159 + panda/src/pnmimage/pnmWriter.I | 95 + panda/src/pnmimage/pnmWriter.cxx | 128 + panda/src/pnmimage/pnmWriter.h | 57 + panda/src/pnmimage/pnmWriterTypes.h | 171 + panda/src/pnmimage/pnmimage_base.h | 52 + panda/src/pnmimage/pnmminmax.h | 52 + panda/src/pnmimagetypes/Sources.pp | 21 + panda/src/pnmimagetypes/bmp.h | 198 + panda/src/pnmimagetypes/color.c | 280 + panda/src/pnmimagetypes/color.h | 142 + panda/src/pnmimagetypes/colrops.c | 231 + .../pnmimagetypes/config_pnmimagetypes.cxx | 100 + .../src/pnmimagetypes/config_pnmimagetypes.h | 39 + panda/src/pnmimagetypes/header.c | 225 + panda/src/pnmimagetypes/pnmFileTypeAlias.cxx | 373 ++ panda/src/pnmimagetypes/pnmFileTypeAlias.h | 72 + panda/src/pnmimagetypes/pnmFileTypeBMP.cxx | 118 + panda/src/pnmimagetypes/pnmFileTypeBMP.h | 85 + .../pnmimagetypes/pnmFileTypeBMPReader.cxx | 547 ++ .../pnmimagetypes/pnmFileTypeBMPWriter.cxx | 636 ++ panda/src/pnmimagetypes/pnmFileTypeIMG.cxx | 332 + panda/src/pnmimagetypes/pnmFileTypeIMG.h | 72 + panda/src/pnmimagetypes/pnmFileTypePNM.cxx | 313 + panda/src/pnmimagetypes/pnmFileTypePNM.h | 81 + .../src/pnmimagetypes/pnmFileTypeRadiance.cxx | 335 + panda/src/pnmimagetypes/pnmFileTypeRadiance.h | 75 + panda/src/pnmimagetypes/pnmFileTypeSGI.cxx | 122 + panda/src/pnmimagetypes/pnmFileTypeSGI.h | 120 + .../pnmimagetypes/pnmFileTypeSGIReader.cxx | 465 ++ .../pnmimagetypes/pnmFileTypeSGIWriter.cxx | 363 + .../pnmimagetypes/pnmFileTypeSoftImage.cxx | 742 +++ .../src/pnmimagetypes/pnmFileTypeSoftImage.h | 79 + panda/src/pnmimagetypes/pnmFileTypeTIFF.cxx | 771 +++ panda/src/pnmimagetypes/pnmFileTypeTIFF.h | 84 + panda/src/pnmimagetypes/pnmFileTypeYUV.cxx | 381 ++ panda/src/pnmimagetypes/pnmFileTypeYUV.h | 80 + panda/src/pnmimagetypes/resolu.c | 95 + panda/src/pnmimagetypes/resolu.h | 50 + panda/src/pnmimagetypes/sgi.h | 29 + panda/src/ps2display/Sources.pp | 14 + panda/src/ps2display/config_ps2display.cxx | 4 + panda/src/ps2display/ps2GraphicsPipe.cxx | 4 + panda/src/ps2display/ps2GraphicsWindow.cxx | 4 + panda/src/ps2gsg/Sources.pp | 18 + panda/src/ps2gsg/config_ps2gsg.cxx | 4 + panda/src/ps2gsg/ps2GraphicsStateGuardian.cxx | 4 + panda/src/ps2gsg/ps2SavedFrameBuffer.cxx | 4 + panda/src/ps2gsg/ps2TextureContext.cxx | 4 + panda/src/pstatclient/Sources.pp | 34 + panda/src/pstatclient/config_pstats.cxx | 31 + panda/src/pstatclient/config_pstats.h | 26 + panda/src/pstatclient/pStatClient.I | 59 + panda/src/pstatclient/pStatClient.cxx | 755 +++ panda/src/pstatclient/pStatClient.h | 136 + .../pstatclient/pStatClientControlMessage.cxx | 110 + .../pstatclient/pStatClientControlMessage.h | 53 + panda/src/pstatclient/pStatCollector.I | 185 + panda/src/pstatclient/pStatCollector.h | 58 + panda/src/pstatclient/pStatCollectorDef.cxx | 71 + panda/src/pstatclient/pStatCollectorDef.h | 38 + panda/src/pstatclient/pStatFrameData.I | 136 + panda/src/pstatclient/pStatFrameData.cxx | 41 + panda/src/pstatclient/pStatFrameData.h | 54 + .../pstatclient/pStatServerControlMessage.cxx | 70 + .../pstatclient/pStatServerControlMessage.h | 43 + panda/src/pstatclient/pStatThread.I | 84 + panda/src/pstatclient/pStatThread.h | 47 + panda/src/pstatclient/pStatTimer.I | 28 + panda/src/pstatclient/pStatTimer.h | 35 + panda/src/pstatclient/test_client.cxx | 183 + panda/src/putil/Sources.pp | 120 + panda/src/putil/bam.h | 23 + panda/src/putil/bamReader.I | 161 + panda/src/putil/bamReader.N | 5 + panda/src/putil/bamReader.cxx | 470 ++ panda/src/putil/bamReader.h | 156 + panda/src/putil/bamReaderParam.I | 58 + panda/src/putil/bamReaderParam.cxx | 8 + panda/src/putil/bamReaderParam.h | 59 + panda/src/putil/bamWriter.I | 17 + panda/src/putil/bamWriter.cxx | 284 + panda/src/putil/bamWriter.h | 84 + panda/src/putil/bitMask.I | 537 ++ panda/src/putil/bitMask.cxx | 12 + panda/src/putil/bitMask.h | 118 + panda/src/putil/buttonEvent.I | 107 + panda/src/putil/buttonEvent.cxx | 22 + panda/src/putil/buttonEvent.h | 47 + panda/src/putil/buttonHandle.I | 91 + panda/src/putil/buttonHandle.cxx | 25 + panda/src/putil/buttonHandle.h | 48 + panda/src/putil/buttonRegistry.I | 42 + panda/src/putil/buttonRegistry.cxx | 189 + panda/src/putil/buttonRegistry.h | 68 + panda/src/putil/config_util.N | 2 + panda/src/putil/config_util.cxx | 76 + panda/src/putil/config_util.h | 33 + panda/src/putil/configurable.cxx | 9 + panda/src/putil/configurable.h | 72 + panda/src/putil/factory.I | 83 + panda/src/putil/factory.h | 56 + panda/src/putil/factoryBase.I | 49 + panda/src/putil/factoryBase.cxx | 265 + panda/src/putil/factoryBase.h | 94 + panda/src/putil/factoryParam.I | 40 + panda/src/putil/factoryParam.cxx | 8 + panda/src/putil/factoryParam.h | 53 + panda/src/putil/factoryParams.I | 27 + panda/src/putil/factoryParams.cxx | 105 + panda/src/putil/factoryParams.h | 54 + panda/src/putil/globPattern.I | 49 + panda/src/putil/globPattern.cxx | 148 + panda/src/putil/globPattern.h | 50 + panda/src/putil/globalPointerRegistry.I | 63 + panda/src/putil/globalPointerRegistry.cxx | 95 + panda/src/putil/globalPointerRegistry.h | 76 + panda/src/putil/indirectCompareTo.I | 15 + panda/src/putil/indirectCompareTo.h | 27 + panda/src/putil/ioPtaDatagramFloat.cxx | 42 + panda/src/putil/ioPtaDatagramFloat.h | 33 + panda/src/putil/ioPtaDatagramInt.cxx | 45 + panda/src/putil/ioPtaDatagramInt.h | 33 + panda/src/putil/ioPtaDatagramShort.cxx | 44 + panda/src/putil/ioPtaDatagramShort.h | 33 + panda/src/putil/iterator_types.h | 72 + panda/src/putil/keyboardButton.cxx | 291 + panda/src/putil/keyboardButton.h | 63 + panda/src/putil/lineStream.I | 53 + panda/src/putil/lineStream.cxx | 6 + panda/src/putil/lineStream.h | 41 + panda/src/putil/lineStreamBuf.I | 47 + panda/src/putil/lineStreamBuf.cxx | 86 + panda/src/putil/lineStreamBuf.h | 42 + panda/src/putil/modifierButtons.I | 88 + panda/src/putil/modifierButtons.cxx | 242 + panda/src/putil/modifierButtons.h | 63 + panda/src/putil/mouseButton.cxx | 81 + panda/src/putil/mouseButton.h | 29 + panda/src/putil/mouseData.cxx | 49 + panda/src/putil/mouseData.h | 34 + panda/src/putil/nameUniquifier.I | 76 + panda/src/putil/nameUniquifier.cxx | 64 + panda/src/putil/nameUniquifier.h | 42 + panda/src/putil/notify_utils.N | 7 + panda/src/putil/notify_utils.h | 17 + panda/src/putil/pointerToArray.I | 783 +++ panda/src/putil/pointerToArray.h | 245 + panda/src/putil/pta_double.cxx | 11 + panda/src/putil/pta_double.h | 36 + panda/src/putil/pta_float.cxx | 11 + panda/src/putil/pta_float.h | 36 + panda/src/putil/pta_int.cxx | 11 + panda/src/putil/pta_int.h | 36 + panda/src/putil/pta_uchar.cxx | 11 + panda/src/putil/pta_uchar.h | 36 + panda/src/putil/pta_ushort.cxx | 11 + panda/src/putil/pta_ushort.h | 36 + panda/src/putil/string_utils.I | 13 + panda/src/putil/string_utils.N | 5 + panda/src/putil/string_utils.cxx | 63 + panda/src/putil/string_utils.h | 31 + panda/src/putil/test_bam.cxx | 209 + panda/src/putil/test_bam.h | 169 + panda/src/putil/test_bamRead.cxx | 38 + panda/src/putil/test_bamWrite.cxx | 51 + panda/src/putil/test_filename.cxx | 106 + panda/src/putil/test_glob.cxx | 68 + panda/src/putil/test_linestream.cxx | 36 + panda/src/putil/test_types.cxx | 260 + panda/src/putil/timedCycle.I | 85 + panda/src/putil/timedCycle.cxx | 38 + panda/src/putil/timedCycle.h | 52 + panda/src/putil/typedWriteable.C.template | 86 + panda/src/putil/typedWriteable.I | 30 + panda/src/putil/typedWriteable.cxx | 31 + panda/src/putil/typedWriteable.h | 91 + panda/src/putil/typedWriteable.h.template | 20 + .../src/putil/typedWriteableReferenceCount.I | 39 + .../putil/typedWriteableReferenceCount.cxx | 7 + .../src/putil/typedWriteableReferenceCount.h | 54 + panda/src/putil/updateSeq.I | 200 + panda/src/putil/updateSeq.cxx | 6 + panda/src/putil/updateSeq.h | 67 + panda/src/putil/vector_double.cxx | 11 + panda/src/putil/vector_double.h | 30 + panda/src/putil/vector_float.cxx | 11 + panda/src/putil/vector_float.h | 30 + panda/src/putil/vector_typedWriteable.cxx | 11 + panda/src/putil/vector_typedWriteable.h | 32 + panda/src/putil/vector_uchar.cxx | 11 + panda/src/putil/vector_uchar.h | 30 + panda/src/putil/vector_ushort.cxx | 11 + panda/src/putil/vector_ushort.h | 30 + panda/src/putil/vector_writeable.cxx | 11 + panda/src/putil/vector_writeable.h | 32 + panda/src/putil/writeable.I | 31 + panda/src/putil/writeable.cxx | 19 + panda/src/putil/writeable.h | 84 + panda/src/putil/writeableConfigurable.cxx | 8 + panda/src/putil/writeableConfigurable.h | 78 + panda/src/putil/writeableParam.I | 58 + panda/src/putil/writeableParam.cxx | 8 + panda/src/putil/writeableParam.h | 60 + panda/src/putil/writeables.txt | 92 + panda/src/ribdisplay/Sources.pp | 20 + panda/src/ribdisplay/config_ribdisplay.cxx | 22 + panda/src/ribdisplay/config_ribdisplay.h | 14 + panda/src/ribdisplay/ribGraphicsPipe.I | 57 + panda/src/ribdisplay/ribGraphicsPipe.cxx | 46 + panda/src/ribdisplay/ribGraphicsPipe.h | 60 + panda/src/ribdisplay/ribGraphicsWindow.I | 164 + panda/src/ribdisplay/ribGraphicsWindow.cxx | 398 ++ panda/src/ribdisplay/ribGraphicsWindow.h | 134 + panda/src/ribgsg/Sources.pp | 19 + panda/src/ribgsg/config_ribgsg.cxx | 19 + panda/src/ribgsg/config_ribgsg.h | 14 + panda/src/ribgsg/ribGraphicsStateGuardian.I | 4 + panda/src/ribgsg/ribGraphicsStateGuardian.cxx | 1234 ++++ panda/src/ribgsg/ribGraphicsStateGuardian.h | 169 + panda/src/ribgsg/ribStuffTraverser.cxx | 43 + panda/src/ribgsg/ribStuffTraverser.h | 35 + panda/src/sgattrib/alphaTransformAttribute.I | 55 + .../src/sgattrib/alphaTransformAttribute.cxx | 106 + panda/src/sgattrib/alphaTransformAttribute.h | 62 + panda/src/sgattrib/alphaTransformProperty.I | 80 + panda/src/sgattrib/alphaTransformProperty.cxx | 16 + panda/src/sgattrib/alphaTransformProperty.h | 42 + panda/src/sgattrib/alphaTransformTransition.I | 55 + .../src/sgattrib/alphaTransformTransition.cxx | 78 + panda/src/sgattrib/alphaTransformTransition.h | 62 + panda/src/sgattrib/attribTraverser.cxx | 188 + panda/src/sgattrib/attribTraverser.h | 48 + panda/src/sgattrib/billboardTransition.I | 143 + panda/src/sgattrib/billboardTransition.cxx | 226 + panda/src/sgattrib/billboardTransition.h | 91 + panda/src/sgattrib/clipPlaneAttribute.I | 53 + panda/src/sgattrib/clipPlaneAttribute.cxx | 84 + panda/src/sgattrib/clipPlaneAttribute.h | 61 + panda/src/sgattrib/clipPlaneTransition.I | 86 + panda/src/sgattrib/clipPlaneTransition.cxx | 70 + panda/src/sgattrib/clipPlaneTransition.h | 66 + panda/src/sgattrib/colorAttribute.I | 72 + panda/src/sgattrib/colorAttribute.cxx | 106 + panda/src/sgattrib/colorAttribute.h | 64 + panda/src/sgattrib/colorBlendAttribute.I | 35 + panda/src/sgattrib/colorBlendAttribute.cxx | 104 + panda/src/sgattrib/colorBlendAttribute.h | 60 + panda/src/sgattrib/colorBlendProperty.I | 45 + panda/src/sgattrib/colorBlendProperty.cxx | 38 + panda/src/sgattrib/colorBlendProperty.h | 49 + panda/src/sgattrib/colorBlendTransition.I | 36 + panda/src/sgattrib/colorBlendTransition.cxx | 78 + panda/src/sgattrib/colorBlendTransition.h | 59 + panda/src/sgattrib/colorMaskAttribute.I | 79 + panda/src/sgattrib/colorMaskAttribute.cxx | 104 + panda/src/sgattrib/colorMaskAttribute.h | 65 + panda/src/sgattrib/colorMaskProperty.I | 59 + panda/src/sgattrib/colorMaskProperty.cxx | 35 + panda/src/sgattrib/colorMaskProperty.h | 45 + panda/src/sgattrib/colorMaskTransition.I | 50 + panda/src/sgattrib/colorMaskTransition.cxx | 78 + panda/src/sgattrib/colorMaskTransition.h | 65 + panda/src/sgattrib/colorMatrixAttribute.I | 13 + panda/src/sgattrib/colorMatrixAttribute.cxx | 57 + panda/src/sgattrib/colorMatrixAttribute.h | 48 + panda/src/sgattrib/colorMatrixTransition.I | 24 + panda/src/sgattrib/colorMatrixTransition.cxx | 76 + panda/src/sgattrib/colorMatrixTransition.h | 59 + panda/src/sgattrib/colorProperty.I | 68 + panda/src/sgattrib/colorProperty.cxx | 20 + panda/src/sgattrib/colorProperty.h | 48 + panda/src/sgattrib/colorTransition.I | 126 + panda/src/sgattrib/colorTransition.cxx | 80 + panda/src/sgattrib/colorTransition.h | 72 + panda/src/sgattrib/config_sgattrib.cxx | 143 + panda/src/sgattrib/config_sgattrib.h | 14 + panda/src/sgattrib/cullFaceAttribute.I | 35 + panda/src/sgattrib/cullFaceAttribute.cxx | 104 + panda/src/sgattrib/cullFaceAttribute.h | 60 + panda/src/sgattrib/cullFaceProperty.I | 60 + panda/src/sgattrib/cullFaceProperty.cxx | 61 + panda/src/sgattrib/cullFaceProperty.h | 56 + panda/src/sgattrib/cullFaceTransition.I | 46 + panda/src/sgattrib/cullFaceTransition.cxx | 145 + panda/src/sgattrib/cullFaceTransition.h | 71 + panda/src/sgattrib/decalAttribute.I | 13 + panda/src/sgattrib/decalAttribute.cxx | 43 + panda/src/sgattrib/decalAttribute.h | 45 + panda/src/sgattrib/decalTransition.I | 26 + panda/src/sgattrib/decalTransition.cxx | 77 + panda/src/sgattrib/decalTransition.h | 65 + panda/src/sgattrib/depthTestAttribute.I | 35 + panda/src/sgattrib/depthTestAttribute.cxx | 104 + panda/src/sgattrib/depthTestAttribute.h | 60 + panda/src/sgattrib/depthTestProperty.I | 45 + panda/src/sgattrib/depthTestProperty.cxx | 50 + panda/src/sgattrib/depthTestProperty.h | 53 + panda/src/sgattrib/depthTestTransition.I | 36 + panda/src/sgattrib/depthTestTransition.cxx | 78 + panda/src/sgattrib/depthTestTransition.h | 64 + panda/src/sgattrib/depthWriteAttribute.I | 15 + panda/src/sgattrib/depthWriteAttribute.cxx | 56 + panda/src/sgattrib/depthWriteAttribute.h | 47 + panda/src/sgattrib/depthWriteTransition.I | 26 + panda/src/sgattrib/depthWriteTransition.cxx | 30 + panda/src/sgattrib/depthWriteTransition.h | 47 + panda/src/sgattrib/drawBoundsTransition.I | 5 + panda/src/sgattrib/drawBoundsTransition.cxx | 137 + panda/src/sgattrib/drawBoundsTransition.h | 61 + panda/src/sgattrib/fogAttribute.I | 38 + panda/src/sgattrib/fogAttribute.cxx | 107 + panda/src/sgattrib/fogAttribute.h | 59 + panda/src/sgattrib/fogTransition.I | 64 + panda/src/sgattrib/fogTransition.cxx | 81 + panda/src/sgattrib/fogTransition.h | 61 + panda/src/sgattrib/linesmoothAttribute.I | 13 + panda/src/sgattrib/linesmoothAttribute.cxx | 56 + panda/src/sgattrib/linesmoothAttribute.h | 47 + panda/src/sgattrib/linesmoothTransition.I | 26 + panda/src/sgattrib/linesmoothTransition.cxx | 30 + panda/src/sgattrib/linesmoothTransition.h | 48 + panda/src/sgattrib/materialAttribute.I | 38 + panda/src/sgattrib/materialAttribute.cxx | 107 + panda/src/sgattrib/materialAttribute.h | 59 + panda/src/sgattrib/materialTransition.I | 63 + panda/src/sgattrib/materialTransition.cxx | 81 + panda/src/sgattrib/materialTransition.h | 60 + panda/src/sgattrib/pointShapeAttribute.I | 34 + panda/src/sgattrib/pointShapeAttribute.cxx | 104 + panda/src/sgattrib/pointShapeAttribute.h | 60 + panda/src/sgattrib/pointShapeProperty.I | 45 + panda/src/sgattrib/pointShapeProperty.cxx | 28 + panda/src/sgattrib/pointShapeProperty.h | 47 + panda/src/sgattrib/pointShapeTransition.I | 35 + panda/src/sgattrib/pointShapeTransition.cxx | 78 + panda/src/sgattrib/pointShapeTransition.h | 59 + panda/src/sgattrib/polygonOffsetAttribute.I | 54 + panda/src/sgattrib/polygonOffsetAttribute.cxx | 106 + panda/src/sgattrib/polygonOffsetAttribute.h | 62 + panda/src/sgattrib/polygonOffsetProperty.I | 81 + panda/src/sgattrib/polygonOffsetProperty.cxx | 16 + panda/src/sgattrib/polygonOffsetProperty.h | 42 + panda/src/sgattrib/polygonOffsetTransition.I | 55 + .../src/sgattrib/polygonOffsetTransition.cxx | 85 + panda/src/sgattrib/polygonOffsetTransition.h | 65 + panda/src/sgattrib/pruneTransition.I | 13 + panda/src/sgattrib/pruneTransition.cxx | 76 + panda/src/sgattrib/pruneTransition.h | 58 + panda/src/sgattrib/renderModeAttribute.I | 55 + panda/src/sgattrib/renderModeAttribute.cxx | 104 + panda/src/sgattrib/renderModeAttribute.h | 63 + panda/src/sgattrib/renderModeProperty.I | 73 + panda/src/sgattrib/renderModeProperty.cxx | 29 + panda/src/sgattrib/renderModeProperty.h | 51 + panda/src/sgattrib/renderModeTransition.I | 57 + panda/src/sgattrib/renderModeTransition.cxx | 78 + panda/src/sgattrib/renderModeTransition.h | 62 + panda/src/sgattrib/renderRelation.I | 41 + panda/src/sgattrib/renderRelation.N | 1 + panda/src/sgattrib/renderRelation.cxx | 100 + panda/src/sgattrib/renderRelation.h | 65 + panda/src/sgattrib/showHideAttribute.I | 13 + panda/src/sgattrib/showHideAttribute.cxx | 66 + panda/src/sgattrib/showHideAttribute.h | 59 + panda/src/sgattrib/showHideNameClass.h | 27 + panda/src/sgattrib/showHideTransition.I | 41 + panda/src/sgattrib/showHideTransition.cxx | 65 + panda/src/sgattrib/showHideTransition.h | 68 + panda/src/sgattrib/stencilAttribute.I | 55 + panda/src/sgattrib/stencilAttribute.cxx | 104 + panda/src/sgattrib/stencilAttribute.h | 64 + panda/src/sgattrib/stencilProperty.I | 69 + panda/src/sgattrib/stencilProperty.cxx | 78 + panda/src/sgattrib/stencilProperty.h | 66 + panda/src/sgattrib/stencilTransition.I | 58 + panda/src/sgattrib/stencilTransition.cxx | 78 + panda/src/sgattrib/stencilTransition.h | 68 + panda/src/sgattrib/test_attrib.cxx | 153 + panda/src/sgattrib/texGenAttribute.I | 66 + panda/src/sgattrib/texGenAttribute.cxx | 104 + panda/src/sgattrib/texGenAttribute.h | 64 + panda/src/sgattrib/texGenProperty.I | 74 + panda/src/sgattrib/texGenProperty.cxx | 35 + panda/src/sgattrib/texGenProperty.h | 53 + panda/src/sgattrib/texGenTransition.I | 95 + panda/src/sgattrib/texGenTransition.cxx | 78 + panda/src/sgattrib/texGenTransition.h | 65 + panda/src/sgattrib/texMatrixAttribute.I | 13 + panda/src/sgattrib/texMatrixAttribute.cxx | 57 + panda/src/sgattrib/texMatrixAttribute.h | 48 + panda/src/sgattrib/texMatrixTransition.I | 24 + panda/src/sgattrib/texMatrixTransition.cxx | 42 + panda/src/sgattrib/texMatrixTransition.h | 54 + panda/src/sgattrib/textureApplyAttribute.I | 35 + panda/src/sgattrib/textureApplyAttribute.cxx | 104 + panda/src/sgattrib/textureApplyAttribute.h | 60 + panda/src/sgattrib/textureApplyProperty.I | 60 + panda/src/sgattrib/textureApplyProperty.cxx | 67 + panda/src/sgattrib/textureApplyProperty.h | 57 + panda/src/sgattrib/textureApplyTransition.I | 46 + panda/src/sgattrib/textureApplyTransition.cxx | 143 + panda/src/sgattrib/textureApplyTransition.h | 72 + panda/src/sgattrib/textureAttribute.I | 39 + panda/src/sgattrib/textureAttribute.cxx | 107 + panda/src/sgattrib/textureAttribute.h | 59 + panda/src/sgattrib/textureTransition.I | 65 + panda/src/sgattrib/textureTransition.cxx | 173 + panda/src/sgattrib/textureTransition.h | 85 + panda/src/sgattrib/transformAttribute.I | 13 + panda/src/sgattrib/transformAttribute.cxx | 57 + panda/src/sgattrib/transformAttribute.h | 48 + panda/src/sgattrib/transformTransition.I | 25 + panda/src/sgattrib/transformTransition.cxx | 76 + panda/src/sgattrib/transformTransition.h | 60 + panda/src/sgattrib/transparencyAttribute.I | 35 + panda/src/sgattrib/transparencyAttribute.cxx | 104 + panda/src/sgattrib/transparencyAttribute.h | 60 + panda/src/sgattrib/transparencyProperty.I | 59 + panda/src/sgattrib/transparencyProperty.cxx | 68 + panda/src/sgattrib/transparencyProperty.h | 57 + panda/src/sgattrib/transparencyTransition.I | 46 + panda/src/sgattrib/transparencyTransition.cxx | 142 + panda/src/sgattrib/transparencyTransition.h | 76 + panda/src/sgidisplay/Sources.pp | 18 + panda/src/sgidisplay/config_sgidisplay.cxx | 17 + panda/src/sgidisplay/config_sgidisplay.h | 11 + panda/src/sgidisplay/sgiGraphicsPipe.cxx | 111 + panda/src/sgidisplay/sgiGraphicsPipe.h | 69 + panda/src/sgidisplay/sgiHardwareChannel.cxx | 54 + panda/src/sgidisplay/sgiHardwareChannel.h | 54 + panda/src/sgiglutdisplay/Sources.pp | 19 + .../sgiglutdisplay/config_sgiglutdisplay.cxx | 18 + .../sgiglutdisplay/config_sgiglutdisplay.h | 14 + .../sgiglutdisplay/sgiglutGraphicsPipe.cxx | 53 + .../src/sgiglutdisplay/sgiglutGraphicsPipe.h | 47 + panda/src/sgiglxdisplay/Sources.pp | 19 + .../sgiglxdisplay/config_sgiglxdisplay.cxx | 18 + .../src/sgiglxdisplay/config_sgiglxdisplay.h | 14 + .../src/sgiglxdisplay/sgiglxGraphicsPipe.cxx | 56 + panda/src/sgiglxdisplay/sgiglxGraphicsPipe.h | 41 + panda/src/sgmanip/Sources.pp | 33 + panda/src/sgmanip/config_sgmanip.cxx | 23 + panda/src/sgmanip/config_sgmanip.h | 15 + panda/src/sgmanip/findApproxLevel.I | 68 + panda/src/sgmanip/findApproxLevel.cxx | 104 + panda/src/sgmanip/findApproxLevel.h | 79 + panda/src/sgmanip/findApproxPath.I | 157 + panda/src/sgmanip/findApproxPath.cxx | 203 + panda/src/sgmanip/findApproxPath.h | 97 + panda/src/sgmanip/get_cout.h | 34 + panda/src/sgmanip/nodePath.I | 1479 +++++ panda/src/sgmanip/nodePath.cxx | 2550 +++++++ panda/src/sgmanip/nodePath.h | 476 ++ panda/src/sgmanip/nodePathBase.I | 53 + panda/src/sgmanip/nodePathBase.cxx | 8 + panda/src/sgmanip/nodePathBase.h | 63 + panda/src/sgmanip/nodePathCollection.I | 16 + panda/src/sgmanip/nodePathCollection.cxx | 363 + panda/src/sgmanip/nodePathCollection.h | 73 + panda/src/sgmanip/nodePathLerps.cxx | 173 + panda/src/sgmanip/nodePathLerps.h | 402 ++ panda/src/sgmanip/test_sgmanip.cxx | 120 + panda/src/sgraph/Sources.pp | 33 + panda/src/sgraph/camera.I | 51 + panda/src/sgraph/camera.cxx | 345 + panda/src/sgraph/camera.h | 105 + panda/src/sgraph/config_sgraph.cxx | 28 + panda/src/sgraph/config_sgraph.h | 14 + panda/src/sgraph/geomNode.I | 4 + panda/src/sgraph/geomNode.cxx | 333 + panda/src/sgraph/geomNode.h | 94 + panda/src/sgraph/geomTransformer.I | 44 + panda/src/sgraph/geomTransformer.cxx | 298 + panda/src/sgraph/geomTransformer.h | 84 + panda/src/sgraph/planeNode.I | 38 + panda/src/sgraph/planeNode.cxx | 48 + panda/src/sgraph/planeNode.h | 63 + panda/src/sgraph/projectionNode.I | 38 + panda/src/sgraph/projectionNode.cxx | 55 + panda/src/sgraph/projectionNode.h | 62 + panda/src/sgraph/renderTraverser.I | 37 + panda/src/sgraph/renderTraverser.cxx | 31 + panda/src/sgraph/renderTraverser.h | 70 + panda/src/sgraph/test_sgraph.cxx | 14 + panda/src/sgraphutil/Sources.pp | 27 + panda/src/sgraphutil/appTraverser.I | 16 + panda/src/sgraphutil/appTraverser.cxx | 34 + panda/src/sgraphutil/appTraverser.h | 47 + panda/src/sgraphutil/config_sgraphutil.cxx | 35 + panda/src/sgraphutil/config_sgraphutil.h | 21 + panda/src/sgraphutil/directRenderLevelState.h | 23 + panda/src/sgraphutil/directRenderTraverser.I | 5 + .../src/sgraphutil/directRenderTraverser.cxx | 179 + panda/src/sgraphutil/directRenderTraverser.h | 87 + panda/src/sgraphutil/frustumCullTraverser.I | 242 + panda/src/sgraphutil/frustumCullTraverser.h | 81 + panda/src/sgraphutil/get_rel_pos.I | 138 + panda/src/sgraphutil/get_rel_pos.cxx | 78 + panda/src/sgraphutil/get_rel_pos.h | 57 + panda/src/sgraphutil/sceneGraphAnalyzer.cxx | 372 ++ panda/src/sgraphutil/sceneGraphAnalyzer.h | 80 + panda/src/sgraphutil/sceneGraphReducer.I | 39 + panda/src/sgraphutil/sceneGraphReducer.cxx | 324 + panda/src/sgraphutil/sceneGraphReducer.h | 68 + panda/src/shader/Sources.pp | 25 + panda/src/shader/casterShader.I | 35 + panda/src/shader/casterShader.cxx | 14 + panda/src/shader/casterShader.h | 67 + panda/src/shader/config_shader.cxx | 55 + panda/src/shader/config_shader.h | 14 + panda/src/shader/outlineShader.cxx | 101 + panda/src/shader/outlineShader.h | 58 + panda/src/shader/planarReflector.cxx | 353 + panda/src/shader/planarReflector.h | 82 + panda/src/shader/projtexShader.cxx | 145 + panda/src/shader/projtexShader.h | 71 + panda/src/shader/projtexShadower.cxx | 249 + panda/src/shader/projtexShadower.h | 77 + panda/src/shader/shader.I | 55 + panda/src/shader/shader.cxx | 101 + panda/src/shader/shader.h | 141 + panda/src/shader/shaderTransition.I | 67 + panda/src/shader/shaderTransition.cxx | 311 + panda/src/shader/shaderTransition.h | 109 + panda/src/shader/spheretexHighlighter.cxx | 176 + panda/src/shader/spheretexHighlighter.h | 75 + panda/src/shader/spheretexReflector.cxx | 225 + panda/src/shader/spheretexReflector.h | 87 + panda/src/shader/spheretexShader.cxx | 122 + panda/src/shader/spheretexShader.h | 72 + panda/src/shader/spotlightShader.cxx | 67 + panda/src/shader/spotlightShader.h | 55 + panda/src/switchnode/LODNode.I | 73 + panda/src/switchnode/LODNode.cxx | 182 + panda/src/switchnode/LODNode.h | 79 + panda/src/switchnode/Sources.pp | 26 + panda/src/switchnode/config_switchnode.cxx | 23 + panda/src/switchnode/config_switchnode.h | 14 + panda/src/switchnode/sequenceNode.cxx | 173 + panda/src/switchnode/sequenceNode.h | 64 + panda/src/switchnode/test_sequences.cxx | 122 + panda/src/testbed/Configrc | 2 + panda/src/testbed/Sources.pp | 126 + panda/src/testbed/chat_test.cxx | 88 + panda/src/testbed/demo.cxx | 588 ++ panda/src/testbed/downloader_test.cxx | 66 + panda/src/testbed/herc.cxx | 512 ++ panda/src/testbed/indirect.cxx | 160 + panda/src/testbed/loader_test.cxx | 50 + panda/src/testbed/lod_test.cxx | 36 + panda/src/testbed/min_herc.cxx | 431 ++ panda/src/testbed/min_shader.cxx | 809 +++ panda/src/testbed/motion.cxx | 113 + panda/src/testbed/panda.cxx | 444 ++ panda/src/testbed/rock-floor.rgb | Bin 0 -> 99161 bytes panda/src/testbed/shader_test.cxx | 703 ++ panda/src/testbed/test_eggWrite.cxx | 98 + panda/src/testbed/test_particles.cxx | 138 + panda/src/testbed/test_recparticles.cxx | 145 + panda/src/testbed/text_test.cxx | 67 + panda/src/testbed/vrpn_demo.cxx | 48 + panda/src/text/Sources.pp | 18 + panda/src/text/config_text.cxx | 18 + panda/src/text/config_text.h | 16 + panda/src/text/textNode.I | 960 +++ panda/src/text/textNode.cxx | 934 +++ panda/src/text/textNode.h | 255 + panda/src/tform/Sources.pp | 25 + panda/src/tform/buttonThrower.cxx | 151 + panda/src/tform/buttonThrower.h | 67 + panda/src/tform/config_tform.cxx | 40 + panda/src/tform/config_tform.h | 25 + panda/src/tform/driveInterface.cxx | 821 +++ panda/src/tform/driveInterface.h | 168 + panda/src/tform/mouseWatcher.I | 265 + panda/src/tform/mouseWatcher.cxx | 313 + panda/src/tform/mouseWatcher.h | 133 + panda/src/tform/mouseWatcherRegion.I | 122 + panda/src/tform/mouseWatcherRegion.cxx | 32 + panda/src/tform/mouseWatcherRegion.h | 70 + panda/src/tform/planarSlider.cxx | 192 + panda/src/tform/planarSlider.h | 83 + panda/src/tform/trackball.cxx | 533 ++ panda/src/tform/trackball.h | 136 + panda/src/tform/trackerTransform.cxx | 82 + panda/src/tform/trackerTransform.h | 67 + panda/src/tform/transform2sg.cxx | 85 + panda/src/tform/transform2sg.h | 59 + panda/src/tiff/README | 226 + panda/src/tiff/Sources.pp | 29 + panda/src/tiff/g3states.h | 5914 +++++++++++++++++ panda/src/tiff/mkg3states.c | 806 +++ panda/src/tiff/mkspans.c | 50 + panda/src/tiff/t4.h | 285 + panda/src/tiff/tif_apple.c | 219 + panda/src/tiff/tif_aux.c | 204 + panda/src/tiff/tif_ccittrle.c | 75 + panda/src/tiff/tif_close.c | 51 + panda/src/tiff/tif_compress.c | 158 + panda/src/tiff/tif_dir.c | 996 +++ panda/src/tiff/tif_dirinfo.c | 303 + panda/src/tiff/tif_dirread.c | 1248 ++++ panda/src/tiff/tif_dirwrite.c | 791 +++ panda/src/tiff/tif_dumpmode.c | 113 + panda/src/tiff/tif_error.c | 63 + panda/src/tiff/tif_fax3.c | 1077 +++ panda/src/tiff/tif_fax3.h | 71 + panda/src/tiff/tif_fax4.c | 114 + panda/src/tiff/tif_flush.c | 62 + panda/src/tiff/tif_getimage.c | 1161 ++++ panda/src/tiff/tif_jpeg.c | 39 + panda/src/tiff/tif_lzw.c | 1231 ++++ panda/src/tiff/tif_machdep.c | 186 + panda/src/tiff/tif_msdos.c | 141 + panda/src/tiff/tif_next.c | 140 + panda/src/tiff/tif_open.c | 374 ++ panda/src/tiff/tif_packbits.c | 249 + panda/src/tiff/tif_print.c | 590 ++ panda/src/tiff/tif_read.c | 563 ++ panda/src/tiff/tif_strip.c | 116 + panda/src/tiff/tif_swab.c | 192 + panda/src/tiff/tif_thunder.c | 152 + panda/src/tiff/tif_tile.c | 192 + panda/src/tiff/tif_unix.c | 170 + panda/src/tiff/tif_version.c | 36 + panda/src/tiff/tif_vms.c | 259 + panda/src/tiff/tif_warning.c | 64 + panda/src/tiff/tif_write.c | 567 ++ panda/src/tiff/tiff.h | 276 + panda/src/tiff/tiffcomp.h | 111 + panda/src/tiff/tiffconf.h | 94 + panda/src/tiff/tiffio.h | 196 + panda/src/tiff/tiffiop.h | 406 ++ panda/src/vrpn/Sources.pp | 17 + panda/src/vrpn/config_vrpn.cxx | 17 + panda/src/vrpn/config_vrpn.h | 14 + panda/src/vrpn/vrpnClient.I | 118 + panda/src/vrpn/vrpnClient.cxx | 255 + panda/src/vrpn/vrpnClient.h | 102 + panda/src/wdxdisplay/Sources.pp | 16 + panda/src/wdxdisplay/config_wdxdisplay.cxx | 24 + panda/src/wdxdisplay/config_wdxdisplay.h | 15 + panda/src/wdxdisplay/wdxGraphicsPipe.cxx | 1877 ++++++ panda/src/wdxdisplay/wdxGraphicsPipe.h | 71 + panda/src/wdxdisplay/wdxGraphicsWindow.cxx | 2016 ++++++ panda/src/wdxdisplay/wdxGraphicsWindow.h | 122 + panda/src/wgldisplay/Sources.pp | 28 + panda/src/wgldisplay/config_wgldisplay.cxx | 24 + panda/src/wgldisplay/config_wgldisplay.h | 15 + panda/src/wgldisplay/test_wgl.cxx | 67 + panda/src/wgldisplay/wglGraphicsPipe.cxx | 374 ++ panda/src/wgldisplay/wglGraphicsPipe.h | 67 + panda/src/wgldisplay/wglGraphicsWindow.cxx | 917 +++ panda/src/wgldisplay/wglGraphicsWindow.h | 126 + 2145 files changed, 269592 insertions(+) create mode 100644 panda/Config.Irix.pp create mode 100644 panda/Config.Linux.pp create mode 100644 panda/Config.Win32.pp create mode 100644 panda/Config.pp create mode 100644 panda/LocalSetup.pp create mode 100644 panda/Package.pp create mode 100644 panda/Sources.pp create mode 100644 panda/acinclude.m4 create mode 100644 panda/metalibs/Sources.pp create mode 100644 panda/metalibs/panda/Sources.pp create mode 100644 panda/metalibs/panda/panda.cxx create mode 100644 panda/metalibs/pandadx/Sources.pp create mode 100644 panda/metalibs/pandadx/pandadx.cxx create mode 100644 panda/metalibs/pandaegg/Sources.pp create mode 100644 panda/metalibs/pandaegg/pandaegg.cxx create mode 100644 panda/metalibs/pandaexpress/Sources.pp create mode 100644 panda/metalibs/pandaexpress/pandaexpress.cxx create mode 100644 panda/metalibs/pandagl/Sources.pp create mode 100644 panda/metalibs/pandagl/pandagl.cxx create mode 100644 panda/metalibs/pandaglut/Sources.pp create mode 100644 panda/metalibs/pandaglut/pandaglut.cxx create mode 100644 panda/metalibs/pandaphysics/Sources.pp create mode 100644 panda/metalibs/pandaphysics/pandaphysics.cxx create mode 100644 panda/metalibs/pandarib/Sources.pp create mode 100644 panda/metalibs/pandarib/pandarib.cxx create mode 100644 panda/src/PACKAGE-DESC create mode 100644 panda/src/Sources.pp create mode 100644 panda/src/audio/Sources.pp create mode 100644 panda/src/audio/audio.h create mode 100644 panda/src/audio/audio_load_midi.cxx create mode 100644 panda/src/audio/audio_load_wav.cxx create mode 100644 panda/src/audio/audio_manager.I create mode 100644 panda/src/audio/audio_manager.cxx create mode 100644 panda/src/audio/audio_manager.h create mode 100644 panda/src/audio/audio_midi.cxx create mode 100644 panda/src/audio/audio_midi.h create mode 100644 panda/src/audio/audio_mikmod_traits.cxx create mode 100644 panda/src/audio/audio_mikmod_traits.h create mode 100644 panda/src/audio/audio_music.I create mode 100644 panda/src/audio/audio_music.cxx create mode 100644 panda/src/audio/audio_music.h create mode 100644 panda/src/audio/audio_null_traits.I create mode 100644 panda/src/audio/audio_null_traits.cxx create mode 100644 panda/src/audio/audio_null_traits.h create mode 100644 panda/src/audio/audio_pool.I create mode 100644 panda/src/audio/audio_pool.cxx create mode 100644 panda/src/audio/audio_pool.h create mode 100644 panda/src/audio/audio_sample.I create mode 100644 panda/src/audio/audio_sample.cxx create mode 100644 panda/src/audio/audio_sample.h create mode 100644 panda/src/audio/audio_trait.cxx create mode 100644 panda/src/audio/audio_trait.h create mode 100644 panda/src/audio/audio_win_traits.I create mode 100644 panda/src/audio/audio_win_traits.cxx create mode 100644 panda/src/audio/audio_win_traits.h create mode 100644 panda/src/audio/config_audio.cxx create mode 100644 panda/src/audio/config_audio.h create mode 100644 panda/src/audio/test_audio.cxx create mode 100644 panda/src/builder/Sources.pp create mode 100644 panda/src/builder/builder.I create mode 100644 panda/src/builder/builder.cxx create mode 100644 panda/src/builder/builder.h create mode 100644 panda/src/builder/builderAttrib.I create mode 100644 panda/src/builder/builderAttrib.cxx create mode 100644 panda/src/builder/builderAttrib.h create mode 100644 panda/src/builder/builderAttribTempl.I create mode 100644 panda/src/builder/builderAttribTempl.h create mode 100644 panda/src/builder/builderBucket.I create mode 100644 panda/src/builder/builderBucket.cxx create mode 100644 panda/src/builder/builderBucket.h create mode 100644 panda/src/builder/builderBucketNode.I create mode 100644 panda/src/builder/builderBucketNode.cxx create mode 100644 panda/src/builder/builderBucketNode.h create mode 100644 panda/src/builder/builderFuncs.I create mode 100644 panda/src/builder/builderFuncs.h create mode 100644 panda/src/builder/builderMisc.cxx create mode 100644 panda/src/builder/builderMisc.h create mode 100644 panda/src/builder/builderNormalVisualizer.I create mode 100644 panda/src/builder/builderNormalVisualizer.cxx create mode 100644 panda/src/builder/builderNormalVisualizer.h create mode 100644 panda/src/builder/builderPrim.cxx create mode 100644 panda/src/builder/builderPrim.h create mode 100644 panda/src/builder/builderPrimTempl.I create mode 100644 panda/src/builder/builderPrimTempl.h create mode 100644 panda/src/builder/builderProperties.cxx create mode 100644 panda/src/builder/builderProperties.h create mode 100644 panda/src/builder/builderTypes.cxx create mode 100644 panda/src/builder/builderTypes.h create mode 100644 panda/src/builder/builderVertex.I create mode 100644 panda/src/builder/builderVertex.cxx create mode 100644 panda/src/builder/builderVertex.h create mode 100644 panda/src/builder/builderVertexTempl.I create mode 100644 panda/src/builder/builderVertexTempl.h create mode 100644 panda/src/builder/config_builder.cxx create mode 100644 panda/src/builder/config_builder.h create mode 100644 panda/src/builder/mesher.cxx create mode 100644 panda/src/builder/mesher.h create mode 100644 panda/src/builder/mesherEdge.I create mode 100644 panda/src/builder/mesherEdge.h create mode 100644 panda/src/builder/mesherFanMaker.I create mode 100644 panda/src/builder/mesherFanMaker.h create mode 100644 panda/src/builder/mesherStrip.I create mode 100644 panda/src/builder/mesherStrip.h create mode 100644 panda/src/builder/mesherTempl.I create mode 100644 panda/src/builder/mesherTempl.h create mode 100644 panda/src/builder/pta_BuilderC.cxx create mode 100644 panda/src/builder/pta_BuilderC.h create mode 100644 panda/src/builder/pta_BuilderN.cxx create mode 100644 panda/src/builder/pta_BuilderN.h create mode 100644 panda/src/builder/pta_BuilderTC.cxx create mode 100644 panda/src/builder/pta_BuilderTC.h create mode 100644 panda/src/builder/pta_BuilderV.cxx create mode 100644 panda/src/builder/pta_BuilderV.h create mode 100644 panda/src/builder/test_builder.cxx create mode 100644 panda/src/builder/test_builder_data.cxx create mode 100644 panda/src/builder/vector_BuilderC.cxx create mode 100644 panda/src/builder/vector_BuilderC.h create mode 100644 panda/src/builder/vector_BuilderN.cxx create mode 100644 panda/src/builder/vector_BuilderN.h create mode 100644 panda/src/builder/vector_BuilderTC.cxx create mode 100644 panda/src/builder/vector_BuilderTC.h create mode 100644 panda/src/builder/vector_BuilderV.cxx create mode 100644 panda/src/builder/vector_BuilderV.h create mode 100644 panda/src/chan/Sources.pp create mode 100644 panda/src/chan/animBundle.I create mode 100644 panda/src/chan/animBundle.cxx create mode 100644 panda/src/chan/animBundle.h create mode 100644 panda/src/chan/animBundleNode.I create mode 100644 panda/src/chan/animBundleNode.cxx create mode 100644 panda/src/chan/animBundleNode.h create mode 100644 panda/src/chan/animChannel.I create mode 100644 panda/src/chan/animChannel.cxx create mode 100644 panda/src/chan/animChannel.h create mode 100644 panda/src/chan/animChannelBase.I create mode 100644 panda/src/chan/animChannelBase.cxx create mode 100644 panda/src/chan/animChannelBase.h create mode 100644 panda/src/chan/animChannelFixed.I create mode 100644 panda/src/chan/animChannelFixed.h create mode 100644 panda/src/chan/animChannelMatrixXfmTable.I create mode 100644 panda/src/chan/animChannelMatrixXfmTable.cxx create mode 100644 panda/src/chan/animChannelMatrixXfmTable.h create mode 100644 panda/src/chan/animChannelScalarTable.I create mode 100644 panda/src/chan/animChannelScalarTable.cxx create mode 100644 panda/src/chan/animChannelScalarTable.h create mode 100644 panda/src/chan/animControl.I create mode 100644 panda/src/chan/animControl.N create mode 100644 panda/src/chan/animControl.cxx create mode 100644 panda/src/chan/animControl.h create mode 100644 panda/src/chan/animControlCollection.I create mode 100644 panda/src/chan/animControlCollection.cxx create mode 100644 panda/src/chan/animControlCollection.h create mode 100644 panda/src/chan/animGroup.I create mode 100644 panda/src/chan/animGroup.cxx create mode 100644 panda/src/chan/animGroup.h create mode 100644 panda/src/chan/auto_bind.cxx create mode 100644 panda/src/chan/auto_bind.h create mode 100644 panda/src/chan/config_chan.cxx create mode 100644 panda/src/chan/config_chan.h create mode 100644 panda/src/chan/movingPart.I create mode 100644 panda/src/chan/movingPart.h create mode 100644 panda/src/chan/movingPartBase.I create mode 100644 panda/src/chan/movingPartBase.cxx create mode 100644 panda/src/chan/movingPartBase.h create mode 100644 panda/src/chan/movingPartMatrix.I create mode 100644 panda/src/chan/movingPartMatrix.cxx create mode 100644 panda/src/chan/movingPartMatrix.h create mode 100644 panda/src/chan/movingPartScalar.I create mode 100644 panda/src/chan/movingPartScalar.cxx create mode 100644 panda/src/chan/movingPartScalar.h create mode 100644 panda/src/chan/partBundle.I create mode 100644 panda/src/chan/partBundle.N create mode 100644 panda/src/chan/partBundle.cxx create mode 100644 panda/src/chan/partBundle.h create mode 100644 panda/src/chan/partBundleNode.I create mode 100644 panda/src/chan/partBundleNode.cxx create mode 100644 panda/src/chan/partBundleNode.h create mode 100644 panda/src/chan/partGroup.I create mode 100644 panda/src/chan/partGroup.cxx create mode 100644 panda/src/chan/partGroup.h create mode 100644 panda/src/chan/vector_PartGroupStar.cxx create mode 100644 panda/src/chan/vector_PartGroupStar.h create mode 100644 panda/src/chancfg/Sources.pp create mode 100644 panda/src/chancfg/chancfg.I create mode 100644 panda/src/chancfg/chancfg.N create mode 100644 panda/src/chancfg/chancfg.cxx create mode 100644 panda/src/chancfg/chancfg.h create mode 100644 panda/src/chancfg/chanlayout.I create mode 100644 panda/src/chancfg/chanlayout.cxx create mode 100644 panda/src/chancfg/chanlayout.h create mode 100644 panda/src/chancfg/chanparse.I create mode 100644 panda/src/chancfg/chanparse.cxx create mode 100644 panda/src/chancfg/chanparse.h create mode 100644 panda/src/chancfg/chansetup.I create mode 100644 panda/src/chancfg/chansetup.cxx create mode 100644 panda/src/chancfg/chansetup.h create mode 100644 panda/src/chancfg/chanshare.h create mode 100644 panda/src/chancfg/chanviewport.I create mode 100644 panda/src/chancfg/chanviewport.h create mode 100644 panda/src/chancfg/chanwindow.I create mode 100644 panda/src/chancfg/chanwindow.cxx create mode 100644 panda/src/chancfg/chanwindow.h create mode 100644 panda/src/chancfg/layout_db create mode 100644 panda/src/chancfg/setup_db create mode 100644 panda/src/chancfg/test_chancfg.cxx create mode 100644 panda/src/chancfg/window_db create mode 100644 panda/src/char/Sources.pp create mode 100644 panda/src/char/character.I create mode 100644 panda/src/char/character.cxx create mode 100644 panda/src/char/character.h create mode 100644 panda/src/char/characterJoint.cxx create mode 100644 panda/src/char/characterJoint.h create mode 100644 panda/src/char/characterJointBundle.I create mode 100644 panda/src/char/characterJointBundle.cxx create mode 100644 panda/src/char/characterJointBundle.h create mode 100644 panda/src/char/characterSlider.cxx create mode 100644 panda/src/char/characterSlider.h create mode 100644 panda/src/char/computedVertices.I create mode 100644 panda/src/char/computedVertices.cxx create mode 100644 panda/src/char/computedVertices.h create mode 100644 panda/src/char/computedVerticesMorph.I create mode 100644 panda/src/char/computedVerticesMorph.cxx create mode 100644 panda/src/char/computedVerticesMorph.h create mode 100644 panda/src/char/config_char.cxx create mode 100644 panda/src/char/config_char.h create mode 100644 panda/src/char/dynamicVertices.cxx create mode 100644 panda/src/char/dynamicVertices.h create mode 100644 panda/src/chat/Sources.pp create mode 100644 panda/src/chat/chatHelpers.cxx create mode 100644 panda/src/chat/chatHelpers.h create mode 100644 panda/src/chat/chatInput.I create mode 100644 panda/src/chat/chatInput.cxx create mode 100644 panda/src/chat/chatInput.h create mode 100644 panda/src/chat/config_chat.cxx create mode 100644 panda/src/chat/config_chat.h create mode 100644 panda/src/collide/Sources.pp create mode 100644 panda/src/collide/collideMask.h create mode 100644 panda/src/collide/collisionEntry.I create mode 100644 panda/src/collide/collisionEntry.cxx create mode 100644 panda/src/collide/collisionEntry.h create mode 100644 panda/src/collide/collisionHandler.cxx create mode 100644 panda/src/collide/collisionHandler.h create mode 100644 panda/src/collide/collisionHandlerEvent.I create mode 100644 panda/src/collide/collisionHandlerEvent.cxx create mode 100644 panda/src/collide/collisionHandlerEvent.h create mode 100644 panda/src/collide/collisionHandlerFloor.I create mode 100644 panda/src/collide/collisionHandlerFloor.cxx create mode 100644 panda/src/collide/collisionHandlerFloor.h create mode 100644 panda/src/collide/collisionHandlerPhysical.I create mode 100644 panda/src/collide/collisionHandlerPhysical.cxx create mode 100644 panda/src/collide/collisionHandlerPhysical.h create mode 100644 panda/src/collide/collisionHandlerPusher.I create mode 100644 panda/src/collide/collisionHandlerPusher.cxx create mode 100644 panda/src/collide/collisionHandlerPusher.h create mode 100644 panda/src/collide/collisionHandlerQueue.cxx create mode 100644 panda/src/collide/collisionHandlerQueue.h create mode 100644 panda/src/collide/collisionLevelState.I create mode 100644 panda/src/collide/collisionLevelState.N create mode 100644 panda/src/collide/collisionLevelState.cxx create mode 100644 panda/src/collide/collisionLevelState.h create mode 100644 panda/src/collide/collisionNode.I create mode 100644 panda/src/collide/collisionNode.cxx create mode 100644 panda/src/collide/collisionNode.h create mode 100644 panda/src/collide/collisionPlane.I create mode 100644 panda/src/collide/collisionPlane.cxx create mode 100644 panda/src/collide/collisionPlane.h create mode 100644 panda/src/collide/collisionPolygon.I create mode 100644 panda/src/collide/collisionPolygon.cxx create mode 100644 panda/src/collide/collisionPolygon.h create mode 100644 panda/src/collide/collisionRay.I create mode 100644 panda/src/collide/collisionRay.cxx create mode 100644 panda/src/collide/collisionRay.h create mode 100644 panda/src/collide/collisionSolid.I create mode 100644 panda/src/collide/collisionSolid.cxx create mode 100644 panda/src/collide/collisionSolid.h create mode 100644 panda/src/collide/collisionSphere.I create mode 100644 panda/src/collide/collisionSphere.cxx create mode 100644 panda/src/collide/collisionSphere.h create mode 100644 panda/src/collide/collisionTraverser.I create mode 100644 panda/src/collide/collisionTraverser.cxx create mode 100644 panda/src/collide/collisionTraverser.h create mode 100644 panda/src/collide/config_collide.cxx create mode 100644 panda/src/collide/config_collide.h create mode 100644 panda/src/collide/test_collide.cxx create mode 100644 panda/src/configfiles/Configrc create mode 100644 panda/src/configfiles/Sources.pp create mode 100644 panda/src/configfiles/panda.emacs create mode 100644 panda/src/configfiles/panda.emacs.Xdefaults create mode 100644 panda/src/configfiles/panda.init create mode 100644 panda/src/cull/Sources.pp create mode 100644 panda/src/cull/config_cull.cxx create mode 100644 panda/src/cull/config_cull.h create mode 100644 panda/src/cull/cullLevelState.h create mode 100644 panda/src/cull/cullState.I create mode 100644 panda/src/cull/cullState.cxx create mode 100644 panda/src/cull/cullState.h create mode 100644 panda/src/cull/cullStateLookup.I create mode 100644 panda/src/cull/cullStateLookup.cxx create mode 100644 panda/src/cull/cullStateLookup.h create mode 100644 panda/src/cull/cullStateSubtree.I create mode 100644 panda/src/cull/cullStateSubtree.cxx create mode 100644 panda/src/cull/cullStateSubtree.h create mode 100644 panda/src/cull/cullTraverser.I create mode 100644 panda/src/cull/cullTraverser.cxx create mode 100644 panda/src/cull/cullTraverser.h create mode 100644 panda/src/cull/directRenderTransition.I create mode 100644 panda/src/cull/directRenderTransition.cxx create mode 100644 panda/src/cull/directRenderTransition.h create mode 100644 panda/src/cull/geomBin.I create mode 100644 panda/src/cull/geomBin.cxx create mode 100644 panda/src/cull/geomBin.h create mode 100644 panda/src/cull/geomBinAttribute.I create mode 100644 panda/src/cull/geomBinAttribute.N create mode 100644 panda/src/cull/geomBinAttribute.cxx create mode 100644 panda/src/cull/geomBinAttribute.h create mode 100644 panda/src/cull/geomBinBackToFront.I create mode 100644 panda/src/cull/geomBinBackToFront.cxx create mode 100644 panda/src/cull/geomBinBackToFront.h create mode 100644 panda/src/cull/geomBinFixed.I create mode 100644 panda/src/cull/geomBinFixed.cxx create mode 100644 panda/src/cull/geomBinFixed.h create mode 100644 panda/src/cull/geomBinGroup.I create mode 100644 panda/src/cull/geomBinGroup.cxx create mode 100644 panda/src/cull/geomBinGroup.h create mode 100644 panda/src/cull/geomBinNormal.cxx create mode 100644 panda/src/cull/geomBinNormal.h create mode 100644 panda/src/cull/geomBinTransition.I create mode 100644 panda/src/cull/geomBinTransition.cxx create mode 100644 panda/src/cull/geomBinTransition.h create mode 100644 panda/src/cull/geomBinUnsorted.I create mode 100644 panda/src/cull/geomBinUnsorted.cxx create mode 100644 panda/src/cull/geomBinUnsorted.h create mode 100644 panda/src/cull/test_cull.cxx create mode 100644 panda/src/device/Sources.pp create mode 100644 panda/src/device/adinputNode.cxx create mode 100644 panda/src/device/adinputNode.h create mode 100644 panda/src/device/analogData.I create mode 100644 panda/src/device/analogData.cxx create mode 100644 panda/src/device/analogData.h create mode 100644 panda/src/device/buttonData.I create mode 100644 panda/src/device/buttonData.cxx create mode 100644 panda/src/device/buttonData.h create mode 100644 panda/src/device/clientBase.cxx create mode 100644 panda/src/device/clientBase.h create mode 100644 panda/src/device/clients.txt create mode 100644 panda/src/device/config_device.cxx create mode 100644 panda/src/device/config_device.h create mode 100644 panda/src/device/dialData.I create mode 100644 panda/src/device/dialData.cxx create mode 100644 panda/src/device/dialData.h create mode 100644 panda/src/device/mouse.cxx create mode 100644 panda/src/device/mouse.h create mode 100644 panda/src/device/trackerData.I create mode 100644 panda/src/device/trackerData.cxx create mode 100644 panda/src/device/trackerData.h create mode 100644 panda/src/device/trackerNode.cxx create mode 100644 panda/src/device/trackerNode.h create mode 100644 panda/src/dgraph/Sources.pp create mode 100644 panda/src/dgraph/buttonEventDataAttribute.I create mode 100644 panda/src/dgraph/buttonEventDataAttribute.cxx create mode 100644 panda/src/dgraph/buttonEventDataAttribute.h create mode 100644 panda/src/dgraph/buttonEventDataTransition.I create mode 100644 panda/src/dgraph/buttonEventDataTransition.cxx create mode 100644 panda/src/dgraph/buttonEventDataTransition.h create mode 100644 panda/src/dgraph/config_dgraph.cxx create mode 100644 panda/src/dgraph/config_dgraph.h create mode 100644 panda/src/dgraph/dataGraphTraversal.cxx create mode 100644 panda/src/dgraph/dataGraphTraversal.h create mode 100644 panda/src/dgraph/dataNode.cxx create mode 100644 panda/src/dgraph/dataNode.h create mode 100644 panda/src/dgraph/dataRelation.I create mode 100644 panda/src/dgraph/dataRelation.N create mode 100644 panda/src/dgraph/dataRelation.cxx create mode 100644 panda/src/dgraph/dataRelation.h create mode 100644 panda/src/dgraph/describe_data_verbose.cxx create mode 100644 panda/src/dgraph/describe_data_verbose.h create mode 100644 panda/src/dgraph/doubleDataAttribute.I create mode 100644 panda/src/dgraph/doubleDataAttribute.cxx create mode 100644 panda/src/dgraph/doubleDataAttribute.h create mode 100644 panda/src/dgraph/doubleDataTransition.I create mode 100644 panda/src/dgraph/doubleDataTransition.cxx create mode 100644 panda/src/dgraph/doubleDataTransition.h create mode 100644 panda/src/dgraph/doublePtrDataAttribute.I create mode 100644 panda/src/dgraph/doublePtrDataAttribute.cxx create mode 100644 panda/src/dgraph/doublePtrDataAttribute.h create mode 100644 panda/src/dgraph/doublePtrDataTransition.I create mode 100644 panda/src/dgraph/doublePtrDataTransition.cxx create mode 100644 panda/src/dgraph/doublePtrDataTransition.h create mode 100644 panda/src/dgraph/intDataAttribute.I create mode 100644 panda/src/dgraph/intDataAttribute.cxx create mode 100644 panda/src/dgraph/intDataAttribute.h create mode 100644 panda/src/dgraph/intDataTransition.I create mode 100644 panda/src/dgraph/intDataTransition.cxx create mode 100644 panda/src/dgraph/intDataTransition.h create mode 100644 panda/src/dgraph/matrixDataAttribute.I create mode 100644 panda/src/dgraph/matrixDataAttribute.cxx create mode 100644 panda/src/dgraph/matrixDataAttribute.h create mode 100644 panda/src/dgraph/matrixDataTransition.I create mode 100644 panda/src/dgraph/matrixDataTransition.cxx create mode 100644 panda/src/dgraph/matrixDataTransition.h create mode 100644 panda/src/dgraph/modifierButtonDataAttribute.I create mode 100644 panda/src/dgraph/modifierButtonDataAttribute.cxx create mode 100644 panda/src/dgraph/modifierButtonDataAttribute.h create mode 100644 panda/src/dgraph/modifierButtonDataTransition.I create mode 100644 panda/src/dgraph/modifierButtonDataTransition.cxx create mode 100644 panda/src/dgraph/modifierButtonDataTransition.h create mode 100644 panda/src/dgraph/numericDataAttribute.I create mode 100644 panda/src/dgraph/numericDataAttribute.h create mode 100644 panda/src/dgraph/numericDataTransition.I create mode 100644 panda/src/dgraph/numericDataTransition.h create mode 100644 panda/src/dgraph/pointerDataAttribute.I create mode 100644 panda/src/dgraph/pointerDataAttribute.h create mode 100644 panda/src/dgraph/pointerDataTransition.I create mode 100644 panda/src/dgraph/pointerDataTransition.h create mode 100644 panda/src/dgraph/test_dgraph.cxx create mode 100644 panda/src/dgraph/vec3DataAttribute.I create mode 100644 panda/src/dgraph/vec3DataAttribute.cxx create mode 100644 panda/src/dgraph/vec3DataAttribute.h create mode 100644 panda/src/dgraph/vec3DataTransition.I create mode 100644 panda/src/dgraph/vec3DataTransition.cxx create mode 100644 panda/src/dgraph/vec3DataTransition.h create mode 100644 panda/src/dgraph/vec4DataAttribute.I create mode 100644 panda/src/dgraph/vec4DataAttribute.cxx create mode 100644 panda/src/dgraph/vec4DataAttribute.h create mode 100644 panda/src/dgraph/vec4DataTransition.I create mode 100644 panda/src/dgraph/vec4DataTransition.cxx create mode 100644 panda/src/dgraph/vec4DataTransition.h create mode 100644 panda/src/dgraph/vectorDataAttribute.I create mode 100644 panda/src/dgraph/vectorDataAttribute.h create mode 100644 panda/src/dgraph/vectorDataTransition.I create mode 100644 panda/src/dgraph/vectorDataTransition.h create mode 100644 panda/src/display/Sources.pp create mode 100644 panda/src/display/config_display.cxx create mode 100644 panda/src/display/config_display.h create mode 100644 panda/src/display/displayRegion.I create mode 100644 panda/src/display/displayRegion.cxx create mode 100644 panda/src/display/displayRegion.h create mode 100644 panda/src/display/displayRegionStack.I create mode 100644 panda/src/display/displayRegionStack.h create mode 100644 panda/src/display/frameBufferStack.I create mode 100644 panda/src/display/frameBufferStack.h create mode 100644 panda/src/display/graphicsChannel.I create mode 100644 panda/src/display/graphicsChannel.cxx create mode 100644 panda/src/display/graphicsChannel.h create mode 100644 panda/src/display/graphicsLayer.I create mode 100644 panda/src/display/graphicsLayer.cxx create mode 100644 panda/src/display/graphicsLayer.h create mode 100644 panda/src/display/graphicsPipe.I create mode 100644 panda/src/display/graphicsPipe.N create mode 100644 panda/src/display/graphicsPipe.cxx create mode 100644 panda/src/display/graphicsPipe.h create mode 100644 panda/src/display/graphicsStateGuardian.I create mode 100644 panda/src/display/graphicsStateGuardian.N create mode 100644 panda/src/display/graphicsStateGuardian.cxx create mode 100644 panda/src/display/graphicsStateGuardian.h create mode 100644 panda/src/display/graphicsWindow.I create mode 100644 panda/src/display/graphicsWindow.N create mode 100644 panda/src/display/graphicsWindow.cxx create mode 100644 panda/src/display/graphicsWindow.h create mode 100644 panda/src/display/graphicsWindowInputDevice.I create mode 100644 panda/src/display/graphicsWindowInputDevice.cxx create mode 100644 panda/src/display/graphicsWindowInputDevice.h create mode 100644 panda/src/display/hardwareChannel.I create mode 100644 panda/src/display/hardwareChannel.cxx create mode 100644 panda/src/display/hardwareChannel.h create mode 100644 panda/src/display/interactiveGraphicsPipe.I create mode 100644 panda/src/display/interactiveGraphicsPipe.cxx create mode 100644 panda/src/display/interactiveGraphicsPipe.h create mode 100644 panda/src/display/noninteractiveGraphicsPipe.I create mode 100644 panda/src/display/noninteractiveGraphicsPipe.cxx create mode 100644 panda/src/display/noninteractiveGraphicsPipe.h create mode 100644 panda/src/display/pipeSpec.I create mode 100644 panda/src/display/pipeSpec.cxx create mode 100644 panda/src/display/pipeSpec.h create mode 100644 panda/src/display/renderBuffer.h create mode 100644 panda/src/display/savedFrameBuffer.I create mode 100644 panda/src/display/savedFrameBuffer.cxx create mode 100644 panda/src/display/savedFrameBuffer.h create mode 100644 panda/src/display/test_display.cxx create mode 100644 panda/src/display/textureContext.I create mode 100644 panda/src/display/textureContext.cxx create mode 100644 panda/src/display/textureContext.h create mode 100644 panda/src/doc/Panda_Whitepaper.doc create mode 100644 panda/src/doc/Sources.pp create mode 100644 panda/src/doc/channel.flo create mode 100644 panda/src/doc/channel.gif create mode 100644 panda/src/doc/drawable.flo create mode 100644 panda/src/doc/drawable.gif create mode 100644 panda/src/doc/eggSyntax.txt create mode 100644 panda/src/doc/graph-ex.flo create mode 100644 panda/src/doc/graph-ex.gif create mode 100644 panda/src/doc/gsg.flo create mode 100644 panda/src/doc/gsg.gif create mode 100644 panda/src/doc/howto.install_panda_on_unix create mode 100644 panda/src/doc/howto.install_panda_on_windows create mode 100644 panda/src/doc/image-trinity.flo create mode 100644 panda/src/doc/image-trinity.gif create mode 100644 panda/src/doc/light.flo create mode 100644 panda/src/doc/light.gif create mode 100644 panda/src/doc/node.flo create mode 100644 panda/src/doc/node.gif create mode 100644 panda/src/doc/pipe.flo create mode 100644 panda/src/doc/pipe.gif create mode 100644 panda/src/doc/sampleClass.I create mode 100644 panda/src/doc/sampleClass.cxx create mode 100644 panda/src/doc/sampleClass.h create mode 100644 panda/src/doc/shader.flo create mode 100644 panda/src/doc/shader.gif create mode 100644 panda/src/doc/window.flo create mode 100644 panda/src/doc/window.gif create mode 100644 panda/src/doc/windowing.flo create mode 100644 panda/src/doc/windowing.gif create mode 100644 panda/src/downloader/Sources.pp create mode 100644 panda/src/downloader/asyncUtility.I create mode 100644 panda/src/downloader/asyncUtility.cxx create mode 100644 panda/src/downloader/asyncUtility.h create mode 100644 panda/src/downloader/config_downloader.cxx create mode 100644 panda/src/downloader/config_downloader.h create mode 100644 panda/src/downloader/decompressor.cxx create mode 100644 panda/src/downloader/decompressor.h create mode 100644 panda/src/downloader/downloadDb.I create mode 100644 panda/src/downloader/downloadDb.cxx create mode 100644 panda/src/downloader/downloadDb.h create mode 100644 panda/src/downloader/download_utils.cxx create mode 100644 panda/src/downloader/download_utils.h create mode 100644 panda/src/downloader/downloader.I create mode 100644 panda/src/downloader/downloader.cxx create mode 100644 panda/src/downloader/downloader.h create mode 100644 panda/src/downloader/extractor.cxx create mode 100644 panda/src/downloader/extractor.h create mode 100644 panda/src/downloader/patcher.cxx create mode 100644 panda/src/downloader/patcher.h create mode 100644 panda/src/downloader/zcompressor.I create mode 100644 panda/src/downloader/zcompressor.cxx create mode 100644 panda/src/downloader/zcompressor.h create mode 100644 panda/src/downloadertools/Sources.pp create mode 100644 panda/src/downloadertools/apply_patch.cxx create mode 100644 panda/src/downloadertools/build_patch.cxx create mode 100644 panda/src/downloadertools/check_adler.cxx create mode 100644 panda/src/downloadertools/check_crc.cxx create mode 100644 panda/src/downloadertools/multify.cxx create mode 100644 panda/src/downloadertools/pcompress.cxx create mode 100644 panda/src/downloadertools/pdecompress.cxx create mode 100644 panda/src/dxgsg/Sources.pp create mode 100644 panda/src/dxgsg/config_dxgsg.cxx create mode 100644 panda/src/dxgsg/config_dxgsg.h create mode 100644 panda/src/dxgsg/dxGraphicsStateGuardian.I create mode 100644 panda/src/dxgsg/dxGraphicsStateGuardian.cxx create mode 100644 panda/src/dxgsg/dxGraphicsStateGuardian.h create mode 100644 panda/src/dxgsg/dxSavedFrameBuffer.I create mode 100644 panda/src/dxgsg/dxSavedFrameBuffer.cxx create mode 100644 panda/src/dxgsg/dxSavedFrameBuffer.h create mode 100644 panda/src/dxgsg/dxTextureContext.I create mode 100644 panda/src/dxgsg/dxTextureContext.cxx create mode 100644 panda/src/dxgsg/dxTextureContext.h create mode 100644 panda/src/effects/Sources.pp create mode 100644 panda/src/effects/config_effects.cxx create mode 100644 panda/src/effects/config_effects.h create mode 100644 panda/src/effects/lensFlareNode.I create mode 100644 panda/src/effects/lensFlareNode.cxx create mode 100644 panda/src/effects/lensFlareNode.h create mode 100644 panda/src/egg/Sources.pp create mode 100644 panda/src/egg/config_egg.cxx create mode 100644 panda/src/egg/config_egg.h create mode 100644 panda/src/egg/eggAlphaMode.I create mode 100644 panda/src/egg/eggAlphaMode.cxx create mode 100644 panda/src/egg/eggAlphaMode.h create mode 100644 panda/src/egg/eggAnimData.I create mode 100644 panda/src/egg/eggAnimData.cxx create mode 100644 panda/src/egg/eggAnimData.h create mode 100644 panda/src/egg/eggAttributes.I create mode 100644 panda/src/egg/eggAttributes.cxx create mode 100644 panda/src/egg/eggAttributes.h create mode 100644 panda/src/egg/eggBin.cxx create mode 100644 panda/src/egg/eggBin.h create mode 100644 panda/src/egg/eggBinMaker.cxx create mode 100644 panda/src/egg/eggBinMaker.h create mode 100644 panda/src/egg/eggComment.I create mode 100644 panda/src/egg/eggComment.cxx create mode 100644 panda/src/egg/eggComment.h create mode 100644 panda/src/egg/eggCoordinateSystem.I create mode 100644 panda/src/egg/eggCoordinateSystem.cxx create mode 100644 panda/src/egg/eggCoordinateSystem.h create mode 100644 panda/src/egg/eggCurve.I create mode 100644 panda/src/egg/eggCurve.cxx create mode 100644 panda/src/egg/eggCurve.h create mode 100644 panda/src/egg/eggData.I create mode 100644 panda/src/egg/eggData.cxx create mode 100644 panda/src/egg/eggData.h create mode 100644 panda/src/egg/eggExternalReference.I create mode 100644 panda/src/egg/eggExternalReference.cxx create mode 100644 panda/src/egg/eggExternalReference.h create mode 100644 panda/src/egg/eggFilenameNode.I create mode 100644 panda/src/egg/eggFilenameNode.cxx create mode 100644 panda/src/egg/eggFilenameNode.h create mode 100644 panda/src/egg/eggGroup.I create mode 100644 panda/src/egg/eggGroup.cxx create mode 100644 panda/src/egg/eggGroup.h create mode 100644 panda/src/egg/eggGroupNode.I create mode 100644 panda/src/egg/eggGroupNode.cxx create mode 100644 panda/src/egg/eggGroupNode.h create mode 100644 panda/src/egg/eggMaterial.I create mode 100644 panda/src/egg/eggMaterial.cxx create mode 100644 panda/src/egg/eggMaterial.h create mode 100644 panda/src/egg/eggMiscFuncs.I create mode 100644 panda/src/egg/eggMiscFuncs.cxx create mode 100644 panda/src/egg/eggMiscFuncs.h create mode 100644 panda/src/egg/eggMorph.I create mode 100644 panda/src/egg/eggMorph.h create mode 100644 panda/src/egg/eggMorphList.I create mode 100644 panda/src/egg/eggMorphList.h create mode 100644 panda/src/egg/eggNamedObject.I create mode 100644 panda/src/egg/eggNamedObject.cxx create mode 100644 panda/src/egg/eggNamedObject.h create mode 100644 panda/src/egg/eggNode.I create mode 100644 panda/src/egg/eggNode.cxx create mode 100644 panda/src/egg/eggNode.h create mode 100644 panda/src/egg/eggNurbsCurve.I create mode 100644 panda/src/egg/eggNurbsCurve.cxx create mode 100644 panda/src/egg/eggNurbsCurve.h create mode 100644 panda/src/egg/eggNurbsSurface.I create mode 100644 panda/src/egg/eggNurbsSurface.cxx create mode 100644 panda/src/egg/eggNurbsSurface.h create mode 100644 panda/src/egg/eggObject.I create mode 100644 panda/src/egg/eggObject.cxx create mode 100644 panda/src/egg/eggObject.h create mode 100644 panda/src/egg/eggParameters.cxx create mode 100644 panda/src/egg/eggParameters.h create mode 100644 panda/src/egg/eggPoint.I create mode 100644 panda/src/egg/eggPoint.cxx create mode 100644 panda/src/egg/eggPoint.h create mode 100644 panda/src/egg/eggPolygon.I create mode 100644 panda/src/egg/eggPolygon.cxx create mode 100644 panda/src/egg/eggPolygon.h create mode 100644 panda/src/egg/eggPrimitive.I create mode 100644 panda/src/egg/eggPrimitive.cxx create mode 100644 panda/src/egg/eggPrimitive.h create mode 100644 panda/src/egg/eggSAnimData.I create mode 100644 panda/src/egg/eggSAnimData.cxx create mode 100644 panda/src/egg/eggSAnimData.h create mode 100644 panda/src/egg/eggSurface.I create mode 100644 panda/src/egg/eggSurface.cxx create mode 100644 panda/src/egg/eggSurface.h create mode 100644 panda/src/egg/eggSwitchCondition.cxx create mode 100644 panda/src/egg/eggSwitchCondition.h create mode 100644 panda/src/egg/eggTable.I create mode 100644 panda/src/egg/eggTable.cxx create mode 100644 panda/src/egg/eggTable.h create mode 100644 panda/src/egg/eggTexture.I create mode 100644 panda/src/egg/eggTexture.cxx create mode 100644 panda/src/egg/eggTexture.h create mode 100644 panda/src/egg/eggTextureCollection.I create mode 100644 panda/src/egg/eggTextureCollection.cxx create mode 100644 panda/src/egg/eggTextureCollection.h create mode 100644 panda/src/egg/eggUtilities.I create mode 100644 panda/src/egg/eggUtilities.cxx create mode 100644 panda/src/egg/eggUtilities.h create mode 100644 panda/src/egg/eggVertex.I create mode 100644 panda/src/egg/eggVertex.cxx create mode 100644 panda/src/egg/eggVertex.h create mode 100644 panda/src/egg/eggVertexPool.I create mode 100644 panda/src/egg/eggVertexPool.cxx create mode 100644 panda/src/egg/eggVertexPool.h create mode 100644 panda/src/egg/eggXfmAnimData.I create mode 100644 panda/src/egg/eggXfmAnimData.cxx create mode 100644 panda/src/egg/eggXfmAnimData.h create mode 100644 panda/src/egg/eggXfmSAnim.I create mode 100644 panda/src/egg/eggXfmSAnim.cxx create mode 100644 panda/src/egg/eggXfmSAnim.h create mode 100644 panda/src/egg/lexer.lxx create mode 100644 panda/src/egg/lexerDefs.h create mode 100644 panda/src/egg/parser.yxx create mode 100644 panda/src/egg/parserDefs.h create mode 100644 panda/src/egg/test_egg.cxx create mode 100644 panda/src/egg2sg/Sources.pp create mode 100644 panda/src/egg2sg/animBundleMaker.cxx create mode 100644 panda/src/egg2sg/animBundleMaker.h create mode 100644 panda/src/egg2sg/characterMaker.cxx create mode 100644 panda/src/egg2sg/characterMaker.h create mode 100644 panda/src/egg2sg/computedVerticesMaker.I create mode 100644 panda/src/egg2sg/computedVerticesMaker.cxx create mode 100644 panda/src/egg2sg/computedVerticesMaker.h create mode 100644 panda/src/egg2sg/computedVerticesMakerEntity.I create mode 100644 panda/src/egg2sg/computedVerticesMakerEntity.h create mode 100644 panda/src/egg2sg/config_egg2sg.cxx create mode 100644 panda/src/egg2sg/config_egg2sg.h create mode 100644 panda/src/egg2sg/deferredArcProperty.cxx create mode 100644 panda/src/egg2sg/deferredArcProperty.h create mode 100644 panda/src/egg2sg/deferredArcTraverser.cxx create mode 100644 panda/src/egg2sg/deferredArcTraverser.h create mode 100644 panda/src/egg2sg/eggBinner.cxx create mode 100644 panda/src/egg2sg/eggBinner.h create mode 100644 panda/src/egg2sg/eggLoader.cxx create mode 100644 panda/src/egg2sg/eggLoader.h create mode 100644 panda/src/egg2sg/load_egg_file.cxx create mode 100644 panda/src/egg2sg/load_egg_file.h create mode 100644 panda/src/egg2sg/loaderFileTypeEgg.cxx create mode 100644 panda/src/egg2sg/loaderFileTypeEgg.h create mode 100644 panda/src/egg2sg/test_loader.cxx create mode 100644 panda/src/event/Sources.pp create mode 100644 panda/src/event/config_event.cxx create mode 100644 panda/src/event/config_event.h create mode 100644 panda/src/event/event.cxx create mode 100644 panda/src/event/event.h create mode 100644 panda/src/event/eventHandler.cxx create mode 100644 panda/src/event/eventHandler.h create mode 100644 panda/src/event/eventParameter.I create mode 100644 panda/src/event/eventParameter.cxx create mode 100644 panda/src/event/eventParameter.h create mode 100644 panda/src/event/eventQueue.I create mode 100644 panda/src/event/eventQueue.cxx create mode 100644 panda/src/event/eventQueue.h create mode 100644 panda/src/event/eventReceiver.cxx create mode 100644 panda/src/event/eventReceiver.h create mode 100644 panda/src/event/pt_Event.cxx create mode 100644 panda/src/event/pt_Event.h create mode 100644 panda/src/event/throw_event.I create mode 100644 panda/src/event/throw_event.h create mode 100644 panda/src/express/Sources.pp create mode 100644 panda/src/express/bigEndian.I create mode 100644 panda/src/express/bigEndian.cxx create mode 100644 panda/src/express/bigEndian.h create mode 100644 panda/src/express/buffer.I create mode 100644 panda/src/express/buffer.cxx create mode 100644 panda/src/express/buffer.h create mode 100644 panda/src/express/circBuffer.I create mode 100644 panda/src/express/circBuffer.h create mode 100644 panda/src/express/clockObject.I create mode 100644 panda/src/express/clockObject.cxx create mode 100644 panda/src/express/clockObject.h create mode 100644 panda/src/express/config_express.cxx create mode 100644 panda/src/express/config_express.h create mode 100644 panda/src/express/datagram.I create mode 100644 panda/src/express/datagram.cxx create mode 100644 panda/src/express/datagram.h create mode 100644 panda/src/express/datagramGenerator.I create mode 100644 panda/src/express/datagramGenerator.cxx create mode 100644 panda/src/express/datagramGenerator.h create mode 100644 panda/src/express/datagramIterator.cxx create mode 100644 panda/src/express/datagramIterator.h create mode 100644 panda/src/express/datagramSink.I create mode 100644 panda/src/express/datagramSink.cxx create mode 100644 panda/src/express/datagramSink.h create mode 100644 panda/src/express/get_config_path.cxx create mode 100644 panda/src/express/get_config_path.h create mode 100644 panda/src/express/indent.I create mode 100644 panda/src/express/indent.cxx create mode 100644 panda/src/express/indent.h create mode 100644 panda/src/express/littleEndian.I create mode 100644 panda/src/express/littleEndian.cxx create mode 100644 panda/src/express/littleEndian.h create mode 100644 panda/src/express/memoryUsage.I create mode 100644 panda/src/express/memoryUsage.cxx create mode 100644 panda/src/express/memoryUsage.h create mode 100644 panda/src/express/memoryUsagePointers.I create mode 100644 panda/src/express/memoryUsagePointers.cxx create mode 100644 panda/src/express/memoryUsagePointers.h create mode 100644 panda/src/express/multifile.I create mode 100644 panda/src/express/multifile.cxx create mode 100644 panda/src/express/multifile.h create mode 100644 panda/src/express/namable.I create mode 100644 panda/src/express/namable.cxx create mode 100644 panda/src/express/namable.h create mode 100644 panda/src/express/numeric_types.h create mode 100644 panda/src/express/patchfile.I create mode 100644 panda/src/express/patchfile.cxx create mode 100644 panda/src/express/patchfile.h create mode 100644 panda/src/express/pointerTo.I create mode 100644 panda/src/express/pointerTo.h create mode 100644 panda/src/express/referenceCount.I create mode 100644 panda/src/express/referenceCount.cxx create mode 100644 panda/src/express/referenceCount.h create mode 100644 panda/src/express/tokenBoard.I create mode 100644 panda/src/express/tokenBoard.h create mode 100644 panda/src/express/trueClock.I create mode 100644 panda/src/express/trueClock.cxx create mode 100644 panda/src/express/trueClock.h create mode 100644 panda/src/express/typeHandle.I create mode 100644 panda/src/express/typeHandle.cxx create mode 100644 panda/src/express/typeHandle.h create mode 100644 panda/src/express/typedReferenceCount.I create mode 100644 panda/src/express/typedReferenceCount.cxx create mode 100644 panda/src/express/typedReferenceCount.h create mode 100644 panda/src/express/typedef.h create mode 100644 panda/src/framework/Sources.pp create mode 100644 panda/src/framework/config_framework.cxx create mode 100644 panda/src/framework/config_framework.h create mode 100644 panda/src/framework/framework.cxx create mode 100644 panda/src/framework/framework.h create mode 100644 panda/src/glgsg/Sources.pp create mode 100644 panda/src/glgsg/config_glgsg.cxx create mode 100644 panda/src/glgsg/config_glgsg.h create mode 100644 panda/src/glgsg/glGraphicsStateGuardian.I create mode 100644 panda/src/glgsg/glGraphicsStateGuardian.cxx create mode 100644 panda/src/glgsg/glGraphicsStateGuardian.h create mode 100644 panda/src/glgsg/glSavedFrameBuffer.I create mode 100644 panda/src/glgsg/glSavedFrameBuffer.cxx create mode 100644 panda/src/glgsg/glSavedFrameBuffer.h create mode 100644 panda/src/glgsg/glTextureContext.I create mode 100644 panda/src/glgsg/glTextureContext.cxx create mode 100644 panda/src/glgsg/glTextureContext.h create mode 100644 panda/src/glutdisplay/Sources.pp create mode 100644 panda/src/glutdisplay/config_glutdisplay.cxx create mode 100644 panda/src/glutdisplay/config_glutdisplay.h create mode 100644 panda/src/glutdisplay/glutGraphicsPipe.cxx create mode 100644 panda/src/glutdisplay/glutGraphicsPipe.h create mode 100644 panda/src/glutdisplay/glutGraphicsWindow.cxx create mode 100644 panda/src/glutdisplay/glutGraphicsWindow.h create mode 100644 panda/src/glutdisplay/test_glut.cxx create mode 100644 panda/src/glutdisplay/test_glut_win.cxx create mode 100644 panda/src/glxdisplay/Sources.pp create mode 100644 panda/src/glxdisplay/config_glxdisplay.cxx create mode 100644 panda/src/glxdisplay/config_glxdisplay.h create mode 100644 panda/src/glxdisplay/glxGraphicsPipe.cxx create mode 100644 panda/src/glxdisplay/glxGraphicsPipe.h create mode 100644 panda/src/glxdisplay/glxGraphicsWindow.I create mode 100644 panda/src/glxdisplay/glxGraphicsWindow.cxx create mode 100644 panda/src/glxdisplay/glxGraphicsWindow.h create mode 100644 panda/src/gobj/LOD.I create mode 100644 panda/src/gobj/LOD.cxx create mode 100644 panda/src/gobj/LOD.h create mode 100644 panda/src/gobj/Sources.pp create mode 100644 panda/src/gobj/config_gobj.cxx create mode 100644 panda/src/gobj/config_gobj.h create mode 100644 panda/src/gobj/drawable.cxx create mode 100644 panda/src/gobj/drawable.h create mode 100644 panda/src/gobj/fog.I create mode 100644 panda/src/gobj/fog.cxx create mode 100644 panda/src/gobj/fog.h create mode 100644 panda/src/gobj/geom.I create mode 100644 panda/src/gobj/geom.N create mode 100644 panda/src/gobj/geom.cxx create mode 100644 panda/src/gobj/geom.h create mode 100644 panda/src/gobj/geomLine.cxx create mode 100644 panda/src/gobj/geomLine.h create mode 100644 panda/src/gobj/geomLinestrip.cxx create mode 100644 panda/src/gobj/geomLinestrip.h create mode 100644 panda/src/gobj/geomPoint.cxx create mode 100644 panda/src/gobj/geomPoint.h create mode 100644 panda/src/gobj/geomPolygon.cxx create mode 100644 panda/src/gobj/geomPolygon.h create mode 100644 panda/src/gobj/geomQuad.cxx create mode 100644 panda/src/gobj/geomQuad.h create mode 100644 panda/src/gobj/geomSphere.cxx create mode 100644 panda/src/gobj/geomSphere.h create mode 100644 panda/src/gobj/geomSprite.I create mode 100644 panda/src/gobj/geomSprite.cxx create mode 100644 panda/src/gobj/geomSprite.h create mode 100644 panda/src/gobj/geomTri.cxx create mode 100644 panda/src/gobj/geomTri.h create mode 100644 panda/src/gobj/geomTrifan.cxx create mode 100644 panda/src/gobj/geomTrifan.h create mode 100644 panda/src/gobj/geomTristrip.cxx create mode 100644 panda/src/gobj/geomTristrip.h create mode 100644 panda/src/gobj/geomprimitives.h create mode 100644 panda/src/gobj/imageBuffer.cxx create mode 100644 panda/src/gobj/imageBuffer.h create mode 100644 panda/src/gobj/material.I create mode 100644 panda/src/gobj/material.cxx create mode 100644 panda/src/gobj/material.h create mode 100644 panda/src/gobj/orthoProjection.I create mode 100644 panda/src/gobj/orthoProjection.cxx create mode 100644 panda/src/gobj/orthoProjection.h create mode 100644 panda/src/gobj/perspectiveProjection.I create mode 100644 panda/src/gobj/perspectiveProjection.cxx create mode 100644 panda/src/gobj/perspectiveProjection.h create mode 100644 panda/src/gobj/pixelBuffer.I create mode 100644 panda/src/gobj/pixelBuffer.N create mode 100644 panda/src/gobj/pixelBuffer.cxx create mode 100644 panda/src/gobj/pixelBuffer.h create mode 100644 panda/src/gobj/projection.cxx create mode 100644 panda/src/gobj/projection.h create mode 100644 panda/src/gobj/test_gobj.cxx create mode 100644 panda/src/gobj/texture.I create mode 100644 panda/src/gobj/texture.N create mode 100644 panda/src/gobj/texture.cxx create mode 100644 panda/src/gobj/texture.h create mode 100644 panda/src/gobj/texturePool.I create mode 100644 panda/src/gobj/texturePool.cxx create mode 100644 panda/src/gobj/texturePool.h create mode 100644 panda/src/graph/Sources.pp create mode 100644 panda/src/graph/allAttributesWrapper.I create mode 100644 panda/src/graph/allAttributesWrapper.cxx create mode 100644 panda/src/graph/allAttributesWrapper.h create mode 100644 panda/src/graph/allTransitionsWrapper.I create mode 100644 panda/src/graph/allTransitionsWrapper.cxx create mode 100644 panda/src/graph/allTransitionsWrapper.h create mode 100644 panda/src/graph/bitMask32Transition.cxx create mode 100644 panda/src/graph/bitMask32Transition.h create mode 100644 panda/src/graph/bitMaskAttribute.I create mode 100644 panda/src/graph/bitMaskAttribute.h create mode 100644 panda/src/graph/bitMaskTransition.I create mode 100644 panda/src/graph/bitMaskTransition.h create mode 100644 panda/src/graph/boundedObject.I create mode 100644 panda/src/graph/boundedObject.N create mode 100644 panda/src/graph/boundedObject.cxx create mode 100644 panda/src/graph/boundedObject.h create mode 100644 panda/src/graph/config_graph.cxx create mode 100644 panda/src/graph/config_graph.h create mode 100644 panda/src/graph/dftraverser.I create mode 100644 panda/src/graph/dftraverser.h create mode 100644 panda/src/graph/graphReducer.cxx create mode 100644 panda/src/graph/graphReducer.h create mode 100644 panda/src/graph/immediateAttribute.I create mode 100644 panda/src/graph/immediateAttribute.cxx create mode 100644 panda/src/graph/immediateAttribute.h create mode 100644 panda/src/graph/immediateTransition.I create mode 100644 panda/src/graph/immediateTransition.cxx create mode 100644 panda/src/graph/immediateTransition.h create mode 100644 panda/src/graph/lmatrix4fTransition.cxx create mode 100644 panda/src/graph/lmatrix4fTransition.h create mode 100644 panda/src/graph/matrixAttribute.I create mode 100644 panda/src/graph/matrixAttribute.h create mode 100644 panda/src/graph/matrixTransition.I create mode 100644 panda/src/graph/matrixTransition.h create mode 100644 panda/src/graph/multiAttribute.I create mode 100644 panda/src/graph/multiAttribute.h create mode 100644 panda/src/graph/multiNodeAttribute.I create mode 100644 panda/src/graph/multiNodeAttribute.cxx create mode 100644 panda/src/graph/multiNodeAttribute.h create mode 100644 panda/src/graph/multiNodeTransition.I create mode 100644 panda/src/graph/multiNodeTransition.cxx create mode 100644 panda/src/graph/multiNodeTransition.h create mode 100644 panda/src/graph/multiTransition.I create mode 100644 panda/src/graph/multiTransition.h create mode 100644 panda/src/graph/multiTransitionHelpers.I create mode 100644 panda/src/graph/multiTransitionHelpers.h create mode 100644 panda/src/graph/namedNode.I create mode 100644 panda/src/graph/namedNode.cxx create mode 100644 panda/src/graph/namedNode.h create mode 100644 panda/src/graph/node.I create mode 100644 panda/src/graph/node.cxx create mode 100644 panda/src/graph/node.h create mode 100644 panda/src/graph/nodeAttribute.I create mode 100644 panda/src/graph/nodeAttribute.N create mode 100644 panda/src/graph/nodeAttribute.cxx create mode 100644 panda/src/graph/nodeAttribute.h create mode 100644 panda/src/graph/nodeAttributeWrapper.I create mode 100644 panda/src/graph/nodeAttributeWrapper.cxx create mode 100644 panda/src/graph/nodeAttributeWrapper.h create mode 100644 panda/src/graph/nodeAttributes.I create mode 100644 panda/src/graph/nodeAttributes.N create mode 100644 panda/src/graph/nodeAttributes.cxx create mode 100644 panda/src/graph/nodeAttributes.h create mode 100644 panda/src/graph/nodeRelation.I create mode 100644 panda/src/graph/nodeRelation.N create mode 100644 panda/src/graph/nodeRelation.cxx create mode 100644 panda/src/graph/nodeRelation.h create mode 100644 panda/src/graph/nodeTransition.I create mode 100644 panda/src/graph/nodeTransition.N create mode 100644 panda/src/graph/nodeTransition.cxx create mode 100644 panda/src/graph/nodeTransition.h create mode 100644 panda/src/graph/nodeTransitionCache.I create mode 100644 panda/src/graph/nodeTransitionCache.cxx create mode 100644 panda/src/graph/nodeTransitionCache.h create mode 100644 panda/src/graph/nodeTransitionCacheEntry.I create mode 100644 panda/src/graph/nodeTransitionCacheEntry.cxx create mode 100644 panda/src/graph/nodeTransitionCacheEntry.h create mode 100644 panda/src/graph/nodeTransitionWrapper.I create mode 100644 panda/src/graph/nodeTransitionWrapper.cxx create mode 100644 panda/src/graph/nodeTransitionWrapper.h create mode 100644 panda/src/graph/nodeTransitions.I create mode 100644 panda/src/graph/nodeTransitions.cxx create mode 100644 panda/src/graph/nodeTransitions.h create mode 100644 panda/src/graph/nullAttributeWrapper.I create mode 100644 panda/src/graph/nullAttributeWrapper.cxx create mode 100644 panda/src/graph/nullAttributeWrapper.h create mode 100644 panda/src/graph/nullLevelState.cxx create mode 100644 panda/src/graph/nullLevelState.h create mode 100644 panda/src/graph/nullTransitionWrapper.I create mode 100644 panda/src/graph/nullTransitionWrapper.cxx create mode 100644 panda/src/graph/nullTransitionWrapper.h create mode 100644 panda/src/graph/onAttribute.I create mode 100644 panda/src/graph/onAttribute.cxx create mode 100644 panda/src/graph/onAttribute.h create mode 100644 panda/src/graph/onOffAttribute.I create mode 100644 panda/src/graph/onOffAttribute.cxx create mode 100644 panda/src/graph/onOffAttribute.h create mode 100644 panda/src/graph/onOffTransition.I create mode 100644 panda/src/graph/onOffTransition.cxx create mode 100644 panda/src/graph/onOffTransition.h create mode 100644 panda/src/graph/onTransition.I create mode 100644 panda/src/graph/onTransition.cxx create mode 100644 panda/src/graph/onTransition.h create mode 100644 panda/src/graph/pointerNameClass.h create mode 100644 panda/src/graph/pt_NamedNode.N create mode 100644 panda/src/graph/pt_NamedNode.cxx create mode 100644 panda/src/graph/pt_NamedNode.h create mode 100644 panda/src/graph/pt_Node.N create mode 100644 panda/src/graph/pt_Node.cxx create mode 100644 panda/src/graph/pt_Node.h create mode 100644 panda/src/graph/setTransitionHelpers.I create mode 100644 panda/src/graph/setTransitionHelpers.h create mode 100644 panda/src/graph/test_graph.cxx create mode 100644 panda/src/graph/test_graphRead.cxx create mode 100644 panda/src/graph/test_graphWrite.cxx create mode 100644 panda/src/graph/transitionDirection.h create mode 100644 panda/src/graph/traverserVisitor.I create mode 100644 panda/src/graph/traverserVisitor.h create mode 100644 panda/src/graph/vector_PT_Node.cxx create mode 100644 panda/src/graph/vector_PT_Node.h create mode 100644 panda/src/graph/wrt.I create mode 100644 panda/src/graph/wrt.h create mode 100644 panda/src/grutil/Sources.pp create mode 100644 panda/src/grutil/config_grutil.cxx create mode 100644 panda/src/grutil/config_grutil.h create mode 100644 panda/src/grutil/lineSegs.I create mode 100644 panda/src/grutil/lineSegs.cxx create mode 100644 panda/src/grutil/lineSegs.h create mode 100644 panda/src/gsgbase/Sources.pp create mode 100644 panda/src/gsgbase/config_gsgbase.cxx create mode 100644 panda/src/gsgbase/config_gsgbase.h create mode 100644 panda/src/gsgbase/graphicsStateGuardianBase.cxx create mode 100644 panda/src/gsgbase/graphicsStateGuardianBase.h create mode 100644 panda/src/gsgbase/pptmpNGk8yj create mode 100644 panda/src/gsgbase/test_gsgbase.cxx create mode 100644 panda/src/gsgmisc/Sources.pp create mode 100644 panda/src/gsgmisc/geomIssuer.I create mode 100644 panda/src/gsgmisc/geomIssuer.cxx create mode 100644 panda/src/gsgmisc/geomIssuer.h create mode 100644 panda/src/ipc/Sources.pp create mode 100644 panda/src/ipc/ipc.cxx create mode 100644 panda/src/ipc/ipc_atomics.h create mode 100644 panda/src/ipc/ipc_condition.h create mode 100644 panda/src/ipc/ipc_file.I create mode 100644 panda/src/ipc/ipc_file.h create mode 100644 panda/src/ipc/ipc_library.h create mode 100644 panda/src/ipc/ipc_mach_traits.h create mode 100644 panda/src/ipc/ipc_mutex.h create mode 100644 panda/src/ipc/ipc_nspr_traits.h create mode 100644 panda/src/ipc/ipc_nt_traits.cxx create mode 100644 panda/src/ipc/ipc_nt_traits.h create mode 100644 panda/src/ipc/ipc_posix_traits.h create mode 100644 panda/src/ipc/ipc_ps2_traits.h create mode 100644 panda/src/ipc/ipc_semaphore.h create mode 100644 panda/src/ipc/ipc_solaris_traits.h create mode 100644 panda/src/ipc/ipc_thread.h create mode 100644 panda/src/ipc/ipc_traits.cxx create mode 100644 panda/src/ipc/ipc_traits.h create mode 100644 panda/src/ipc/loom.cxx create mode 100644 panda/src/ipc/loom.h create mode 100644 panda/src/ipc/loom_internal.h create mode 100644 panda/src/ipc/loom_main.cxx create mode 100644 panda/src/ipc/loom_test1.cxx create mode 100644 panda/src/ipc/loom_test2.cxx create mode 100644 panda/src/ipc/test_diners.cxx create mode 100644 panda/src/ipc/test_file.cxx create mode 100644 panda/src/ipc/test_priority.cxx create mode 100644 panda/src/ipc/test_prodcons.cxx create mode 100644 panda/src/ipc/test_thread.cxx create mode 100644 panda/src/ipc/test_threaddata.cxx create mode 100644 panda/src/lerp/Sources.pp create mode 100644 panda/src/lerp/config_lerp.cxx create mode 100644 panda/src/lerp/config_lerp.h create mode 100644 panda/src/lerp/lerp.cxx create mode 100644 panda/src/lerp/lerp.h create mode 100644 panda/src/lerp/lerpblend.cxx create mode 100644 panda/src/lerp/lerpblend.h create mode 100644 panda/src/lerp/lerpchans.h create mode 100644 panda/src/lerp/lerpfunctor.cxx create mode 100644 panda/src/lerp/lerpfunctor.h create mode 100644 panda/src/light/Sources.pp create mode 100644 panda/src/light/ambientLight.cxx create mode 100644 panda/src/light/ambientLight.h create mode 100644 panda/src/light/config_light.cxx create mode 100644 panda/src/light/config_light.h create mode 100644 panda/src/light/directionalLight.I create mode 100644 panda/src/light/directionalLight.cxx create mode 100644 panda/src/light/directionalLight.h create mode 100644 panda/src/light/light.cxx create mode 100644 panda/src/light/light.h create mode 100644 panda/src/light/lightAttribute.I create mode 100644 panda/src/light/lightAttribute.cxx create mode 100644 panda/src/light/lightAttribute.h create mode 100644 panda/src/light/lightNameClass.h create mode 100644 panda/src/light/lightTransition.I create mode 100644 panda/src/light/lightTransition.cxx create mode 100644 panda/src/light/lightTransition.h create mode 100644 panda/src/light/pointLight.I create mode 100644 panda/src/light/pointLight.cxx create mode 100644 panda/src/light/pointLight.h create mode 100644 panda/src/light/pt_Light.cxx create mode 100644 panda/src/light/pt_Light.h create mode 100644 panda/src/light/spotlight.I create mode 100644 panda/src/light/spotlight.cxx create mode 100644 panda/src/light/spotlight.h create mode 100644 panda/src/light/vector_PT_Light.cxx create mode 100644 panda/src/light/vector_PT_Light.h create mode 100644 panda/src/linmath/Sources.pp create mode 100644 panda/src/linmath/cast_to_double.I create mode 100644 panda/src/linmath/cast_to_double.h create mode 100644 panda/src/linmath/cast_to_float.I create mode 100644 panda/src/linmath/cast_to_float.h create mode 100644 panda/src/linmath/cmath.I create mode 100644 panda/src/linmath/cmath.h create mode 100644 panda/src/linmath/compose_matrix.I create mode 100644 panda/src/linmath/compose_matrix.cxx create mode 100644 panda/src/linmath/compose_matrix.h create mode 100644 panda/src/linmath/config_linmath.cxx create mode 100644 panda/src/linmath/config_linmath.h create mode 100644 panda/src/linmath/coordinateSystem.cxx create mode 100644 panda/src/linmath/coordinateSystem.h create mode 100644 panda/src/linmath/deg_2_rad.h create mode 100644 panda/src/linmath/ioPtaDatagramLinMath.I create mode 100644 panda/src/linmath/ioPtaDatagramLinMath.cxx create mode 100644 panda/src/linmath/ioPtaDatagramLinMath.h create mode 100644 panda/src/linmath/lmat_ops.I create mode 100644 panda/src/linmath/lmat_ops.h create mode 100644 panda/src/linmath/lmatrix.cxx create mode 100644 panda/src/linmath/lmatrix.h create mode 100644 panda/src/linmath/lmatrix3.I create mode 100644 panda/src/linmath/lmatrix3.h create mode 100644 panda/src/linmath/lmatrix4.I create mode 100644 panda/src/linmath/lmatrix4.h create mode 100644 panda/src/linmath/lorientation.I create mode 100644 panda/src/linmath/lorientation.h create mode 100644 panda/src/linmath/lpoint2.I create mode 100644 panda/src/linmath/lpoint2.h create mode 100644 panda/src/linmath/lpoint3.I create mode 100644 panda/src/linmath/lpoint3.h create mode 100644 panda/src/linmath/lpoint4.I create mode 100644 panda/src/linmath/lpoint4.h create mode 100644 panda/src/linmath/lquaternion.I create mode 100644 panda/src/linmath/lquaternion.h create mode 100644 panda/src/linmath/lrotation.I create mode 100644 panda/src/linmath/lrotation.h create mode 100644 panda/src/linmath/luse.I create mode 100644 panda/src/linmath/luse.N create mode 100644 panda/src/linmath/luse.cxx create mode 100644 panda/src/linmath/luse.h create mode 100644 panda/src/linmath/lvec2_ops.I create mode 100644 panda/src/linmath/lvec2_ops.h create mode 100644 panda/src/linmath/lvec3_ops.I create mode 100644 panda/src/linmath/lvec3_ops.h create mode 100644 panda/src/linmath/lvec4_ops.I create mode 100644 panda/src/linmath/lvec4_ops.h create mode 100644 panda/src/linmath/lvecBase2.I create mode 100644 panda/src/linmath/lvecBase2.h create mode 100644 panda/src/linmath/lvecBase3.I create mode 100644 panda/src/linmath/lvecBase3.h create mode 100644 panda/src/linmath/lvecBase4.I create mode 100644 panda/src/linmath/lvecBase4.h create mode 100644 panda/src/linmath/lvector2.I create mode 100644 panda/src/linmath/lvector2.h create mode 100644 panda/src/linmath/lvector3.I create mode 100644 panda/src/linmath/lvector3.h create mode 100644 panda/src/linmath/lvector4.I create mode 100644 panda/src/linmath/lvector4.h create mode 100644 panda/src/linmath/mathNumbers.cxx create mode 100644 panda/src/linmath/mathNumbers.h create mode 100644 panda/src/linmath/nearly_zero.h create mode 100644 panda/src/linmath/pta_Colorf.cxx create mode 100644 panda/src/linmath/pta_Colorf.h create mode 100644 panda/src/linmath/pta_Normalf.cxx create mode 100644 panda/src/linmath/pta_Normalf.h create mode 100644 panda/src/linmath/pta_TexCoordf.cxx create mode 100644 panda/src/linmath/pta_TexCoordf.h create mode 100644 panda/src/linmath/pta_Vertexf.cxx create mode 100644 panda/src/linmath/pta_Vertexf.h create mode 100644 panda/src/linmath/test_math.cxx create mode 100644 panda/src/linmath/vector_Colorf.cxx create mode 100644 panda/src/linmath/vector_Colorf.h create mode 100644 panda/src/linmath/vector_LPoint2f.cxx create mode 100644 panda/src/linmath/vector_LPoint2f.h create mode 100644 panda/src/linmath/vector_Normalf.cxx create mode 100644 panda/src/linmath/vector_Normalf.h create mode 100644 panda/src/linmath/vector_TexCoordf.h create mode 100644 panda/src/linmath/vector_Vertexf.cxx create mode 100644 panda/src/linmath/vector_Vertexf.h create mode 100644 panda/src/loader/Sources.pp create mode 100644 panda/src/loader/bamFile.I create mode 100644 panda/src/loader/bamFile.cxx create mode 100644 panda/src/loader/bamFile.h create mode 100644 panda/src/loader/config_loader.cxx create mode 100644 panda/src/loader/config_loader.h create mode 100644 panda/src/loader/loader.I create mode 100644 panda/src/loader/loader.cxx create mode 100644 panda/src/loader/loader.h create mode 100644 panda/src/loader/loaderFileType.cxx create mode 100644 panda/src/loader/loaderFileType.h create mode 100644 panda/src/loader/loaderFileTypeBam.cxx create mode 100644 panda/src/loader/loaderFileTypeBam.h create mode 100644 panda/src/loader/loaderFileTypeRegistry.cxx create mode 100644 panda/src/loader/loaderFileTypeRegistry.h create mode 100644 panda/src/loader/modelPool.I create mode 100644 panda/src/loader/modelPool.cxx create mode 100644 panda/src/loader/modelPool.h create mode 100644 panda/src/mathutil/Sources.pp create mode 100644 panda/src/mathutil/boundingHexahedron.I create mode 100644 panda/src/mathutil/boundingHexahedron.cxx create mode 100644 panda/src/mathutil/boundingHexahedron.h create mode 100644 panda/src/mathutil/boundingLine.I create mode 100644 panda/src/mathutil/boundingLine.cxx create mode 100644 panda/src/mathutil/boundingLine.h create mode 100644 panda/src/mathutil/boundingSphere.I create mode 100644 panda/src/mathutil/boundingSphere.cxx create mode 100644 panda/src/mathutil/boundingSphere.h create mode 100644 panda/src/mathutil/boundingVolume.I create mode 100644 panda/src/mathutil/boundingVolume.cxx create mode 100644 panda/src/mathutil/boundingVolume.h create mode 100644 panda/src/mathutil/config_mathutil.cxx create mode 100644 panda/src/mathutil/config_mathutil.h create mode 100644 panda/src/mathutil/finiteBoundingVolume.cxx create mode 100644 panda/src/mathutil/finiteBoundingVolume.h create mode 100644 panda/src/mathutil/frustum.I create mode 100644 panda/src/mathutil/frustum.h create mode 100644 panda/src/mathutil/geometricBoundingVolume.I create mode 100644 panda/src/mathutil/geometricBoundingVolume.cxx create mode 100644 panda/src/mathutil/geometricBoundingVolume.h create mode 100644 panda/src/mathutil/look_at.I create mode 100644 panda/src/mathutil/look_at.cxx create mode 100644 panda/src/mathutil/look_at.h create mode 100644 panda/src/mathutil/mathHelpers.I create mode 100644 panda/src/mathutil/mathHelpers.h create mode 100644 panda/src/mathutil/mathutil.h create mode 100644 panda/src/mathutil/omniBoundingVolume.I create mode 100644 panda/src/mathutil/omniBoundingVolume.cxx create mode 100644 panda/src/mathutil/omniBoundingVolume.h create mode 100644 panda/src/mathutil/plane.I create mode 100644 panda/src/mathutil/plane.N create mode 100644 panda/src/mathutil/plane.cxx create mode 100644 panda/src/mathutil/plane.h create mode 100644 panda/src/mathutil/rotate_to.cxx create mode 100644 panda/src/mathutil/rotate_to.h create mode 100644 panda/src/mathutil/test_mathutil.cxx create mode 100644 panda/src/net/Sources.pp create mode 100644 panda/src/net/config_net.cxx create mode 100644 panda/src/net/config_net.h create mode 100644 panda/src/net/connection.cxx create mode 100644 panda/src/net/connection.h create mode 100644 panda/src/net/connectionListener.cxx create mode 100644 panda/src/net/connectionListener.h create mode 100644 panda/src/net/connectionManager.N create mode 100644 panda/src/net/connectionManager.cxx create mode 100644 panda/src/net/connectionManager.h create mode 100644 panda/src/net/connectionReader.cxx create mode 100644 panda/src/net/connectionReader.h create mode 100644 panda/src/net/connectionWriter.cxx create mode 100644 panda/src/net/connectionWriter.h create mode 100644 panda/src/net/datagramQueue.cxx create mode 100644 panda/src/net/datagramQueue.h create mode 100644 panda/src/net/datagramTCPHeader.cxx create mode 100644 panda/src/net/datagramTCPHeader.h create mode 100644 panda/src/net/datagramUDPHeader.cxx create mode 100644 panda/src/net/datagramUDPHeader.h create mode 100644 panda/src/net/datagram_ui.cxx create mode 100644 panda/src/net/datagram_ui.h create mode 100644 panda/src/net/netAddress.cxx create mode 100644 panda/src/net/netAddress.h create mode 100644 panda/src/net/netDatagram.I create mode 100644 panda/src/net/netDatagram.cxx create mode 100644 panda/src/net/netDatagram.h create mode 100644 panda/src/net/pprerror.cxx create mode 100644 panda/src/net/pprerror.h create mode 100644 panda/src/net/queuedConnectionListener.I create mode 100644 panda/src/net/queuedConnectionListener.cxx create mode 100644 panda/src/net/queuedConnectionListener.h create mode 100644 panda/src/net/queuedConnectionManager.cxx create mode 100644 panda/src/net/queuedConnectionManager.h create mode 100644 panda/src/net/queuedConnectionReader.cxx create mode 100644 panda/src/net/queuedConnectionReader.h create mode 100644 panda/src/net/queuedReturn.I create mode 100644 panda/src/net/queuedReturn.h create mode 100644 panda/src/net/recentConnectionReader.cxx create mode 100644 panda/src/net/recentConnectionReader.h create mode 100644 panda/src/net/test_datagram.cxx create mode 100644 panda/src/net/test_spam_client.cxx create mode 100644 panda/src/net/test_spam_server.cxx create mode 100644 panda/src/net/test_tcp_client.cxx create mode 100644 panda/src/net/test_tcp_server.cxx create mode 100644 panda/src/net/test_udp.cxx create mode 100644 panda/src/pandabase/Sources.pp create mode 100644 panda/src/pandabase/libpandabase.in create mode 100644 panda/src/pandabase/pandabase.cxx create mode 100644 panda/src/pandabase/pandabase.h create mode 100644 panda/src/pandabase/pandasymbols.h create mode 100644 panda/src/particlesystem/Sources.pp create mode 100644 panda/src/particlesystem/baseParticle.I create mode 100644 panda/src/particlesystem/baseParticle.cxx create mode 100644 panda/src/particlesystem/baseParticle.h create mode 100644 panda/src/particlesystem/baseParticleEmitter.I create mode 100644 panda/src/particlesystem/baseParticleEmitter.cxx create mode 100644 panda/src/particlesystem/baseParticleEmitter.h create mode 100644 panda/src/particlesystem/baseParticleFactory.I create mode 100644 panda/src/particlesystem/baseParticleFactory.cxx create mode 100644 panda/src/particlesystem/baseParticleFactory.h create mode 100644 panda/src/particlesystem/baseParticleRenderer.I create mode 100644 panda/src/particlesystem/baseParticleRenderer.cxx create mode 100644 panda/src/particlesystem/baseParticleRenderer.h create mode 100644 panda/src/particlesystem/boxEmitter.I create mode 100644 panda/src/particlesystem/boxEmitter.cxx create mode 100644 panda/src/particlesystem/boxEmitter.h create mode 100644 panda/src/particlesystem/config_particlesystem.cxx create mode 100644 panda/src/particlesystem/config_particlesystem.h create mode 100644 panda/src/particlesystem/discEmitter.I create mode 100644 panda/src/particlesystem/discEmitter.cxx create mode 100644 panda/src/particlesystem/discEmitter.h create mode 100644 panda/src/particlesystem/emitters.h create mode 100644 panda/src/particlesystem/geomParticleRenderer.I create mode 100644 panda/src/particlesystem/geomParticleRenderer.cxx create mode 100644 panda/src/particlesystem/geomParticleRenderer.h create mode 100644 panda/src/particlesystem/lineEmitter.I create mode 100644 panda/src/particlesystem/lineEmitter.cxx create mode 100644 panda/src/particlesystem/lineEmitter.h create mode 100644 panda/src/particlesystem/orientedParticle.I create mode 100644 panda/src/particlesystem/orientedParticle.cxx create mode 100644 panda/src/particlesystem/orientedParticle.h create mode 100644 panda/src/particlesystem/orientedParticleFactory.I create mode 100644 panda/src/particlesystem/orientedParticleFactory.cxx create mode 100644 panda/src/particlesystem/orientedParticleFactory.h create mode 100644 panda/src/particlesystem/particleSystem.I create mode 100644 panda/src/particlesystem/particleSystem.cxx create mode 100644 panda/src/particlesystem/particleSystem.h create mode 100644 panda/src/particlesystem/particleSystemManager.I create mode 100644 panda/src/particlesystem/particleSystemManager.cxx create mode 100644 panda/src/particlesystem/particleSystemManager.h create mode 100644 panda/src/particlesystem/particlefactories.h create mode 100644 panda/src/particlesystem/particles.h create mode 100644 panda/src/particlesystem/particlesystems.txt create mode 100644 panda/src/particlesystem/pointEmitter.I create mode 100644 panda/src/particlesystem/pointEmitter.cxx create mode 100644 panda/src/particlesystem/pointEmitter.h create mode 100644 panda/src/particlesystem/pointParticle.cxx create mode 100644 panda/src/particlesystem/pointParticle.h create mode 100644 panda/src/particlesystem/pointParticleFactory.cxx create mode 100644 panda/src/particlesystem/pointParticleFactory.h create mode 100644 panda/src/particlesystem/pointParticleRenderer.I create mode 100644 panda/src/particlesystem/pointParticleRenderer.cxx create mode 100644 panda/src/particlesystem/pointParticleRenderer.h create mode 100644 panda/src/particlesystem/rectangleEmitter.I create mode 100644 panda/src/particlesystem/rectangleEmitter.cxx create mode 100644 panda/src/particlesystem/rectangleEmitter.h create mode 100644 panda/src/particlesystem/ringEmitter.I create mode 100644 panda/src/particlesystem/ringEmitter.cxx create mode 100644 panda/src/particlesystem/ringEmitter.h create mode 100644 panda/src/particlesystem/sparkleParticleRenderer.I create mode 100644 panda/src/particlesystem/sparkleParticleRenderer.cxx create mode 100644 panda/src/particlesystem/sparkleParticleRenderer.h create mode 100644 panda/src/particlesystem/sphereSurfaceEmitter.I create mode 100644 panda/src/particlesystem/sphereSurfaceEmitter.cxx create mode 100644 panda/src/particlesystem/sphereSurfaceEmitter.h create mode 100644 panda/src/particlesystem/sphereVolumeEmitter.I create mode 100644 panda/src/particlesystem/sphereVolumeEmitter.cxx create mode 100644 panda/src/particlesystem/sphereVolumeEmitter.h create mode 100644 panda/src/particlesystem/spriteParticleRenderer.I create mode 100644 panda/src/particlesystem/spriteParticleRenderer.cxx create mode 100644 panda/src/particlesystem/spriteParticleRenderer.h create mode 100644 panda/src/particlesystem/tangentRingEmitter.I create mode 100644 panda/src/particlesystem/tangentRingEmitter.cxx create mode 100644 panda/src/particlesystem/tangentRingEmitter.h create mode 100644 panda/src/particlesystem/zSpinParticle.I create mode 100644 panda/src/particlesystem/zSpinParticle.cxx create mode 100644 panda/src/particlesystem/zSpinParticle.h create mode 100644 panda/src/particlesystem/zSpinParticleFactory.I create mode 100644 panda/src/particlesystem/zSpinParticleFactory.cxx create mode 100644 panda/src/particlesystem/zSpinParticleFactory.h create mode 100644 panda/src/physics/Sources.pp create mode 100644 panda/src/physics/actorNode.I create mode 100644 panda/src/physics/actorNode.cxx create mode 100644 panda/src/physics/actorNode.h create mode 100644 panda/src/physics/angularEulerIntegrator.cxx create mode 100644 panda/src/physics/angularEulerIntegrator.h create mode 100644 panda/src/physics/angularForce.cxx create mode 100644 panda/src/physics/angularForce.h create mode 100644 panda/src/physics/angularIntegrator.cxx create mode 100644 panda/src/physics/angularIntegrator.h create mode 100644 panda/src/physics/angularVectorForce.I create mode 100644 panda/src/physics/angularVectorForce.cxx create mode 100644 panda/src/physics/angularVectorForce.h create mode 100644 panda/src/physics/baseForce.I create mode 100644 panda/src/physics/baseForce.cxx create mode 100644 panda/src/physics/baseForce.h create mode 100644 panda/src/physics/baseIntegrator.I create mode 100644 panda/src/physics/baseIntegrator.cxx create mode 100644 panda/src/physics/baseIntegrator.h create mode 100644 panda/src/physics/config_physics.cxx create mode 100644 panda/src/physics/config_physics.h create mode 100644 panda/src/physics/forceNode.I create mode 100644 panda/src/physics/forceNode.cxx create mode 100644 panda/src/physics/forceNode.h create mode 100644 panda/src/physics/forces.h create mode 100644 panda/src/physics/linearCylinderVortexForce.I create mode 100644 panda/src/physics/linearCylinderVortexForce.cxx create mode 100644 panda/src/physics/linearCylinderVortexForce.h create mode 100644 panda/src/physics/linearDistanceForce.I create mode 100644 panda/src/physics/linearDistanceForce.cxx create mode 100644 panda/src/physics/linearDistanceForce.h create mode 100644 panda/src/physics/linearEulerIntegrator.cxx create mode 100644 panda/src/physics/linearEulerIntegrator.h create mode 100644 panda/src/physics/linearForce.I create mode 100644 panda/src/physics/linearForce.cxx create mode 100644 panda/src/physics/linearForce.h create mode 100644 panda/src/physics/linearFrictionForce.I create mode 100644 panda/src/physics/linearFrictionForce.cxx create mode 100644 panda/src/physics/linearFrictionForce.h create mode 100644 panda/src/physics/linearIntegrator.cxx create mode 100644 panda/src/physics/linearIntegrator.h create mode 100644 panda/src/physics/linearJitterForce.cxx create mode 100644 panda/src/physics/linearJitterForce.h create mode 100644 panda/src/physics/linearNoiseForce.I create mode 100644 panda/src/physics/linearNoiseForce.cxx create mode 100644 panda/src/physics/linearNoiseForce.h create mode 100644 panda/src/physics/linearRandomForce.I create mode 100644 panda/src/physics/linearRandomForce.cxx create mode 100644 panda/src/physics/linearRandomForce.h create mode 100644 panda/src/physics/linearSinkForce.cxx create mode 100644 panda/src/physics/linearSinkForce.h create mode 100644 panda/src/physics/linearSourceForce.cxx create mode 100644 panda/src/physics/linearSourceForce.h create mode 100644 panda/src/physics/linearUserDefinedForce.I create mode 100644 panda/src/physics/linearUserDefinedForce.cxx create mode 100644 panda/src/physics/linearUserDefinedForce.h create mode 100644 panda/src/physics/linearVectorForce.I create mode 100644 panda/src/physics/linearVectorForce.cxx create mode 100644 panda/src/physics/linearVectorForce.h create mode 100644 panda/src/physics/physical.I create mode 100644 panda/src/physics/physical.cxx create mode 100644 panda/src/physics/physical.h create mode 100644 panda/src/physics/physicalNode.I create mode 100644 panda/src/physics/physicalNode.cxx create mode 100644 panda/src/physics/physicalNode.h create mode 100644 panda/src/physics/physics.txt create mode 100644 panda/src/physics/physicsManager.I create mode 100644 panda/src/physics/physicsManager.cxx create mode 100644 panda/src/physics/physicsManager.h create mode 100644 panda/src/physics/physicsObject.I create mode 100644 panda/src/physics/physicsObject.cxx create mode 100644 panda/src/physics/physicsObject.h create mode 100644 panda/src/physics/test_physics.cxx create mode 100644 panda/src/pnm/Sources.pp create mode 100644 panda/src/pnm/bitio.c create mode 100644 panda/src/pnm/bitio.h create mode 100644 panda/src/pnm/compile.h create mode 100644 panda/src/pnm/libpbm.h create mode 100644 panda/src/pnm/libpbm1.c create mode 100644 panda/src/pnm/libpbm2.c create mode 100644 panda/src/pnm/libpbm3.c create mode 100644 panda/src/pnm/libpbm4.c create mode 100644 panda/src/pnm/libpbm5.c create mode 100644 panda/src/pnm/libpgm.h create mode 100644 panda/src/pnm/libpgm1.c create mode 100644 panda/src/pnm/libpgm2.c create mode 100644 panda/src/pnm/libpnm1.c create mode 100644 panda/src/pnm/libpnm2.c create mode 100644 panda/src/pnm/libpnm3.c create mode 100644 panda/src/pnm/libpnm4.c create mode 100644 panda/src/pnm/libppm.h create mode 100644 panda/src/pnm/libppm1.c create mode 100644 panda/src/pnm/libppm2.c create mode 100644 panda/src/pnm/libppm3.c create mode 100644 panda/src/pnm/libppm4.c create mode 100644 panda/src/pnm/libppm5.c create mode 100644 panda/src/pnm/pbm.h create mode 100644 panda/src/pnm/pbmfont.h create mode 100644 panda/src/pnm/pbmplus.h create mode 100644 panda/src/pnm/pgm.h create mode 100644 panda/src/pnm/pnm.h create mode 100644 panda/src/pnm/ppm.h create mode 100644 panda/src/pnm/ppmcmap.h create mode 100644 panda/src/pnm/ppmdraw.h create mode 100644 panda/src/pnm/rast.h create mode 100644 panda/src/pnm/version.h create mode 100644 panda/src/pnmimage/Sources.pp create mode 100644 panda/src/pnmimage/config_pnmimage.cxx create mode 100644 panda/src/pnmimage/config_pnmimage.h create mode 100644 panda/src/pnmimage/pnm-image-alias.cxx create mode 100644 panda/src/pnmimage/pnm-image-filter-core.cxx create mode 100644 panda/src/pnmimage/pnm-image-filter.cxx create mode 100644 panda/src/pnmimage/pnm-image-img.cxx create mode 100644 panda/src/pnmimage/pnm-image-radiance.cxx create mode 100644 panda/src/pnmimage/pnm-image-readbmp.cxx create mode 100644 panda/src/pnmimage/pnm-image-readsgi.cxx create mode 100644 panda/src/pnmimage/pnm-image-readyuv.cxx create mode 100644 panda/src/pnmimage/pnm-image-softimage.cxx create mode 100644 panda/src/pnmimage/pnm-image-tiff.cxx create mode 100644 panda/src/pnmimage/pnm-image-writebmp.cxx create mode 100644 panda/src/pnmimage/pnm-image-writesgi.cxx create mode 100644 panda/src/pnmimage/pnm-image-writeyuv.cxx create mode 100644 panda/src/pnmimage/pnmFileType.cxx create mode 100644 panda/src/pnmimage/pnmFileType.h create mode 100644 panda/src/pnmimage/pnmFileTypeRegistry.cxx create mode 100644 panda/src/pnmimage/pnmFileTypeRegistry.h create mode 100644 panda/src/pnmimage/pnmImage.I create mode 100644 panda/src/pnmimage/pnmImage.cxx create mode 100644 panda/src/pnmimage/pnmImage.h create mode 100644 panda/src/pnmimage/pnmImageHeader.I create mode 100644 panda/src/pnmimage/pnmImageHeader.cxx create mode 100644 panda/src/pnmimage/pnmImageHeader.h create mode 100644 panda/src/pnmimage/pnmReader.I create mode 100644 panda/src/pnmimage/pnmReader.cxx create mode 100644 panda/src/pnmimage/pnmReader.h create mode 100644 panda/src/pnmimage/pnmReaderTypes.h create mode 100644 panda/src/pnmimage/pnmWriter.I create mode 100644 panda/src/pnmimage/pnmWriter.cxx create mode 100644 panda/src/pnmimage/pnmWriter.h create mode 100644 panda/src/pnmimage/pnmWriterTypes.h create mode 100644 panda/src/pnmimage/pnmimage_base.h create mode 100644 panda/src/pnmimage/pnmminmax.h create mode 100644 panda/src/pnmimagetypes/Sources.pp create mode 100644 panda/src/pnmimagetypes/bmp.h create mode 100644 panda/src/pnmimagetypes/color.c create mode 100644 panda/src/pnmimagetypes/color.h create mode 100644 panda/src/pnmimagetypes/colrops.c create mode 100644 panda/src/pnmimagetypes/config_pnmimagetypes.cxx create mode 100644 panda/src/pnmimagetypes/config_pnmimagetypes.h create mode 100644 panda/src/pnmimagetypes/header.c create mode 100644 panda/src/pnmimagetypes/pnmFileTypeAlias.cxx create mode 100644 panda/src/pnmimagetypes/pnmFileTypeAlias.h create mode 100644 panda/src/pnmimagetypes/pnmFileTypeBMP.cxx create mode 100644 panda/src/pnmimagetypes/pnmFileTypeBMP.h create mode 100644 panda/src/pnmimagetypes/pnmFileTypeBMPReader.cxx create mode 100644 panda/src/pnmimagetypes/pnmFileTypeBMPWriter.cxx create mode 100644 panda/src/pnmimagetypes/pnmFileTypeIMG.cxx create mode 100644 panda/src/pnmimagetypes/pnmFileTypeIMG.h create mode 100644 panda/src/pnmimagetypes/pnmFileTypePNM.cxx create mode 100644 panda/src/pnmimagetypes/pnmFileTypePNM.h create mode 100644 panda/src/pnmimagetypes/pnmFileTypeRadiance.cxx create mode 100644 panda/src/pnmimagetypes/pnmFileTypeRadiance.h create mode 100644 panda/src/pnmimagetypes/pnmFileTypeSGI.cxx create mode 100644 panda/src/pnmimagetypes/pnmFileTypeSGI.h create mode 100644 panda/src/pnmimagetypes/pnmFileTypeSGIReader.cxx create mode 100644 panda/src/pnmimagetypes/pnmFileTypeSGIWriter.cxx create mode 100644 panda/src/pnmimagetypes/pnmFileTypeSoftImage.cxx create mode 100644 panda/src/pnmimagetypes/pnmFileTypeSoftImage.h create mode 100644 panda/src/pnmimagetypes/pnmFileTypeTIFF.cxx create mode 100644 panda/src/pnmimagetypes/pnmFileTypeTIFF.h create mode 100644 panda/src/pnmimagetypes/pnmFileTypeYUV.cxx create mode 100644 panda/src/pnmimagetypes/pnmFileTypeYUV.h create mode 100644 panda/src/pnmimagetypes/resolu.c create mode 100644 panda/src/pnmimagetypes/resolu.h create mode 100644 panda/src/pnmimagetypes/sgi.h create mode 100644 panda/src/ps2display/Sources.pp create mode 100644 panda/src/ps2display/config_ps2display.cxx create mode 100644 panda/src/ps2display/ps2GraphicsPipe.cxx create mode 100644 panda/src/ps2display/ps2GraphicsWindow.cxx create mode 100644 panda/src/ps2gsg/Sources.pp create mode 100644 panda/src/ps2gsg/config_ps2gsg.cxx create mode 100644 panda/src/ps2gsg/ps2GraphicsStateGuardian.cxx create mode 100644 panda/src/ps2gsg/ps2SavedFrameBuffer.cxx create mode 100644 panda/src/ps2gsg/ps2TextureContext.cxx create mode 100644 panda/src/pstatclient/Sources.pp create mode 100644 panda/src/pstatclient/config_pstats.cxx create mode 100644 panda/src/pstatclient/config_pstats.h create mode 100644 panda/src/pstatclient/pStatClient.I create mode 100644 panda/src/pstatclient/pStatClient.cxx create mode 100644 panda/src/pstatclient/pStatClient.h create mode 100644 panda/src/pstatclient/pStatClientControlMessage.cxx create mode 100644 panda/src/pstatclient/pStatClientControlMessage.h create mode 100644 panda/src/pstatclient/pStatCollector.I create mode 100644 panda/src/pstatclient/pStatCollector.h create mode 100644 panda/src/pstatclient/pStatCollectorDef.cxx create mode 100644 panda/src/pstatclient/pStatCollectorDef.h create mode 100644 panda/src/pstatclient/pStatFrameData.I create mode 100644 panda/src/pstatclient/pStatFrameData.cxx create mode 100644 panda/src/pstatclient/pStatFrameData.h create mode 100644 panda/src/pstatclient/pStatServerControlMessage.cxx create mode 100644 panda/src/pstatclient/pStatServerControlMessage.h create mode 100644 panda/src/pstatclient/pStatThread.I create mode 100644 panda/src/pstatclient/pStatThread.h create mode 100644 panda/src/pstatclient/pStatTimer.I create mode 100644 panda/src/pstatclient/pStatTimer.h create mode 100644 panda/src/pstatclient/test_client.cxx create mode 100644 panda/src/putil/Sources.pp create mode 100644 panda/src/putil/bam.h create mode 100644 panda/src/putil/bamReader.I create mode 100644 panda/src/putil/bamReader.N create mode 100644 panda/src/putil/bamReader.cxx create mode 100644 panda/src/putil/bamReader.h create mode 100644 panda/src/putil/bamReaderParam.I create mode 100644 panda/src/putil/bamReaderParam.cxx create mode 100644 panda/src/putil/bamReaderParam.h create mode 100644 panda/src/putil/bamWriter.I create mode 100644 panda/src/putil/bamWriter.cxx create mode 100644 panda/src/putil/bamWriter.h create mode 100644 panda/src/putil/bitMask.I create mode 100644 panda/src/putil/bitMask.cxx create mode 100644 panda/src/putil/bitMask.h create mode 100644 panda/src/putil/buttonEvent.I create mode 100644 panda/src/putil/buttonEvent.cxx create mode 100644 panda/src/putil/buttonEvent.h create mode 100644 panda/src/putil/buttonHandle.I create mode 100644 panda/src/putil/buttonHandle.cxx create mode 100644 panda/src/putil/buttonHandle.h create mode 100644 panda/src/putil/buttonRegistry.I create mode 100644 panda/src/putil/buttonRegistry.cxx create mode 100644 panda/src/putil/buttonRegistry.h create mode 100644 panda/src/putil/config_util.N create mode 100644 panda/src/putil/config_util.cxx create mode 100644 panda/src/putil/config_util.h create mode 100644 panda/src/putil/configurable.cxx create mode 100644 panda/src/putil/configurable.h create mode 100644 panda/src/putil/factory.I create mode 100644 panda/src/putil/factory.h create mode 100644 panda/src/putil/factoryBase.I create mode 100644 panda/src/putil/factoryBase.cxx create mode 100644 panda/src/putil/factoryBase.h create mode 100644 panda/src/putil/factoryParam.I create mode 100644 panda/src/putil/factoryParam.cxx create mode 100644 panda/src/putil/factoryParam.h create mode 100644 panda/src/putil/factoryParams.I create mode 100644 panda/src/putil/factoryParams.cxx create mode 100644 panda/src/putil/factoryParams.h create mode 100644 panda/src/putil/globPattern.I create mode 100644 panda/src/putil/globPattern.cxx create mode 100644 panda/src/putil/globPattern.h create mode 100644 panda/src/putil/globalPointerRegistry.I create mode 100644 panda/src/putil/globalPointerRegistry.cxx create mode 100644 panda/src/putil/globalPointerRegistry.h create mode 100644 panda/src/putil/indirectCompareTo.I create mode 100644 panda/src/putil/indirectCompareTo.h create mode 100644 panda/src/putil/ioPtaDatagramFloat.cxx create mode 100644 panda/src/putil/ioPtaDatagramFloat.h create mode 100644 panda/src/putil/ioPtaDatagramInt.cxx create mode 100644 panda/src/putil/ioPtaDatagramInt.h create mode 100644 panda/src/putil/ioPtaDatagramShort.cxx create mode 100644 panda/src/putil/ioPtaDatagramShort.h create mode 100644 panda/src/putil/iterator_types.h create mode 100644 panda/src/putil/keyboardButton.cxx create mode 100644 panda/src/putil/keyboardButton.h create mode 100644 panda/src/putil/lineStream.I create mode 100644 panda/src/putil/lineStream.cxx create mode 100644 panda/src/putil/lineStream.h create mode 100644 panda/src/putil/lineStreamBuf.I create mode 100644 panda/src/putil/lineStreamBuf.cxx create mode 100644 panda/src/putil/lineStreamBuf.h create mode 100644 panda/src/putil/modifierButtons.I create mode 100644 panda/src/putil/modifierButtons.cxx create mode 100644 panda/src/putil/modifierButtons.h create mode 100644 panda/src/putil/mouseButton.cxx create mode 100644 panda/src/putil/mouseButton.h create mode 100644 panda/src/putil/mouseData.cxx create mode 100644 panda/src/putil/mouseData.h create mode 100644 panda/src/putil/nameUniquifier.I create mode 100644 panda/src/putil/nameUniquifier.cxx create mode 100644 panda/src/putil/nameUniquifier.h create mode 100644 panda/src/putil/notify_utils.N create mode 100644 panda/src/putil/notify_utils.h create mode 100644 panda/src/putil/pointerToArray.I create mode 100644 panda/src/putil/pointerToArray.h create mode 100644 panda/src/putil/pta_double.cxx create mode 100644 panda/src/putil/pta_double.h create mode 100644 panda/src/putil/pta_float.cxx create mode 100644 panda/src/putil/pta_float.h create mode 100644 panda/src/putil/pta_int.cxx create mode 100644 panda/src/putil/pta_int.h create mode 100644 panda/src/putil/pta_uchar.cxx create mode 100644 panda/src/putil/pta_uchar.h create mode 100644 panda/src/putil/pta_ushort.cxx create mode 100644 panda/src/putil/pta_ushort.h create mode 100644 panda/src/putil/string_utils.I create mode 100644 panda/src/putil/string_utils.N create mode 100644 panda/src/putil/string_utils.cxx create mode 100644 panda/src/putil/string_utils.h create mode 100644 panda/src/putil/test_bam.cxx create mode 100644 panda/src/putil/test_bam.h create mode 100644 panda/src/putil/test_bamRead.cxx create mode 100644 panda/src/putil/test_bamWrite.cxx create mode 100644 panda/src/putil/test_filename.cxx create mode 100644 panda/src/putil/test_glob.cxx create mode 100644 panda/src/putil/test_linestream.cxx create mode 100644 panda/src/putil/test_types.cxx create mode 100644 panda/src/putil/timedCycle.I create mode 100644 panda/src/putil/timedCycle.cxx create mode 100644 panda/src/putil/timedCycle.h create mode 100644 panda/src/putil/typedWriteable.C.template create mode 100644 panda/src/putil/typedWriteable.I create mode 100644 panda/src/putil/typedWriteable.cxx create mode 100644 panda/src/putil/typedWriteable.h create mode 100644 panda/src/putil/typedWriteable.h.template create mode 100644 panda/src/putil/typedWriteableReferenceCount.I create mode 100644 panda/src/putil/typedWriteableReferenceCount.cxx create mode 100644 panda/src/putil/typedWriteableReferenceCount.h create mode 100644 panda/src/putil/updateSeq.I create mode 100644 panda/src/putil/updateSeq.cxx create mode 100644 panda/src/putil/updateSeq.h create mode 100644 panda/src/putil/vector_double.cxx create mode 100644 panda/src/putil/vector_double.h create mode 100644 panda/src/putil/vector_float.cxx create mode 100644 panda/src/putil/vector_float.h create mode 100644 panda/src/putil/vector_typedWriteable.cxx create mode 100644 panda/src/putil/vector_typedWriteable.h create mode 100644 panda/src/putil/vector_uchar.cxx create mode 100644 panda/src/putil/vector_uchar.h create mode 100644 panda/src/putil/vector_ushort.cxx create mode 100644 panda/src/putil/vector_ushort.h create mode 100644 panda/src/putil/vector_writeable.cxx create mode 100644 panda/src/putil/vector_writeable.h create mode 100644 panda/src/putil/writeable.I create mode 100644 panda/src/putil/writeable.cxx create mode 100644 panda/src/putil/writeable.h create mode 100644 panda/src/putil/writeableConfigurable.cxx create mode 100644 panda/src/putil/writeableConfigurable.h create mode 100644 panda/src/putil/writeableParam.I create mode 100644 panda/src/putil/writeableParam.cxx create mode 100644 panda/src/putil/writeableParam.h create mode 100644 panda/src/putil/writeables.txt create mode 100644 panda/src/ribdisplay/Sources.pp create mode 100644 panda/src/ribdisplay/config_ribdisplay.cxx create mode 100644 panda/src/ribdisplay/config_ribdisplay.h create mode 100644 panda/src/ribdisplay/ribGraphicsPipe.I create mode 100644 panda/src/ribdisplay/ribGraphicsPipe.cxx create mode 100644 panda/src/ribdisplay/ribGraphicsPipe.h create mode 100644 panda/src/ribdisplay/ribGraphicsWindow.I create mode 100644 panda/src/ribdisplay/ribGraphicsWindow.cxx create mode 100644 panda/src/ribdisplay/ribGraphicsWindow.h create mode 100644 panda/src/ribgsg/Sources.pp create mode 100644 panda/src/ribgsg/config_ribgsg.cxx create mode 100644 panda/src/ribgsg/config_ribgsg.h create mode 100644 panda/src/ribgsg/ribGraphicsStateGuardian.I create mode 100644 panda/src/ribgsg/ribGraphicsStateGuardian.cxx create mode 100644 panda/src/ribgsg/ribGraphicsStateGuardian.h create mode 100644 panda/src/ribgsg/ribStuffTraverser.cxx create mode 100644 panda/src/ribgsg/ribStuffTraverser.h create mode 100644 panda/src/sgattrib/alphaTransformAttribute.I create mode 100644 panda/src/sgattrib/alphaTransformAttribute.cxx create mode 100644 panda/src/sgattrib/alphaTransformAttribute.h create mode 100644 panda/src/sgattrib/alphaTransformProperty.I create mode 100644 panda/src/sgattrib/alphaTransformProperty.cxx create mode 100644 panda/src/sgattrib/alphaTransformProperty.h create mode 100644 panda/src/sgattrib/alphaTransformTransition.I create mode 100644 panda/src/sgattrib/alphaTransformTransition.cxx create mode 100644 panda/src/sgattrib/alphaTransformTransition.h create mode 100644 panda/src/sgattrib/attribTraverser.cxx create mode 100644 panda/src/sgattrib/attribTraverser.h create mode 100644 panda/src/sgattrib/billboardTransition.I create mode 100644 panda/src/sgattrib/billboardTransition.cxx create mode 100644 panda/src/sgattrib/billboardTransition.h create mode 100644 panda/src/sgattrib/clipPlaneAttribute.I create mode 100644 panda/src/sgattrib/clipPlaneAttribute.cxx create mode 100644 panda/src/sgattrib/clipPlaneAttribute.h create mode 100644 panda/src/sgattrib/clipPlaneTransition.I create mode 100644 panda/src/sgattrib/clipPlaneTransition.cxx create mode 100644 panda/src/sgattrib/clipPlaneTransition.h create mode 100644 panda/src/sgattrib/colorAttribute.I create mode 100644 panda/src/sgattrib/colorAttribute.cxx create mode 100644 panda/src/sgattrib/colorAttribute.h create mode 100644 panda/src/sgattrib/colorBlendAttribute.I create mode 100644 panda/src/sgattrib/colorBlendAttribute.cxx create mode 100644 panda/src/sgattrib/colorBlendAttribute.h create mode 100644 panda/src/sgattrib/colorBlendProperty.I create mode 100644 panda/src/sgattrib/colorBlendProperty.cxx create mode 100644 panda/src/sgattrib/colorBlendProperty.h create mode 100644 panda/src/sgattrib/colorBlendTransition.I create mode 100644 panda/src/sgattrib/colorBlendTransition.cxx create mode 100644 panda/src/sgattrib/colorBlendTransition.h create mode 100644 panda/src/sgattrib/colorMaskAttribute.I create mode 100644 panda/src/sgattrib/colorMaskAttribute.cxx create mode 100644 panda/src/sgattrib/colorMaskAttribute.h create mode 100644 panda/src/sgattrib/colorMaskProperty.I create mode 100644 panda/src/sgattrib/colorMaskProperty.cxx create mode 100644 panda/src/sgattrib/colorMaskProperty.h create mode 100644 panda/src/sgattrib/colorMaskTransition.I create mode 100644 panda/src/sgattrib/colorMaskTransition.cxx create mode 100644 panda/src/sgattrib/colorMaskTransition.h create mode 100644 panda/src/sgattrib/colorMatrixAttribute.I create mode 100644 panda/src/sgattrib/colorMatrixAttribute.cxx create mode 100644 panda/src/sgattrib/colorMatrixAttribute.h create mode 100644 panda/src/sgattrib/colorMatrixTransition.I create mode 100644 panda/src/sgattrib/colorMatrixTransition.cxx create mode 100644 panda/src/sgattrib/colorMatrixTransition.h create mode 100644 panda/src/sgattrib/colorProperty.I create mode 100644 panda/src/sgattrib/colorProperty.cxx create mode 100644 panda/src/sgattrib/colorProperty.h create mode 100644 panda/src/sgattrib/colorTransition.I create mode 100644 panda/src/sgattrib/colorTransition.cxx create mode 100644 panda/src/sgattrib/colorTransition.h create mode 100644 panda/src/sgattrib/config_sgattrib.cxx create mode 100644 panda/src/sgattrib/config_sgattrib.h create mode 100644 panda/src/sgattrib/cullFaceAttribute.I create mode 100644 panda/src/sgattrib/cullFaceAttribute.cxx create mode 100644 panda/src/sgattrib/cullFaceAttribute.h create mode 100644 panda/src/sgattrib/cullFaceProperty.I create mode 100644 panda/src/sgattrib/cullFaceProperty.cxx create mode 100644 panda/src/sgattrib/cullFaceProperty.h create mode 100644 panda/src/sgattrib/cullFaceTransition.I create mode 100644 panda/src/sgattrib/cullFaceTransition.cxx create mode 100644 panda/src/sgattrib/cullFaceTransition.h create mode 100644 panda/src/sgattrib/decalAttribute.I create mode 100644 panda/src/sgattrib/decalAttribute.cxx create mode 100644 panda/src/sgattrib/decalAttribute.h create mode 100644 panda/src/sgattrib/decalTransition.I create mode 100644 panda/src/sgattrib/decalTransition.cxx create mode 100644 panda/src/sgattrib/decalTransition.h create mode 100644 panda/src/sgattrib/depthTestAttribute.I create mode 100644 panda/src/sgattrib/depthTestAttribute.cxx create mode 100644 panda/src/sgattrib/depthTestAttribute.h create mode 100644 panda/src/sgattrib/depthTestProperty.I create mode 100644 panda/src/sgattrib/depthTestProperty.cxx create mode 100644 panda/src/sgattrib/depthTestProperty.h create mode 100644 panda/src/sgattrib/depthTestTransition.I create mode 100644 panda/src/sgattrib/depthTestTransition.cxx create mode 100644 panda/src/sgattrib/depthTestTransition.h create mode 100644 panda/src/sgattrib/depthWriteAttribute.I create mode 100644 panda/src/sgattrib/depthWriteAttribute.cxx create mode 100644 panda/src/sgattrib/depthWriteAttribute.h create mode 100644 panda/src/sgattrib/depthWriteTransition.I create mode 100644 panda/src/sgattrib/depthWriteTransition.cxx create mode 100644 panda/src/sgattrib/depthWriteTransition.h create mode 100644 panda/src/sgattrib/drawBoundsTransition.I create mode 100644 panda/src/sgattrib/drawBoundsTransition.cxx create mode 100644 panda/src/sgattrib/drawBoundsTransition.h create mode 100644 panda/src/sgattrib/fogAttribute.I create mode 100644 panda/src/sgattrib/fogAttribute.cxx create mode 100644 panda/src/sgattrib/fogAttribute.h create mode 100644 panda/src/sgattrib/fogTransition.I create mode 100644 panda/src/sgattrib/fogTransition.cxx create mode 100644 panda/src/sgattrib/fogTransition.h create mode 100644 panda/src/sgattrib/linesmoothAttribute.I create mode 100644 panda/src/sgattrib/linesmoothAttribute.cxx create mode 100644 panda/src/sgattrib/linesmoothAttribute.h create mode 100644 panda/src/sgattrib/linesmoothTransition.I create mode 100644 panda/src/sgattrib/linesmoothTransition.cxx create mode 100644 panda/src/sgattrib/linesmoothTransition.h create mode 100644 panda/src/sgattrib/materialAttribute.I create mode 100644 panda/src/sgattrib/materialAttribute.cxx create mode 100644 panda/src/sgattrib/materialAttribute.h create mode 100644 panda/src/sgattrib/materialTransition.I create mode 100644 panda/src/sgattrib/materialTransition.cxx create mode 100644 panda/src/sgattrib/materialTransition.h create mode 100644 panda/src/sgattrib/pointShapeAttribute.I create mode 100644 panda/src/sgattrib/pointShapeAttribute.cxx create mode 100644 panda/src/sgattrib/pointShapeAttribute.h create mode 100644 panda/src/sgattrib/pointShapeProperty.I create mode 100644 panda/src/sgattrib/pointShapeProperty.cxx create mode 100644 panda/src/sgattrib/pointShapeProperty.h create mode 100644 panda/src/sgattrib/pointShapeTransition.I create mode 100644 panda/src/sgattrib/pointShapeTransition.cxx create mode 100644 panda/src/sgattrib/pointShapeTransition.h create mode 100644 panda/src/sgattrib/polygonOffsetAttribute.I create mode 100644 panda/src/sgattrib/polygonOffsetAttribute.cxx create mode 100644 panda/src/sgattrib/polygonOffsetAttribute.h create mode 100644 panda/src/sgattrib/polygonOffsetProperty.I create mode 100644 panda/src/sgattrib/polygonOffsetProperty.cxx create mode 100644 panda/src/sgattrib/polygonOffsetProperty.h create mode 100644 panda/src/sgattrib/polygonOffsetTransition.I create mode 100644 panda/src/sgattrib/polygonOffsetTransition.cxx create mode 100644 panda/src/sgattrib/polygonOffsetTransition.h create mode 100644 panda/src/sgattrib/pruneTransition.I create mode 100644 panda/src/sgattrib/pruneTransition.cxx create mode 100644 panda/src/sgattrib/pruneTransition.h create mode 100644 panda/src/sgattrib/renderModeAttribute.I create mode 100644 panda/src/sgattrib/renderModeAttribute.cxx create mode 100644 panda/src/sgattrib/renderModeAttribute.h create mode 100644 panda/src/sgattrib/renderModeProperty.I create mode 100644 panda/src/sgattrib/renderModeProperty.cxx create mode 100644 panda/src/sgattrib/renderModeProperty.h create mode 100644 panda/src/sgattrib/renderModeTransition.I create mode 100644 panda/src/sgattrib/renderModeTransition.cxx create mode 100644 panda/src/sgattrib/renderModeTransition.h create mode 100644 panda/src/sgattrib/renderRelation.I create mode 100644 panda/src/sgattrib/renderRelation.N create mode 100644 panda/src/sgattrib/renderRelation.cxx create mode 100644 panda/src/sgattrib/renderRelation.h create mode 100644 panda/src/sgattrib/showHideAttribute.I create mode 100644 panda/src/sgattrib/showHideAttribute.cxx create mode 100644 panda/src/sgattrib/showHideAttribute.h create mode 100644 panda/src/sgattrib/showHideNameClass.h create mode 100644 panda/src/sgattrib/showHideTransition.I create mode 100644 panda/src/sgattrib/showHideTransition.cxx create mode 100644 panda/src/sgattrib/showHideTransition.h create mode 100644 panda/src/sgattrib/stencilAttribute.I create mode 100644 panda/src/sgattrib/stencilAttribute.cxx create mode 100644 panda/src/sgattrib/stencilAttribute.h create mode 100644 panda/src/sgattrib/stencilProperty.I create mode 100644 panda/src/sgattrib/stencilProperty.cxx create mode 100644 panda/src/sgattrib/stencilProperty.h create mode 100644 panda/src/sgattrib/stencilTransition.I create mode 100644 panda/src/sgattrib/stencilTransition.cxx create mode 100644 panda/src/sgattrib/stencilTransition.h create mode 100644 panda/src/sgattrib/test_attrib.cxx create mode 100644 panda/src/sgattrib/texGenAttribute.I create mode 100644 panda/src/sgattrib/texGenAttribute.cxx create mode 100644 panda/src/sgattrib/texGenAttribute.h create mode 100644 panda/src/sgattrib/texGenProperty.I create mode 100644 panda/src/sgattrib/texGenProperty.cxx create mode 100644 panda/src/sgattrib/texGenProperty.h create mode 100644 panda/src/sgattrib/texGenTransition.I create mode 100644 panda/src/sgattrib/texGenTransition.cxx create mode 100644 panda/src/sgattrib/texGenTransition.h create mode 100644 panda/src/sgattrib/texMatrixAttribute.I create mode 100644 panda/src/sgattrib/texMatrixAttribute.cxx create mode 100644 panda/src/sgattrib/texMatrixAttribute.h create mode 100644 panda/src/sgattrib/texMatrixTransition.I create mode 100644 panda/src/sgattrib/texMatrixTransition.cxx create mode 100644 panda/src/sgattrib/texMatrixTransition.h create mode 100644 panda/src/sgattrib/textureApplyAttribute.I create mode 100644 panda/src/sgattrib/textureApplyAttribute.cxx create mode 100644 panda/src/sgattrib/textureApplyAttribute.h create mode 100644 panda/src/sgattrib/textureApplyProperty.I create mode 100644 panda/src/sgattrib/textureApplyProperty.cxx create mode 100644 panda/src/sgattrib/textureApplyProperty.h create mode 100644 panda/src/sgattrib/textureApplyTransition.I create mode 100644 panda/src/sgattrib/textureApplyTransition.cxx create mode 100644 panda/src/sgattrib/textureApplyTransition.h create mode 100644 panda/src/sgattrib/textureAttribute.I create mode 100644 panda/src/sgattrib/textureAttribute.cxx create mode 100644 panda/src/sgattrib/textureAttribute.h create mode 100644 panda/src/sgattrib/textureTransition.I create mode 100644 panda/src/sgattrib/textureTransition.cxx create mode 100644 panda/src/sgattrib/textureTransition.h create mode 100644 panda/src/sgattrib/transformAttribute.I create mode 100644 panda/src/sgattrib/transformAttribute.cxx create mode 100644 panda/src/sgattrib/transformAttribute.h create mode 100644 panda/src/sgattrib/transformTransition.I create mode 100644 panda/src/sgattrib/transformTransition.cxx create mode 100644 panda/src/sgattrib/transformTransition.h create mode 100644 panda/src/sgattrib/transparencyAttribute.I create mode 100644 panda/src/sgattrib/transparencyAttribute.cxx create mode 100644 panda/src/sgattrib/transparencyAttribute.h create mode 100644 panda/src/sgattrib/transparencyProperty.I create mode 100644 panda/src/sgattrib/transparencyProperty.cxx create mode 100644 panda/src/sgattrib/transparencyProperty.h create mode 100644 panda/src/sgattrib/transparencyTransition.I create mode 100644 panda/src/sgattrib/transparencyTransition.cxx create mode 100644 panda/src/sgattrib/transparencyTransition.h create mode 100644 panda/src/sgidisplay/Sources.pp create mode 100644 panda/src/sgidisplay/config_sgidisplay.cxx create mode 100644 panda/src/sgidisplay/config_sgidisplay.h create mode 100644 panda/src/sgidisplay/sgiGraphicsPipe.cxx create mode 100644 panda/src/sgidisplay/sgiGraphicsPipe.h create mode 100644 panda/src/sgidisplay/sgiHardwareChannel.cxx create mode 100644 panda/src/sgidisplay/sgiHardwareChannel.h create mode 100644 panda/src/sgiglutdisplay/Sources.pp create mode 100644 panda/src/sgiglutdisplay/config_sgiglutdisplay.cxx create mode 100644 panda/src/sgiglutdisplay/config_sgiglutdisplay.h create mode 100644 panda/src/sgiglutdisplay/sgiglutGraphicsPipe.cxx create mode 100644 panda/src/sgiglutdisplay/sgiglutGraphicsPipe.h create mode 100644 panda/src/sgiglxdisplay/Sources.pp create mode 100644 panda/src/sgiglxdisplay/config_sgiglxdisplay.cxx create mode 100644 panda/src/sgiglxdisplay/config_sgiglxdisplay.h create mode 100644 panda/src/sgiglxdisplay/sgiglxGraphicsPipe.cxx create mode 100644 panda/src/sgiglxdisplay/sgiglxGraphicsPipe.h create mode 100644 panda/src/sgmanip/Sources.pp create mode 100644 panda/src/sgmanip/config_sgmanip.cxx create mode 100644 panda/src/sgmanip/config_sgmanip.h create mode 100644 panda/src/sgmanip/findApproxLevel.I create mode 100644 panda/src/sgmanip/findApproxLevel.cxx create mode 100644 panda/src/sgmanip/findApproxLevel.h create mode 100644 panda/src/sgmanip/findApproxPath.I create mode 100644 panda/src/sgmanip/findApproxPath.cxx create mode 100644 panda/src/sgmanip/findApproxPath.h create mode 100644 panda/src/sgmanip/get_cout.h create mode 100644 panda/src/sgmanip/nodePath.I create mode 100644 panda/src/sgmanip/nodePath.cxx create mode 100644 panda/src/sgmanip/nodePath.h create mode 100644 panda/src/sgmanip/nodePathBase.I create mode 100644 panda/src/sgmanip/nodePathBase.cxx create mode 100644 panda/src/sgmanip/nodePathBase.h create mode 100644 panda/src/sgmanip/nodePathCollection.I create mode 100644 panda/src/sgmanip/nodePathCollection.cxx create mode 100644 panda/src/sgmanip/nodePathCollection.h create mode 100644 panda/src/sgmanip/nodePathLerps.cxx create mode 100644 panda/src/sgmanip/nodePathLerps.h create mode 100644 panda/src/sgmanip/test_sgmanip.cxx create mode 100644 panda/src/sgraph/Sources.pp create mode 100644 panda/src/sgraph/camera.I create mode 100644 panda/src/sgraph/camera.cxx create mode 100644 panda/src/sgraph/camera.h create mode 100644 panda/src/sgraph/config_sgraph.cxx create mode 100644 panda/src/sgraph/config_sgraph.h create mode 100644 panda/src/sgraph/geomNode.I create mode 100644 panda/src/sgraph/geomNode.cxx create mode 100644 panda/src/sgraph/geomNode.h create mode 100644 panda/src/sgraph/geomTransformer.I create mode 100644 panda/src/sgraph/geomTransformer.cxx create mode 100644 panda/src/sgraph/geomTransformer.h create mode 100644 panda/src/sgraph/planeNode.I create mode 100644 panda/src/sgraph/planeNode.cxx create mode 100644 panda/src/sgraph/planeNode.h create mode 100644 panda/src/sgraph/projectionNode.I create mode 100644 panda/src/sgraph/projectionNode.cxx create mode 100644 panda/src/sgraph/projectionNode.h create mode 100644 panda/src/sgraph/renderTraverser.I create mode 100644 panda/src/sgraph/renderTraverser.cxx create mode 100644 panda/src/sgraph/renderTraverser.h create mode 100644 panda/src/sgraph/test_sgraph.cxx create mode 100644 panda/src/sgraphutil/Sources.pp create mode 100644 panda/src/sgraphutil/appTraverser.I create mode 100644 panda/src/sgraphutil/appTraverser.cxx create mode 100644 panda/src/sgraphutil/appTraverser.h create mode 100644 panda/src/sgraphutil/config_sgraphutil.cxx create mode 100644 panda/src/sgraphutil/config_sgraphutil.h create mode 100644 panda/src/sgraphutil/directRenderLevelState.h create mode 100644 panda/src/sgraphutil/directRenderTraverser.I create mode 100644 panda/src/sgraphutil/directRenderTraverser.cxx create mode 100644 panda/src/sgraphutil/directRenderTraverser.h create mode 100644 panda/src/sgraphutil/frustumCullTraverser.I create mode 100644 panda/src/sgraphutil/frustumCullTraverser.h create mode 100644 panda/src/sgraphutil/get_rel_pos.I create mode 100644 panda/src/sgraphutil/get_rel_pos.cxx create mode 100644 panda/src/sgraphutil/get_rel_pos.h create mode 100644 panda/src/sgraphutil/sceneGraphAnalyzer.cxx create mode 100644 panda/src/sgraphutil/sceneGraphAnalyzer.h create mode 100644 panda/src/sgraphutil/sceneGraphReducer.I create mode 100644 panda/src/sgraphutil/sceneGraphReducer.cxx create mode 100644 panda/src/sgraphutil/sceneGraphReducer.h create mode 100644 panda/src/shader/Sources.pp create mode 100644 panda/src/shader/casterShader.I create mode 100644 panda/src/shader/casterShader.cxx create mode 100644 panda/src/shader/casterShader.h create mode 100644 panda/src/shader/config_shader.cxx create mode 100644 panda/src/shader/config_shader.h create mode 100644 panda/src/shader/outlineShader.cxx create mode 100644 panda/src/shader/outlineShader.h create mode 100644 panda/src/shader/planarReflector.cxx create mode 100644 panda/src/shader/planarReflector.h create mode 100644 panda/src/shader/projtexShader.cxx create mode 100644 panda/src/shader/projtexShader.h create mode 100644 panda/src/shader/projtexShadower.cxx create mode 100644 panda/src/shader/projtexShadower.h create mode 100644 panda/src/shader/shader.I create mode 100644 panda/src/shader/shader.cxx create mode 100644 panda/src/shader/shader.h create mode 100644 panda/src/shader/shaderTransition.I create mode 100644 panda/src/shader/shaderTransition.cxx create mode 100644 panda/src/shader/shaderTransition.h create mode 100644 panda/src/shader/spheretexHighlighter.cxx create mode 100644 panda/src/shader/spheretexHighlighter.h create mode 100644 panda/src/shader/spheretexReflector.cxx create mode 100644 panda/src/shader/spheretexReflector.h create mode 100644 panda/src/shader/spheretexShader.cxx create mode 100644 panda/src/shader/spheretexShader.h create mode 100644 panda/src/shader/spotlightShader.cxx create mode 100644 panda/src/shader/spotlightShader.h create mode 100644 panda/src/switchnode/LODNode.I create mode 100644 panda/src/switchnode/LODNode.cxx create mode 100644 panda/src/switchnode/LODNode.h create mode 100644 panda/src/switchnode/Sources.pp create mode 100644 panda/src/switchnode/config_switchnode.cxx create mode 100644 panda/src/switchnode/config_switchnode.h create mode 100644 panda/src/switchnode/sequenceNode.cxx create mode 100644 panda/src/switchnode/sequenceNode.h create mode 100644 panda/src/switchnode/test_sequences.cxx create mode 100644 panda/src/testbed/Configrc create mode 100644 panda/src/testbed/Sources.pp create mode 100644 panda/src/testbed/chat_test.cxx create mode 100644 panda/src/testbed/demo.cxx create mode 100644 panda/src/testbed/downloader_test.cxx create mode 100644 panda/src/testbed/herc.cxx create mode 100644 panda/src/testbed/indirect.cxx create mode 100644 panda/src/testbed/loader_test.cxx create mode 100644 panda/src/testbed/lod_test.cxx create mode 100644 panda/src/testbed/min_herc.cxx create mode 100644 panda/src/testbed/min_shader.cxx create mode 100644 panda/src/testbed/motion.cxx create mode 100644 panda/src/testbed/panda.cxx create mode 100644 panda/src/testbed/rock-floor.rgb create mode 100644 panda/src/testbed/shader_test.cxx create mode 100644 panda/src/testbed/test_eggWrite.cxx create mode 100644 panda/src/testbed/test_particles.cxx create mode 100644 panda/src/testbed/test_recparticles.cxx create mode 100644 panda/src/testbed/text_test.cxx create mode 100644 panda/src/testbed/vrpn_demo.cxx create mode 100644 panda/src/text/Sources.pp create mode 100644 panda/src/text/config_text.cxx create mode 100644 panda/src/text/config_text.h create mode 100644 panda/src/text/textNode.I create mode 100644 panda/src/text/textNode.cxx create mode 100644 panda/src/text/textNode.h create mode 100644 panda/src/tform/Sources.pp create mode 100644 panda/src/tform/buttonThrower.cxx create mode 100644 panda/src/tform/buttonThrower.h create mode 100644 panda/src/tform/config_tform.cxx create mode 100644 panda/src/tform/config_tform.h create mode 100644 panda/src/tform/driveInterface.cxx create mode 100644 panda/src/tform/driveInterface.h create mode 100644 panda/src/tform/mouseWatcher.I create mode 100644 panda/src/tform/mouseWatcher.cxx create mode 100644 panda/src/tform/mouseWatcher.h create mode 100644 panda/src/tform/mouseWatcherRegion.I create mode 100644 panda/src/tform/mouseWatcherRegion.cxx create mode 100644 panda/src/tform/mouseWatcherRegion.h create mode 100644 panda/src/tform/planarSlider.cxx create mode 100644 panda/src/tform/planarSlider.h create mode 100644 panda/src/tform/trackball.cxx create mode 100644 panda/src/tform/trackball.h create mode 100644 panda/src/tform/trackerTransform.cxx create mode 100644 panda/src/tform/trackerTransform.h create mode 100644 panda/src/tform/transform2sg.cxx create mode 100644 panda/src/tform/transform2sg.h create mode 100644 panda/src/tiff/README create mode 100644 panda/src/tiff/Sources.pp create mode 100644 panda/src/tiff/g3states.h create mode 100644 panda/src/tiff/mkg3states.c create mode 100644 panda/src/tiff/mkspans.c create mode 100644 panda/src/tiff/t4.h create mode 100644 panda/src/tiff/tif_apple.c create mode 100644 panda/src/tiff/tif_aux.c create mode 100644 panda/src/tiff/tif_ccittrle.c create mode 100644 panda/src/tiff/tif_close.c create mode 100644 panda/src/tiff/tif_compress.c create mode 100644 panda/src/tiff/tif_dir.c create mode 100644 panda/src/tiff/tif_dirinfo.c create mode 100644 panda/src/tiff/tif_dirread.c create mode 100644 panda/src/tiff/tif_dirwrite.c create mode 100644 panda/src/tiff/tif_dumpmode.c create mode 100644 panda/src/tiff/tif_error.c create mode 100644 panda/src/tiff/tif_fax3.c create mode 100644 panda/src/tiff/tif_fax3.h create mode 100644 panda/src/tiff/tif_fax4.c create mode 100644 panda/src/tiff/tif_flush.c create mode 100644 panda/src/tiff/tif_getimage.c create mode 100644 panda/src/tiff/tif_jpeg.c create mode 100644 panda/src/tiff/tif_lzw.c create mode 100644 panda/src/tiff/tif_machdep.c create mode 100644 panda/src/tiff/tif_msdos.c create mode 100644 panda/src/tiff/tif_next.c create mode 100644 panda/src/tiff/tif_open.c create mode 100644 panda/src/tiff/tif_packbits.c create mode 100644 panda/src/tiff/tif_print.c create mode 100644 panda/src/tiff/tif_read.c create mode 100644 panda/src/tiff/tif_strip.c create mode 100644 panda/src/tiff/tif_swab.c create mode 100644 panda/src/tiff/tif_thunder.c create mode 100644 panda/src/tiff/tif_tile.c create mode 100644 panda/src/tiff/tif_unix.c create mode 100644 panda/src/tiff/tif_version.c create mode 100644 panda/src/tiff/tif_vms.c create mode 100644 panda/src/tiff/tif_warning.c create mode 100644 panda/src/tiff/tif_write.c create mode 100644 panda/src/tiff/tiff.h create mode 100644 panda/src/tiff/tiffcomp.h create mode 100644 panda/src/tiff/tiffconf.h create mode 100644 panda/src/tiff/tiffio.h create mode 100644 panda/src/tiff/tiffiop.h create mode 100644 panda/src/vrpn/Sources.pp create mode 100644 panda/src/vrpn/config_vrpn.cxx create mode 100644 panda/src/vrpn/config_vrpn.h create mode 100644 panda/src/vrpn/vrpnClient.I create mode 100644 panda/src/vrpn/vrpnClient.cxx create mode 100644 panda/src/vrpn/vrpnClient.h create mode 100644 panda/src/wdxdisplay/Sources.pp create mode 100644 panda/src/wdxdisplay/config_wdxdisplay.cxx create mode 100644 panda/src/wdxdisplay/config_wdxdisplay.h create mode 100644 panda/src/wdxdisplay/wdxGraphicsPipe.cxx create mode 100644 panda/src/wdxdisplay/wdxGraphicsPipe.h create mode 100644 panda/src/wdxdisplay/wdxGraphicsWindow.cxx create mode 100644 panda/src/wdxdisplay/wdxGraphicsWindow.h create mode 100644 panda/src/wgldisplay/Sources.pp create mode 100644 panda/src/wgldisplay/config_wgldisplay.cxx create mode 100644 panda/src/wgldisplay/config_wgldisplay.h create mode 100644 panda/src/wgldisplay/test_wgl.cxx create mode 100644 panda/src/wgldisplay/wglGraphicsPipe.cxx create mode 100644 panda/src/wgldisplay/wglGraphicsPipe.h create mode 100644 panda/src/wgldisplay/wglGraphicsWindow.cxx create mode 100644 panda/src/wgldisplay/wglGraphicsWindow.h diff --git a/panda/Config.Irix.pp b/panda/Config.Irix.pp new file mode 100644 index 0000000000..c01560fce3 --- /dev/null +++ b/panda/Config.Irix.pp @@ -0,0 +1,49 @@ +// +// Config.Irix.pp +// +// This file defines some custom config variables for the SGI/Irix +// platform. It makes some initial guesses about compiler features, +// etc. +// + +// Is the platform big-endian (like an SGI workstation) or +// little-endian (like a PC)? Define this to the empty string to +// indicate little-endian, or nonempty to indicate big-endian. +#define WORDS_BIGENDIAN 1 + +// Does the C++ compiler support namespaces? +#define HAVE_NAMESPACE 1 + +// Does the C++ compiler support ios::binary? +#define HAVE_IOS_BINARY + +// Do we have a gettimeofday() function? +#define HAVE_GETTIMEOFDAY 1 + +// Does gettimeofday() take only one parameter? +#define GETTIMEOFDAY_ONE_PARAM + +// Do we have getopt() and/or getopt_long_only() built into the +// system? +#define HAVE_GETOPT 1 +#define HAVE_GETOPT_LONG_ONLY + +// Should we include or ? Define HAVE_IOSTREAM +// to nonempty if we should use , or empty if we should use +// . +#define HAVE_IOSTREAM + +// Do we have a true stringstream class defined in ? +#define HAVE_SSTREAM + +// Do we have ? +#define HAVE_MALLOC_H 1 + +// Do we have ? +#define HAVE_ALLOCA_H 1 + +// Do we have ? +#define HAVE_SYS_TYPES_H 1 + +// Do we have ? +#define HAVE_UNISTD_H 1 diff --git a/panda/Config.Linux.pp b/panda/Config.Linux.pp new file mode 100644 index 0000000000..f33593d9a4 --- /dev/null +++ b/panda/Config.Linux.pp @@ -0,0 +1,49 @@ +// +// Config.Linux.pp +// +// This file defines some custom config variables for the Linux +// platform. It makes some initial guesses about compiler features, +// etc. +// + +// Is the platform big-endian (like an SGI workstation) or +// little-endian (like a PC)? Define this to the empty string to +// indicate little-endian, or nonempty to indicate big-endian. +#define WORDS_BIGENDIAN + +// Does the C++ compiler support namespaces? +#define HAVE_NAMESPACE 1 + +// Does the C++ compiler support ios::binary? +#define HAVE_IOS_BINARY 1 + +// Do we have a gettimeofday() function? +#define HAVE_GETTIMEOFDAY 1 + +// Does gettimeofday() take only one parameter? +#define GETTIMEOFDAY_ONE_PARAM + +// Do we have getopt() and/or getopt_long_only() built into the +// system? +#define HAVE_GETOPT 1 +#define HAVE_GETOPT_LONG_ONLY 1 + +// Should we include or ? Define HAVE_IOSTREAM +// to nonempty if we should use , or empty if we should use +// . +#define HAVE_IOSTREAM + +// Do we have a true stringstream class defined in ? +#define HAVE_SSTREAM + +// Do we have ? +#define HAVE_MALLOC_H 1 + +// Do we have ? +#define HAVE_ALLOCA_H 1 + +// Do we have ? +#define HAVE_SYS_TYPES_H 1 + +// Do we have ? +#define HAVE_UNISTD_H 1 diff --git a/panda/Config.Win32.pp b/panda/Config.Win32.pp new file mode 100644 index 0000000000..748945306f --- /dev/null +++ b/panda/Config.Win32.pp @@ -0,0 +1,49 @@ +// +// Config.Win32.pp +// +// This file defines some custom config variables for the Windows +// platform. It makes some initial guesses about compiler features, +// etc. +// + +// Is the platform big-endian (like an SGI workstation) or +// little-endian (like a PC)? Define this to the empty string to +// indicate little-endian, or nonempty to indicate big-endian. +#define WORDS_BIGENDIAN + +// Does the C++ compiler support namespaces? +#define HAVE_NAMESPACE 1 + +// Does the C++ compiler support ios::binary? +#define HAVE_IOS_BINARY 1 + +// Do we have a gettimeofday() function? +#define HAVE_GETTIMEOFDAY + +// Does gettimeofday() take only one parameter? +#define GETTIMEOFDAY_ONE_PARAM + +// Do we have getopt() and/or getopt_long_only() built into the +// system? +#define HAVE_GETOPT +#define HAVE_GETOPT_LONG_ONLY + +// Should we include or ? Define HAVE_IOSTREAM +// to nonempty if we should use , or empty if we should use +// . +#define HAVE_IOSTREAM 1 + +// Do we have a true stringstream class defined in ? +#define HAVE_SSTREAM 1 + +// Do we have ? +#define HAVE_MALLOC_H 1 + +// Do we have ? +#define HAVE_ALLOCA_H + +// Do we have ? +#define HAVE_SYS_TYPES_H 1 + +// Do we have ? +#define HAVE_UNISTD_H diff --git a/panda/Config.pp b/panda/Config.pp new file mode 100644 index 0000000000..9e27543718 --- /dev/null +++ b/panda/Config.pp @@ -0,0 +1,167 @@ +// +// Config.pp +// +// This file defines certain configuration variables that are written +// into the various make scripts. It is processed by ppremake (along +// with the Sources.pp files in each of the various directories) to +// generate build scripts appropriate to each environment. +// +// ppremake is capable of generating generic Unix autoconf/automake +// style build scripts, as well as makefiles customized for SGI's +// MipsPRO compiler or for Microsoft's Visual C++. It can also +// generate Microsoft Developer's Studio project files directly. In +// principle, it can be extended to generate suitable build script +// files for any number of different build environments. +// +// All of these build scripts can be tuned for a particular +// environment via this file. This is the place for the user to +// specify which external packages are installed and where, or to +// enable or disable certain optional features. However, it is +// suggested that rather than modify this file directly, you create a +// custom file in your home directory and there redefine whatever +// variables are appropriate, and set the environment variable +// PPREMAKE_CONFIG to refer to it. In this way, you can easily get an +// updated source tree (including a new Config.pp) without risking +// accidentally losing your customizations. This also avoids having +// to redefine the same variables in different packages (for instance, +// in dtool and in panda). +// +// If you *do* decide to make changes directly to this file, you +// should also comment out the line near the bottom that includes the +// file $[TOPDIRPREFIX]Config.$[PLATFORM].pp, to avoid stomping on the +// changes you make. +// +// The syntax in this file resembles some hybrid between C++ +// preprocessor declarations and GNU make variables. This is the same +// syntax used in the various ppremake system configure files; it's +// designed to be easy to use as a macro language to generate +// makefiles and their ilk. +// + +// What kind of build scripts are we generating? This selects a +// suitable template file from the ppremake system files. The +// allowable choices, at present, are: +// +// autoconf - Generate configure.in and a series of Makefile.am files, +// suitable for using with autoconf/automake. Not quite +// there yet. +// stopgap - Generate original Cary-style Makefile/Makefile.install/etc. +// files, to ease transition to the new system. +// +#define BUILD_TYPE stopgap + +// Define the directory in which the system ppremake files are +// installed. +#define PPREMAKE_DIR /usr/local/panda/share + + +// In which directory should this package be installed when you type +// "make install"? This has no meaning when BUILD_TYPE is "stopgap". +#define INSTALL_DIR /usr/local/panda + + +// What level of compiler optimization/debug symbols should we build? +// The various optimize levels are defined as follows: +// +// 1 - No compiler optimizations, full debug symbols +// 2 - Full compiler optimizations, full debug symbols +// (if the compiler supports this) +// 3 - Full compiler optimizations, no debug symbols +// 4 - Full optimizations, no debug symbols, and asserts removed +// +// Setting this has no effect when BUILD_TYPE is "stopgap". In this +// case, the compiler optimizations are selected by setting the +// environment variable OPTIMIZE accordingly at compile time. +#define OPTIMIZE 1 + + + +//////////////////////////////////////////////////////////////////// +// The remaining variables are considered only if BUILD_TYPE is not +// "autoconf". (Autoconf can determine these directly.) +//////////////////////////////////////////////////////////////////// + +// NOTE: In the following, to indicate "yes" to a yes/no question, +// define the variable to be a nonempty string. To indicate "no", +// define the variable to be an empty string. + +// Is Python installed, and should Python interfaces be generated? If +// Python is installed, which directory is it in? (If the directory +// is someplace standard like /usr/include, you may leave it blank.) +#define HAVE_PYTHON 1 +#define PYTHON_INCLUDE /usr/local/include/python1.6 +#define PYTHON_LIB + +// Is NSPR installed, and where? +#define HAVE_NSPR 1 +#define NSPR_INCLUDE /usr/local/mozilla/dist/*/include +#define NSPR_LIB + +// Is VRPN installed, and where? +#define HAVE_VRPN +#define VRPN_INCLUDE +#define VRPN_LIB + +// Is ZLIB installed, and where? +#define HAVE_ZLIB 1 +#define ZLIB_INCLUDE +#define ZLIB_LIB + +// Is OpenGL installed, and where? +#define HAVE_GL 1 +#define GL_INCLUDE +#define GL_LIB +#define GLU_INCLUDE +#define GLU_LIB + +// How about GLX? +#define HAVE_GLX 1 +#define GLX_INCLUDE +#define GLX_LIB + +// Glut? +#define HAVE_GLUT +#define GLUT_INCLUDE +#define GLUT_LIB + +// Should we try to build the WGL interface? +#define HAVE_WGL + +// Should we try to build the DirectX interface? +#define HAVE_DX + +// Do you want to build the Renderman interface? +#define HAVE_RIB + +// Is Mikmod installed? +#define HAVE_MIKMOD +#define MIKMOD_CFLAGS +#define MIKMOD_INCLUDE +#define MIKMOD_LIB + + +////////////////////////////////////////////////////////////////////// +// There are also some additional variables that control specific +// compiler/platform features or characteristics, defined in the +// platform specific file Config.platform.pp. Be sure to inspect +// these variables for correctness too. As above, these are +// unnecessary when BUILD_TYPE is "autoconf". +////////////////////////////////////////////////////////////////////// +#include $[TOPDIRPREFIX]Config.$[PLATFORM].pp + + +// Also pull in whatever package-specific variables there may be. +#include $[TOPDIRPREFIX]Package.pp + + +// If the environment variable PPREMAKE_CONFIG is set, it points to a +// user-customized Config.pp file, for instance in the user's home +// directory. This file might redefine any of the variables defined +// above. +#if $[ne $[PPREMAKE_CONFIG],] + #include $[PPREMAKE_CONFIG] +#endif + + +// Finally, include the system configure file. +#include $[PPREMAKE_DIR]/System.pp diff --git a/panda/LocalSetup.pp b/panda/LocalSetup.pp new file mode 100644 index 0000000000..f3506cc238 --- /dev/null +++ b/panda/LocalSetup.pp @@ -0,0 +1,211 @@ +// +// LocalSetup.pp +// +// This file contains further instructions to set up the DTOOL package +// when using ppremake. In particular, it creates the dtool_config.h +// file based on the user's selected configure variables. This script +// need not execute when BUILD_TYPE is "autoconf"; in this case, the +// dtool_config.h file will automatically be correctly generated by +// configure. +// + +#if $[ne $[BUILD_TYPE],autoconf] + +// A couple of variables to output the C #define and #undef +// directives, since we can't type them literally. +#define define #define +#define undef #undef + +#output dtool_config.h +#format straight +/* dtool_config.h. Generated automatically by $[PROGRAM] $[PROGVER] from $[SOURCEFILE]. */ + +/* Define if you have the ANSI C header files. */ +$[define] STDC_HEADERS 1 + +/* Define if your processor stores words with the most significant + byte first (like Motorola and SPARC, unlike Intel and VAX). */ +#if $[WORDS_BIGENDIAN] +$[define] WORDS_BIGENDIAN 1 +#else +$[undef] WORDS_BIGENDIAN +#endif + +/* Define if the X Window System is missing or not being used. */ +$[undef] X_DISPLAY_MISSING + +/* Define if lex declares yytext as a char * by default, not a char[]. */ +$[undef] YYTEXT_POINTER + +/* Define if the C++ compiler uses namespaces. */ +#if $[HAVE_NAMESPACE] +$[define] HAVE_NAMESPACE 1 +#else +$[undef] HAVE_NAMESPACE +#endif + +/* Define if the C++ iostream library supports ios::binary. */ +#if $[HAVE_IOS_BINARY] +$[define] HAVE_IOS_BINARY 1 +#else +$[undef] HAVE_IOS_BINARY +#endif + +/* Define if we have Python installed. */ +#if $[HAVE_PYTHON] +$[define] HAVE_PYTHON 1 +#else +$[undef] HAVE_PYTHON +#endif + +/* Define if we have NSPR installed. */ +#if $[HAVE_NSPR] +$[define] HAVE_NSPR 1 +#else +$[undef] HAVE_NSPR +#endif + +/* Define if we have VRPN installed. */ +#if $[HAVE_VRPN] +$[define] HAVE_VRPN 1 +#else +$[undef] HAVE_VRPN +#endif + +/* Define if we have zlib installed. */ +#if $[HAVE_ZLIB] +$[define] HAVE_ZLIB 1 +#else +$[undef] HAVE_ZLIB +#endif + +/* Define if we have OpenGL installed and want to build for GL. */ +#if $[HAVE_GL] +$[define] HAVE_GL 1 +#else +$[undef] HAVE_GL +#endif + +/* Define if we have GLU installed. */ +#if $[HAVE_GLU] +$[define] HAVE_GLU 1 +#else +$[undef] HAVE_GLU +#endif + +/* Define if we have GLX installed and want to build for GLX. */ +#if $[HAVE_GLX] +$[define] HAVE_GLX 1 +#else +$[undef] HAVE_GLX +#endif + +/* Define if we have Glut installed and want to build for Glut. */ +#if $[HAVE_GLUT] +$[define] HAVE_GLUT 1 +#else +$[undef] HAVE_GLUT +#endif + +/* Define if we want to build the Renderman interface. */ +#if $[HAVE_RIB] +$[define] HAVE_RIB 1 +#else +$[undef] HAVE_RIB +#endif + +/* Define if we want to use mikmod for audio. */ +#if $[HAVE_MIKMOD] +$[define] HAVE_MIKMOD 1 +#else +$[undef] HAVE_MIKMOD +#endif + +/* Define if we have a gettimeofday() function. */ +#if $[HAVE_GETTIMEOFDAY] +$[define] HAVE_GETTIMEOFDAY 1 +#else +$[undef] HAVE_GETTIMEOFDAY +#endif + +/* Define if gettimeofday() takes only one parameter. */ +#if $[GETTIMEOFDAY_ONE_PARAM] +$[define] GETTIMEOFDAY_ONE_PARAM 1 +#else +$[undef] GETTIMEOFDAY_ONE_PARAM +#endif + +/* Define if you have the getopt function. */ +#if $[HAVE_GETOPT] +$[define] HAVE_GETOPT 1 +#else +$[undef] HAVE_GETOPT +#endif + +/* Define if you have the getopt_long_only function. */ +#if $[HAVE_GETOPT_LONG_ONLY] +$[define] HAVE_GETOPT_LONG_ONLY 1 +#else +$[undef] HAVE_GETOPT_LONG_ONLY +#endif + +/* Define if you have the header file. */ +$[define] HAVE_ALLOCA_H 1 + +/* Define if you have the header file. */ +$[undef] HAVE_IO_H + +/* Define if you have the header file. */ +#if $[HAVE_IOSTREAM] +$[define] HAVE_IOSTREAM 1 +#else +$[undef] HAVE_IOSTREAM +#endif + +/* Define if you have the header file. */ +#if $[HAVE_MALLOC_H] +$[define] HAVE_MALLOC_H 1 +#else +$[undef] HAVE_MALLOC_H +#endif + +/* Define if you have the header file. */ +#if $[HAVE_ALLOCA_H] +$[define] HAVE_ALLOCA_H 1 +#else +$[undef] HAVE_ALLOCA_H +#endif + +/* Define if you have the header file. */ +$[undef] HAVE_MINMAX_H + +/* Define if you have the header file. */ +#if $[HAVE_SSTREAM] +$[define] HAVE_SSTREAM 1 +#else +$[undef] HAVE_SSTREAM +#endif + +/* Define if you have the header file. */ +#if $[HAVE_SYS_TYPES] +$[define] HAVE_SYS_TYPES 1 +#else +$[undef] HAVE_SYS_TYPES +#endif + +/* Define if you have the header file. */ +#if $[HAVE_UNISTD_H] +$[define] HAVE_UNISTD_H 1 +#else +$[undef] HAVE_UNISTD_H +#endif + +/* Name of package */ +$[define] PACKAGE $[PACKAGE] + +/* Version number of package */ +$[define] VERSION $[VERSION] + +#end dtool_config.h + +#endif // BUILD_TYPE diff --git a/panda/Package.pp b/panda/Package.pp new file mode 100644 index 0000000000..f6e09a4264 --- /dev/null +++ b/panda/Package.pp @@ -0,0 +1,11 @@ +// +// Package.pp +// +// This file defines a few more configuration variables that are +// inconvenient to store in Config.pp, simply because they are more +// specific to a particular package and not likely to be edited by a +// configuring user. + +// What is the name and version of this source tree? +#define PACKAGE panda +#define VERSION 0.80 diff --git a/panda/Sources.pp b/panda/Sources.pp new file mode 100644 index 0000000000..9cd7cae722 --- /dev/null +++ b/panda/Sources.pp @@ -0,0 +1,10 @@ +// This is the toplevel directory. It contains configure.in and other +// stuff. + +#define DIR_TYPE toplevel + +#define SAMPLE_SOURCE_FILE src/pandabase/pandabase.cxx +#define REQUIRED_TREES dtool + +#define EXTRA_DIST \ + Config.Irix.pp Config.Linux.pp Config.Win32.pp LocalSetup.pp Package.pp diff --git a/panda/acinclude.m4 b/panda/acinclude.m4 new file mode 100644 index 0000000000..1b43f1b250 --- /dev/null +++ b/panda/acinclude.m4 @@ -0,0 +1,933 @@ +AC_DEFUN(AC_GETTIMEOFDAY, +[AC_CACHE_CHECK([for gettimeofday()], + ac_cv_gettimeofday, +[ + AC_TRY_COMPILE([ +#include + ],[ + gettimeofday((struct timeval *)NULL, (struct timezone *)NULL); + ], ac_cv_gettimeofday="2 params", ac_cv_gettimeofday=no) + if test "$ac_cv_gettimeofday" = no; then + AC_TRY_COMPILE([ +#include + ],[ + gettimeofday((struct timeval *)NULL, (struct timezone *)NULL); + ], ac_cv_gettimeofday="1 param", ac_cv_gettimeofday=no) + fi +]) +if test "$ac_cv_gettimeofday" = "1 param"; then + AC_DEFINE(GETTIMEOFDAY_ONE_PARAM) + AC_DEFINE(HAVE_GETTIMEOFDAY) +elif test "$ac_cv_gettimeofday" = "2 params"; then + AC_DEFINE(HAVE_GETTIMEOFDAY) +fi +]) + + +AC_DEFUN(AC_HEADER_IOSTREAM, +[AC_CHECK_HEADERS(iostream,[have_iostream=yes],[have_iostream=no])]) + +AC_DEFUN(AC_IOS_BINARY, +[AC_CACHE_CHECK([for ios::binary], + ac_cv_ios_binary, +[ +if test $have_iostream = yes; then + AC_TRY_COMPILE([ +#include + ],[ + int x; x = ios::binary; + ], ac_cv_ios_binary=yes, ac_cv_ios_binary=no) +else + AC_TRY_COMPILE([ +#include + ],[ + int x; x = ios::binary; + ], ac_cv_ios_binary=yes, ac_cv_ios_binary=no) +fi + +]) +if test $ac_cv_ios_binary = yes; then + AC_DEFINE(HAVE_IOS_BINARY) +fi +]) + + +AC_DEFUN(AC_NAMESPACE, +[AC_CACHE_CHECK([for compiler namespace support], + ac_cv_namespace, +[AC_TRY_COMPILE( +[namespace std { }; +using namespace std;], +[], + ac_cv_namespace=yes, ac_cv_namespace=no)]) +if test $ac_cv_namespace = yes; then + AC_DEFINE(HAVE_NAMESPACE) +fi +]) + + +dnl A handy function to see if a library is in a particular directory. +dnl AC_CHECK_LIB_LOC(directory, library, function, action-if-found, action-if-not-found, other-libraries) +dnl +AC_DEFUN(AC_CHECK_LIB_LOC, +[AC_MSG_CHECKING([for lib$2 in $1]) +ac_lib_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'` +AC_CACHE_VAL(ac_cv_lib_loc_$ac_lib_var, +[ac_save_LIBS="$LIBS" +LIBS="-L$1 -l$2 $6 $LIBS" +AC_TRY_LINK(dnl +ifelse(AC_LANG, CPLUSPLUS, [#ifdef __cplusplus +extern "C" +#endif +])dnl +[/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $3(); +], + [$3()], + eval "ac_cv_lib_loc_$ac_lib_var=yes", + eval "ac_cv_lib_loc_$ac_lib_var=no") +LIBS="$ac_save_LIBS" +])dnl +if eval "test \"`echo '$ac_cv_lib_loc_'$ac_lib_var`\" = yes"; then + AC_MSG_RESULT(yes) + ifelse([$4], , +[LIBS="-L$1 -l$2 $LIBS" +], [$4]) +else + AC_MSG_RESULT(no) +ifelse([$5], , , [$5 +])dnl +fi +]) + +dnl A handy function to search a number of possible locations for a library. +dnl AC_SEARCH_LIB(search-dirs, library, function, package, other-libraries) +dnl +dnl Sets $package_LIB to the directory containing the library, or to the +dnl string "no" if the library cannot be found. +AC_DEFUN(AC_SEARCH_LIB, [ +ac_found_lib="no" +if test "$1" = ""; then + AC_CHECK_LIB($2, $3, [ ac_found_lib=""; ],, $5) +else + for ac_check_dir in $1; do + if test "$ac_found_lib" = "no"; then + AC_CHECK_LIB_LOC($ac_check_dir, $2, $3, [ ac_found_lib="$ac_check_dir"; ],, $5) + fi + done +fi +$4_LIB="$ac_found_lib" +]) + +dnl A handy function to see if a header file is in a particular directory. +dnl AC_CHECK_HEADER_LOC(directory, header, action-if-found, action-if-not-found) +dnl +AC_DEFUN(AC_CHECK_HEADER_LOC, [ + AC_MSG_CHECKING([for $2 in $1]) + ac_include_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'` + AC_CACHE_VAL(ac_cv_include_loc_$ac_include_var, [ + ac_save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="-I$1 $CPPFLAGS" + AC_TRY_CPP([#include <$2>], ac_ch_found_it="yes", ac_ch_found_it="no") + if test "$ac_ch_found_it" = "yes"; then + AC_MSG_RESULT(yes) + ifelse([$3], , :, [$3]) + else + AC_MSG_RESULT(no) + ifelse([$4], , , [$4]) + fi + CPPFLAGS="$ac_save_CPPFLAGS" + ]) +]) + + +dnl A handy function to search a number of possible locations for a header +dnl file. +dnl +dnl AC_SEARCH_HEADER(search-dirs, header, package) +dnl +dnl Sets $package_INCLUDE to the directory containing the header, or to +dnl the string "no" if the header cannot be found. +AC_DEFUN(AC_SEARCH_HEADER, [ +ac_found_header="no" +if test "$1" = ""; then + AC_CHECK_HEADER($2, [ ac_found_header="";]) +else + for ac_check_dir in $1; do + if test "$ac_found_header" = "no"; then + AC_CHECK_HEADER_LOC($ac_check_dir, $2, [ ac_found_header="$ac_check_dir";]) + fi + done +fi +$3_INCLUDE="$ac_found_header" +]) + + +dnl A handy function to scan for a third-party package, consisting of at +dnl least a library and an include file. A few assumptions are made about +dnl the relationships between lib and include directories. +dnl +dnl AC_SEARCH_PACKAGE(search-dirs, package-names, header, library, function, package, other-libraries) +dnl +dnl search-dirs is the whitespace-separated list of directory prefixes to +dnl check. +dnl package-names is a whitespace-separated list of possible names the +dnl package may have been installed under. +dnl +dnl For each combination of ${search-dir} and ${package-name}, the following +dnl directories are searched: +dnl +dnl ${search-dir} +dnl ${search-dir}/lib +dnl ${search-dir}/${package-name} +dnl ${search-dir}/${package-name}/lib +dnl ${search-dir}/lib/${package-name} +dnl +dnl And similarly for include. +dnl +dnl Sets the variables $package_INCLUDE and $package_LIB to the directories +dnl containing the header file and library, respectively. If both pieces +dnl are located, also sets the variable $package_PKG to "yes"; otherwise, +dnl sets the variable $package_PKG to "no". +dnl +AC_DEFUN(AC_SEARCH_PACKAGE, [ +$6_LIB="no" +$6_INCLUDE="no" +$6_PKG="no" + +dnl Look for the library. +AC_SEARCH_LIB("", $4, $5, $6, $7) +for ac_sp_dir in $1; do + if test "[$]$6_LIB" = "no"; then + AC_SEARCH_LIB("$ac_sp_dir" "$ac_sp_dir/lib", $4, $5, $6, $7) + for ac_sp_pkg in $2; do + if test "[$]$6_LIB" = "no"; then + AC_SEARCH_LIB("$ac_sp_dir/$ac_sp_pkg" "$ac_sp_dir/$ac_sp_pkg/lib" \ + "$ac_sp_dir/lib/$ac_sp_pkg", $4, $5, $6, $7) + fi + done + fi +done + +dnl Now look for the header file. Don't bother looking if the library +dnl wasn't found. +if test "[$]$6_LIB" != "no"; then + dnl First look in the obvious directory corresponding to the lib dir. + ac_sp_testinc=`echo [$]$6_LIB | sed 's:/lib:/include:'` + AC_SEARCH_HEADER("$ac_sp_testinc", $3, $6) + + dnl If it wasn't found there, cast about. + if test "[$]$6_INCLUDE" = "no"; then + for ac_sp_dir in $1; do + if test "[$]$6_INCLUDE" = "no"; then + AC_SEARCH_HEADER("$ac_sp_dir" "$ac_sp_dir/include", $3, $6) + for ac_sp_pkg in $2; do + if test "[$]$6_INCLUDE" = "no"; then + AC_SEARCH_HEADER("$ac_sp_dir/$ac_sp_pkg" \ + "$ac_sp_dir/$ac_sp_pkg/include" \ + "$ac_sp_dir/include/$ac_sp_pkg", $3, $6) + fi + done + fi + done + fi + + dnl If we got both a header and a library, set the PKG variable. + if test "[$]$6_INCLUDE" != "no"; then + $6_PKG="yes" + fi +fi +]) + +dnl A handy function, similar to AC_SEARCH_PACKAGE, above, that looks for a +dnl package that only consists of header files, no libraries. +dnl +dnl AC_SEARCH_HPACKAGE(search-dirs, package-names, header, package) +dnl +dnl search-dirs is the whitespace-separated list of directory prefixes to +dnl check. +dnl package-names is a whitespace-separated list of possible names the +dnl package may have been installed under. +dnl +dnl For each combination of ${search-dir} and ${package-name}, the following +dnl directories are searched: +dnl +dnl ${search-dir} +dnl ${search-dir}/include +dnl ${search-dir}/${package-name} +dnl ${search-dir}/${package-name}/include +dnl ${search-dir}/include/${package-name} +dnl +dnl Sets the variable $package_INCLUDE to the directory containing the +dnl header file, and sets the variable $package_PKG to "yes"; if the +dnl directory is not found, sets the variable $package_PKG to "no". +dnl +AC_DEFUN(AC_SEARCH_HPACKAGE, [ +$4_INCLUDE="no" +$4_PKG="no" + +dnl Look for the header. +AC_SEARCH_HEADER("", $3, $4) +for ac_sp_dir in $1; do + if test "[$]$4_INCLUDE" = "no"; then + AC_SEARCH_HEADER("$ac_sp_dir" "$ac_sp_dir/include", $3, $4) + for ac_sp_pkg in $2; do + if test "[$]$4_INCLUDE" = "no"; then + AC_SEARCH_HEADER("$ac_sp_dir/$ac_sp_pkg" \ + "$ac_sp_dir/$ac_sp_pkg/include" \ + "$ac_sp_dir/include/$ac_sp_pkg", $3, $4) + fi + done + fi +done + +dnl If we got both a header file, set the PKG variable. +if test "[$]$4_INCLUDE" != "no"; then + $4_PKG="yes" +fi +]) + +# Define a conditional. + +AC_DEFUN(AM_CONDITIONAL, +[AC_SUBST($1_TRUE) +AC_SUBST($1_FALSE) +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi]) + + + + +# Configure paths for GTK-- +# Erik Andersen 30 May 1998 +# Modified by Tero Pulkkinen (added the compiler checks... I hope they work..) + +dnl Test for GTKMM, and define GTKMM_CFLAGS and GTKMM_LIBS +dnl to be used as follows: +dnl AM_PATH_GTKMM([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl +AC_DEFUN(AM_PATH_GTKMM, +[dnl +dnl Get the cflags and libraries from the gtkmm-config script +dnl +AC_ARG_WITH(gtkmm-prefix,[ --with-gtkmm-prefix=PREFIX + Prefix where GTK-- is installed (optional)], + gtkmm_config_prefix="$withval", gtkmm_config_prefix="") +AC_ARG_WITH(gtkmm-exec-prefix,[ --with-gtkmm-exec-prefix=PREFIX + Exec prefix where GTK-- is installed (optional)], + gtkmm_config_exec_prefix="$withval", gtkmm_config_exec_prefix="") +AC_ARG_ENABLE(gtkmmtest, [ --disable-gtkmmtest Do not try to compile and run a test GTK-- program], + , enable_gtkmmtest=yes) + + if test x$gtkmm_config_exec_prefix != x ; then + gtkmm_config_args="$gtkmm_config_args --exec-prefix=$gtkmm_config_exec_prefix" + if test x${GTKMM_CONFIG+set} != xset ; then + GTKMM_CONFIG=$gtkmm_config_exec_prefix/bin/gtkmm-config + fi + fi + if test x$gtkmm_config_prefix != x ; then + gtkmm_config_args="$gtkmm_config_args --prefix=$gtkmm_config_prefix" + if test x${GTKMM_CONFIG+set} != xset ; then + GTKMM_CONFIG=$gtkmm_config_prefix/bin/gtkmm-config + fi + fi + + AC_PATH_PROG(GTKMM_CONFIG, gtkmm-config, no) + min_gtkmm_version=ifelse([$1], ,0.10.0,$1) + + AC_MSG_CHECKING(for GTK-- - version >= $min_gtkmm_version) + no_gtkmm="" + if test "$GTKMM_CONFIG" = "no" ; then + no_gtkmm=yes + else + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + + GTKMM_CFLAGS=`$GTKMM_CONFIG $gtkmm_config_args --cflags` + GTKMM_LIBS=`$GTKMM_CONFIG $gtkmm_config_args --libs` + gtkmm_config_major_version=`$GTKMM_CONFIG $gtkmm_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + gtkmm_config_minor_version=`$GTKMM_CONFIG $gtkmm_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + gtkmm_config_micro_version=`$GTKMM_CONFIG $gtkmm_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "x$enable_gtkmmtest" = "xyes" ; then + ac_save_CXXFLAGS="$CXXFLAGS" + ac_save_LIBS="$LIBS" + CXXFLAGS="$CXXFLAGS $GTKMM_CFLAGS" + LIBS="$LIBS $GTKMM_LIBS" +dnl +dnl Now check if the installed GTK-- is sufficiently new. (Also sanity +dnl checks the results of gtkmm-config to some extent +dnl + rm -f conf.gtkmmtest + AC_TRY_RUN([ +#include +#include +#include + +int +main () +{ + int major, minor, micro; + char *tmp_version; + + system ("touch conf.gtkmmtest"); + + /* HP/UX 0 (%@#!) writes to sscanf strings */ + tmp_version = g_strdup("$min_gtkmm_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_gtkmm_version"); + exit(1); + } + + if ((gtkmm_major_version != $gtkmm_config_major_version) || + (gtkmm_minor_version != $gtkmm_config_minor_version) || + (gtkmm_micro_version != $gtkmm_config_micro_version)) + { + printf("\n*** 'gtkmm-config --version' returned %d.%d.%d, but GTK-- (%d.%d.%d)\n", + $gtkmm_config_major_version, $gtkmm_config_minor_version, $gtkmm_config_micro_version, + gtkmm_major_version, gtkmm_minor_version, gtkmm_micro_version); + printf ("*** was found! If gtkmm-config was correct, then it is best\n"); + printf ("*** to remove the old version of GTK--. You may also be able to fix the error\n"); + printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n"); + printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n"); + printf("*** required on your system.\n"); + printf("*** If gtkmm-config was wrong, set the environment variable GTKMM_CONFIG\n"); + printf("*** to point to the correct copy of gtkmm-config, and remove the file config.cache\n"); + printf("*** before re-running configure\n"); + } +/* GTK-- does not have the GTKMM_*_VERSION constants */ +/* + else if ((gtkmm_major_version != GTKMM_MAJOR_VERSION) || + (gtkmm_minor_version != GTKMM_MINOR_VERSION) || + (gtkmm_micro_version != GTKMM_MICRO_VERSION)) + { + printf("*** GTK-- header files (version %d.%d.%d) do not match\n", + GTKMM_MAJOR_VERSION, GTKMM_MINOR_VERSION, GTKMM_MICRO_VERSION); + printf("*** library (version %d.%d.%d)\n", + gtkmm_major_version, gtkmm_minor_version, gtkmm_micro_version); + } +*/ + else + { + if ((gtkmm_major_version > major) || + ((gtkmm_major_version == major) && (gtkmm_minor_version > minor)) || + ((gtkmm_major_version == major) && (gtkmm_minor_version == minor) && (gtkmm_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** An old version of GTK-- (%d.%d.%d) was found.\n", + gtkmm_major_version, gtkmm_minor_version, gtkmm_micro_version); + printf("*** You need a version of GTK-- newer than %d.%d.%d. The latest version of\n", + major, minor, micro); + printf("*** GTK-- is always available from ftp://ftp.gtk.org.\n"); + printf("***\n"); + printf("*** If you have already installed a sufficiently new version, this error\n"); + printf("*** probably means that the wrong copy of the gtkmm-config shell script is\n"); + printf("*** being found. The easiest way to fix this is to remove the old version\n"); + printf("*** of GTK--, but you can also set the GTKMM_CONFIG environment to point to the\n"); + printf("*** correct copy of gtkmm-config. (In this case, you will have to\n"); + printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); + printf("*** so that the correct libraries are found at run-time))\n"); + } + } + return 1; +} +],, no_gtkmm=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CXXFLAGS="$ac_save_CXXFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + if test "x$no_gtkmm" = x ; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + if test "$GTKMM_CONFIG" = "no" ; then + echo "*** The gtkmm-config script installed by GTK-- could not be found" + echo "*** If GTK-- was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the GTKMM_CONFIG environment variable to the" + echo "*** full path to gtkmm-config." + echo "*** The gtkmm-config script was not available in GTK-- versions" + echo "*** prior to 0.9.12. Perhaps you need to update your installed" + echo "*** version to 0.9.12 or later" + else + if test -f conf.gtkmmtest ; then + : + else + echo "*** Could not run GTK-- test program, checking why..." + CXXFLAGS="$CFLAGS $GTKMM_CXXFLAGS" + LIBS="$LIBS $GTKMM_LIBS" + AC_TRY_LINK([ +#include +#include +], [ return ((gtkmm_major_version) || (gtkmm_minor_version) || (gtkmm_micro_version)); ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding GTK-- or finding the wrong" + echo "*** version of GTK--. If it is not finding GTK--, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means GTK-- was incorrectly installed" + echo "*** or that you have moved GTK-- since it was installed. In the latter case, you" + echo "*** may want to edit the gtkmm-config script: $GTKMM_CONFIG" ]) + CXXFLAGS="$ac_save_CXXFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + GTKMM_CFLAGS="" + GTKMM_LIBS="" + ifelse([$3], , :, [$3]) + AC_LANG_RESTORE + fi + AC_SUBST(GTKMM_CFLAGS) + AC_SUBST(GTKMM_LIBS) + rm -f conf.gtkmmtest +]) + + +## libtool.m4 - Configure libtool for the target system. -*-Shell-script-*- +## Copyright (C) 1996-1999 Free Software Foundation, Inc. +## Originally by Gordon Matzigkeit , 1996 +## +## 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. +## +## As a special exception to the GNU General Public License, if you +## distribute this file as part of a program that contains a +## configuration script generated by Autoconf, you may include it under +## the same distribution terms that you use for the rest of that program. + +# serial 40 AC_PROG_LIBTOOL +AC_DEFUN(AC_PROG_LIBTOOL, +[AC_REQUIRE([AC_LIBTOOL_SETUP])dnl + +# Save cache, so that ltconfig can load it +AC_CACHE_SAVE + +# Actually configure libtool. ac_aux_dir is where install-sh is found. +CC="$CC" CFLAGS="$CFLAGS" CPPFLAGS="$CPPFLAGS" \ +LD="$LD" LDFLAGS="$LDFLAGS" LIBS="$LIBS" \ +LN_S="$LN_S" NM="$NM" RANLIB="$RANLIB" \ +DLLTOOL="$DLLTOOL" AS="$AS" OBJDUMP="$OBJDUMP" \ +${CONFIG_SHELL-/bin/sh} $ac_aux_dir/ltconfig --no-reexec \ +$libtool_flags --no-verify $ac_aux_dir/ltmain.sh $host \ +|| AC_MSG_ERROR([libtool configure failed]) + +# Reload cache, that may have been modified by ltconfig +AC_CACHE_LOAD + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ac_aux_dir/ltconfig $ac_aux_dir/ltmain.sh" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' +AC_SUBST(LIBTOOL)dnl + +# Redirect the config.log output again, so that the ltconfig log is not +# clobbered by the next message. +exec 5>>./config.log +]) + +AC_DEFUN(AC_LIBTOOL_SETUP, +[AC_PREREQ(2.13)dnl +AC_REQUIRE([AC_ENABLE_SHARED])dnl +AC_REQUIRE([AC_ENABLE_STATIC])dnl +AC_REQUIRE([AC_ENABLE_FAST_INSTALL])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_REQUIRE([AC_PROG_RANLIB])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_LD])dnl +AC_REQUIRE([AC_PROG_NM])dnl +AC_REQUIRE([AC_PROG_LN_S])dnl +dnl + +# Check for any special flags to pass to ltconfig. +# +# the following will cause an existing older ltconfig to fail, so +# we ignore this at the expense of the cache file... Checking this +# will just take longer ... bummer! +#libtool_flags="--cache-file=$cache_file" +# +test "$enable_shared" = no && libtool_flags="$libtool_flags --disable-shared" +test "$enable_static" = no && libtool_flags="$libtool_flags --disable-static" +test "$enable_fast_install" = no && libtool_flags="$libtool_flags --disable-fast-install" +test "$ac_cv_prog_gcc" = yes && libtool_flags="$libtool_flags --with-gcc" +test "$ac_cv_prog_gnu_ld" = yes && libtool_flags="$libtool_flags --with-gnu-ld" +ifdef([AC_PROVIDE_AC_LIBTOOL_DLOPEN], +[libtool_flags="$libtool_flags --enable-dlopen"]) +ifdef([AC_PROVIDE_AC_LIBTOOL_WIN32_DLL], +[libtool_flags="$libtool_flags --enable-win32-dll"]) +AC_ARG_ENABLE(libtool-lock, + [ --disable-libtool-lock avoid locking (might break parallel builds)]) +test "x$enable_libtool_lock" = xno && libtool_flags="$libtool_flags --disable-lock" +test x"$silent" = xyes && libtool_flags="$libtool_flags --silent" + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case "$host" in +*-*-irix6*) + # Find out which ABI we are using. + echo '[#]line __oline__ "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case "`/usr/bin/file conftest.o`" in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, + [AC_TRY_LINK([],[],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])]) + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; + +ifdef([AC_PROVIDE_AC_LIBTOOL_WIN32_DLL], +[*-*-cygwin* | *-*-mingw*) + AC_CHECK_TOOL(DLLTOOL, dlltool, false) + AC_CHECK_TOOL(AS, as, false) + AC_CHECK_TOOL(OBJDUMP, objdump, false) + ;; +]) +esac +]) + +# AC_LIBTOOL_DLOPEN - enable checks for dlopen support +AC_DEFUN(AC_LIBTOOL_DLOPEN, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])]) + +# AC_LIBTOOL_WIN32_DLL - declare package support for building win32 dll's +AC_DEFUN(AC_LIBTOOL_WIN32_DLL, [AC_BEFORE([$0], [AC_LIBTOOL_SETUP])]) + +# AC_ENABLE_SHARED - implement the --enable-shared flag +# Usage: AC_ENABLE_SHARED[(DEFAULT)] +# Where DEFAULT is either `yes' or `no'. If omitted, it defaults to +# `yes'. +AC_DEFUN(AC_ENABLE_SHARED, [dnl +define([AC_ENABLE_SHARED_DEFAULT], ifelse($1, no, no, yes))dnl +AC_ARG_ENABLE(shared, +changequote(<<, >>)dnl +<< --enable-shared[=PKGS] build shared libraries [default=>>AC_ENABLE_SHARED_DEFAULT], +changequote([, ])dnl +[p=${PACKAGE-default} +case "$enableval" in +yes) enable_shared=yes ;; +no) enable_shared=no ;; +*) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:," + for pkg in $enableval; do + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS="$ac_save_ifs" + ;; +esac], +enable_shared=AC_ENABLE_SHARED_DEFAULT)dnl +]) + +# AC_DISABLE_SHARED - set the default shared flag to --disable-shared +AC_DEFUN(AC_DISABLE_SHARED, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl +AC_ENABLE_SHARED(no)]) + +# AC_ENABLE_STATIC - implement the --enable-static flag +# Usage: AC_ENABLE_STATIC[(DEFAULT)] +# Where DEFAULT is either `yes' or `no'. If omitted, it defaults to +# `yes'. +AC_DEFUN(AC_ENABLE_STATIC, [dnl +define([AC_ENABLE_STATIC_DEFAULT], ifelse($1, no, no, yes))dnl +AC_ARG_ENABLE(static, +changequote(<<, >>)dnl +<< --enable-static[=PKGS] build static libraries [default=>>AC_ENABLE_STATIC_DEFAULT], +changequote([, ])dnl +[p=${PACKAGE-default} +case "$enableval" in +yes) enable_static=yes ;; +no) enable_static=no ;; +*) + enable_static=no + # Look at the argument we got. We use all the common list separators. + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:," + for pkg in $enableval; do + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS="$ac_save_ifs" + ;; +esac], +enable_static=AC_ENABLE_STATIC_DEFAULT)dnl +]) + +# AC_DISABLE_STATIC - set the default static flag to --disable-static +AC_DEFUN(AC_DISABLE_STATIC, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl +AC_ENABLE_STATIC(no)]) + + +# AC_ENABLE_FAST_INSTALL - implement the --enable-fast-install flag +# Usage: AC_ENABLE_FAST_INSTALL[(DEFAULT)] +# Where DEFAULT is either `yes' or `no'. If omitted, it defaults to +# `yes'. +AC_DEFUN(AC_ENABLE_FAST_INSTALL, [dnl +define([AC_ENABLE_FAST_INSTALL_DEFAULT], ifelse($1, no, no, yes))dnl +AC_ARG_ENABLE(fast-install, +changequote(<<, >>)dnl +<< --enable-fast-install[=PKGS] optimize for fast installation [default=>>AC_ENABLE_FAST_INSTALL_DEFAULT], +changequote([, ])dnl +[p=${PACKAGE-default} +case "$enableval" in +yes) enable_fast_install=yes ;; +no) enable_fast_install=no ;; +*) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:," + for pkg in $enableval; do + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS="$ac_save_ifs" + ;; +esac], +enable_fast_install=AC_ENABLE_FAST_INSTALL_DEFAULT)dnl +]) + +# AC_ENABLE_FAST_INSTALL - set the default to --disable-fast-install +AC_DEFUN(AC_DISABLE_FAST_INSTALL, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl +AC_ENABLE_FAST_INSTALL(no)]) + +# AC_PROG_LD - find the path to the GNU or non-GNU linker +AC_DEFUN(AC_PROG_LD, +[AC_ARG_WITH(gnu-ld, +[ --with-gnu-ld assume the C compiler uses GNU ld [default=no]], +test "$withval" = no || with_gnu_ld=yes, with_gnu_ld=no) +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +ac_prog=ld +if test "$ac_cv_prog_gcc" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by GCC]) + ac_prog=`($CC -print-prog-name=ld) 2>&5` + case "$ac_prog" in + # Accept absolute paths. +changequote(,)dnl + [\\/]* | [A-Za-z]:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' +changequote([,])dnl + # Canonicalize the path of ld + ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'` + while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do + ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(ac_cv_path_LD, +[if test -z "$LD"; then + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + ac_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some GNU ld's only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + if "$ac_cv_path_LD" -v 2>&1 < /dev/null | egrep '(GNU|with BFD)' > /dev/null; then + test "$with_gnu_ld" != no && break + else + test "$with_gnu_ld" != yes && break + fi + fi + done + IFS="$ac_save_ifs" +else + ac_cv_path_LD="$LD" # Let the user override the test with a path. +fi]) +LD="$ac_cv_path_LD" +if test -n "$LD"; then + AC_MSG_RESULT($LD) +else + AC_MSG_RESULT(no) +fi +test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH]) +AC_SUBST(LD) +AC_PROG_LD_GNU +]) + +AC_DEFUN(AC_PROG_LD_GNU, +[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], ac_cv_prog_gnu_ld, +[# I'd rather use --version here, but apparently some GNU ld's only accept -v. +if $LD -v 2>&1 &5; then + ac_cv_prog_gnu_ld=yes +else + ac_cv_prog_gnu_ld=no +fi]) +]) + +# AC_PROG_NM - find the path to a BSD-compatible name lister +AC_DEFUN(AC_PROG_NM, +[AC_MSG_CHECKING([for BSD-compatible nm]) +AC_CACHE_VAL(ac_cv_path_NM, +[if test -n "$NM"; then + # Let the user override the test. + ac_cv_path_NM="$NM" +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}" + for ac_dir in $PATH /usr/ccs/bin /usr/ucb /bin; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/nm || test -f $ac_dir/nm$ac_exeext ; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + if ($ac_dir/nm -B /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then + ac_cv_path_NM="$ac_dir/nm -B" + break + elif ($ac_dir/nm -p /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then + ac_cv_path_NM="$ac_dir/nm -p" + break + else + ac_cv_path_NM=${ac_cv_path_NM="$ac_dir/nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + fi + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_NM" && ac_cv_path_NM=nm +fi]) +NM="$ac_cv_path_NM" +AC_MSG_RESULT([$NM]) +AC_SUBST(NM) +]) + +# AC_CHECK_LIBM - check for math library +AC_DEFUN(AC_CHECK_LIBM, +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +LIBM= +case "$host" in +*-*-beos* | *-*-cygwin*) + # These system don't have libm + ;; +*-ncr-sysv4.3*) + AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") + AC_CHECK_LIB(m, main, LIBM="$LIBM -lm") + ;; +*) + AC_CHECK_LIB(m, main, LIBM="-lm") + ;; +esac +]) + +# AC_LIBLTDL_CONVENIENCE[(dir)] - sets LIBLTDL to the link flags for +# the libltdl convenience library, adds --enable-ltdl-convenience to +# the configure arguments. Note that LIBLTDL is not AC_SUBSTed, nor +# is AC_CONFIG_SUBDIRS called. If DIR is not provided, it is assumed +# to be `${top_builddir}/libltdl'. Make sure you start DIR with +# '${top_builddir}/' (note the single quotes!) if your package is not +# flat, and, if you're not using automake, define top_builddir as +# appropriate in the Makefiles. +AC_DEFUN(AC_LIBLTDL_CONVENIENCE, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl + case "$enable_ltdl_convenience" in + no) AC_MSG_ERROR([this package needs a convenience libltdl]) ;; + "") enable_ltdl_convenience=yes + ac_configure_args="$ac_configure_args --enable-ltdl-convenience" ;; + esac + LIBLTDL=ifelse($#,1,$1,['${top_builddir}/libltdl'])/libltdlc.la + INCLTDL=ifelse($#,1,-I$1,['-I${top_builddir}/libltdl']) +]) + +# AC_LIBLTDL_INSTALLABLE[(dir)] - sets LIBLTDL to the link flags for +# the libltdl installable library, and adds --enable-ltdl-install to +# the configure arguments. Note that LIBLTDL is not AC_SUBSTed, nor +# is AC_CONFIG_SUBDIRS called. If DIR is not provided, it is assumed +# to be `${top_builddir}/libltdl'. Make sure you start DIR with +# '${top_builddir}/' (note the single quotes!) if your package is not +# flat, and, if you're not using automake, define top_builddir as +# appropriate in the Makefiles. +# In the future, this macro may have to be called after AC_PROG_LIBTOOL. +AC_DEFUN(AC_LIBLTDL_INSTALLABLE, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl + AC_CHECK_LIB(ltdl, main, + [test x"$enable_ltdl_install" != xyes && enable_ltdl_install=no], + [if test x"$enable_ltdl_install" = xno; then + AC_MSG_WARN([libltdl not installed, but installation disabled]) + else + enable_ltdl_install=yes + fi + ]) + if test x"$enable_ltdl_install" = x"yes"; then + ac_configure_args="$ac_configure_args --enable-ltdl-install" + LIBLTDL=ifelse($#,1,$1,['${top_builddir}/libltdl'])/libltdl.la + INCLTDL=ifelse($#,1,-I$1,['-I${top_builddir}/libltdl']) + else + ac_configure_args="$ac_configure_args --enable-ltdl-install=no" + LIBLTDL="-lltdl" + INCLTDL= + fi +]) + +dnl old names +AC_DEFUN(AM_PROG_LIBTOOL, [indir([AC_PROG_LIBTOOL])])dnl +AC_DEFUN(AM_ENABLE_SHARED, [indir([AC_ENABLE_SHARED], $@)])dnl +AC_DEFUN(AM_ENABLE_STATIC, [indir([AC_ENABLE_STATIC], $@)])dnl +AC_DEFUN(AM_DISABLE_SHARED, [indir([AC_DISABLE_SHARED], $@)])dnl +AC_DEFUN(AM_DISABLE_STATIC, [indir([AC_DISABLE_STATIC], $@)])dnl +AC_DEFUN(AM_PROG_LD, [indir([AC_PROG_LD])])dnl +AC_DEFUN(AM_PROG_NM, [indir([AC_PROG_NM])])dnl + +dnl This is just to silence aclocal about the macro not being used +ifelse([AC_DISABLE_FAST_INSTALL])dnl diff --git a/panda/metalibs/Sources.pp b/panda/metalibs/Sources.pp new file mode 100644 index 0000000000..93e28f9e7c --- /dev/null +++ b/panda/metalibs/Sources.pp @@ -0,0 +1,7 @@ +// This is a group directory: a directory level above a number of +// source subdirectories. + +#define DIR_TYPE group + +// The metalibs directory always depends on the src directory. +#define DEPENDS src diff --git a/panda/metalibs/panda/Sources.pp b/panda/metalibs/panda/Sources.pp new file mode 100644 index 0000000000..6ca0339a13 --- /dev/null +++ b/panda/metalibs/panda/Sources.pp @@ -0,0 +1,25 @@ +// DIR_TYPE "metalib" indicates we are building a shared library that +// consists mostly of references to other shared libraries. Under +// Windows, this directly produces a DLL (as opposed to the regular +// src libraries, which don't produce anything but a pile of OBJ files +// under Windows). + +#define DIR_TYPE metalib +#define BUILDING_DLL BUILDING_PANDA + +#define LOCAL_LIBS \ + pstatclient grutil chan chancfg \ + char chat collide cull device \ + dgraph display gobj graph gsgbase \ + gsgmisc light linmath mathutil net pnm \ + pnmimagetypes pnmimage sgattrib sgmanip sgraph sgraphutil \ + switchnode text tform lerp loader putil effects \ + audio pandabase + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin metalib_target + #define TARGET panda + + #define SOURCES panda.cxx +#end metalib_target diff --git a/panda/metalibs/panda/panda.cxx b/panda/metalibs/panda/panda.cxx new file mode 100644 index 0000000000..61f50efbdd --- /dev/null +++ b/panda/metalibs/panda/panda.cxx @@ -0,0 +1,9 @@ +// Filename: panda.cxx +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + +// This is a dummy file whose sole purpose is to give the compiler +// something to compile when making libpanda.so in NO_DEFER mode, +// which generates an empty library that itself links with all the +// other shared libraries that make up libpanda. diff --git a/panda/metalibs/pandadx/Sources.pp b/panda/metalibs/pandadx/Sources.pp new file mode 100644 index 0000000000..cb7dcae1d3 --- /dev/null +++ b/panda/metalibs/pandadx/Sources.pp @@ -0,0 +1,21 @@ +#define DIRECTORY_IF_DX yes + +// DIR_TYPE "metalib" indicates we are building a shared library that +// consists mostly of references to other shared libraries. Under +// Windows, this directly produces a DLL (as opposed to the regular +// src libraries, which don't produce anything but a pile of OBJ files +// under Windows). + +#define DIR_TYPE metalib +#define BUILDING_DLL BUILDING_PANDADX + +#define LOCAL_LIBS \ + dxgsg wdxdisplay + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin metalib_target + #define TARGET pandadx + + #define SOURCES pandadx.cxx +#end metalib_target diff --git a/panda/metalibs/pandadx/pandadx.cxx b/panda/metalibs/pandadx/pandadx.cxx new file mode 100644 index 0000000000..a3373b41b0 --- /dev/null +++ b/panda/metalibs/pandadx/pandadx.cxx @@ -0,0 +1,9 @@ +// Filename: pandadx.cxx +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + +// This is a dummy file whose sole purpose is to give the compiler +// something to compile when making libpandadx.so in NO_DEFER mode, +// which generates an empty library that itself links with all the +// other shared libraries that make up libpandadx. diff --git a/panda/metalibs/pandaegg/Sources.pp b/panda/metalibs/pandaegg/Sources.pp new file mode 100644 index 0000000000..2f5da22462 --- /dev/null +++ b/panda/metalibs/pandaegg/Sources.pp @@ -0,0 +1,19 @@ +// DIR_TYPE "metalib" indicates we are building a shared library that +// consists mostly of references to other shared libraries. Under +// Windows, this directly produces a DLL (as opposed to the regular +// src libraries, which don't produce anything but a pile of OBJ files +// under Windows). + +#define DIR_TYPE metalib +#define BUILDING_DLL BUILDING_PANDAEGG + +#define LOCAL_LIBS \ + egg2sg egg builder + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin metalib_target + #define TARGET pandaegg + + #define SOURCES pandaegg.cxx +#end metalib_target diff --git a/panda/metalibs/pandaegg/pandaegg.cxx b/panda/metalibs/pandaegg/pandaegg.cxx new file mode 100644 index 0000000000..b326cc6e14 --- /dev/null +++ b/panda/metalibs/pandaegg/pandaegg.cxx @@ -0,0 +1,9 @@ +// Filename: pandaegg.cxx +// Created by: drose (16May00) +// +//////////////////////////////////////////////////////////////////// + +// This is a dummy file whose sole purpose is to give the compiler +// something to compile when making libpandaegg.so in NO_DEFER mode, +// which generates an empty library that itself links with all the +// other shared libraries that make up libpandaegg. diff --git a/panda/metalibs/pandaexpress/Sources.pp b/panda/metalibs/pandaexpress/Sources.pp new file mode 100644 index 0000000000..e1fcbe592a --- /dev/null +++ b/panda/metalibs/pandaexpress/Sources.pp @@ -0,0 +1,17 @@ +// DIR_TYPE "metalib" indicates we are building a shared library that +// consists mostly of references to other shared libraries. Under +// Windows, this directly produces a DLL (as opposed to the regular +// src libraries, which don't produce anything but a pile of OBJ files +// under Windows). + +#define DIR_TYPE metalib +#define BUILDING_DLL BUILDING_PANDAEXPRESS + +#define LOCAL_LIBS downloader event ipc express pandabase +#define OTHER_LIBS interrogatedb dconfig dtoolutil + +#begin metalib_target + #define TARGET pandaexpress + + #define SOURCES pandaexpress.cxx +#end metalib_target diff --git a/panda/metalibs/pandaexpress/pandaexpress.cxx b/panda/metalibs/pandaexpress/pandaexpress.cxx new file mode 100644 index 0000000000..f6268be616 --- /dev/null +++ b/panda/metalibs/pandaexpress/pandaexpress.cxx @@ -0,0 +1,9 @@ +// Filename: pandaexpress.cxx +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + +// This is a dummy file whose sole purpose is to give the compiler +// something to compile when making libpanda.so in NO_DEFER mode, +// which generates an empty library that itself links with all the +// other shared libraries that make up libpanda. diff --git a/panda/metalibs/pandagl/Sources.pp b/panda/metalibs/pandagl/Sources.pp new file mode 100644 index 0000000000..223ee07771 --- /dev/null +++ b/panda/metalibs/pandagl/Sources.pp @@ -0,0 +1,21 @@ +#define DIRECTORY_IF_GL yes + +// DIR_TYPE "metalib" indicates we are building a shared library that +// consists mostly of references to other shared libraries. Under +// Windows, this directly produces a DLL (as opposed to the regular +// src libraries, which don't produce anything but a pile of OBJ files +// under Windows). + +#define DIR_TYPE metalib +#define BUILDING_DLL BUILDING_PANDAGL + +#define LOCAL_LIBS \ + glgsg glxdisplay // sgiglxdisplay + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin metalib_target + #define TARGET pandagl + + #define SOURCES pandagl.cxx +#end metalib_target diff --git a/panda/metalibs/pandagl/pandagl.cxx b/panda/metalibs/pandagl/pandagl.cxx new file mode 100644 index 0000000000..6e2984ca1e --- /dev/null +++ b/panda/metalibs/pandagl/pandagl.cxx @@ -0,0 +1,9 @@ +// Filename: pandagl.cxx +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + +// This is a dummy file whose sole purpose is to give the compiler +// something to compile when making libpandagl.so in NO_DEFER mode, +// which generates an empty library that itself links with all the +// other shared libraries that make up libpandagl. diff --git a/panda/metalibs/pandaglut/Sources.pp b/panda/metalibs/pandaglut/Sources.pp new file mode 100644 index 0000000000..929ba93e32 --- /dev/null +++ b/panda/metalibs/pandaglut/Sources.pp @@ -0,0 +1,21 @@ +#define DIRECTORY_IF_GLUT yes + +// DIR_TYPE "metalib" indicates we are building a shared library that +// consists mostly of references to other shared libraries. Under +// Windows, this directly produces a DLL (as opposed to the regular +// src libraries, which don't produce anything but a pile of OBJ files +// under Windows). + +#define DIR_TYPE metalib +#define BUILDING_DLL BUILDING_PANDAGLUT + +#define LOCAL_LIBS \ + glutdisplay // sgiglutdisplay + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin metalib_target + #define TARGET pandaglut + + #define SOURCES pandaglut.cxx +#end metalib_target diff --git a/panda/metalibs/pandaglut/pandaglut.cxx b/panda/metalibs/pandaglut/pandaglut.cxx new file mode 100644 index 0000000000..a9bb983c4f --- /dev/null +++ b/panda/metalibs/pandaglut/pandaglut.cxx @@ -0,0 +1,9 @@ +// Filename: pandaglut.cxx +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + +// This is a dummy file whose sole purpose is to give the compiler +// something to compile when making libpandaglut.so in NO_DEFER mode, +// which generates an empty library that itself links with all the +// other shared libraries that make up libpandaglut. diff --git a/panda/metalibs/pandaphysics/Sources.pp b/panda/metalibs/pandaphysics/Sources.pp new file mode 100644 index 0000000000..432d6c3fb9 --- /dev/null +++ b/panda/metalibs/pandaphysics/Sources.pp @@ -0,0 +1,19 @@ +// DIR_TYPE "metalib" indicates we are building a shared library that +// consists mostly of references to other shared libraries. Under +// Windows, this directly produces a DLL (as opposed to the regular +// src libraries, which don't produce anything but a pile of OBJ files +// under Windows). + +#define DIR_TYPE metalib +#define BUILDING_DLL BUILDING_PANDAPHYSICS + +#define LOCAL_LIBS \ + physics particlesystem + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin metalib_target + #define TARGET pandaphysics + + #define SOURCES pandaphysics.cxx +#end metalib_target diff --git a/panda/metalibs/pandaphysics/pandaphysics.cxx b/panda/metalibs/pandaphysics/pandaphysics.cxx new file mode 100644 index 0000000000..acf791875c --- /dev/null +++ b/panda/metalibs/pandaphysics/pandaphysics.cxx @@ -0,0 +1,9 @@ +// Filename: pandaphysics.cxx +// Created by: drose (16May00) +// +//////////////////////////////////////////////////////////////////// + +// This is a dummy file whose sole purpose is to give the compiler +// something to compile when making libpandaphysics.so in NO_DEFER mode, +// which generates an empty library that itself links with all the +// other shared libraries that make up libpandaphysics. diff --git a/panda/metalibs/pandarib/Sources.pp b/panda/metalibs/pandarib/Sources.pp new file mode 100644 index 0000000000..4a7ac83b9d --- /dev/null +++ b/panda/metalibs/pandarib/Sources.pp @@ -0,0 +1,21 @@ +#define DIRECTORY_IF_RIB yes + +// DIR_TYPE "metalib" indicates we are building a shared library that +// consists mostly of references to other shared libraries. Under +// Windows, this directly produces a DLL (as opposed to the regular +// src libraries, which don't produce anything but a pile of OBJ files +// under Windows). + +#define DIR_TYPE metalib +#define BUILDING_DLL BUILDING_PANDARIB + +#define LOCAL_LIBS \ + ribgsg ribdisplay + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin metalib_target + #define TARGET pandarib + + #define SOURCES pandarib.cxx +#end metalib_target diff --git a/panda/metalibs/pandarib/pandarib.cxx b/panda/metalibs/pandarib/pandarib.cxx new file mode 100644 index 0000000000..0318c5126d --- /dev/null +++ b/panda/metalibs/pandarib/pandarib.cxx @@ -0,0 +1,9 @@ +// Filename: pandarib.cxx +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + +// This is a dummy file whose sole purpose is to give the compiler +// something to compile when making libpandarib.so in NO_DEFER mode, +// which generates an empty library that itself links with all the +// other shared libraries that make up libpandarib. diff --git a/panda/src/PACKAGE-DESC b/panda/src/PACKAGE-DESC new file mode 100644 index 0000000000..caaea0a35d --- /dev/null +++ b/panda/src/PACKAGE-DESC @@ -0,0 +1,212 @@ +Quick descriptions of the packages: + +audio - + Audio API for panda +builder - + A tool to create Geoms and GeomNodes from a pile of unconnected + triangles or polygons. It separates geometry according to state + (texturing, normals, etc.), discovers connections where they exist + and tries to build nearly-optimal tristrips. +chan - + Animation channels. This defines the various kinds of + AnimChannels that may be defined, as well as the MovingPart class + which binds to the channels and plays the animation. This is a + support library for char, as well as any other libraries that want + to define objects whose values change over time. +chancfg - + This package contains the code for the 'channel configuration' system. + This is responsible for asking for a window to be opened, possibly binding + 'pipe video' channels to it, and dividing it up into Display Regions. +chat - + Implementation of a text-entry system along the bottom edge of the + window, along with any other support functions for an online chat + system that might be required. +collide - + This package contains the classes that control and detect collisions +configfiles - + This package contains the housekeeping and configuration files needed by + things like attach, and emacs. +cull - + This package contains the Cull Traverser. The cull traversal collects all + state changes specified, and removes unneccesary state change requests. Also + does all the depth sorting for proper alphaing. +device - + Device drivers, such as mouse and keyboard, trackers, etc... The + base class for using various device APIs is here. +dgraph - + Defines and manages the data graph, which is the hierarchy of + devices, tforms, and any other things which might have an input or + an output and need to execute every frame. +display - + Abstract display classes, including pipes, windows, channels, and + display regions +doc - + Documentation that doesn't fit in any of the packages +downloader - + Tool to allow automatic download of files in the background +dxgsg - + Handles all communication with the DirectX backend, and manages state + to minimize redundant state changes. +effects - + Various graphics effects that aren't shaders. I.E Lens Flares +egg - + A.k.a. the "egg library", this reads, writes, and manipulates egg + files. It knows nothing about the scene graph structure in the + rest of the player; it lives in its own little egg world. +egg2sg - + A.k.a. the "egg loader", this converts the egg structure read from + the egg library, above, to a scene graph structure, suitable for + rendering. +event - + Tools for throwing, handling and receiving events +framework - + A simple, stupid framework around which to write a simple, stupid + demo program. Handy for quickly writing programs that open a + window and display the OmniTriangle. +glgsg - + Handles all communication with the GL backend, and manages state + to minimize redundant state changes. +glidedisplay - (defunct) + This package contains the code to manage pipes/windows/channels on glide + hardware. +glidegsg - (defunct) + This package contains the GSG for the glide backend. All glide calls + should live in here. +glutdisplay - + Glut specific display classes. This uses the Glut library to open + a window and manage keyboard and mouse events. Glut has the + advantage of being widely ported, but unfortunately it needs to + control the main loop and so limits the kind of programs you can + write. +glxdisplay - + X windows display classes that replace Glut functionality. +gobj - + Graphical non-scene-graph objects, such as textures and geometry primitives +graph - + Defines the basic graph operations that are not specific to scene + graphs. This includes nodes, relations, transitions, and + attributes. +gsg - + The base definition for the GraphicsStateGuardian. This is the + abstract base class for all backend specifications. +gsgbase - + Base GSG class defined to avoid cyclical dependency build +gsgmisc - + Some utility functions for gsg that could not live in the same + directory for circular dependency reasons. +ipc - + This package contains the abstraction/adaptation layer for: mutexes, + semiphores, condition variables, threads, and files. +light - + Lights needed to be their own package to avoid multiple inheritance + problems. All concrete light types are both lights and nodes. +linmath - + Linear algebra library. +loader - + Tool for loading various kinds of files into Graph structures. + Can be done asynchronously +mathutil - + Math utility functions, such as frustum and plane +net - + Net connection classes +panda - + Builds the libpanda shared library. This is a single library that + encapsulates most of the packages in Panda, especially those that + are considered essential to Panda's basic functionality. On + Windows platforms, the individual packages are not themselves + built into shared libraries; the single LIBPANDA.DLL is the only + library file. On Unix platforms, the individual packages are each + built into their own shared library files, and a trivial + libpanda.so is built that unifies all of them. +pandadx - + As above, for the DirectX libraries. This includes all of the + packages, in addition to those in libpanda, that are required for + rendering on a DirectX platform. This also includes + Windows-specific libraries. +pandaegg - + As above, for the egg reader. This includes all of the packages, + in addition to those in libpanda, that are required for reading + egg files into the scene graph. +pandagl - + As above, for the OpenGL libraries. This includes all of the + packages, in addition to those in libpanda, that are required for + rendering on an OpenGL platform, with the exception of Glut. +pandaglut - + As above, for the OpenGL Glut libraries. +pandaphysics - + As above, for the physics/particle systems libraries. +pandarib - + As above, for the Renderman non-realtime renderer. +particlesystem - + Tool for doing particle systems. Contains various kinds of particles, + emiters, factories and renderers. +physics - + Base classes for physical objects and forces. Also contains the + physics manager class +pnm - + A more-or-less intact version of the NetPBM package, compiled as a + single library. This is a support library for pnmimage. +pnmimage - + Reads and writes image files in various formats, by using the pnm + and tiff libraries. +pstats - + Package for gathering performance statistics +ps2display - (defunct) + Playstation 2 display classes. +p2gsg - (defunct) + Play station 2 specific rendering backend. +ribdisplay - + RIB-specific "display" classes. These don't actually open + windows, but actually specify filenames. +ribgsg - + The RIB-specific rendering backend. Writing a frame to this + backend causes a RIB file to be produced, which can be used to + render the scene offline. +sgattrib - + Scene graph attributes and transitions (state information) +sgidisplay - + This package contains the code to manage pipes/windows/channels on SGI + hardware. +sgiglutdisplay - + This package contains the code to manage pipes/windows/channels on SGI + hardware for GLUT +sgiglxdisplay - + SGI specific X windows display classes that replace Glut functionality. +sgmanip - + High-level tools for manipulation of scene graphs. This primarily + defines NodePath, the principle interface for high-level scripting + languages into the scene graph. +sgraph - + Graph operations and nodes that are specific to render-type scene + graphs. This includes GeomNode, Camera, etc. +sgraphutil - + Handy utility functions for working with scene graphs. +shader - + Shaders that generate special effects by modifying the render + traversal and computing multiple passes. +stats - (defunct) + Package for gathering performance stats +statsdisplay - (defunct) + Package for remotely displaying the perfomance stats on a running program +switchnode - + Package of nodes that switch out geometry underneath them based on + various conditions. +testbed - + C test programs, that primarily link with framework. +text - + Package for generating renderable text using textured polygons. +tform - + Data transforming objects that live in the data graph and convert + raw data (as read from an input device, for instance) to something + more useful. +tiff - + Another support library for pnmimage. +util - + This package contains all the little fiddley things that are atomic to + just about all the code. Things like: config, ref count, notify, etc. +vrpn - + Defines the specific client code for interfacing to the VRPN API. +wdxdisplay - + Windows DirectX specific display classes +wgldisplay - + Windows OpenGL specific display classes diff --git a/panda/src/Sources.pp b/panda/src/Sources.pp new file mode 100644 index 0000000000..fb8681dab2 --- /dev/null +++ b/panda/src/Sources.pp @@ -0,0 +1,4 @@ +// This is a group directory: a directory level above a number of +// source subdirectories. + +#define DIR_TYPE group diff --git a/panda/src/audio/Sources.pp b/panda/src/audio/Sources.pp new file mode 100644 index 0000000000..2e107eb433 --- /dev/null +++ b/panda/src/audio/Sources.pp @@ -0,0 +1,49 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET audio + #define LOCAL_LIBS putil ipc + + #define SOURCES \ + audio_manager.I audio_manager.cxx audio_manager.h audio_midi.cxx \ + audio_midi.h audio_music.I audio_music.cxx audio_music.h \ + audio_pool.I audio_pool.cxx audio_pool.h audio_sample.I \ + audio_sample.cxx audio_sample.h audio_trait.cxx audio_trait.h \ + config_audio.cxx config_audio.h + + #define INSTALL_HEADERS \ + audio.h audio_manager.h audio_music.h audio_pool.I audio_pool.h \ + audio_sample.I audio_sample.h audio_trait.h + + #define IGATESCAN audio.h + +#end lib_target + +#begin lib_target + #define TARGET audio_load_midi + #define LOCAL_LIBS \ + audio + + #define SOURCES \ + audio_load_midi.cxx + +#end lib_target + +#begin lib_target + #define TARGET audio_load_wav + #define LOCAL_LIBS \ + audio + + #define SOURCES \ + audio_load_wav.cxx + +#end lib_target + +#begin test_bin_target + #define TARGET test_audio + + #define SOURCES \ + test_audio.cxx + +#end test_bin_target + diff --git a/panda/src/audio/audio.h b/panda/src/audio/audio.h new file mode 100644 index 0000000000..f2de9fe5a7 --- /dev/null +++ b/panda/src/audio/audio.h @@ -0,0 +1,15 @@ +// Filename: audio.h +// Created by: frang (06Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __AUDIO_H__ +#define __AUDIO_H__ + +#include "audio_trait.h" +#include "audio_sample.h" +#include "audio_music.h" +#include "audio_manager.h" +#include "audio_pool.h" + +#endif /* __AUDIO_H__ */ diff --git a/panda/src/audio/audio_load_midi.cxx b/panda/src/audio/audio_load_midi.cxx new file mode 100644 index 0000000000..19c1eedd06 --- /dev/null +++ b/panda/src/audio/audio_load_midi.cxx @@ -0,0 +1,75 @@ +// Filename: audio_load_midi.cxx +// Created by: cary (26Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include "audio_pool.h" +#include "config_audio.h" + +Configure(audio_load_midi); + +#ifdef USE_MIKMOD + +#include "audio_mikmod_traits.h" + +void AudioDestroyMidi(AudioTraits::MusicClass* music) { + MikModMidi::destroy(music); +} + +void AudioLoadMidi(AudioTraits::MusicClass** music, + AudioTraits::PlayerClass** player, + AudioTraits::DeleteMusicFunc** destroy, Filename filename) { + *music = MikModMidi::load_midi(filename); + if (*music == (AudioTraits::MusicClass*)0L) + return; + *player = MikModMidiPlayer::get_instance(); + *destroy = AudioDestroyMidi; +} + +#else + +#ifdef PENV_WIN32 + +#include "audio_win_traits.h" + +void AudioDestroyMidi(AudioTraits::MusicClass* music) { + WinMusic::destroy(music); +} + +void AudioLoadMidi(AudioTraits::MusicClass** music, + AudioTraits::PlayerClass** player, + AudioTraits::DeleteMusicFunc** destroy, Filename filename) { + *music = WinMusic::load_midi(filename); + if (*music == (AudioTraits::MusicClass*)0L) + return; + *player = WinPlayer::get_instance(); + *destroy = AudioDestroyMidi; + audio_cat->debug() << "sanity check: music = " << (void*)*music + << " player = " << (void*)*player << " destroy = " + << (void*)*destroy << endl; +} +#else + +// Null driver +#include "audio_null_traits.h" + +void AudioDestroyMidi(AudioTraits::MusicClass* music) { + delete music; +} + +void AudioLoadMidi(AudioTraits::MusicClass** music, + AudioTraits::PlayerClass** player, + AudioTraits::DeleteMusicFunc** destroy, Filename) { + *music = new NullMusic(); + *player = new NullPlayer(); + *destroy = AudioDestroyMidi; +} + +#endif /* win32 */ +#endif /* mikmod */ + +ConfigureFn(audio_load_midi) { + AudioPool::register_music_loader("midi", AudioLoadMidi); + AudioPool::register_music_loader("mid", AudioLoadMidi); +} diff --git a/panda/src/audio/audio_load_wav.cxx b/panda/src/audio/audio_load_wav.cxx new file mode 100644 index 0000000000..f31b536385 --- /dev/null +++ b/panda/src/audio/audio_load_wav.cxx @@ -0,0 +1,71 @@ +// Filename: audio_load_wav.cxx +// Created by: cary (23Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include "audio_pool.h" + +Configure(audio_load_wav); + +#ifdef USE_MIKMOD + +#include "audio_mikmod_traits.h" + +void AudioDestroyWav(AudioTraits::SampleClass* sample) { + MikModSample::destroy(sample); +} + +void AudioLoadWav(AudioTraits::SampleClass** sample, + AudioTraits::PlayerClass** player, + AudioTraits::DeleteSampleFunc** destroy, Filename filename) { + *sample = MikModSample::load_wav(filename); + if (*sample == (AudioTraits::SampleClass*)0L) + return; + *player = MikModSamplePlayer::get_instance(); + *destroy = AudioDestroyWav; +} + +#else /* no MikMod */ + +#ifdef PENV_WIN32 + +#include "audio_win_traits.h" + +void AudioDestroyWav(AudioTraits::SampleClass* sample) { + WinSample::destroy(sample); +} + +void AudioLoadWav(AudioTraits::SampleClass** sample, + AudioTraits::PlayerClass** player, + AudioTraits::DeleteSampleFunc** destroy, Filename filename) { + *sample = WinSample::load_wav(filename); + if (*sample == (AudioTraits::SampleClass*)0L) + return; + *player = WinPlayer::get_instance(); + *destroy = AudioDestroyWav; +} + +#else /* no win32 */ + +// Null driver +#include "audio_null_traits.h" + +void AudioDestroyWav(AudioTraits::SampleClass* sample) { + delete sample; +} + +void AudioLoadWav(AudioTraits::SampleClass** sample, + AudioTraits::PlayerClass** player, + AudioTraits::DeleteSampleFunc** destroy, Filename) { + *sample = new NullSample(); + *player = new NullPlayer(); + *destroy = AudioDestroyWav; +} + +#endif /* win32 */ +#endif /* MikMod */ + +ConfigureFn(audio_load_wav) { + AudioPool::register_sample_loader("wav", AudioLoadWav); +} diff --git a/panda/src/audio/audio_manager.I b/panda/src/audio/audio_manager.I new file mode 100644 index 0000000000..4c55dfb3f9 --- /dev/null +++ b/panda/src/audio/audio_manager.I @@ -0,0 +1,69 @@ +// Filename: audio_manager.I +// Created by: cary (24Sep00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: AudioManager::play (AudioSample) +// Access: Public, Static +// Description: Play an audio sample +//////////////////////////////////////////////////////////////////// +INLINE void AudioManager::play(AudioSample* sample) { + get_ptr()->ns_play(sample); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioManager::play (AudioMusic) +// Access: Public, Static +// Description: Play an audio music instance +//////////////////////////////////////////////////////////////////// +INLINE void AudioManager::play(AudioMusic* music) { + get_ptr()->ns_play(music); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioManager::update +// Access: Public, Static +// Description: make sure buffers are full +//////////////////////////////////////////////////////////////////// +INLINE void AudioManager::update(void) { + mutex_lock l(_manager_mutex); + if (_update_func != (UpdateFunc*)0L) + (*_update_func)(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioManager::spawn_update +// Access: Public, Static +// Description: spawn a thread to call update +//////////////////////////////////////////////////////////////////// +INLINE void AudioManager::spawn_update(void) { + get_ptr()->ns_spawn_update(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioManager::set_volume (sample) +// Access: Public, Static +// Description: set the volume on a sample +//////////////////////////////////////////////////////////////////// +INLINE void AudioManager::set_volume(AudioSample* sample, int v) { + get_ptr()->ns_set_volume(sample, v); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioManager::set_volume (music) +// Access: Public, Static +// Description: set the volume on music +//////////////////////////////////////////////////////////////////// +INLINE void AudioManager::set_volume(AudioMusic* music, int v) { + get_ptr()->ns_set_volume(music, v); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioManager::Constructor +// Access: Private +// Description: The constructor is not intended to be called +// directly; there's only supposed to be one AudioManager +// in the universe and it constructs itself. +//////////////////////////////////////////////////////////////////// +INLINE AudioManager::AudioManager(void) : _spawned((thread*)0L) {} diff --git a/panda/src/audio/audio_manager.cxx b/panda/src/audio/audio_manager.cxx new file mode 100644 index 0000000000..1ac3788dc0 --- /dev/null +++ b/panda/src/audio/audio_manager.cxx @@ -0,0 +1,95 @@ +// Filename: audio_manager.cxx +// Created by: cary (24Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "audio_manager.h" +#include "config_audio.h" + +AudioManager* AudioManager::_global_ptr = (AudioManager*)0L; +AudioManager::UpdateFunc* AudioManager::_update_func = + (AudioManager::UpdateFunc*)0L; +mutex AudioManager::_manager_mutex; + +//////////////////////////////////////////////////////////////////// +// Function: AudioManager::set_update_func +// Access: Public, Static +// Description: register a function that will maintain the buffers +// for audio output +//////////////////////////////////////////////////////////////////// +void AudioManager::set_update_func(AudioManager::UpdateFunc* func) { + if (_update_func != (AudioManager::UpdateFunc*)0L) + audio_cat->error() << "There maybe be more then one audio driver installed" + << endl; + _update_func = func; +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioManager::get_ptr +// Access: Private, Static +// Description: Initializes and/or returns the global pointer to the +// one AudioManager object in the system. +//////////////////////////////////////////////////////////////////// +AudioManager* AudioManager::get_ptr(void) { + if (_global_ptr == (AudioManager*)0L) + _global_ptr = new AudioManager; + return _global_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioManager::ns_play (AudioSample) +// Access: Private +// Description: get the player off the sample, and start it playing +//////////////////////////////////////////////////////////////////// +void AudioManager::ns_play(AudioSample* sample) { + sample->get_player()->play_sample(sample->get_sample()); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioManager::ns_play (AudioMusic) +// Access: Private +// Description: get the player off the music, and start it playing +//////////////////////////////////////////////////////////////////// +void AudioManager::ns_play(AudioMusic* music) { + music->get_player()->play_music(music->get_music()); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioManager::spawned_update +// Access: static +// Description: the thread function to call update forever. +//////////////////////////////////////////////////////////////////// +void AudioManager::spawned_update(void*) { + while (1) { + AudioManager::update(); + ipc_traits::sleep(0, 1000000); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioManager::ns_set_volume (AudioSample) +// Access: Private +// Description: get the player off the sample, and set volume on it +//////////////////////////////////////////////////////////////////// +void AudioManager::ns_set_volume(AudioSample* sample, int v) { + sample->get_player()->set_volume(sample->get_sample(), v); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioManager::ns_set_volume (AudioMusic) +// Access: Private +// Description: get the player off the music, and set volume on it +//////////////////////////////////////////////////////////////////// +void AudioManager::ns_set_volume(AudioMusic* music, int v) { + music->get_player()->set_volume(music->get_music(), v); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioManager::ns_spawn_update +// Access: Private +// Description: spawn a thread that calls update every so often +//////////////////////////////////////////////////////////////////// +void AudioManager::ns_spawn_update(void) { + _spawned = thread::create(spawned_update, (void*)0L, + thread::PRIORITY_NORMAL); +} diff --git a/panda/src/audio/audio_manager.h b/panda/src/audio/audio_manager.h new file mode 100644 index 0000000000..7cf6a7c81c --- /dev/null +++ b/panda/src/audio/audio_manager.h @@ -0,0 +1,47 @@ +// Filename: audio_manager.h +// Created by: cary (22Sep00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __AUDIO_MANAGER_H__ +#define __AUDIO_MANAGER_H__ + +#include "audio_trait.h" +#include "audio_sample.h" +#include "audio_music.h" + +#include +#include + +class EXPCL_PANDA AudioManager { +private: + INLINE AudioManager(void); + + void ns_play(AudioSample*); + void ns_play(AudioMusic*); + void ns_spawn_update(void); + void ns_set_volume(AudioSample*, int); + void ns_set_volume(AudioMusic*, int); + + static AudioManager* get_ptr(void); + static void spawned_update(void*); + + typedef void UpdateFunc(void); + static AudioManager* _global_ptr; + static UpdateFunc* _update_func; + static mutex _manager_mutex; + thread* _spawned; +public: + static void set_update_func(UpdateFunc*); + + INLINE static void play(AudioSample*); + INLINE static void play(AudioMusic*); + INLINE static void update(void); + INLINE static void spawn_update(void); + INLINE static void set_volume(AudioSample*, int); + INLINE static void set_volume(AudioMusic*, int); +}; + +#include "audio_manager.I" + +#endif /* __AUDIO_MANAGER_H__ */ diff --git a/panda/src/audio/audio_midi.cxx b/panda/src/audio/audio_midi.cxx new file mode 100644 index 0000000000..953dac6fdf --- /dev/null +++ b/panda/src/audio/audio_midi.cxx @@ -0,0 +1,233 @@ +// Filename: audio_midi.cxx +// Created by: cary (26Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "audio_midi.h" +#include "config_audio.h" +#include // get iostream and fstream + +#define SHORT short +#define LONG long + +#define RIFF 0x52494646 +#define CTMF 0x43544d46 +#define MThd 0x4d546864 +#define MTrk 0x4d54726b + +// take data off the supplimental stream until it's empty +inline static unsigned char read8(istream& is, istream& supp) { + unsigned char b; + if (supp.eof()) { + is >> b; + } else { + supp >> b; + } + return b; +} + +inline static unsigned SHORT read16(istream& is, istream& supp) { + unsigned char b1, b2; + b1 = read8(is, supp); + b2 = read8(is, supp); + unsigned SHORT ret = (b1 << 8) | (b2); + return ret; +} + +inline static unsigned LONG read32(istream& is, istream& supp) { + unsigned char b1, b2, b3, b4; + b1 = read8(is, supp); + b2 = read8(is, supp); + b3 = read8(is, supp); + b4 = read8(is, supp); + unsigned int LONG ret = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4); + return ret; +} + +inline static void scroll8(unsigned LONG& prev, unsigned LONG& curr, + istream& is, istream& supp) { + unsigned char b1 = ((curr >> 24) & 0xff); + prev = ((prev << 8) & 0xffffff00) | (b1); + b1 = read8(is, supp); + curr = ((curr << 8) & 0xffffff00) | (b1); +} + +AudioMidi::AudioMidi(Filename filename, int header_idx) { + filename.set_binary(); + ifstream in; + if (!filename.open_read(in)) { + cerr << "ACK, cannot read '" << filename << "'" << endl; + return; + } + + istringstream dummy(""); + unsigned LONG prev = 0x0; + unsigned LONG curr = read32(in, dummy); + int count = 0; + bool done = false; + do { + if (curr == MThd) { + ++count; + if (count == header_idx) + done = true; + else { + scroll8(prev, curr, in, dummy); + scroll8(prev, curr, in, dummy); + scroll8(prev, curr, in, dummy); + scroll8(prev, curr, in, dummy); + } + } else { + scroll8(prev, curr, in, dummy); + } + if (in.eof()) + done = true; + } while (!done); + if (in.eof()) { + cerr << "fewer then " << header_idx << " headers in file (" << count + << ")" << endl; + return; + } + if (prev == RIFF) { + if (audio_cat->is_debug()) + audio_cat->debug() << "it's a RIFF file" << endl; + curr = read32(in, dummy); + curr = read32(in, dummy); + curr = read32(in, dummy); + curr = read32(in, dummy); + } + unsigned LONG tracklen; + unsigned SHORT format; + unsigned SHORT numtracks; + unsigned SHORT division; + if (curr == MThd) { + if (audio_cat->is_debug()) + audio_cat->debug() << "easy header" << endl; + tracklen = read32(in, dummy); + format = read16(in, dummy); + numtracks = read16(in, dummy); + division = read16(in, dummy); + } else if (curr == CTMF) { + // Creative Labs CMF file. We're not supporting this yet + cerr << "don't support Creative Labs CMF files yet" << endl; + return; + } else { + if (audio_cat->is_debug()) + audio_cat->debug() << "hard header" << endl; + done = false; + do { + if (curr == MThd) + done = true; + else + scroll8(prev, curr, in, dummy); + if (in.eof()) + done = true; + } while (!done); + if (in.eof()) { + cerr << "truncated file!" << endl; + return; + } + tracklen = read32(in, dummy); + format = read16(in, dummy); + numtracks = read16(in, dummy); + division = read16(in, dummy); + } + if (audio_cat->is_debug()) + audio_cat->debug() << "Read header. tracklen = " << tracklen + << " format = " << format << " numtracks = " + << numtracks << " division = " << division << endl; + for (int currtrack = 0; currtrack < numtracks; ++currtrack) { + string fudge; + curr = read32(in, dummy); + if (curr != MTrk) { + if (audio_cat->is_debug()) + audio_cat->debug() << "having to seach for track #" << currtrack + << endl; + if (curr == MThd) { + if (audio_cat->is_debug()) + audio_cat->debug() << "hit a header instead, skipping track" << endl; + continue; + } else { + done = false; + if (currtrack > 0) { + string stmp = (*(_seq.rbegin())); + int i = stmp.rfind("MTrk"); + if (i != string::npos) { + fudge = stmp.substr(i+4, string::npos); + unsigned char b; + b = ((curr >> 24) & 0xff); + fudge += b; + b = ((curr >> 16) & 0xff); + fudge += b; + b = ((curr >> 8) & 0xff); + fudge += b; + b = (curr & 0xff); + fudge += b; + done = true; + } + } + if (!done) { + do { + if (curr == MTrk) + done = true; + else + scroll8(prev, curr, in, dummy); + if (in.eof()) + done = true; + } while (!done); + if (in.eof()) { + cerr << "truncated file" << endl; + return; + } + } + } + } + if (audio_cat->is_debug()) + audio_cat->debug() << "fudge = '" << fudge << "'" << endl; + istringstream fudges(fudge); + if (fudge.empty()) { + // force EOF + unsigned char b; + fudges >> b; + } + unsigned LONG thislen = read32(in, fudges); + if (audio_cat->is_debug()) + audio_cat->debug() << "found track #" << currtrack << " with length = " + << thislen << endl; + { + ostringstream os; + int i; + for (i=0; iis_debug()) + audio_cat->debug() << "track data (" << s.length() << "): '" << s + << "'" << endl; + } + } + if ((_seq.size() != numtracks) && audio_cat->is_debug()) + audio_cat->debug() + << "actual number of tracks read does not match header. (" + << _seq.size() << " != " << numtracks << ")" << endl; +} + +AudioMidi::AudioMidi(const AudioMidi& c) : _seq(c._seq) {} + +AudioMidi::~AudioMidi(void) { +} + +AudioMidi& AudioMidi::operator=(const AudioMidi&) { + return *this; +} + +bool AudioMidi::operator==(const AudioMidi&) const { + return false; +} diff --git a/panda/src/audio/audio_midi.h b/panda/src/audio/audio_midi.h new file mode 100644 index 0000000000..afa0582e31 --- /dev/null +++ b/panda/src/audio/audio_midi.h @@ -0,0 +1,26 @@ +// Filename: audio_midi.h +// Created by: cary (26Sep00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __AUDIO_MIDI_H__ +#define __AUDIO_MIDI_H__ + +#include +#include + +// define an internal representation for a midi file +class AudioMidi { +private: + typedef list StrList; + StrList _seq; +public: + AudioMidi(Filename, int = 1); + AudioMidi(const AudioMidi&); + ~AudioMidi(void); + + AudioMidi& operator=(const AudioMidi&); + bool operator==(const AudioMidi&) const; +}; + +#endif /* __AUDIO_MIDI_H__ */ diff --git a/panda/src/audio/audio_mikmod_traits.cxx b/panda/src/audio/audio_mikmod_traits.cxx new file mode 100644 index 0000000000..2240de2f60 --- /dev/null +++ b/panda/src/audio/audio_mikmod_traits.cxx @@ -0,0 +1,305 @@ +// Filename: audio_mikmod_traits.cxx +// Created by: cary (23Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "audio_mikmod_traits.h" +#include "audio_manager.h" +#include "config_audio.h" +#include +#include + +static bool have_initialized = false; +static bool initialization_error = false; + +static void update_mikmod(void) { + MikMod_Update(); +} + +static void initialize(void) { + if (have_initialized) + return; + if (initialization_error) + return; + /* register the drivers */ + MikMod_RegisterAllDrivers(); + /* initialize the mikmod library */ + md_mixfreq = audio_mix_freq; + { + // I think this is defined elsewhere + typedef list StrList; + typedef Serialize::Deserializer OptBuster; + StrList opts; + OptBuster buster(*audio_mode_flags, " "); + opts = buster(); + for (StrList::iterator i=opts.begin(); i!=opts.end(); ++i) { + if ((*i) == "DMODE_INTERP") { + md_mode |= DMODE_INTERP; + } else if ((*i) == "DMODE_REVERSE") { + md_mode |= DMODE_REVERSE; + } else if ((*i) == "DMODE_SURROUND") { + md_mode |= DMODE_SURROUND; + } else if ((*i) == "DMODE_16BITS") { + md_mode |= DMODE_16BITS; + } else if ((*i) == "DMODE_HQMIXER") { + md_mode |= DMODE_HQMIXER; + } else if ((*i) == "DMODE_SOFT_MUSIC") { + md_mode |= DMODE_SOFT_MUSIC; + } else if ((*i) == "DMODE_SOFT_SNDFX") { + md_mode |= DMODE_SOFT_SNDFX; + } else if ((*i) == "DMODE_STEREO") { + md_mode |= DMODE_STEREO; + } else { + audio_cat->error() << "unknown audio driver flag '" << *i << "'" + << endl; + } + } + if (audio_cat->is_debug()) { + audio_cat->debug() << "final driver mode is ("; + bool any_out = false; + if (md_mode & DMODE_INTERP) { + audio_cat->debug(false) << "DMODE_INTERP"; + any_out = true; + } + if (md_mode & DMODE_REVERSE) { + if (any_out) + audio_cat->debug(false) << ", "; + audio_cat->debug(false) << "DMODE_REVERSE"; + any_out = true; + } + if (md_mode & DMODE_SURROUND) { + if (any_out) + audio_cat->debug(false) << ", "; + audio_cat->debug(false) << "DMODE_SURROUND"; + any_out = true; + } + if (md_mode & DMODE_16BITS) { + if (any_out) + audio_cat->debug(false) << ", "; + audio_cat->debug(false) << "DMODE_16BITS"; + any_out = true; + } + if (md_mode & DMODE_HQMIXER) { + if (any_out) + audio_cat->debug(false) << ", "; + audio_cat->debug(false) << "DMODE_HQMIXER"; + any_out = true; + } + if (md_mode & DMODE_SOFT_MUSIC) { + if (any_out) + audio_cat->debug(false) << ", "; + audio_cat->debug(false) << "DMODE_SOFT_MUSIC"; + any_out = true; + } + if (md_mode & DMODE_SOFT_SNDFX) { + if (any_out) + audio_cat->debug(false) << ", "; + audio_cat->debug(false) << "DMODE_SOFT_SNDFX"; + any_out = true; + } + if (md_mode & DMODE_STEREO) { + if (any_out) + audio_cat->debug(false) << ", "; + audio_cat->debug(false) << "DMODE_STEREO"; + any_out = true; + } + audio_cat->debug(false) << ")" << endl; + } + } + md_device = audio_driver_select; + if (MikMod_Init((char*)(audio_driver_params->c_str()))) { + audio_cat->error() << "Could not initialize the audio drivers. '" + << MikMod_strerror(MikMod_errno) << "'" << endl; + initialization_error = true; + return; + } + if (audio_cat->is_debug()) { + audio_cat->debug() << "driver info" << endl << MikMod_InfoDriver() << endl; + } + MikMod_SetNumVoices(-1, audio_sample_voices); + AudioManager::set_update_func(update_mikmod); + have_initialized = true; +} + +MikModSample::MikModSample(SAMPLE* sample) : _sample(sample), _voice(-1) { +} + +MikModSample::~MikModSample(void) { + Sample_Free(_sample); +} + +float MikModSample::length(void) { + float len = _sample->length; + float speed = _sample->speed; + return len / speed; +} + +AudioTraits::SampleClass::SampleStatus MikModSample::status(void) { + if (_voice == -1) + return READY; + if (Voice_Stopped(_voice)) + return PLAYING; + return READY; +} + +MikModSample* MikModSample::load_wav(Filename filename) { + initialize(); + SAMPLE* sample = Sample_Load((char*)(filename.c_str())); + if (sample == (SAMPLE*)0L) { + audio_cat->error() << "error loading sample '" << filename << "' because '" + << MikMod_strerror(MikMod_errno) << "'" << endl; + return (MikModSample*)0L; + } + return new MikModSample(sample); +} + +void MikModSample::destroy(AudioTraits::SampleClass* sample) { + delete sample; +} + +void MikModSample::set_voice(int v) { + _voice = v; +} + +int MikModSample::get_voice(void) { + return _voice; +} + +SAMPLE* MikModSample::get_sample(void) { + return _sample; +} + +int MikModSample::get_freq(void) { + return _sample->speed; +} + +MikModMusic::MikModMusic(void) { +} + +MikModMusic::~MikModMusic(void) { +} + +AudioTraits::MusicClass::MusicStatus MikModMusic::status(void) { + return READY; +} + +MikModMidi::MikModMidi(void) { +} + +MikModMidi::~MikModMidi(void) { +} + +MikModMidi* MikModMidi::load_midi(Filename) { + initialize(); + return new MikModMidi(); +} + +void MikModMidi::destroy(AudioTraits::MusicClass* music) { + delete music; +} + +AudioTraits::MusicClass::MusicStatus MikModMidi::status(void) { + return READY; +} + +MikModSamplePlayer* MikModSamplePlayer::_global_instance = + (MikModSamplePlayer*)0L; + +MikModSamplePlayer::MikModSamplePlayer(void) : AudioTraits::PlayerClass() { +} + +MikModSamplePlayer::~MikModSamplePlayer(void) { +} + +void MikModSamplePlayer::play_sample(AudioTraits::SampleClass* sample) { + if (!have_initialized) + initialize(); + if (!MikMod_Active()) { + if (MikMod_EnableOutput()) { + audio_cat->error() << "could not enable sample output '" + << MikMod_strerror(MikMod_errno) << "'" << endl; + } + } + // cast to the correct type + MikModSample* msample = (MikModSample*)sample; + // fire it off + msample->set_voice(Sample_Play(msample->get_sample(), 0, 0)); + Voice_SetFrequency(msample->get_voice(), msample->get_freq()); + if (Voice_GetFrequency(msample->get_voice()) != msample->get_freq()) + audio_cat->error() << "setting freq did not stick!" << endl; + Voice_SetPanning(msample->get_voice(), 127); +} + +void MikModSamplePlayer::play_music(AudioTraits::MusicClass*) { + audio_cat->error() << "trying to play music with a MikModSamplePlayer" + << endl; +} + +void MikModSamplePlayer::set_volume(AudioTraits::SampleClass* sample, int v) { + initialize(); + MikModSample* msample = (MikModSample*)sample; + Voice_SetVolume(msample->get_voice(), v); +} + +void MikModSamplePlayer::set_volume(AudioTraits::MusicClass*, int) { + audio_cat->error() + << "trying to set volume on music withe a MikModSamplePlayer" << endl; +} + +MikModSamplePlayer* MikModSamplePlayer::get_instance(void) { + if (_global_instance == (MikModSamplePlayer*)0L) + _global_instance = new MikModSamplePlayer(); + return _global_instance; +} + +MikModFmsynthPlayer::MikModFmsynthPlayer(void) { +} + +MikModFmsynthPlayer::~MikModFmsynthPlayer(void) { +} + +void MikModFmsynthPlayer::play_sample(AudioTraits::SampleClass*) { + audio_cat->error() << "trying to play a sample with a MikModFmsynthPlayer" + << endl; +} + +void MikModFmsynthPlayer::play_music(AudioTraits::MusicClass* music) { +} + +void MikModFmsynthPlayer::set_volume(AudioTraits::SampleClass*, int) { + audio_cat->error() + << "trying to set volume on a sample with a MikModFmsynthPlayer" << endl; +} + +void MikModFmsynthPlayer::set_volume(AudioTraits::MusicClass*, int) { +} + +MikModMidiPlayer* MikModMidiPlayer::_global_instance = (MikModMidiPlayer*)0L; + +MikModMidiPlayer::MikModMidiPlayer(void) { +} + +MikModMidiPlayer::~MikModMidiPlayer(void) { +} + +void MikModMidiPlayer::play_sample(AudioTraits::SampleClass*) { + audio_cat->error() << "trying to play a sample with a MikModMidiPlayer" + << endl; +} + +void MikModMidiPlayer::play_music(AudioTraits::MusicClass* music) { +} + +void MikModMidiPlayer::set_volume(AudioTraits::SampleClass*, int) { + audio_cat->error() + << "trying to set volume on a sample with a MikModMidiPlayer" << endl; +} + +void MikModMidiPlayer::set_volume(AudioTraits::MusicClass*, int) { +} + +MikModMidiPlayer* MikModMidiPlayer::get_instance(void) { + if (_global_instance == (MikModMidiPlayer*)0L) + _global_instance = new MikModMidiPlayer(); + return _global_instance; +} diff --git a/panda/src/audio/audio_mikmod_traits.h b/panda/src/audio/audio_mikmod_traits.h new file mode 100644 index 0000000000..0670f5138e --- /dev/null +++ b/panda/src/audio/audio_mikmod_traits.h @@ -0,0 +1,102 @@ +// Filename: audio_mikmod_traits.h +// Created by: frang (06Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __AUDIO_MIKMOD_TRAITS_H__ +#define __AUDIO_MIKMOD_TRAITS_H__ + +#include "audio_trait.h" +#include +#include +#include + +class EXPCL_PANDA MikModSample : public AudioTraits::SampleClass { +private: + SAMPLE* _sample; + int _voice; + + MikModSample(SAMPLE*); +public: + virtual ~MikModSample(void); + + virtual float length(void); + virtual AudioTraits::SampleClass::SampleStatus status(void); +public: + // used by the readers + static MikModSample* load_wav(Filename); + static void destroy(AudioTraits::SampleClass*); + // used by the players + virtual void set_voice(int); + virtual int get_voice(void); + virtual SAMPLE* get_sample(void); + virtual int get_freq(void); +}; + +class EXPCL_PANDA MikModMusic : public AudioTraits::MusicClass { +private: + MODULE* _music; +public: + MikModMusic(void); + virtual ~MikModMusic(void); + + virtual AudioTraits::MusicClass::MusicStatus status(void); +}; + +class EXPCL_PANDA MikModMidi : public AudioTraits::MusicClass { +private: +public: + MikModMidi(void); + virtual ~MikModMidi(void); + + virtual AudioTraits::MusicClass::MusicStatus status(void); +public: + // used by the readers + static MikModMidi* load_midi(Filename); + static void destroy(AudioTraits::MusicClass*); +}; + +class EXPCL_PANDA MikModSamplePlayer : public AudioTraits::PlayerClass { +public: + MikModSamplePlayer(void); + virtual ~MikModSamplePlayer(void); + + virtual void play_sample(AudioTraits::SampleClass*); + virtual void play_music(AudioTraits::MusicClass*); + virtual void set_volume(AudioTraits::SampleClass*, int); + virtual void set_volume(AudioTraits::MusicClass*, int); +public: + // used by the readers + static MikModSamplePlayer* get_instance(void); +private: + static MikModSamplePlayer* _global_instance; +}; + +class EXPCL_PANDA MikModFmsynthPlayer : public AudioTraits::PlayerClass { +public: + MikModFmsynthPlayer(void); + virtual ~MikModFmsynthPlayer(void); + + virtual void play_sample(AudioTraits::SampleClass*); + virtual void play_music(AudioTraits::MusicClass*); + virtual void set_volume(AudioTraits::SampleClass*, int); + virtual void set_volume(AudioTraits::MusicClass*, int); +}; + +class EXPCL_PANDA MikModMidiPlayer : public AudioTraits::PlayerClass { +public: + MikModMidiPlayer(void); + virtual ~MikModMidiPlayer(void); + + virtual void play_sample(AudioTraits::SampleClass*); + virtual void play_music(AudioTraits::MusicClass*); + virtual void set_volume(AudioTraits::SampleClass*, int); + virtual void set_volume(AudioTraits::MusicClass*, int); +public: + // used by the readers + static MikModMidiPlayer* get_instance(void); +private: + static MikModMidiPlayer* _global_instance; +}; + +#endif /* __AUDIO_MIKMOD_TRAITS_H__ */ diff --git a/panda/src/audio/audio_music.I b/panda/src/audio/audio_music.I new file mode 100644 index 0000000000..c85aedce70 --- /dev/null +++ b/panda/src/audio/audio_music.I @@ -0,0 +1,67 @@ +// Filename: audio_music.I +// Created by: cary (26Sep00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: AudioMusic::constructor +// Access: Protected +// Description: initialize a new music +//////////////////////////////////////////////////////////////////// +INLINE AudioMusic::AudioMusic(AudioTraits::MusicClass* music, + AudioTraits::PlayerClass* player, + AudioTraits::DeleteMusicFunc* destroy, + const string& filename) : Namable(filename), + _music(music), + _player(player), + _destroy(destroy) {} + +//////////////////////////////////////////////////////////////////// +// Function: AudioMusic::copy constructor +// Access: Protected +// Description: copy a music, but we don't really want to allow this +//////////////////////////////////////////////////////////////////// +INLINE AudioMusic::AudioMusic(const AudioMusic& c) : Namable(c.get_name()), + _music(c._music), + _player(c._player), + _destroy(c._destroy) {} + +//////////////////////////////////////////////////////////////////// +// Function: AudioMusic::assignment operator +// Access: Protected +// Description: copy a music, but we don't really want to allow this +//////////////////////////////////////////////////////////////////// +INLINE AudioMusic& AudioMusic::operator=(const AudioMusic& c) { + this->set_name(c.get_name()); + this->_music = c._music; + this->_player = c._player; + this->_destroy = c._destroy; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioMusic::get_player +// Access: Protected +// Description: return the player for this music +//////////////////////////////////////////////////////////////////// +INLINE AudioTraits::PlayerClass* AudioMusic::get_player(void) { + return _player; +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioMusic::get_music +// Access: Protected +// Description: return the trait music class for this music +//////////////////////////////////////////////////////////////////// +INLINE AudioTraits::MusicClass* AudioMusic::get_music(void) { + return _music; +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioMusic::equality operator +// Access: Public +// Description: test to see if two musics are the same +//////////////////////////////////////////////////////////////////// +INLINE bool AudioMusic::operator==(const AudioMusic& c) const { + return (_music == c._music); +} diff --git a/panda/src/audio/audio_music.cxx b/panda/src/audio/audio_music.cxx new file mode 100644 index 0000000000..4de5ae6ec1 --- /dev/null +++ b/panda/src/audio/audio_music.cxx @@ -0,0 +1,36 @@ +// Filename: audio_music.cxx +// Created by: cary (26Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "audio_music.h" +#include "config_audio.h" + +TypeHandle AudioMusic::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: AudioMusic::destructor +// Access: Public +// Description: deletes the music data and then lets the system +// destroy this structure +//////////////////////////////////////////////////////////////////// +AudioMusic::~AudioMusic(void) { + (*_destroy)(_music); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioMusic::status +// Access: Public +// Description: return the current play status of this music +//////////////////////////////////////////////////////////////////// +AudioMusic::MusicStatus AudioMusic::status(void) { + AudioTraits::MusicClass::MusicStatus stat = _music->status(); + switch (stat) { + case AudioTraits::MusicClass::READY: + return READY; + case AudioTraits::MusicClass::PLAYING: + return PLAYING; + } + audio_cat->error() << "unknown status for music" << endl; + return READY; +} diff --git a/panda/src/audio/audio_music.h b/panda/src/audio/audio_music.h new file mode 100644 index 0000000000..64b38697c6 --- /dev/null +++ b/panda/src/audio/audio_music.h @@ -0,0 +1,62 @@ +// Filename: audio_music.h +// Created by: cary (22Sep00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __AUDIO_MUSIC_H__ +#define __AUDIO_MUSIC_H__ + +#include "audio_trait.h" +#include "typedReferenceCount.h" +#include "namable.h" + +class AudioPool; +class AudioManager; + +class EXPCL_PANDA AudioMusic : public TypedReferenceCount, public Namable { +private: + AudioTraits::MusicClass *_music; + AudioTraits::PlayerClass *_player; + AudioTraits::DeleteMusicFunc *_destroy; +protected: + INLINE AudioMusic(AudioTraits::MusicClass*, AudioTraits::PlayerClass*, + AudioTraits::DeleteMusicFunc*, const string&); + INLINE AudioMusic(const AudioMusic&); + INLINE AudioMusic& operator=(const AudioMusic&); + + INLINE AudioTraits::PlayerClass* get_player(void); + INLINE AudioTraits::MusicClass* get_music(void); + + friend class AudioPool; + friend class AudioManager; +public: + virtual ~AudioMusic(void); + INLINE bool operator==(const AudioMusic&) const; + + enum MusicStatus { READY, PLAYING }; + + MusicStatus status(void); +public: + // type stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + TypedReferenceCount::init_type(); + register_type(_type_handle, "AudioMusic", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +#include "audio_music.I" + +#endif /* __AUDIO_MUSIC_H__ */ diff --git a/panda/src/audio/audio_null_traits.I b/panda/src/audio/audio_null_traits.I new file mode 100644 index 0000000000..60dd1be58e --- /dev/null +++ b/panda/src/audio/audio_null_traits.I @@ -0,0 +1,13 @@ +// Filename: audio_null_traits.I +// Created by: cary (25Sep00) +// +//////////////////////////////////////////////////////////////////// + +INLINE NullSample::NullSample(void) { +} + +INLINE NullMusic::NullMusic(void) { +} + +INLINE NullPlayer::NullPlayer(void) { +} diff --git a/panda/src/audio/audio_null_traits.cxx b/panda/src/audio/audio_null_traits.cxx new file mode 100644 index 0000000000..9c28ae1323 --- /dev/null +++ b/panda/src/audio/audio_null_traits.cxx @@ -0,0 +1,70 @@ +// Filename: audio_null_traits.cxx +// Created by: cary (25Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "audio_null_traits.h" +#include "audio_manager.h" +#include "config_audio.h" + +static bool have_initialized = false; + +static void update_null(void) { + if (audio_cat->is_debug()) + audio_cat->debug() << "Update in Null audio driver" << endl; +} + +static void initialize(void) { + if (have_initialized) + return; + AudioManager::set_update_func(update_null); + have_initialized = true; +} + +NullSample::~NullSample(void) { +} + +float NullSample::length(void) { + if (audio_cat->is_debug()) + audio_cat->debug() << "in sample length in Null audio driver" << endl; + return 0.; +} + +AudioTraits::SampleClass::SampleStatus NullSample::status(void) { + if (audio_cat->is_debug()) + audio_cat->debug() << "in sample status in Null audio driver" << endl; + return AudioTraits::SampleClass::READY; +} + +NullMusic::~NullMusic(void) { +} + +AudioTraits::MusicClass::MusicStatus NullMusic::status(void) { + if (audio_cat->is_debug()) + audio_cat->debug() << "in music status in Null audio driver" << endl; + return READY; +} + +NullPlayer::~NullPlayer(void) { +} + +void NullPlayer::play_sample(AudioTraits::SampleClass*) { + if (audio_cat->is_debug()) + audio_cat->debug() << "in play sample in Null audio driver" << endl; +} + +void NullPlayer::play_music(AudioTraits::MusicClass*) { + if (audio_cat->is_debug()) + audio_cat->debug() << "in play music in Null audio driver" << endl; +} + +void NullPlayer::set_volume(AudioTraits::SampleClass*, int) { + if (audio_cat->is_debug()) + audio_cat->debug() << "in set volume (sample) in Null audio driver" + << endl; +} + +void NullPlayer::set_volume(AudioTraits::MusicClass*, int) { + if (audio_cat->is_debug()) + audio_cat->debug() << "in set volume (music) in Null audio driver" << endl; +} diff --git a/panda/src/audio/audio_null_traits.h b/panda/src/audio/audio_null_traits.h new file mode 100644 index 0000000000..2c4cf97999 --- /dev/null +++ b/panda/src/audio/audio_null_traits.h @@ -0,0 +1,41 @@ +// Filename: audio_null_traits.h +// Created by: cary (25Sep00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __AUDIO_NULL_TRAITS_H__ +#define __AUDIO_NULL_TRAITS_H__ + +#include "audio_trait.h" + +class EXPCL_PANDA NullSample : public AudioTraits::SampleClass { +public: + INLINE NullSample(void); + virtual ~NullSample(void); + + virtual float length(void); + virtual AudioTraits::SampleClass::SampleStatus status(void); +}; + +class EXPCL_PANDA NullMusic : public AudioTraits::MusicClass { +public: + INLINE NullMusic(void); + virtual ~NullMusic(void); + + virtual AudioTraits::MusicClass::MusicStatus status(void); +}; + +class EXPCL_PANDA NullPlayer : public AudioTraits::PlayerClass { +public: + INLINE NullPlayer(void); + virtual ~NullPlayer(void); + + virtual void play_sample(AudioTraits::SampleClass*); + virtual void play_music(AudioTraits::MusicClass*); + virtual void set_volume(AudioTraits::SampleClass*, int); + virtual void set_volume(AudioTraits::MusicClass*, int); +}; + +#include "audio_null_traits.I" + +#endif /* __AUDIO_NULL_TRAITS_H__ */ diff --git a/panda/src/audio/audio_pool.I b/panda/src/audio/audio_pool.I new file mode 100644 index 0000000000..a7ce32ae87 --- /dev/null +++ b/panda/src/audio/audio_pool.I @@ -0,0 +1,141 @@ +// Filename: audio_pool.I +// Created by: cary (22Sep00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::has_sample +// Access: Public, Static +// Description: Returns true if the sample has ever been loaded, +// false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool AudioPool::has_sample(const string& filename) { + return get_ptr()->ns_has_sample(filename); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::verify_sample +// Access: Public, Static +// Description: Loads the given filename up into a sample, if it has +// not already been loaded, and returns true to indicate +// success, or false to indicate failure. If this +// returns true, it is guaranteed that a subsequent call +// to load_sample() with the same sample name will +// return a valid AudioSample pointer. +//////////////////////////////////////////////////////////////////// +INLINE bool AudioPool::verify_sample(const string& filename) { + return load_sample(filename) != (AudioSample*)0L; +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::load_sample +// Access: Public, Static +// Description: Loads the given filename up into a sample, if it has +// not already been loaded, and returns the new sample. +// If a sample with the same filename was previously +// loaded, returns that one instead. If the sample +// file cannot be found, returns NULL. +//////////////////////////////////////////////////////////////////// +INLINE AudioSample* AudioPool::load_sample(const string& filename) { + return get_ptr()->ns_load_sample(filename); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::release_sample +// Access: Public, Static +// Description: Removes the indicated sample from the pool, +// indicating it will never be loaded again; the sample +// may then be freed. If this function is never called, +// a reference count will be maintained on every sample +// ever loaded, and samples will never be freed. +// +// The sample's name should not have been changed +// during its lifetime, or this function may fail to +// locate it in the pool. +//////////////////////////////////////////////////////////////////// +INLINE void AudioPool::release_sample(AudioSample* sample) { + get_ptr()->ns_release_sample(sample); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::release_all_samples +// Access: Public, Static +// Description: Releases all samples in the pool and restores the +// pool to the empty state. +//////////////////////////////////////////////////////////////////// +INLINE void AudioPool::release_all_samples(void) { + get_ptr()->ns_release_all_samples(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::has_music +// Access: Public, Static +// Description: Returns true if the music has ever been loaded, +// false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool AudioPool::has_music(const string& filename) { + return get_ptr()->ns_has_music(filename); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::verify_music +// Access: Public, Static +// Description: Loads the given filename up into a music, if it has +// not already been loaded, and returns true to indicate +// success, or false to indicate failure. If this +// returns true, it is guaranteed that a subsequent call +// to load_music() with the same music name will +// return a valid AudioMusic pointer. +//////////////////////////////////////////////////////////////////// +INLINE bool AudioPool::verify_music(const string& filename) { + return load_music(filename) != (AudioMusic*)0L; +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::load_music +// Access: Public, Static +// Description: Loads the given filename up into a music, if it has +// not already been loaded, and returns the new music. +// If a music with the same filename was previously +// loaded, returns that one instead. If the music +// file cannot be found, returns NULL. +//////////////////////////////////////////////////////////////////// +INLINE AudioMusic* AudioPool::load_music(const string& filename) { + return get_ptr()->ns_load_music(filename); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::release_music +// Access: Public, Static +// Description: Removes the indicated music from the pool, +// indicating it will never be loaded again; the music +// may then be freed. If this function is never called, +// a reference count will be maintained on every music +// ever loaded, and music will never be freed. +// +// The music's name should not have been changed +// during its lifetime, or this function may fail to +// locate it in the pool. +//////////////////////////////////////////////////////////////////// +INLINE void AudioPool::release_music(AudioMusic* music) { + get_ptr()->ns_release_music(music); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::release_all_music +// Access: Public, Static +// Description: Releases all music in the pool and restores the +// pool to the empty state. +//////////////////////////////////////////////////////////////////// +INLINE void AudioPool::release_all_music(void) { + get_ptr()->ns_release_all_music(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::Constructor +// Access: Private +// Description: The constructor is not intended to be called +// directly; there's only supposed to be one AudioPool +// in the universe and it constructs itself. +//////////////////////////////////////////////////////////////////// +INLINE AudioPool::AudioPool(void) {} diff --git a/panda/src/audio/audio_pool.cxx b/panda/src/audio/audio_pool.cxx new file mode 100644 index 0000000000..dea66c69fe --- /dev/null +++ b/panda/src/audio/audio_pool.cxx @@ -0,0 +1,243 @@ +// Filename: audio_pool.cxx +// Created by: cary (22Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "audio_pool.h" +#include "config_audio.h" +#include + +AudioPool* AudioPool::_global_ptr = (AudioPool*)0L; +typedef map SampleLoaders; +SampleLoaders* _sample_loaders = (SampleLoaders*)0L; +typedef map MusicLoaders; +MusicLoaders* _music_loaders = (MusicLoaders*)0L; + +//////////////////////////////////////////////////////////////////// +// Function: check_sample_loaders +// Access: Static +// Description: ensure that the sample loaders map has been initialized +//////////////////////////////////////////////////////////////////// +static void check_sample_loaders(void) { + if (_sample_loaders == (SampleLoaders*)0L) + _sample_loaders = new SampleLoaders; +} + +//////////////////////////////////////////////////////////////////// +// Function: check_music_loaders +// Access: Static +// Description: ensure that the music loaders map has been initialized +//////////////////////////////////////////////////////////////////// +static void check_music_loaders(void) { + if (_music_loaders == (MusicLoaders*)0L) + _music_loaders = new MusicLoaders; +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::get_ptr +// Access: Private, Static +// Description: Initializes and/or returns the global pointer to the +// one AudioPool object in the system. +//////////////////////////////////////////////////////////////////// +AudioPool* AudioPool::get_ptr(void) { + if (_global_ptr == (AudioPool*)0L) + _global_ptr = new AudioPool; + return _global_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::ns_has_sample +// Access: Private +// Description: The nonstatic implementation of has_sample(). +//////////////////////////////////////////////////////////////////// +bool AudioPool::ns_has_sample(Filename filename) { + filename.resolve_filename(get_sound_path()); + + SampleMap::const_iterator si; + si = _samples.find(filename); + if (si != _samples.end()) { + // this sample was previously loaded + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::ns_load_sample +// Access: Private +// Description: The nonstatic implementation of load_sample(). +//////////////////////////////////////////////////////////////////// +AudioSample* AudioPool::ns_load_sample(Filename filename) { + filename.resolve_filename(get_sound_path()); + + SampleMap::const_iterator si; + si = _samples.find(filename); + if (si != _samples.end()) { + // this sample was previously loaded + return (*si).second; + } + audio_cat.info() << "Loading sample " << filename << "\n"; + AudioTraits::SampleClass* sample = (AudioTraits::SampleClass*)0L; + AudioTraits::PlayerClass* player = (AudioTraits::PlayerClass*)0L; + AudioTraits::DeleteSampleFunc* destroy = (AudioTraits::DeleteSampleFunc*)0L; + string ext = filename.get_extension(); + SampleLoaders::const_iterator sli; + check_sample_loaders(); + sli = _sample_loaders->find(ext); + if (sli == _sample_loaders->end()) { + audio_cat->error() << "no loader available for audio type '" << ext + << "'" << endl; + return (AudioSample*)0L; + } + (*((*sli).second))(&sample, &player, &destroy, filename); + if ((sample == (AudioTraits::SampleClass*)0L) || + (player == (AudioTraits::PlayerClass*)0L) || + (destroy == (AudioTraits::DeleteSampleFunc*)0L)) { + audio_cat->error() << "could not load '" << filename << "'" << endl; + return (AudioSample*)0L; + } + PT(AudioSample) the_sample = new AudioSample(sample, player, destroy, + filename); + _samples[filename] = the_sample; + return the_sample; +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::ns_release_sample +// Access: Private +// Description: The nonstatic implementation of release_sample(). +//////////////////////////////////////////////////////////////////// +void AudioPool::ns_release_sample(AudioSample* sample) { + string filename = sample->get_name(); + SampleMap::iterator si; + si = _samples.find(filename); + if (si != _samples.end() && (*si).second == sample) { + _samples.erase(si); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::ns_release_all_samples +// Access: Private +// Description: The nonstatic implementation of release_all_samples(). +//////////////////////////////////////////////////////////////////// +void AudioPool::ns_release_all_samples(void) { + _samples.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::register_sample_loader +// Access: Public, static +// Description: A static function to register a function for loading +// audio samples. +//////////////////////////////////////////////////////////////////// +void AudioPool::register_sample_loader(const string& ext, + AudioPool::SampleLoadFunc* func) { + SampleLoaders::const_iterator sli; + check_sample_loaders(); + sli = _sample_loaders->find(ext); + if (sli != _sample_loaders->end()) { + audio_cat->warning() << "attempted to register a loader for audio type '" + << ext << "' more then once." << endl; + return; + } + (*_sample_loaders)[ext] = func; +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::ns_has_music +// Access: Private +// Description: The nonstatic implementation of has_music(). +//////////////////////////////////////////////////////////////////// +bool AudioPool::ns_has_music(Filename filename) { + filename.resolve_filename(get_sound_path()); + + MusicMap::const_iterator si; + si = _music.find(filename); + if (si != _music.end()) { + // this music was previously loaded + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::ns_load_music +// Access: Private +// Description: The nonstatic implementation of load_music(). +//////////////////////////////////////////////////////////////////// +AudioMusic* AudioPool::ns_load_music(Filename filename) { + filename.resolve_filename(get_sound_path()); + + MusicMap::const_iterator si; + si = _music.find(filename); + if (si != _music.end()) { + // this sample was previously loaded + return (*si).second; + } + audio_cat.info() << "Loading music " << filename << "\n"; + AudioTraits::MusicClass* music = (AudioTraits::MusicClass*)0L; + AudioTraits::PlayerClass* player = (AudioTraits::PlayerClass*)0L; + AudioTraits::DeleteMusicFunc* destroy = (AudioTraits::DeleteMusicFunc*)0L; + string ext = filename.get_extension(); + MusicLoaders::const_iterator sli; + check_music_loaders(); + sli = _music_loaders->find(ext); + if (sli == _music_loaders->end()) { + audio_cat->error() << "no loader available for audio type '" << ext + << "'" << endl; + return (AudioMusic*)0L; + } + (*((*sli).second))(&music, &player, &destroy, filename); + if ((music == (AudioTraits::MusicClass*)0L) || + (player == (AudioTraits::PlayerClass*)0L) || + (destroy == (AudioTraits::DeleteMusicFunc*)0L)) { + audio_cat->error() << "could not load '" << filename << "'" << endl; + return (AudioMusic*)0L; + } + PT(AudioMusic) the_music = new AudioMusic(music, player, destroy, filename); + _music[filename] = the_music; + return the_music; +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::ns_release_music +// Access: Private +// Description: The nonstatic implementation of release_music(). +//////////////////////////////////////////////////////////////////// +void AudioPool::ns_release_music(AudioMusic* music) { + string filename = music->get_name(); + MusicMap::iterator si; + si = _music.find(filename); + if (si != _music.end() && (*si).second == music) { + _music.erase(si); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::ns_release_all_music +// Access: Private +// Description: The nonstatic implementation of release_all_music(). +//////////////////////////////////////////////////////////////////// +void AudioPool::ns_release_all_music(void) { + _music.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioPool::register_music_loader +// Access: Public, static +// Description: A static function to register a function for loading +// audio music. +//////////////////////////////////////////////////////////////////// +void AudioPool::register_music_loader(const string& ext, + AudioPool::MusicLoadFunc* func) { + MusicLoaders::const_iterator sli; + check_music_loaders(); + sli = _music_loaders->find(ext); + if (sli != _music_loaders->end()) { + audio_cat->warning() << "attempted to register a loader for audio type '" + << ext << "' more then once." << endl; + return; + } + (*_music_loaders)[ext] = func; +} diff --git a/panda/src/audio/audio_pool.h b/panda/src/audio/audio_pool.h new file mode 100644 index 0000000000..30f276d868 --- /dev/null +++ b/panda/src/audio/audio_pool.h @@ -0,0 +1,63 @@ +// Filename: audio_pool.h +// Created by: cary (22Sep00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __AUDIO_POOL_H__ +#define __AUDIO_POOL_H__ + +#include "audio_sample.h" +#include "audio_music.h" +#include +#include +#include +#include + +class EXPCL_PANDA AudioPool { +private: + INLINE AudioPool(void); + + bool ns_has_sample(Filename filename); + AudioSample* ns_load_sample(Filename filename); + void ns_release_sample(AudioSample* sample); + void ns_release_all_samples(void); + + bool ns_has_music(Filename filename); + AudioMusic* ns_load_music(Filename filename); + void ns_release_music(AudioMusic* music); + void ns_release_all_music(void); + + static AudioPool* get_ptr(void); + + static AudioPool *_global_ptr; + typedef map SampleMap; + SampleMap _samples; + typedef map MusicMap; + MusicMap _music; +public: + typedef void SampleLoadFunc(AudioTraits::SampleClass**, + AudioTraits::PlayerClass**, + AudioTraits::DeleteSampleFunc**, Filename); + + INLINE static bool has_sample(const string& filename); + INLINE static bool verify_sample(const string& filename); + INLINE static AudioSample* load_sample(const string& filename); + INLINE static void release_sample(AudioSample* sample); + INLINE static void release_all_samples(void); + static void register_sample_loader(const string&, SampleLoadFunc*); + + typedef void MusicLoadFunc(AudioTraits::MusicClass**, + AudioTraits::PlayerClass**, + AudioTraits::DeleteMusicFunc**, Filename); + + INLINE static bool has_music(const string& filename); + INLINE static bool verify_music(const string& filename); + INLINE static AudioMusic* load_music(const string& filename); + INLINE static void release_music(AudioMusic* music); + INLINE static void release_all_music(void); + static void register_music_loader(const string&, MusicLoadFunc*); +}; + +#include "audio_pool.I" + +#endif /* __AUDIO_POOL_H__ */ diff --git a/panda/src/audio/audio_sample.I b/panda/src/audio/audio_sample.I new file mode 100644 index 0000000000..b291635881 --- /dev/null +++ b/panda/src/audio/audio_sample.I @@ -0,0 +1,67 @@ +// Filename: audio_sample.I +// Created by: cary (23Sep00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: AudioSample::constructor +// Access: Protected +// Description: initialize a new sample +//////////////////////////////////////////////////////////////////// +INLINE AudioSample::AudioSample(AudioTraits::SampleClass* sample, + AudioTraits::PlayerClass* player, + AudioTraits::DeleteSampleFunc* destroy, + const string& filename) : Namable(filename), + _sample(sample), + _player(player), + _destroy(destroy) {} + +//////////////////////////////////////////////////////////////////// +// Function: AudioSample::copy constructor +// Access: Protected +// Description: copy a sample, but we don't really want to allow this +//////////////////////////////////////////////////////////////////// +INLINE AudioSample::AudioSample(const AudioSample& c) : Namable(c.get_name()), + _sample(c._sample), + _player(c._player), + _destroy(c._destroy) {} + +//////////////////////////////////////////////////////////////////// +// Function: AudioSample::assignment operator +// Access: Protected +// Description: copy a sample, but we don't really want to allow this +//////////////////////////////////////////////////////////////////// +INLINE AudioSample& AudioSample::operator=(const AudioSample& c) { + this->set_name(c.get_name()); + this->_sample = c._sample; + this->_player = c._player; + this->_destroy = c._destroy; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioSample::get_player +// Access: Protected +// Description: return the player for this sample +//////////////////////////////////////////////////////////////////// +INLINE AudioTraits::PlayerClass* AudioSample::get_player(void) { + return _player; +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioSample::get_sample +// Access: Protected +// Description: return the trait sample class for this sample +//////////////////////////////////////////////////////////////////// +INLINE AudioTraits::SampleClass* AudioSample::get_sample(void) { + return _sample; +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioSample::equality operator +// Access: Public +// Description: test to see if two samples are the same +//////////////////////////////////////////////////////////////////// +INLINE bool AudioSample::operator==(const AudioSample& c) const { + return (_sample == c._sample); +} diff --git a/panda/src/audio/audio_sample.cxx b/panda/src/audio/audio_sample.cxx new file mode 100644 index 0000000000..f156d75525 --- /dev/null +++ b/panda/src/audio/audio_sample.cxx @@ -0,0 +1,45 @@ +// Filename: audio_sample.cxx +// Created by: cary (23Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "audio_sample.h" +#include "config_audio.h" + +TypeHandle AudioSample::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: AudioSample::destructor +// Access: Public +// Description: deletes the sample data and then lets the system +// destroy this structure +//////////////////////////////////////////////////////////////////// +AudioSample::~AudioSample(void) { + (*_destroy)(_sample); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioSample::length +// Access: Public +// Description: return the length (in seconds) of the sample +//////////////////////////////////////////////////////////////////// +float AudioSample::length(void) { + return _sample->length(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AudioSample::status +// Access: Public +// Description: return the current play status of this sample +//////////////////////////////////////////////////////////////////// +AudioSample::SampleStatus AudioSample::status(void) { + AudioTraits::SampleClass::SampleStatus stat = _sample->status(); + switch (stat) { + case AudioTraits::SampleClass::READY: + return READY; + case AudioTraits::SampleClass::PLAYING: + return PLAYING; + } + audio_cat->error() << "unknown status for sample" << endl; + return READY; +} diff --git a/panda/src/audio/audio_sample.h b/panda/src/audio/audio_sample.h new file mode 100644 index 0000000000..1a17cb1e46 --- /dev/null +++ b/panda/src/audio/audio_sample.h @@ -0,0 +1,63 @@ +// Filename: audio_sample.h +// Created by: cary (22Sep00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __AUDIO_SAMPLE_H__ +#define __AUDIO_SAMPLE_H__ + +#include "audio_trait.h" +#include "typedReferenceCount.h" +#include "namable.h" + +class AudioPool; +class AudioManager; + +class EXPCL_PANDA AudioSample : public TypedReferenceCount, public Namable { +private: + AudioTraits::SampleClass *_sample; + AudioTraits::PlayerClass *_player; + AudioTraits::DeleteSampleFunc *_destroy; +protected: + INLINE AudioSample(AudioTraits::SampleClass*, AudioTraits::PlayerClass*, + AudioTraits::DeleteSampleFunc*, const string&); + INLINE AudioSample(const AudioSample&); + INLINE AudioSample& operator=(const AudioSample&); + + INLINE AudioTraits::PlayerClass* get_player(void); + INLINE AudioTraits::SampleClass* get_sample(void); + + friend class AudioPool; + friend class AudioManager; +public: + virtual ~AudioSample(void); + INLINE bool operator==(const AudioSample&) const; + + enum SampleStatus { READY, PLAYING } ; + + float length(void); + SampleStatus status(void); +public: + // type stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + TypedReferenceCount::init_type(); + register_type(_type_handle, "AudioSample", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +#include "audio_sample.I" + +#endif /* __AUDIO_SAMPLE_H__ */ diff --git a/panda/src/audio/audio_trait.cxx b/panda/src/audio/audio_trait.cxx new file mode 100644 index 0000000000..6dabe33541 --- /dev/null +++ b/panda/src/audio/audio_trait.cxx @@ -0,0 +1,48 @@ +// Filename: audio_trait.cxx +// Created by: cary (23Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "audio_trait.h" +#include "config_audio.h" + +AudioTraits::SampleClass::~SampleClass(void) { +} + +float AudioTraits::SampleClass::length(void) { + audio_cat->error() << "In abstract SampleClass::length!" << endl; + return 0.; +} + +AudioTraits::SampleClass::SampleStatus AudioTraits::SampleClass::status(void) { + audio_cat->error() << "In abstract SampleClass::status!" << endl; + return READY; +} + +AudioTraits::MusicClass::~MusicClass(void) { +} + +AudioTraits::MusicClass::MusicStatus AudioTraits::MusicClass::status(void) { + audio_cat->error() << "In abstract MusicClass::status!" << endl; + return READY; +} + +AudioTraits::PlayerClass::~PlayerClass(void) { +} + +void AudioTraits::PlayerClass::play_sample(AudioTraits::SampleClass*) { + audio_cat->error() << "In abstract PlayerClass::play_sample!" << endl; +} + +void AudioTraits::PlayerClass::play_music(AudioTraits::MusicClass*) { + audio_cat->error() << "In abstract PlayerClass::play_music!" << endl; +} + +void AudioTraits::PlayerClass::set_volume(AudioTraits::SampleClass*, int) { + audio_cat->error() << "In abstract PlayerClass::set_volume (sample)!" + << endl; +} + +void AudioTraits::PlayerClass::set_volume(AudioTraits::MusicClass*, int) { + audio_cat->error() << "In abstract PlayerClass::set_volume (music)!" << endl; +} diff --git a/panda/src/audio/audio_trait.h b/panda/src/audio/audio_trait.h new file mode 100644 index 0000000000..eae6763750 --- /dev/null +++ b/panda/src/audio/audio_trait.h @@ -0,0 +1,49 @@ +// Filename: audio_trait.h +// Created by: frang (06Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __AUDIO_TRAIT_H__ +#define __AUDIO_TRAIT_H__ + +#include + +class EXPCL_PANDA AudioTraits { +public: + class SampleClass; + class MusicClass; + + typedef void DeleteSampleFunc(SampleClass*); + typedef void DeleteMusicFunc(MusicClass*); + + class EXPCL_PANDA SampleClass { + public: + SampleClass(void) {} + virtual ~SampleClass(void); + + enum SampleStatus { READY, PLAYING } ; + + virtual float length(void) = 0; + virtual SampleStatus status(void) = 0; + }; + class EXPCL_PANDA MusicClass { + public: + MusicClass(void) {} + virtual ~MusicClass(void); + + enum MusicStatus { READY, PLAYING }; + virtual MusicStatus status(void) = 0; + }; + class EXPCL_PANDA PlayerClass { + public: + PlayerClass(void) {} + virtual ~PlayerClass(void); + + virtual void play_sample(SampleClass*) = 0; + virtual void play_music(MusicClass*) = 0; + virtual void set_volume(SampleClass*, int) = 0; + virtual void set_volume(MusicClass*, int) = 0; + }; +}; + +#endif /* __AUDIO_TRAIT_H__ */ diff --git a/panda/src/audio/audio_win_traits.I b/panda/src/audio/audio_win_traits.I new file mode 100644 index 0000000000..b1802d77fe --- /dev/null +++ b/panda/src/audio/audio_win_traits.I @@ -0,0 +1,28 @@ +// Filename: audio_win_traits.I +// Created by: cary (27Sep00) +// +//////////////////////////////////////////////////////////////////// + +INLINE WinSample::WinSample(void) : _channel(NULL), _data(NULL), _len(0) { +} + +INLINE LPDIRECTSOUNDBUFFER WinSample::get_channel(void) { + return _channel; +} + +INLINE WinMusic::WinMusic(void) : _performance(NULL), _music(NULL), + _buffer(NULL), _synth(NULL), _data(NULL), + _len(0) { + init(); +} + +INLINE IDirectMusicPerformance* WinMusic::get_performance(void) { + return _performance; +} + +INLINE IDirectMusicSegment* WinMusic::get_music(void) { + return _music; +} + +INLINE WinPlayer::WinPlayer(void) { +} diff --git a/panda/src/audio/audio_win_traits.cxx b/panda/src/audio/audio_win_traits.cxx new file mode 100644 index 0000000000..10a8a8adbd --- /dev/null +++ b/panda/src/audio/audio_win_traits.cxx @@ -0,0 +1,660 @@ +// Filename: audio_win_traits.cxx +// Created by: cary (27Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "audio_win_traits.h" +#include "audio_manager.h" +#include "config_audio.h" + +#include + +static bool have_initialized = false; +static HWND global_hwnd; + +// these are used by the direct sound playing stuff +static LPDIRECTSOUNDBUFFER soundPrimaryBuffer = NULL; +static LPDIRECTSOUND soundDirectSound = NULL; + +// these are used by the direct music playing stuff +static IDirectMusic* musicDirectMusic = NULL; +static IDirectSound* musicDirectSound = NULL; + +#define CHECK_RESULT(_result, _msg) \ + if (FAILED(_result)) { \ + audio_cat->error() << _msg << " at " << __FILE__ << ":" << __LINE__ \ + << endl; \ + return; \ + } + +// #define MULTI_TO_WIDE(_in, _out) MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, _in, -1, _out, DMUS_MAX_FILENAME) +#define MULTI_TO_WIDE(x,y) MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, y, -1, x, _MAX_PATH); + +static void update_win(void) { +} + +static void initialize(void) { + if (have_initialized) + return; + + if (audio_cat->is_debug()) + audio_cat->debug() << "in winAudio initialize" << endl; + + // rumor has it this will work, if it doesn't we need to create an invisible + // application window for this kind of thing + global_hwnd = GetDesktopWindow(); + + // initialize COM + HRESULT result = CoInitialize(NULL); + CHECK_RESULT(result, "CoInitialize failed"); + + // + // initialize the globals for the direct sound drivers + // + + // create a direct sound object + result = DirectSoundCreate(NULL, &soundDirectSound, NULL); + CHECK_RESULT(result, "could not create a Direct Sound (tm) object (c)"); + + // set the cooperative level + result = soundDirectSound->SetCooperativeLevel(global_hwnd, DSSCL_PRIORITY); + if (FAILED(result)) { + audio_cat->warning() << "could not set Direct Sound co-op level to " + << "DSSCL_PRIORITY, trying DSSCL_NORMAL" << endl; + result = soundDirectSound->SetCooperativeLevel(global_hwnd, DSSCL_NORMAL); + CHECK_RESULT(result, "failed setting to DSSCL_NORMAL"); + } + + // Move any unused portions of onboard sound memory to a contiguous block + // so that the largest portion of free memory will be available. + soundDirectSound->Compact(); + + // create the primary buffer + DSBUFFERDESC dsbd; + ZeroMemory(&dsbd, sizeof(DSBUFFERDESC)); + dsbd.dwSize = sizeof(DSBUFFERDESC); + dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; + result = soundDirectSound->CreateSoundBuffer(&dsbd, &soundPrimaryBuffer, + NULL); + CHECK_RESULT(result, "could not create primary buffer"); + + // set primary buffer format to 22kHz and 16-bit output + // COME BACK LATER TO MAKE THIS CONFIG + WAVEFORMATEX wfx; + ZeroMemory(&wfx, sizeof(WAVEFORMATEX)); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 2; + wfx.nSamplesPerSec = 22050; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + result = soundPrimaryBuffer->SetFormat(&wfx); + // SetFormat requires at least DSSCL_PRIORITY, which we may not have + if (result == DSERR_PRIOLEVELNEEDED) + audio_cat->warning() << "could not set format of Primary Buffer because " + << "we didn't get DSSCL_PRIORITY" << endl; + +/* + // + // initialize the globals for the direct music drivers + // + + // create the direct sound object + result = DirectSoundCreate(NULL, &musicDirectSound, NULL); + CHECK_RESULT(result, + "could not create a second Direct Sound (tm) object (c)"); + + // set the cooperative level + result = musicDirectSound->SetCooperativeLevel(global_hwnd, DSSCL_PRIORITY); + if (FAILED(result)) { + audio_cat->warning() << "could not set Direct Sound (2) co-op level to " + << "DSSCL_PRIORITY, trying DSSCL_NORMAL" << endl; + result = musicDirectSound->SetCooperativeLevel(global_hwnd, DSSCL_NORMAL); + CHECK_RESULT(result, "failed setting to DSSCL_NORMAL"); + } + + // create the direct music object + result = CoCreateInstance(CLSID_DirectMusic, NULL, CLSCTX_INPROC, + IID_IDirectMusic, (void**)&musicDirectMusic); + CHECK_RESULT(result, "could not create Direct Music (tm) object (c)"); + + // set direct sound for direct music + result = musicDirectMusic->SetDirectSound(musicDirectSound, NULL); + CHECK_RESULT(result, "could not add Direct Sound (tm) to Direct Music (tm)"); +*/ + + // + // finish out with our stuff + // + + AudioManager::set_update_func(update_win); + have_initialized = true; +} + +static void shutdown(void) { + // release the primary sound buffer + if (soundPrimaryBuffer) { + soundPrimaryBuffer->Release(); + soundPrimaryBuffer = NULL; + } + + // release direct sound object + if (soundDirectSound) { + soundDirectSound->Release(); + soundDirectSound = NULL; + } + + // release direct music object + if (musicDirectMusic) { + musicDirectMusic->Release(); + musicDirectMusic = NULL; + } + + if (musicDirectSound) { + musicDirectSound->Release(); + musicDirectSound = NULL; + } + + // shutdown COM + CoUninitialize(); +} + +WinSample::~WinSample(void) { + // unload any data we have + if (_channel) { + _channel->Release(); + _channel = NULL; + } + // we may or may not be leaking the _data +} + +float WinSample::length(void) { + // DO THIS + return 0.; +} + +AudioTraits::SampleClass::SampleStatus WinSample::status(void) { + if (_channel) { + DWORD dwStatus; + _channel->GetStatus(&dwStatus); + if (dwStatus & DSBSTATUS_PLAYING) + return AudioTraits::SampleClass::PLAYING; + } + return AudioTraits::SampleClass::READY; +} + +BYTE* WinSample::lock(void) { + HRESULT result = _channel->Lock(0, 0, (void**)&_data, &_len, NULL, 0, + DSBLOCK_ENTIREBUFFER); + if (FAILED(result)) { + audio_cat->error() << "failed to lock buffer" << endl; + return NULL; + } + return _data; +} + +void WinSample::unlock(void) { + HRESULT result = _channel->Unlock(_data, _len, NULL, 0); + CHECK_RESULT(result, "failed to unlock buffer"); +} + +// these are used by the wav loader +WAVEFORMATEX* pwfx; +HMMIO hmmioIn; +MMCKINFO ckIn; +MMCKINFO ckInRiff; + +HRESULT readMMIO(HMMIO hmmio, MMCKINFO* pckInRIFF, WAVEFORMATEX** ppwfxInfo) { + MMCKINFO ckin; + PCMWAVEFORMAT pcmWaveFormat; + + *ppwfxInfo = NULL; + if (mmioDescend(hmmio, pckInRIFF, NULL, 0) != 0) + return E_FAIL; + if ((pckInRIFF->ckid != FOURCC_RIFF) || + (mmioFOURCC('W', 'A', 'V', 'E') != pckInRIFF->fccType)) + return E_FAIL; + ckin.ckid = mmioFOURCC('f', 'm', 't', ' '); + if (mmioDescend(hmmio, &ckin, pckInRIFF, MMIO_FINDCHUNK) != 0) + return E_FAIL; + if (ckin.cksize < (LONG)sizeof(PCMWAVEFORMAT)) + return E_FAIL; + if (mmioRead(hmmio, (HPSTR)&pcmWaveFormat, sizeof(pcmWaveFormat)) != + sizeof(pcmWaveFormat)) + return E_FAIL; + if (pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM) { + if ((*ppwfxInfo = new WAVEFORMATEX) == NULL) + return E_FAIL; + memcpy(*ppwfxInfo, &pcmWaveFormat, sizeof(pcmWaveFormat)); + (*ppwfxInfo)->cbSize = 0; + } else { + WORD cbExtraBytes = 0L; + if (mmioRead(hmmio, (CHAR*)&cbExtraBytes, sizeof(WORD)) != sizeof(WORD)) + return E_FAIL; + *ppwfxInfo = (WAVEFORMATEX*)new CHAR[sizeof(WAVEFORMATEX)+cbExtraBytes]; + if (*ppwfxInfo == NULL) + return E_FAIL; + memcpy(*ppwfxInfo, &pcmWaveFormat, sizeof(pcmWaveFormat)); + (*ppwfxInfo)->cbSize = cbExtraBytes; + if (mmioRead(hmmio, (CHAR*)(((BYTE*)&((*ppwfxInfo)->cbSize))+sizeof(WORD)), + cbExtraBytes) != cbExtraBytes) { + delete *ppwfxInfo; + *ppwfxInfo = NULL; + return E_FAIL; + } + } + if (mmioAscend(hmmio, &ckin, 0) != 0) { + delete *ppwfxInfo; + *ppwfxInfo = NULL; + return E_FAIL; + } + return S_OK; +} + +HRESULT wave_open_file(const CHAR* strFileName, HMMIO* phmmioIn, + WAVEFORMATEX** ppwfxInfo, MMCKINFO* pckInRIFF) { + HMMIO hmmio = NULL; + if ((hmmio = mmioOpen(const_cast(strFileName), NULL, + MMIO_ALLOCBUF | MMIO_READ)) == NULL) + return E_FAIL; + HRESULT hr; + if (FAILED(hr = readMMIO(hmmio, pckInRIFF, ppwfxInfo))) { + mmioClose(hmmio, 0); + return hr; + } + *phmmioIn = hmmio; + return S_OK; +} + +HRESULT wave_start_data_read(HMMIO* phmmioIn, MMCKINFO* pckIn, + MMCKINFO* pckInRIFF) { + // seek to the data + if (mmioSeek(*phmmioIn, pckInRIFF->dwDataOffset + sizeof(FOURCC), + SEEK_SET) == -1) + return E_FAIL; + //search the input file for the 'data' chunk + pckIn->ckid = mmioFOURCC('d', 'a', 't', 'a'); + if (mmioDescend(*phmmioIn, pckIn, pckInRIFF, MMIO_FINDCHUNK) != 0) + return E_FAIL; + return S_OK; +} + +HRESULT wave_read_file(HMMIO hmmio, UINT cbRead, BYTE* pbDest, MMCKINFO* pckIn, + UINT* cbActualRead) { + MMIOINFO mmioinfoIn; + *cbActualRead = 0; + if (mmioGetInfo(hmmio, &mmioinfoIn, 0) != 0) + return E_FAIL; + UINT cbDataIn = cbRead; + if (cbDataIn > pckIn->cksize) + cbDataIn = pckIn->cksize; + for (DWORD cT=0; cTcbSize = sizeof(wavInfo); + result = soundDirectSound->CreateSoundBuffer(&dsbdDesc, &(ret->_channel), NULL); + if (FAILED(result)) { + delete ret; + ret = (WinSample*)0L; + } + + if (ret) { + BYTE* dst = NULL; + dst = ret->lock(); + try { + memcpy(dst, wavData, wavSize); + } + catch(...) { + delete ret; + ret = (WinSample*)0L; + } + if (ret) + ret->unlock(); + } + + if (wavData) + delete [] wavData; + + return ret; +} + +void WinSample::destroy(AudioTraits::SampleClass* sample) { + delete sample; +} + +WinMusic::~WinMusic(void) { + // AudioManager::stop(this); + if (audio_cat->is_debug()) + audio_cat->debug() << "in WinMusic::~WinMusic()" << endl; + + if (_music) { + _music->Release(); + _music = NULL; + } + + if (_synth) { + _synth->Release(); + _synth = NULL; + } + + if (_performance) { + _performance->Release(); + _performance = NULL; + } + + if (_buffer) { + _buffer->Release(); + _buffer = NULL; + } + + if (audio_cat->is_debug()) + audio_cat->debug() << "out of WinMusic::~WinMusic()" << endl; +} + +void WinMusic::init(void) { + if (audio_cat->is_debug()) + audio_cat->debug() << "in WinMusic::init()" << endl; + + initialize(); + // create the direct sound performance object + HRESULT result = CoCreateInstance(CLSID_DirectMusicPerformance, NULL, + CLSCTX_INPROC, + IID_IDirectMusicPerformance2, + (void**)&_performance); + if (FAILED(result)) { + audio_cat->error() << "could not create performance object" << endl; + _performance = NULL; + return; + } + + // initialize performance object + // result = _performance->Init(&musicDirectMusic, NULL, NULL); + result = _performance->Init(NULL, NULL, NULL); + CHECK_RESULT(result, "could not initialize performance object"); + +/* + // create the output synth object + DMUS_PORTPARAMS params; + ZeroMemory(¶ms, sizeof(DMUS_PORTPARAMS)); + params.dwSize = sizeof(DMUS_PORTPARAMS); + result = musicDirectMusic->CreatePort(GUID_NULL, ¶ms, &_synth, NULL); + CHECK_RESULT(result, "could not create synth"); + + // create sound buffer + WAVEFORMATEX format; + DWORD formatExSize; + DWORD bufferSize; + ZeroMemory(&format, sizeof(WAVEFORMATEX)); + formatExSize = format.cbSize = sizeof(WAVEFORMATEX); + result = _synth->GetFormat(&format, &formatExSize, &bufferSize); + CHECK_RESULT(result, "failed to get format from synth"); + DSBUFFERDESC bufferDesc; + ZeroMemory(&bufferDesc, sizeof(DSBUFFERDESC)); + bufferDesc.dwSize = sizeof(DSBUFFERDESC); +* + bufferDesc.dwFlags = DSBCAPS_CTRLDEFAULT | DSBCAPS_STICKYFOCUS; +* + bufferDesc.dwFlags = DSBCAPS_STICKYFOCUS; + bufferDesc.dwBufferBytes = bufferSize; + bufferDesc.lpwfxFormat = &format; + bufferDesc.lpwfxFormat->cbSize = sizeof(WAVEFORMATEX); + result = musicDirectSound->CreateSoundBuffer(&bufferDesc, &_buffer, NULL); + CHECK_RESULT(result, "could not create buffer for music"); + + // initialize synth + result = _synth->SetDirectSound(musicDirectSound, _buffer); + CHECK_RESULT(result, "failed to initialize synth"); + + // activate synth + result = _synth->Activate(TRUE); + CHECK_RESULT(result, "failed to activate synth"); +*/ + + // add the synth to the performance + // result = _performance->AddPort(_synth); + result = _performance->AddPort(NULL); + CHECK_RESULT(result, "failed to add synth to performance"); + +/* + // allocate performance channels + result = _performance->AssignPChannelBlock(0, _synth, 1); + CHECK_RESULT(result, "failed to assign performance channels"); +*/ + + if (audio_cat->is_debug()) + audio_cat->debug() << "out of WinMusic::init() _performance = " + << (void*)_performance << " _synth = " + << (void*)_synth << " _buffer = " << (void*)_buffer + << endl; +} + +AudioTraits::MusicClass::MusicStatus WinMusic::status(void) { + if (audio_cat->is_debug()) + audio_cat->debug() << "in WinMusic::status()" << endl; + + if (_performance && _music) { + if (_performance->IsPlaying(_music, NULL) == S_OK) { + if (audio_cat->is_debug()) + audio_cat->debug() << "returning PLAYING" << endl; + return PLAYING; + } + } else + if (audio_cat->is_debug()) + audio_cat->debug() << "MusicStatus no performance or music!" << endl; + if (audio_cat->is_debug()) + audio_cat->debug() << "returning READY" << endl; + return READY; +} + +WinMusic* WinMusic::load_midi(Filename filename) { + if (audio_cat->is_debug()) + audio_cat->debug() << "in WinMusic::load_midi()" << endl; + initialize(); + // WinMusic* ret = (WinMusic*)0L; + WinMusic* ret = new WinMusic(); + if (ret->_performance && ret->_music) { + if (audio_cat->is_debug()) + audio_cat->debug() << "for some reason, have to stop" << endl; + ret->_performance->Stop(NULL, NULL, 0, 0); + } + ret->_music = NULL; + IDirectMusicLoader* loader; + HRESULT result = CoCreateInstance(CLSID_DirectMusicLoader, NULL, + CLSCTX_INPROC, IID_IDirectMusicLoader, + (void**)&loader); + if (FAILED(result)) { + audio_cat->error() << "could not create music loader" << endl; + delete ret; + ret = (WinMusic*)0L; + return ret; + } + + char szDir[_MAX_PATH]; + WCHAR wszDir[_MAX_PATH]; + if (_getcwd(szDir, _MAX_PATH)==NULL) { + audio_cat->error() << "could not getcwd" << endl; + delete ret; + ret = (WinMusic*)0L; + return ret; + } + MULTI_TO_WIDE(wszDir, szDir); + result = loader->SetSearchDirectory(GUID_DirectMusicAllTypes, wszDir, FALSE); + if (FAILED(result)) { + audio_cat->error() << "could not set search directory" << endl; + delete ret; + ret = (WinMusic*)0L; + return ret; + } + + DMUS_OBJECTDESC fdesc; + fdesc.guidClass = CLSID_DirectMusicSegment; + fdesc.dwSize = sizeof(DMUS_OBJECTDESC); + // MULTI_TO_WIDE(filename.c_str(), fdesc.wszFileName); + MULTI_TO_WIDE(fdesc.wszFileName, filename.c_str()); + // fdesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH; + fdesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME; + result = loader->GetObject(&fdesc, IID_IDirectMusicSegment2, + (void**)&(ret->_music)); + if (FAILED(result)) { + audio_cat->error() << "failed to load file" << endl; + loader->Release(); + delete ret; + ret = (WinMusic*)0L; + return ret; + } + ret->_music->SetParam(GUID_StandardMIDIFile, -1, 0, 0, + (void*)(ret->_performance)); + ret->_music->SetParam(GUID_Download, -1, 0, 0, (void*)(ret->_performance)); + // ret->_buffer->SetVolume(0); + // ret->_buffer->SetPan(0); + if (audio_cat->is_debug()) + audio_cat->debug() << "out of WinMusic::load_midi() _music = " + << (void*)ret->_music << endl; + return ret; +} + +void WinMusic::destroy(AudioTraits::MusicClass* music) { + if (audio_cat->is_debug()) + audio_cat->debug() << "in WinMusic::destroy()" << endl; + delete music; + if (audio_cat->is_debug()) + audio_cat->debug() << "out of WinMusic::destroy()" << endl; +} + +WinPlayer* WinPlayer::_global_instance = (WinPlayer*)0L; + +WinPlayer::~WinPlayer(void) { +} + +void WinPlayer::play_sample(AudioTraits::SampleClass* sample) { + initialize(); + WinSample* wsample = (WinSample*)sample; + LPDIRECTSOUNDBUFFER chan = wsample->get_channel(); + if (chan) { + chan->Stop(); + HRESULT result = chan->Play(0, 0, 0); + if (FAILED(result)) + audio_cat->error() << "sample play failed" << endl; + } +} + +void WinPlayer::play_music(AudioTraits::MusicClass* music) { + if (audio_cat->is_debug()) + audio_cat->debug() << "in WinPlayer::play_music()" << endl; + initialize(); + WinMusic* wmusic = (WinMusic*)music; + IDirectMusicPerformance* _perf = wmusic->get_performance(); + IDirectMusicSegment* _msc = wmusic->get_music(); + if (audio_cat->is_debug()) + audio_cat->debug() << "about to jump in: _perf = " << (void*)_perf + << " _msc = " << (void*)_msc << endl; + if (_perf && _msc) { + if (audio_cat->is_debug()) + audio_cat->debug() << "made it inside" << endl; + // _msc->SetRepeats(0); + IDirectMusicSegmentState* segState; + // HRESULT result = _perf->PlaySegment(_msc, 0, 0, NULL); + HRESULT result = _perf->PlaySegment(_msc, 0, 0, &segState); + if (result != S_OK) { + audio_cat->error() << "music play failed" << endl; + switch (result) { + case E_OUTOFMEMORY: audio_cat->error() << "reports out of memory" << endl; + break; + case E_POINTER: audio_cat->error() << "reports invalid pointer" << endl; + break; + case DMUS_E_NO_MASTER_CLOCK: audio_cat->error() << "reports no master clock" << endl; + break; + case DMUS_E_SEGMENT_INIT_FAILED: audio_cat->error() << "reports segment init failed" << endl; + break; + case DMUS_E_TIME_PAST: audio_cat->error() << "reports time past" << endl; + break; + }; + } + } + if (audio_cat->is_debug()) + audio_cat->debug() << "out of WinPlayer::play_music()" << endl; +} + +void WinPlayer::set_volume(AudioTraits::SampleClass*, int) { +} + +void WinPlayer::set_volume(AudioTraits::MusicClass*, int) { +} + +WinPlayer* WinPlayer::get_instance(void) { + if (_global_instance == (WinPlayer*)0L) + _global_instance = new WinPlayer(); + return _global_instance; +} diff --git a/panda/src/audio/audio_win_traits.h b/panda/src/audio/audio_win_traits.h new file mode 100644 index 0000000000..d339a83d20 --- /dev/null +++ b/panda/src/audio/audio_win_traits.h @@ -0,0 +1,78 @@ +// Filename: audio_win_traits.h +// Created by: cary (27Sep00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __AUDIO_WIN_TRAITS_H__ +#define __AUDIO_WIN_TRAITS_H__ + +#include "audio_trait.h" +#include + +#include +#include +#include + +class EXPCL_PANDA WinSample : public AudioTraits::SampleClass { +private: + LPDIRECTSOUNDBUFFER _channel; + BYTE* _data; + DWORD _len; +public: + INLINE WinSample(void); + virtual ~WinSample(void); + + virtual float length(void); + virtual AudioTraits::SampleClass::SampleStatus status(void); +public: + // these are used by the laoders + BYTE* lock(void); + void unlock(void); + static WinSample* load_wav(Filename); + static void destroy(AudioTraits::SampleClass*); + // these are used by the player + INLINE LPDIRECTSOUNDBUFFER get_channel(void); +}; + +class EXPCL_PANDA WinMusic : public AudioTraits::MusicClass { +private: + IDirectMusicPerformance* _performance; + IDirectMusicSegment* _music; + IDirectSoundBuffer* _buffer; + IDirectMusicPort* _synth; + BYTE* _data; + DWORD _len; + + void init(void); +public: + INLINE WinMusic(void); + virtual ~WinMusic(void); + + virtual AudioTraits::MusicClass::MusicStatus status(void); + // these are used by the loaders + static WinMusic* load_midi(Filename); + static void destroy(AudioTraits::MusicClass*); + // these are used by the players + INLINE IDirectMusicPerformance* get_performance(void); + INLINE IDirectMusicSegment* get_music(void); +}; + +class EXPCL_PANDA WinPlayer : public AudioTraits::PlayerClass { +public: + INLINE WinPlayer(void); + virtual ~WinPlayer(void); + + virtual void play_sample(AudioTraits::SampleClass*); + virtual void play_music(AudioTraits::MusicClass*); + virtual void set_volume(AudioTraits::SampleClass*, int); + virtual void set_volume(AudioTraits::MusicClass*, int); +public: + // used by the readers + static WinPlayer* get_instance(void); +private: + static WinPlayer* _global_instance; +}; + +#include "audio_win_traits.I" + +#endif /* __AUDIO_WIN_TRAITS_H__ */ diff --git a/panda/src/audio/config_audio.cxx b/panda/src/audio/config_audio.cxx new file mode 100644 index 0000000000..b7118fb4a1 --- /dev/null +++ b/panda/src/audio/config_audio.cxx @@ -0,0 +1,41 @@ +// Filename: config_audio.cxx +// Created by: cary (22Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_audio.h" +#include "audio_sample.h" +#include "audio_music.h" +#include + +Configure(config_audio); +NotifyCategoryDef(audio, ""); + +int audio_sample_voices = config_audio.GetInt("audio-sample-voices", 8); +int audio_mix_freq = config_audio.GetInt("audio-mix-freq", 11025); +string* audio_mode_flags; +int audio_driver_select = config_audio.GetInt("audio-driver-select", 0); +string* audio_driver_params; + +ConfigureFn(config_audio) { + AudioSample::init_type(); + AudioMusic::init_type(); + + Config::ConfigTable::Symbol mode; + config_audio.GetAll("audio-mode-flag", mode); + Config::ConfigTable::Symbol::iterator i; + audio_mode_flags = new string; + for (i=mode.begin(); i!=mode.end(); ++i) { + if (!audio_mode_flags->empty()) + *audio_mode_flags += " "; + *audio_mode_flags += (*i).Val(); + } + Config::ConfigTable::Symbol parms; + config_audio.GetAll("audio-driver-param", parms); + audio_driver_params = new string; + for (i=parms.begin(); i!=parms.end(); ++i) { + if (!audio_driver_params->empty()) + *audio_driver_params += " "; + *audio_driver_params += (*i).Val(); + } +} diff --git a/panda/src/audio/config_audio.h b/panda/src/audio/config_audio.h new file mode 100644 index 0000000000..4156ff2e10 --- /dev/null +++ b/panda/src/audio/config_audio.h @@ -0,0 +1,20 @@ +// Filename: config_audio.h +// Created by: cary (22Sep00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __CONFIG_AUDIO_H__ +#define __CONFIG_AUDIO_H__ + +#include +#include + +NotifyCategoryDecl(audio, EXPCL_PANDA, EXPTP_PANDA); + +extern int audio_sample_voices; +extern int audio_mix_freq; +extern string* audio_mode_flags; +extern int audio_driver_select; +extern string* audio_driver_params; + +#endif /* __CONFIG_AUDIO_H__ */ diff --git a/panda/src/audio/test_audio.cxx b/panda/src/audio/test_audio.cxx new file mode 100644 index 0000000000..f318e46454 --- /dev/null +++ b/panda/src/audio/test_audio.cxx @@ -0,0 +1,41 @@ +// Filename: test_audio.cxx +// Created by: cary (24Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include "audio.h" + +#include "config_audio.h" +#include + +int +main(int argc, char* argv[]) { + if (! AudioPool::verify_sample("test.wav")) { + audio_cat->fatal() << "could not locate 'test.wav'" << endl; + exit(-1); + } + AudioSample* sample = AudioPool::load_sample("test.wav"); + audio_cat->info() << "test.wav is " << sample->length() << "sec long" + << endl; + audio_cat->info() << "Playing test.wav" << endl; + AudioManager::play(sample); + while (sample->status() == AudioSample::PLAYING) { + AudioManager::update(); + ipc_traits::sleep(0, 1000000); + } + + // AudioMidi foo("test.midi"); + if (! AudioPool::verify_music("test.midi")) { + audio_cat->fatal() << "could not locate 'test.midi'" << endl; + exit(-1); + } + AudioMusic* music = AudioPool::load_music("test.midi"); + audio_cat->info() << "Playing test.midi" << endl; + AudioManager::play(music); + while (music->status() == AudioMusic::PLAYING) { + AudioManager::update(); + ipc_traits::sleep(0, 1000000); + } + return 0; +} diff --git a/panda/src/builder/Sources.pp b/panda/src/builder/Sources.pp new file mode 100644 index 0000000000..e4cd1dccb2 --- /dev/null +++ b/panda/src/builder/Sources.pp @@ -0,0 +1,47 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET builder + #define LOCAL_LIBS \ + linmath gobj sgraph sgattrib graph putil gsgbase mathutil pnmimage \ + pandabase + + #define SOURCES \ + builder.I builder.cxx builder.h builderAttrib.I builderAttrib.cxx \ + builderAttrib.h builderBucket.I builderBucket.cxx builderBucket.h \ + builderBucketNode.I builderBucketNode.cxx builderBucketNode.h \ + builderMisc.cxx builderMisc.h builderNormalVisualizer.I \ + builderNormalVisualizer.cxx builderNormalVisualizer.h \ + builderPrim.cxx builderPrim.h builderProperties.cxx \ + builderProperties.h builderTypes.cxx builderTypes.h builderVertex.I \ + builderVertex.cxx builderVertex.h config_builder.cxx \ + config_builder.h mesher.cxx mesher.h pta_BuilderC.cxx \ + pta_BuilderC.h pta_BuilderN.cxx pta_BuilderN.h pta_BuilderTC.cxx \ + pta_BuilderTC.h pta_BuilderV.cxx pta_BuilderV.h vector_BuilderC.cxx \ + vector_BuilderC.h vector_BuilderN.cxx vector_BuilderN.h \ + vector_BuilderTC.cxx vector_BuilderTC.h vector_BuilderV.cxx \ + vector_BuilderV.h + + #define INSTALL_HEADERS \ + builder.I builder.h builderAttrib.I builderAttrib.h \ + builderAttribTempl.I builderAttribTempl.h builderBucket.I \ + builderBucket.h builderBucketNode.I builderBucketNode.h \ + builderNormalVisualizer.I builderNormalVisualizer.h builderPrim.h \ + builderPrimTempl.I builderPrimTempl.h builderProperties.h \ + builderTypes.h builderVertex.I builderVertex.h builderVertexTempl.I \ + builderVertexTempl.h config_builder.h pta_BuilderC.h pta_BuilderN.h \ + pta_BuilderTC.h pta_BuilderV.h vector_BuilderC.h vector_BuilderN.h \ + vector_BuilderTC.h vector_BuilderV.h + +#end lib_target + +#begin test_bin_target + #define TARGET test_builder + #define LOCAL_LIBS \ + builder + + #define SOURCES \ + test_builder.cxx test_builder_data.cxx + +#end test_bin_target + diff --git a/panda/src/builder/builder.I b/panda/src/builder/builder.I new file mode 100644 index 0000000000..f07817508e --- /dev/null +++ b/panda/src/builder/builder.I @@ -0,0 +1,69 @@ +// Filename: builder.I +// Created by: drose (09Sep97) +// +//////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////// +// Function: Builder::add_prim +// Access: Public +// Description: Adds the indicated nonindexed primitive, along with +// its associated bucket, to the builder's internal +// structures. This copies all relevant information +// into the builder. +// +// Returns true if the primitive was successfully added, +// false if it was invalid for some reason (for +// instance, a polygon with two vertices). +//////////////////////////////////////////////////////////////////// +INLINE bool Builder:: +add_prim(const BuilderBucket &bucket, + const BuilderPrim &prim) { + add_bucket(bucket); + return ((BuilderBucketNode &)(*_bi)).add_prim(prim); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Builder::add_prim +// Access: Public +// Description: Adds the indicated indexed primitive, along with +// its associated bucket, to the builder's internal +// structures. This copies all relevant information +// into the builder. +// +// Returns true if the primitive was successfully added, +// false if it was invalid for some reason (for +// instance, a polygon with two vertices). +//////////////////////////////////////////////////////////////////// +INLINE bool Builder:: +add_prim(const BuilderBucket &bucket, + const BuilderPrimI &prim) { + add_bucket(bucket); + return ((BuilderBucketNode &)(*_bi)).add_prim(prim); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Builder::add_prim_nonindexed +// Access: Public +// Description: Adds the specified indexed primitive as if it were +// nonindexed. This simply looks up each coordinate +// value on the prim in its associated array and stores +// a nonindexed primitive with the actual coordinate +// values, instead of the index numbers. +// +// This is handy for having code that calls the builder +// and might want to build either indexed or nonindexed +// geometry, as selected by the user at run-time. +// Simply build indexed geometry in all cases, then call +// either add_prim or add_prim_nonindexed, according to +// the user's selection. +//////////////////////////////////////////////////////////////////// +INLINE bool Builder:: +add_prim_nonindexed(const BuilderBucket &bucket, + const BuilderPrimI &prim) { + add_bucket(bucket); + return ((BuilderBucketNode &)(*_bi)).add_prim_nonindexed(prim); +} diff --git a/panda/src/builder/builder.cxx b/panda/src/builder/builder.cxx new file mode 100644 index 0000000000..12283edd2f --- /dev/null +++ b/panda/src/builder/builder.cxx @@ -0,0 +1,229 @@ +// Filename: builder.cxx +// Created by: drose (09Sep97) +// +//////////////////////////////////////////////////////////////////// + +#include "builder.h" +#include "builderFuncs.h" +#include "builderMisc.h" + +#include +#include +#include +#include + +#include + + +//////////////////////////////////////////////////////////////////// +// Function: Builder::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Builder:: +Builder() { + _bi = _buckets.end(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Builder::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Builder:: +~Builder() { + // Free all the buckets we allocated. We allocated 'em, we free + // 'em. + Buckets::iterator bi; + for (bi = _buckets.begin(); + bi != _buckets.end(); + ++bi) { + BuilderBucket *bucket = (*bi).get_bucket(); + delete bucket; + } +} + + +// We use the NodeMap class to build up a map of Nodes to GeomNodes. +// There may be several buckets that point to the same Node; these +// should all be given the same GeomNode, when possible. + +// However, if two buckets have different sets of scene graph +// properties--that is, the _trans member is different--they must be +// given separate GeomNodes. + +// Furthermore, it's possible to name each bucket. If two buckets +// with the same Node pointer have different names, then they should +// be given two different GeomNodes. + +class NodeMap : public Namable { +public: + NodeMap(NamedNode *node, const BuilderBucket *bucket) + : _node(node), _bucket(bucket) { } + + bool operator < (const NodeMap &other) const { + if (_node != other._node) { + return _node < other._node; + } + if (_bucket->get_name() != other._bucket->get_name()) { + return _bucket->get_name() < other._bucket->get_name(); + } + return (_bucket->_trans.compare_to(other._bucket->_trans) < 0); + } + + NamedNode *_node; + + // Although a bucket pointer is stored here in the NodeMap class, + // you should not use it except to extract the name and/or the + // _trans member. Remember, this bucket pointer stands for any of + // possibly several bucket pointers, all different, except that they + // share the same name and _trans. + const BuilderBucket *_bucket; +}; + + + +//////////////////////////////////////////////////////////////////// +// Function: Builder::build +// Access: Public +// Description: Creates Geoms for all the primitives added to all +// buckets, and adds them where appropriate to place +// them in the scene graph under their respective +// parents, and/or returns a single GeomNode that +// contains all geometry whose bucket did not reference +// a particular scene graph node to parent them to. +// +// If a bucket's _node pointer was a GeomNode, the +// geometry will be added directly to that node. If the +// _node pointer was any other kind of node, a GeomNode +// will be created and parented to that node, and its +// name will be the name of the bucket. In this case, +// the name of the bucket can also be used to different +// nodes: if two buckets reference the same node, but +// have different names, then two different GeomNodes +// are created, one with each name. +//////////////////////////////////////////////////////////////////// +GeomNode *Builder:: +build(const string &default_name) { + typedef map > GeomNodeMap; + GeomNodeMap geom_nodes; + + // First, build all the Geoms and create GeomNodes for them. Each + // unique Node gets its own GeomNode. If the Node is itself a + // GeomNode, that GeomNode is used directly. + Buckets::iterator i; + for (i = _buckets.begin(); + i != _buckets.end(); + ++i) { + BuilderBucket *bucket = (*i).get_bucket(); + NamedNode *node = bucket->_node; + const string &name = bucket->get_name(); + GeomNode *geom_node = NULL; + + if (node!=NULL && node->is_of_type(GeomNode::get_class_type())) { + // The node is a GeomNode. In this case, we simply use that + // node. We can't separate them out by name in this case; we'll + // just assign to it the first nonempty name we encounter. + geom_node = (GeomNode *)node; + + // Since the caller already created this GeomNode and passed it + // in, we'll leave it up to the caller to name the node and set + // up the state transitions leading into it. + + } else { + // The node is not a GeomNode, so look it up in the map. + GeomNodeMap::iterator f = geom_nodes.find(NodeMap(node, bucket)); + if (f != geom_nodes.end()) { + geom_node = (*f).second; + + } else { + // No such node/name combination. Create a new one. + geom_node = bucket->make_geom_node(); + if (geom_node != NULL) { + geom_nodes[NodeMap(node, bucket)] = geom_node; + } + } + } + + if (geom_node != NULL) { + (*i).build(geom_node); + } + } + + // Now go through and parent the geom_nodes under their respective + // group nodes. Save out the geom_node associated with a NULL Node; + // this one is returned from this function. + + GeomNode *base_geom_node = NULL; + + GeomNodeMap::iterator gi; + + for (gi = geom_nodes.begin(); + gi != geom_nodes.end(); + ++gi) { + const NodeMap &nm = (*gi).first; + GeomNode *geom_node = (*gi).second; + + NamedNode *node = nm._node; + const string &name = nm._bucket->get_name(); + const NodeTransitions &trans = nm._bucket->_trans; + + // Assign the name to the geom, if it doesn't have one already. + if (!geom_node->has_name()) { + if (!name.empty()) { + geom_node->set_name(name); + + } else if (!default_name.empty()) { + geom_node->set_name(default_name); + } + } + + // Only reparent the geom_node if it has no parent already. + if (geom_node->_parents.empty()) { + if (geom_node->get_num_geoms() == 0) { + // If there was nothing added, never mind. + delete geom_node; + + } else if (node==NULL) { + nassertr(base_geom_node == NULL, NULL); + base_geom_node = geom_node; + + } else { + RenderRelation *arc = new RenderRelation(node, geom_node); + // Now, this is our only opportunity to apply the scene-graph + // state specified in the bucket to the node: we have created + // our own geom_node for these buckets, and we have parented + // it to the scene graph. + arc->copy_transitions_from(trans); + } + } + } + + return base_geom_node; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Builder::add_bucket +// Access: Protected +// Description: Adds a new BuilderBucket just like the given one to +// the set of all used BuilderBuckets, and makes it the +// current bucket. Future primitives will be added to +// this bucket. +//////////////////////////////////////////////////////////////////// +void Builder:: +add_bucket(const BuilderBucket &bucket) { + // Optimization: maybe it's the same bucket we used last time. + if (_bi != _buckets.end() && + (*_bi) == BuilderBucketNode((BuilderBucket *)&bucket)) { + return; + } + + // Nope. Look again. + _bi = _buckets.find((BuilderBucket *)&bucket); + if (_bi == _buckets.end()) { + BuilderBucket *new_bucket = bucket.make_copy(); + _bi = _buckets.insert(new_bucket).first; + } +} + diff --git a/panda/src/builder/builder.h b/panda/src/builder/builder.h new file mode 100644 index 0000000000..30ddbc577e --- /dev/null +++ b/panda/src/builder/builder.h @@ -0,0 +1,198 @@ +// Filename: builder.h +// Created by: drose (09Sep97) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BUILDER_H +#define BUILDER_H + +//////////////////////////////////////////////////////////////////// +// +// Builder +// +// The builder accepts as input a loose collection of polygons with +// various attributes, sizes, and shapes, and does all the work of +// grouping relating polygons and creating triangle strips, etc., +// ultimately storing the resulting optimized geometry into one or +// more GeomNodes. +// +// It is intended that the builder should be the single entry point +// for all code wishing to create geometry in the scene graph. The +// builder can know about the kinds of geometry that are optimal for a +// particular platform, or even about the kinds of geometry that are +// available for a given platform. (For instance, perhaps on some +// bizarre platform, triangle strips do not exist, but quadstrips are +// really fast. User code should not create triangle strips +// directly.) +// +// Actually, there are two fairly separate pieces in this package. +// The first is the builder itself, which handles the interface to +// user code, and is responsible for collecting polygons from the +// caller, sorting them according to their attributes, and creating +// Geoms that represent the resulting geometry. The second piece is +// the mesher, which receives geometry from the builder and tries to +// create optimal triangle strips (or whichever kind of higher-level +// structure is most appropriate) from them, which it hands back to +// the builder. +// +// It is possible to use the builder without invoking the mesher, in +// which case the builder will create Geoms with the individual prims +// exactly as the user passed them in. It is not possible to use the +// mesher without first going through the builder. +// +// +// The general system of using the builder is as follows: +// +// (a) Create a Builder object. +// +// (b) Iterate through the polygons. For each polygon: +// +// (c) Create a BuilderBucket object and assign to it the +// scene-graph level attributes, such as texture, lighting, +// etc. for your polygon. If several polygons share the same +// attributes, they can of course use the same bucket. But +// there's no reason to be afraid of creating a new bucket +// object each time, if that's more convenient. +// +// (d) Create a BuilderPrim object to describe the polygon. If +// the polygon is to have a polygon color or polygon normal, +// set these on the BuilderPrim. +// +// (e) Iterate through the polygon vertices, in counterclockwise +// order when viewed from the front of the polygon. For each +// vertex: +// +// (f) Create a BuilderVertex object. If the vertex has a +// texture coordinate, normal, or color, set this on the +// BuilderVertex. +// +// (g) Add the BuilderVertex to the BuilderPrim. +// +// (h) Add the BuilderPrim to the Builder. +// +// (i) Call Builder::build() and receive your new geometry! +// +// All of these objects--BuilderBucket, BuilderPrim, and +// BuilderVertex--can, and probably should, be ordinary local +// variables. When they are added into their respective data +// structures they are copied, not referenced, so there's no need to +// try to keep them around after that. +// +// The BuilderBucket is the builder's system for grouping polygons +// that share similar characteristings. Polygons that were added to +// the builder with equivalent (though not necessarily identical) +// buckets may be candidates for joining together into triangle strips +// when possible. +// +// +// That's the basic operation. There are lots of fancy features on +// top of that. +// +// * Other kinds of geometry than polygons are supported. Presently, +// these are light points and line segments. To add these kinds of +// geometry, call set_type() on your BuilderPrim with either +// BPT_point or BPT_line. +// +// * Indexed geometry is supported as well as nonindexed. Indexed +// geometry means that the vertices, UV's, etc. are referenced +// indirectly; an index number into a table is stored instead of +// the actual coordinate values. Indexed geometry may be freely +// mixed in with nonindexed geometry; the builder will sort them +// out (although each polygon must be either entirely indexed or +// entirely nonindexed). To create indexed geometry, use a +// BuilderPrimI object, and assign to it a number of BuilderVertexI +// vertices. The coordinate values you will assign are ushort +// array index numbers. Store the array pointers these refer to in +// the BuilderBucket, via set_coords(), set_normals(), etc. +// +// * The builder is to a certain extent scene-graph aware. In the +// normal usage, you give it a bunch of polygons which are all +// lumped together, and when you call build() it allocates and +// returns a GeomNode which has all of your geometry in it. +// However, you can also ask it to distribute the geometry +// throughout a pre-existing scene graph. To do this, assign the +// _node pointer of your BuilderBucket to point to the node each +// polygon belongs under, as you create the polygons. Now when you +// call build(), the builder will create all the polygons under the +// nodes you indicated, creating new GeomNodes whenever necessary. +// The advantage to this method is that you don't have to process +// your polygons in scene-graph order; the builder can sort them +// out for you. Another advantage is it allows the builder to set +// up the state for you, see the next point: +// +// * It is only when you are taking advantage of the scene-graph +// awareness of the builder that the builder can assign the state +// transitions (like texturing, etc.) you specify to the geometry +// it builds. This is because the state transitions are stored on +// the arcs of the scene graph, and in non-scene graph mode there +// are no arcs! +// +// * You can fine-tune the mesher behavior via a number of parameters +// on the BuilderBucket. Look in builderProperties.h for these +// parameters (BuilderBucket inherits from BuilderProperties). +// This is also where you turn the mesher off if you don't want it. +// +// * You can set global properties on all buckets easily either by +// creating your own default BuilderBucket that you use to +// initialize each individual BuilderBucket you create, or by +// changing the parameters stored in the bucket pointed to by +// BuilderBucket::get_default_bucket(), which is what is used to +// initialize any BuilderBucket created with a default constructor. +// It is suggested that the get_default_bucket() pointer be used to +// define global defaults at applications start-up, while a local +// default BuilderBucket should be used for local defaults. +// +// * You can also control the binning behavior, if you have some +// particular user-specific parameters you want your geometry to be +// grouped on. To do this, subclass from BuilderBucket and +// redefine the comparison operator (it's a virtual function), as +// well as the make_copy() function. +// +//////////////////////////////////////////////////////////////////// + + + +#include + +#include "builderAttrib.h" +#include "builderBucketNode.h" + +#include + +#include + + +class GeomNode; + + +/////////////////////////////////////////////////////////////////// +// Class : Builder +// Description : The main driver class to the builder package. See +// the comments above. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG Builder { +public: + Builder(); + ~Builder(); + + INLINE bool add_prim(const BuilderBucket &bucket, + const BuilderPrim &prim); + INLINE bool add_prim(const BuilderBucket &bucket, + const BuilderPrimI &prim); + INLINE bool add_prim_nonindexed(const BuilderBucket &bucket, + const BuilderPrimI &prim); + + GeomNode *build(const string &default_name = ""); + +protected: + void add_bucket(const BuilderBucket &bucket); + + typedef set > Buckets; + + Buckets _buckets; + Buckets::iterator _bi; +}; + +#include "builder.I" + +#endif diff --git a/panda/src/builder/builderAttrib.I b/panda/src/builder/builderAttrib.I new file mode 100644 index 0000000000..8c3668ee24 --- /dev/null +++ b/panda/src/builder/builderAttrib.I @@ -0,0 +1,69 @@ +// Filename: builderAttrib.I +// Created by: drose (22Jan99) +// +//////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttrib::set_normal_value +// Access: Public +// Description: Reassigns the normal, without knowing whether the +// attribute is indexed or nonindexed. A nonindexed +// attribute will look up the index in the array and +// store the resulting value, while an indexed attribute +// will just store the index number (which assumes the +// array is the same one it's indexing on). +//////////////////////////////////////////////////////////////////// +INLINE void BuilderAttrib:: +set_normal_value(const BuilderN *array, ushort index) { + set_normal(array[index]); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttrib::set_color_value +// Access: Public +// Description: Reassigns the color, without knowing whether the +// attribute is indexed or nonindexed. A nonindexed +// attribute will look up the index in the array and +// store the resulting value, while an indexed attribute +// will just store the index number (which assumes the +// array is the same one it's indexing on). +//////////////////////////////////////////////////////////////////// +INLINE void BuilderAttrib:: +set_color_value(const BuilderC *array, ushort index) { + set_color(array[index]); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribI::set_normal_value +// Access: Public +// Description: Reassigns the normal, without knowing whether the +// attribute is indexed or nonindexed. A nonindexed +// attribute will look up the index in the array and +// store the resulting value, while an indexed attribute +// will just store the index number (which assumes the +// array is the same one it's indexing on). +//////////////////////////////////////////////////////////////////// +INLINE void BuilderAttribI:: +set_normal_value(const BuilderN *, ushort index) { + set_normal(index); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribI::set_color_value +// Access: Public +// Description: Reassigns the color, without knowing whether the +// attribute is indexed or nonindexed. A nonindexed +// attribute will look up the index in the array and +// store the resulting value, while an indexed attribute +// will just store the index number (which assumes the +// array is the same one it's indexing on). +//////////////////////////////////////////////////////////////////// +INLINE void BuilderAttribI:: +set_color_value(const BuilderC *, ushort index) { + set_color(index); +} diff --git a/panda/src/builder/builderAttrib.cxx b/panda/src/builder/builderAttrib.cxx new file mode 100644 index 0000000000..c5b8768409 --- /dev/null +++ b/panda/src/builder/builderAttrib.cxx @@ -0,0 +1,12 @@ +// Filename: builderAttrib.cxx +// Created by: drose (11May00) +// +//////////////////////////////////////////////////////////////////// + +#include "builderAttrib.h" + + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/builder/builderAttrib.h b/panda/src/builder/builderAttrib.h new file mode 100644 index 0000000000..372bd83d32 --- /dev/null +++ b/panda/src/builder/builderAttrib.h @@ -0,0 +1,59 @@ +// Filename: builderAttrib.h +// Created by: drose (22Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BUILDERATTRIB_H +#define BUILDERATTRIB_H + +/////////////////////////////////////////////////////////////////// +// +// BuilderAttrib, BuilderAttribI +// +// This is the parent class of both BuilderVertex and BuilderPrim, and +// contains the attribute values which may be set on either of them: +// specifically, normal, color, and pixel size. (Pixel size is the +// thickness of the lines, for a polygon or a line, or the size of the +// point, in pixels.) +// +// Like BuilderPrim and BuilderVertex, the two classes BuilderAttrib +// and BuilderAttribI are actually both instantiations of the same +// template class, BuilderAttribTempl. +// +/////////////////////////////////////////////////////////////////// + +#include + +#include "builderAttribTempl.h" + +#define BUILDERATTRIBTEMPL_BUILDERV BuilderAttribTempl +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, BUILDERATTRIBTEMPL_BUILDERV); +#define BUILDERATTRIBTEMPL_USHORT BuilderAttribTempl +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, BUILDERATTRIBTEMPL_USHORT); + +class EXPCL_PANDAEGG BuilderAttrib + : public BuilderAttribTempl { +public: + BuilderAttrib() {} + + INLINE void set_normal_value(const BuilderN *array, ushort index); + INLINE void set_color_value(const BuilderC *array, ushort index); +}; + +class EXPCL_PANDAEGG BuilderAttribI + : public BuilderAttribTempl { +public: + BuilderAttribI() {} + + INLINE void set_normal_value(const BuilderN *array, ushort index); + INLINE void set_color_value(const BuilderC *array, ushort index); +}; + +#include "builderAttrib.I" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/builder/builderAttribTempl.I b/panda/src/builder/builderAttribTempl.I new file mode 100644 index 0000000000..5800ef5d33 --- /dev/null +++ b/panda/src/builder/builderAttribTempl.I @@ -0,0 +1,244 @@ +// Filename: builderAttribTempl.I +// Created by: drose (17Sep97) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribTempl::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderAttribTempl:: +BuilderAttribTempl() { + _flags = 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribTempl::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderAttribTempl:: +BuilderAttribTempl(const BuilderAttribTempl ©) { + (*this) = copy; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribTempl::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderAttribTempl &BuilderAttribTempl:: +operator = (const BuilderAttribTempl ©) { + _normal = copy._normal; + _color = copy._color; + _pixel_size = copy._pixel_size; + _flags = copy._flags; + + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribTempl::clear +// Access: Public +// Description: Resets the attribute flags to their original, empty +// state--where no attributes have been applied. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderAttribTempl &BuilderAttribTempl:: +clear() { + _flags = 0; + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribTempl::has_normal +// Access: Public +// Description: Returns true if the attribute has a normal. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderAttribTempl:: +has_normal() const { + return (_flags & BAF_normal)!=0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribTempl::get_normal +// Access: Public +// Description: Returns the attribute's normal. It is an error to +// call this without first verifying that has_normal() is +// true. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderAttribTempl::NType BuilderAttribTempl:: +get_normal() const { + nassertr(has_normal(), _normal); + return _normal; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribTempl::set_normal +// Access: Public +// Description: Resets the attribute's normal. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderAttribTempl &BuilderAttribTempl:: +set_normal(const NType &n) { + _flags |= BAF_normal; + _normal = n; + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribTempl::has_color +// Access: Public +// Description: Returns true if the attribute has a color. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderAttribTempl:: +has_color() const { + return (_flags & BAF_color)!=0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribTempl::get_color +// Access: Public +// Description: Returns the attribute's color. It is an error to +// call this without first verifying that has_color() is +// true. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderAttribTempl::CType BuilderAttribTempl:: +get_color() const { + nassertr(has_color(), _color); + return _color; +} + +template + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribTempl::set_color +// Access: Public +// Description: Resets the attribute's color. +//////////////////////////////////////////////////////////////////// +INLINE BuilderAttribTempl &BuilderAttribTempl:: +set_color(const CType &c) { + _flags |= BAF_color; + _color = c; + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribTempl::has_pixel_size +// Access: Public +// Description: Returns true if the attribute has a pixel_size. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderAttribTempl:: +has_pixel_size() const { + return (_flags & BAF_pixel_size)!=0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribTempl::get_pixel_size +// Access: Public +// Description: Returns the attribute's pixel_size. It is an error to +// call this without first verifying that has_pixel_size() is +// true. +//////////////////////////////////////////////////////////////////// +template +INLINE float BuilderAttribTempl:: +get_pixel_size() const { + nassertr(has_pixel_size(), _pixel_size); + return _pixel_size; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribTempl::set_pixel_size +// Access: Public +// Description: Resets the attribute's pixel_size. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderAttribTempl &BuilderAttribTempl:: +set_pixel_size(float s) { + _flags |= BAF_pixel_size; + _pixel_size = s; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribTempl::operator == +// Access: Public +// Description: Assigns an ordering to the vertices. This is used by +// the Mesher to group identical vertices. This assumes +// that all vertices in the locus of consideration will +// share the same state: with or without normals, +// texcoords, etc. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderAttribTempl:: +operator == (const BuilderAttribTempl &other) const { + if (has_normal() && !(_normal == other._normal)) + return false; + + if (has_color() && !(_color == other._color)) + return false; + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribTempl::operator < +// Access: Public +// Description: Assigns an ordering to the vertices. This is used by +// the Mesher to group identical vertices. This assumes +// that all vertices in the locus of consideration will +// share the same state: with or without normals, +// texcoords, etc. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderAttribTempl:: +operator < (const BuilderAttribTempl &other) const { + if (has_normal() && !(_normal == other._normal)) + return _normal < other._normal; + + if (has_color() && !(_color == other._color)) + return _color < other._color; + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderAttribTempl::output +// Access: Public +// Description: Formats the attribs for output in some sensible way. +//////////////////////////////////////////////////////////////////// +template +INLINE ostream &BuilderAttribTempl:: +output(ostream &out) const { + if (this!=NULL) { + if (has_normal()) { + out << " normal " << get_normal(); + } + + if (has_color()) { + out << " color " << get_color(); + } + } + return out; +} diff --git a/panda/src/builder/builderAttribTempl.h b/panda/src/builder/builderAttribTempl.h new file mode 100644 index 0000000000..f19932922b --- /dev/null +++ b/panda/src/builder/builderAttribTempl.h @@ -0,0 +1,70 @@ +// Filename: builderAttribTempl.h +// Created by: drose (17Sep97) +// +//////////////////////////////////////////////////////////////////// +#ifndef BUILDERATTRIBTEMPL_H +#define BUILDERATTRIBTEMPL_H + +#include + +#include "builderTypes.h" + +#include + + +//////////////////////////////////////////////////////////////////// +// Class : BuilderAttribTempl +// Description : The main body of BuilderAttrib and BuilderAttribI, +// and the base class for BuilderVertexTempl and +// BuilderPrimTempl, this class defines the attributes +// that may be specified for either vertices or +// primitives. +//////////////////////////////////////////////////////////////////// +template +class BuilderAttribTempl { +public: + typedef VT VType; + typedef NT NType; + typedef TT TType; + typedef CT CType; + + INLINE BuilderAttribTempl(); + INLINE BuilderAttribTempl(const BuilderAttribTempl ©); + INLINE BuilderAttribTempl &operator = (const BuilderAttribTempl ©); + + INLINE BuilderAttribTempl &clear(); + + INLINE bool has_normal() const; + INLINE NType get_normal() const; + INLINE BuilderAttribTempl &set_normal(const NType &n); + + INLINE bool has_color() const; + INLINE CType get_color() const; + INLINE BuilderAttribTempl &set_color(const CType &c); + + INLINE bool has_pixel_size() const; + INLINE float get_pixel_size() const; + INLINE BuilderAttribTempl &set_pixel_size(float s); + + INLINE bool operator == (const BuilderAttribTempl &other) const; + INLINE bool operator < (const BuilderAttribTempl &other) const; + + INLINE ostream &output(ostream &out) const; + +protected: + NType _normal; + CType _color; + float _pixel_size; + int _flags; +}; + +template +INLINE ostream &operator << (ostream &out, + const BuilderAttribTempl &attrib) { + return attrib.output(out); +} + +#include "builderAttribTempl.I" + +#endif + diff --git a/panda/src/builder/builderBucket.I b/panda/src/builder/builderBucket.I new file mode 100644 index 0000000000..d3128ec7b3 --- /dev/null +++ b/panda/src/builder/builderBucket.I @@ -0,0 +1,117 @@ +// Filename: builderBucket.I +// Created by: drose (09Sep97) +// +//////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::set_coords +// Access: Public +// Description: Sets the array that will be used to define the +// vertices for any indexed geometry that is associated +// with this bucket. +//////////////////////////////////////////////////////////////////// +INLINE void BuilderBucket:: +set_coords(const PTA_Vertexf &coords) { + _coords = coords; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::get_coords +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PTA_Vertexf BuilderBucket:: +get_coords() const { + return _coords; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::set_normals +// Access: Public +// Description: Sets the array that will be used to define the +// normals for any indexed geometry that is associated +// with this bucket. +//////////////////////////////////////////////////////////////////// +INLINE void BuilderBucket:: +set_normals(const PTA_Normalf &normals) { + _normals = normals; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::get_normals +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PTA_Normalf BuilderBucket:: +get_normals() const { + return _normals; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::set_texcoords +// Access: Public +// Description: Sets the array that will be used to define the +// texture coordinates for any indexed geometry that is +// associated with this bucket. +//////////////////////////////////////////////////////////////////// +INLINE void BuilderBucket:: +set_texcoords(const PTA_TexCoordf &texcoords) { + _texcoords = texcoords; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::get_texcoords +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PTA_TexCoordf BuilderBucket:: +get_texcoords() const { + return _texcoords; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::set_colors +// Access: Public +// Description: Sets the array that will be used to define the +// colors for any indexed geometry that is associated +// with this bucket. +//////////////////////////////////////////////////////////////////// +INLINE void BuilderBucket:: +set_colors(const PTA_Colorf &colors) { + _colors = colors; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::get_colors +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PTA_Colorf BuilderBucket:: +get_colors() const { + return _colors; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::get_default_bucket +// Access: Public, Static +// Description: Returns a pointer to the BuilderBucket that is used +// to initialize any BuilderBuckets created with a +// default constructor. This is just a convenient way +// to set some global parameters. +//////////////////////////////////////////////////////////////////// +INLINE BuilderBucket *BuilderBucket:: +get_default_bucket() { + if (_default_bucket==NULL) { + _default_bucket = new BuilderBucket(true); + } + return _default_bucket; +} diff --git a/panda/src/builder/builderBucket.cxx b/panda/src/builder/builderBucket.cxx new file mode 100644 index 0000000000..fecb174fe6 --- /dev/null +++ b/panda/src/builder/builderBucket.cxx @@ -0,0 +1,240 @@ +// Filename: builderBucket.cxx +// Created by: drose (10Sep97) +// +//////////////////////////////////////////////////////////////////// + +#include "builderAttrib.h" +#include "builderBucket.h" +#include "builderFuncs.h" +#include "builderMisc.h" + +#include +#include + +BuilderBucket *BuilderBucket::_default_bucket = NULL; + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +BuilderBucket:: +BuilderBucket() { + _node = NULL; + (*this) = (*get_default_bucket()); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +BuilderBucket:: +BuilderBucket(const BuilderBucket ©) { + _node = NULL; + (*this) = copy; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +BuilderBucket &BuilderBucket:: +operator = (const BuilderBucket ©) { + ((BuilderProperties &)*this) = (BuilderProperties &)copy; + + // setGState(copy._state); + set_name(copy.get_name()); + set_coords(copy._coords); + set_normals(copy._normals); + set_texcoords(copy._texcoords); + set_colors(copy._colors); + + _node = copy._node; + _drawBin = copy._drawBin; + _drawOrder = copy._drawOrder; + + _trans = copy._trans; + + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +BuilderBucket:: +~BuilderBucket() { +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::make_copy +// Access: Public, Virtual +// Description: Allocates and returns a new copy of this object. If +// you are subclassing from BuilderBucket, you must +// redefine this to return an instance of your new +// subclass, because the Builder will call this function +// to get its own copy. +//////////////////////////////////////////////////////////////////// +BuilderBucket *BuilderBucket:: +make_copy() const { + return new BuilderBucket(*this); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::make_geom_node +// Access: Public, Virtual +// Description: Called by the builder when it is time to create a new +// GeomNode. This function should allocate and return a +// new GeomNode suitable for adding geometry to. You +// may redefine it to return a subclass of GeomNode, or +// to do some initialization to the node. +//////////////////////////////////////////////////////////////////// +GeomNode *BuilderBucket:: +make_geom_node() { + return new GeomNode; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::done_geom +// Access: Public, Virtual +// Description: Called after all the geometry has been added to the +// Geom. This is just a hook for the user to redefine +// to do any post-processing that may be desired on the +// geometry. It may deallocate it and return a new +// copy. If it returns NULL, the geom is discarded. +//////////////////////////////////////////////////////////////////// +Geom *BuilderBucket:: +done_geom(Geom *geom) { + return geom; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::Ordering operator +// Access: Public, Virtual +// Description: Defines an arbitrary ordering among different +// buckets, and groups identical buckets together. +// (Buckets a and b are identical if !(a < b) and !(b < +// a).) +// +// The actual order between different buckets is +// arbitrary and largely irrelevant, so long as it is +// consistent. That is, if (a < b) and (b < c), it must +// also be true that (a < c). Also, if (a < b), it +// cannot be true that (b < a). +//////////////////////////////////////////////////////////////////// +bool BuilderBucket:: +operator < (const BuilderBucket &other) const { + if (get_name() != other.get_name()) { + return get_name() < other.get_name(); + } + + if (_node != other._node) + return _node < other._node; + + if (_coords != other._coords) + return _coords < other._coords; + if (_normals != other._normals) + return _normals < other._normals; + if (_texcoords != other._texcoords) + return _texcoords < other._texcoords; + if (_colors != other._colors) + return _colors < other._colors; + + if (_drawBin != other._drawBin) + return _drawBin < other._drawBin; + if (_drawOrder != other._drawOrder) + return _drawOrder < other._drawOrder; + + int compare = _trans.compare_to(other._trans); + if (compare != 0) { + return (compare < 0); + } + + return BuilderProperties::operator < (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::output +// Access: Public, Virtual +// Description: Formats the bucket for output in some sensible way. +//////////////////////////////////////////////////////////////////// +void BuilderBucket:: +output(ostream &out) const { + out << "Bucket \"" << get_name() << "\""; + + if (_node != (NamedNode *)NULL) { + out << " attached to " << *_node << "\n"; + } + out << "\n"; + + + if (_coords != (Vertexf *)NULL) { + out << "_coords = " << (void *)_coords << "\n"; + } + + if (_normals != (Normalf *)NULL) { + out << "_normals = " << (void *)_normals << "\n"; + } + + if (_texcoords != (TexCoordf *)NULL) { + out << "_texcoords = " << (void *)_texcoords << "\n"; + } + + if (_colors != (Colorf *)NULL) { + out << "_colors = " << (void *)_colors << "\n"; + } + + if (_drawBin != -1) { + out << "_drawBin = " << _drawBin << "\n"; + } + + if (_drawOrder != 0) { + out << "_drawOrder = " << _drawOrder << "\n"; + } + + BuilderProperties::output(out); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::private Constructor +// Access: Private +// Description: This special constructor is used only to initialize +// the _default_bucket pointer. It sets up the initial +// defaults. The normal constructor copies from this +// instance. +//////////////////////////////////////////////////////////////////// +BuilderBucket:: +BuilderBucket(int) { + _node = NULL; + + _drawBin = -1; + _drawOrder = 0; + + // From BuilderProperties + _mesh = true; + _retesselate_coplanar = true; + _show_tstrips = false; + _show_qsheets = false; + _show_quads = false; + _show_normals = false; + _normal_color._v.set(1.0, 0.0, 0.0, 1.0); + _normal_scale = 1.0; + _subdivide_polys = true; + _coplanar_threshold = 0.01; + + _unroll_fans = true; + _consider_fans = true; + _max_tfan_angle = 40.0; + _min_tfan_tris = 0; +} diff --git a/panda/src/builder/builderBucket.h b/panda/src/builder/builderBucket.h new file mode 100644 index 0000000000..83ac31c750 --- /dev/null +++ b/panda/src/builder/builderBucket.h @@ -0,0 +1,104 @@ +// Filename: builderBucket.h +// Created by: drose (09Sep97) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BUILDERBUCKET_H +#define BUILDERBUCKET_H + +#include + +#include "builderProperties.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +class NamedNode; +class Geom; +class GeomNode; + + +/////////////////////////////////////////////////////////////////// +// Class : BuilderBucket +// Description : The main grouping tool for BuilderPrims. See the +// comments at the beginning of builder.h. +// +// As each primitive is added to the builder, it is +// associated with a bucket. The bucket stores the +// scene-graph properties of the primitive, and is used +// to identify primitives that have the same properties +// and thus may be joined into a common triangle strip. +// +// This grouping is done via the ordering operator, <, +// which defines an arbitrary ordering for buckets and +// identifies those buckets which are equivalent to each +// other. By subclassing off of BuilderBucket and +// redefining this operator, you can change the grouping +// behavior to suit your needs, if necessary. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG BuilderBucket : public BuilderProperties, public Namable { +public: + BuilderBucket(); + BuilderBucket(const BuilderBucket ©); + BuilderBucket &operator = (const BuilderBucket ©); + virtual ~BuilderBucket(); + + virtual BuilderBucket *make_copy() const; + virtual GeomNode *make_geom_node(); + virtual Geom *done_geom(Geom *geom); + + virtual bool operator < (const BuilderBucket &other) const; + + INLINE void set_coords(const PTA_Vertexf &coords); + INLINE PTA_Vertexf get_coords() const; + + INLINE void set_normals(const PTA_Normalf &normals); + INLINE PTA_Normalf get_normals() const; + + INLINE void set_texcoords(const PTA_TexCoordf &texcoords); + INLINE PTA_TexCoordf get_texcoords() const; + + INLINE void set_colors(const PTA_Colorf &colors); + INLINE PTA_Colorf get_colors() const; + + INLINE static BuilderBucket *get_default_bucket(); + + virtual void output(ostream &out) const; + + NamedNode *_node; + + short _drawBin; + unsigned int _drawOrder; + + NodeTransitions _trans; + +protected: + PTA_Vertexf _coords; + PTA_Normalf _normals; + PTA_TexCoordf _texcoords; + PTA_Colorf _colors; + + static BuilderBucket *_default_bucket; + +private: + BuilderBucket(int); +}; + +INLINE ostream &operator << (ostream &out, const BuilderBucket &bucket) { + bucket.output(out); + return out; +} + +#include "builderBucket.I" + +#endif + diff --git a/panda/src/builder/builderBucketNode.I b/panda/src/builder/builderBucketNode.I new file mode 100644 index 0000000000..7ab9334ec1 --- /dev/null +++ b/panda/src/builder/builderBucketNode.I @@ -0,0 +1,92 @@ +// Filename: builderBucketNode.I +// Created by: drose (10Sep97) +// +//////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucketNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BuilderBucketNode:: +BuilderBucketNode(BuilderBucket *bucket) : _bucket(bucket) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucketNode::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BuilderBucketNode:: +BuilderBucketNode(const BuilderBucketNode ©) { + (*this) = copy; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucketNode::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void BuilderBucketNode:: +operator = (const BuilderBucketNode ©) { + _bucket = copy._bucket; + _prims = copy._prims; + _iprims = copy._iprims; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucketNode::add_prim_nonindexed +// Access: Public +// Description: Adds the indicated indexed primitive to the bucket as +// if it were nonindexed, and returns true if the +// primitive was valid. Intended to be called from +// Builder::add_prim_nonindexed(). +//////////////////////////////////////////////////////////////////// +INLINE bool BuilderBucketNode:: +add_prim_nonindexed(const BuilderPrimI &prim) { + BuilderPrim nonindexed; + nonindexed.nonindexed_copy(prim, *_bucket); + return add_prim(nonindexed); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucketNode::get_bucket +// Access: Public +// Description: Returns the BuilderBucket object associated with this +// node. +//////////////////////////////////////////////////////////////////// +INLINE BuilderBucket *BuilderBucketNode:: +get_bucket() const { + return _bucket; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucketNode::Ordering operator +// Access: Public +// Description: Returns true if this bucket precedes the indicated +// one in ordering. This function is used by the STL +// set in the Builder object to sort buckets into order, +// and to collect similar buckets together. +//////////////////////////////////////////////////////////////////// +INLINE bool BuilderBucketNode:: +operator < (const BuilderBucketNode &other) const { + return (*_bucket) < (*other._bucket); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucketNode::Equivalence operator +// Access: Public +// Description: Returns true if the two buckets are equivalent, based +// on the ordering operator, above. +//////////////////////////////////////////////////////////////////// +INLINE bool BuilderBucketNode:: +operator == (const BuilderBucketNode &other) const { + return !((*_bucket) < (*other._bucket) || + (*other._bucket) < (*_bucket)); +} diff --git a/panda/src/builder/builderBucketNode.cxx b/panda/src/builder/builderBucketNode.cxx new file mode 100644 index 0000000000..4cdbe7a742 --- /dev/null +++ b/panda/src/builder/builderBucketNode.cxx @@ -0,0 +1,87 @@ +// Filename: builderBucketNode.cxx +// Created by: drose (10Sep97) +// +//////////////////////////////////////////////////////////////////// + +#include "builderFuncs.h" +#include "builderBucketNode.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucketNode::add_prim +// Access: Public +// Description: Adds the indicated indexed primitive to the +// bucket, and returns true if the primitive was valid. +// Intended to be called from Builder::add_prim(). +//////////////////////////////////////////////////////////////////// +bool BuilderBucketNode:: +add_prim(const BuilderPrim &prim) { + bool result = expand(prim, *_bucket, inserter(_prims, _prims.begin())); + return result; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucketNode::add_prim +// Access: Public +// Description: Adds the indicated nonindexed primitive to the +// bucket, and returns true if the primitive was valid. +// Intended to be called from Builder::add_prim(). +//////////////////////////////////////////////////////////////////// +bool BuilderBucketNode:: +add_prim(const BuilderPrimI &prim) { + bool result = expand(prim, *_bucket, inserter(_iprims, _iprims.begin())); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucketNode::build +// Access: Public +// Description: Builds all the geometry assigned to this particular +// bucket, and assigns it to the indicated GeomNode. +// Returns the number of Geoms created. +//////////////////////////////////////////////////////////////////// +int BuilderBucketNode:: +build(GeomNode *geom_node) const { + int count = 0; + + { + // First, the nonindexed. + Prims::const_iterator pi, last_pi; + last_pi = _prims.begin(); + + for (pi = _prims.begin(); + pi != _prims.end(); + ++pi) { + if ((*last_pi) < (*pi)) { + count += mesh_and_build(last_pi, pi, *_bucket, geom_node, + (BuilderPrim *)0); + last_pi = pi; + } + } + count += mesh_and_build(last_pi, pi, *_bucket, geom_node, + (BuilderPrim *)0); + } + + { + // Then, the indexed. + IPrims::const_iterator pi, last_pi; + last_pi = _iprims.begin(); + + for (pi = _iprims.begin(); + pi != _iprims.end(); + ++pi) { + if ((*last_pi) < (*pi)) { + count += mesh_and_build(last_pi, pi, *_bucket, geom_node, + (BuilderPrimI *)0); + last_pi = pi; + } + } + count += mesh_and_build(last_pi, pi, *_bucket, geom_node, + (BuilderPrimI *)0); + } + + return count; +} + diff --git a/panda/src/builder/builderBucketNode.h b/panda/src/builder/builderBucketNode.h new file mode 100644 index 0000000000..dd88f0b454 --- /dev/null +++ b/panda/src/builder/builderBucketNode.h @@ -0,0 +1,58 @@ +// Filename: builderBucketNode.h +// Created by: drose (10Sep97) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BUILDERBUCKETNODE_H +#define BUILDERBUCKETNODE_H + +#include + +#include "builderPrim.h" +#include "builderBucket.h" + +#include + +class GeomNode; + + +/////////////////////////////////////////////////////////////////// +// Class : BuilderBucketNode +// Description : This is a wrapper class around BuilderBucket, used by +// the Builder class. It stores a pointer to a +// BuilderBucket object, as well as lists of the +// primitives that have been added to it. +// +// There are no functions in this class that are +// intended to be called directly by user code; instead, +// use the interface provided by Builder. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG BuilderBucketNode { +public: + INLINE BuilderBucketNode(BuilderBucket *bucket); + INLINE BuilderBucketNode(const BuilderBucketNode ©); + INLINE void operator = (const BuilderBucketNode ©); + + bool add_prim(const BuilderPrim &prim); + bool add_prim(const BuilderPrimI &prim); + INLINE bool add_prim_nonindexed(const BuilderPrimI &prim); + + INLINE BuilderBucket *get_bucket() const; + + INLINE bool operator < (const BuilderBucketNode &other) const; + INLINE bool operator == (const BuilderBucketNode &other) const; + + int build(GeomNode *geom_node) const; + +protected: + typedef multiset > Prims; + typedef multiset > IPrims; + + BuilderBucket *_bucket; + Prims _prims; + IPrims _iprims; +}; + +#include "builderBucketNode.I" + +#endif diff --git a/panda/src/builder/builderFuncs.I b/panda/src/builder/builderFuncs.I new file mode 100644 index 0000000000..d232c47a6f --- /dev/null +++ b/panda/src/builder/builderFuncs.I @@ -0,0 +1,908 @@ +// Filename: builderFuncs.cxx +// Created by: drose (09Sep97) +// +//////////////////////////////////////////////////////////////////// + +#include "builderPrim.h" +#include "mesherTempl.h" +#include "builderNormalVisualizer.h" +#include "config_builder.h" + +#include +#include +#include + +#include +#ifndef PENV_WIN32 +#include +#endif + +#ifdef WIN32_VC +struct Vtx { + int index; + BuilderV coord; + struct Vtx *next; +}; +#endif + +//////////////////////////////////////////////////////////////////// +// Function: decomp_concave +// Description: Decomposes a concave polygon into triangles. Returns +// true if successful, false if the polygon is +// self-intersecting. +//////////////////////////////////////////////////////////////////// +template +static bool +decomp_concave(const PrimType &prim, BuilderBucket &bucket, + OutputIterator result, + int asum, int x, int y) { +#ifndef WIN32_VC + struct Vtx { + int index; + BuilderV coord; + struct Vtx *next; + }; +#endif + +#define VX(p, c) p->coord[c] + + vector output_prims; + + Vtx *p0, *p1, *p2, *t0, *vert; + Vtx *m[3]; + float xmin, xmax, ymin, ymax; + int i, init, csum, chek; + float a[3], b[3], c[3], s[3]; + + int num_verts = prim.get_num_verts(); + nassertr(num_verts >= 3, false); + + /* Make linked list of verts */ + vert = (Vtx *) alloca(sizeof(Vtx)); + vert->index = 0; + vert->coord = prim.get_vertex(0).get_coord_value(bucket); + p1 = vert; + + for (i = 1; i < num_verts; i++) { + p0 = (Vtx *) alloca(sizeof(Vtx)); + p0->index = i; + p0->coord = prim.get_vertex(i).get_coord_value(bucket); + // There shouldn't be two consecutive identical vertices. If + // there are, skip one. + if (!(p0->coord == p1->coord)) { + p1->next = p0; + p1 = p0; + } + } + p1->next = vert; + + p0 = vert; + p1 = p0->next; + p2 = p1->next; + m[0] = p0; + m[1] = p1; + m[2] = p2; + chek = 0; + + while (p0 != p2->next) { + /* Polygon is self-intersecting so punt */ + if (chek && + m[0] == p0 && + m[1] == p1 && + m[2] == p2) { + + // builder_cat.info() << "Could not decompose concave polygon!"; + return false; + } + + chek = 1; + + a[0] = VX(p1, y) - VX(p2, y); + b[0] = VX(p2, x) - VX(p1, x); + a[2] = VX(p0, y) - VX(p1, y); + b[2] = VX(p1, x) - VX(p0, x); + + csum = ((b[0] * a[2] - b[2] * a[0] >= 0.0) ? 1 : 0); + + if (csum ^ asum) { + /* current angle is concave */ + p0 = p1; + p1 = p2; + p2 = p2->next; + + } else { + /* current angle is convex */ + xmin = (VX(p0, x) < VX(p1, x)) ? VX(p0, x) : VX(p1, x); + if (xmin > VX(p2, x)) + xmin = VX(p2, x); + + xmax = (VX(p0, x) > VX(p1, x)) ? VX(p0, x) : VX(p1, x); + if (xmax < VX(p2, x)) + xmax = VX(p2, x); + + ymin = (VX(p0, y) < VX(p1, y)) ? VX(p0, y) : VX(p1, y); + if (ymin > VX(p2, y)) + ymin = VX(p2, y); + + ymax = (VX(p0, y) > VX(p1, y)) ? VX(p0, y) : VX(p1, y); + if (ymax < VX(p2, y)) + ymax = VX(p2, y); + + for (init = 1, t0 = p2->next; t0 != p0; t0 = t0->next) { + if (VX(t0, x) >= xmin && VX(t0, x) <= xmax && + VX(t0, y) >= ymin && VX(t0, y) <= ymax) { + if (init) { + a[1] = VX(p2, y) - VX(p0, y); + b[1] = VX(p0, x) - VX(p2, x); + init = 0; + c[0] = VX(p1, x) * VX(p2, y) - VX(p2, x) * VX(p1, y); + c[1] = VX(p2, x) * VX(p0, y) - VX(p0, x) * VX(p2, y); + c[2] = VX(p0, x) * VX(p1, y) - VX(p1, x) * VX(p0, y); + } + + s[0] = a[0] * VX(t0, x) + b[0] * VX(t0, y) + c[0]; + s[1] = a[1] * VX(t0, x) + b[1] * VX(t0, y) + c[1]; + s[2] = a[2] * VX(t0, x) + b[2] * VX(t0, y) + c[2]; + + if (asum) { + if (s[0] >= 0.0 && s[1] >= 0.0 && s[2] >= 0.0) + break; + } else { + if (s[0] <= 0.0 && s[1] <= 0.0 && s[2] <= 0.0) + break; + } + } + } + + if (t0 != p0) { + p0 = p1; + p1 = p2; + p2 = p2->next; + } else { + PrimType new_prim(prim); + new_prim.set_type(BPT_tri); + new_prim.clear_vertices(); + new_prim.add_vertex(prim.get_vertex(p0->index)); + new_prim.add_vertex(prim.get_vertex(p1->index)); + new_prim.add_vertex(prim.get_vertex(p2->index)); + output_prims.push_back(new_prim); + + p0->next = p1->next; + p1 = p2; + p2 = p2->next; + + m[0] = p0; + m[1] = p1; + m[2] = p2; + chek = 0; + } + } + } + + PrimType new_prim(prim); + new_prim.set_type(BPT_tri); + new_prim.clear_vertices(); + new_prim.add_vertex(prim.get_vertex(p0->index)); + new_prim.add_vertex(prim.get_vertex(p1->index)); + new_prim.add_vertex(prim.get_vertex(p2->index)); + output_prims.push_back(new_prim); + + copy(output_prims.begin(), output_prims.end(), result); + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: triangulate_poly +// Description: Breaks a (possibly concave) higher-order polygon into +// a series of constituent triangles. +//////////////////////////////////////////////////////////////////// +template +static bool +triangulate_poly(const PrimType &prim, BuilderBucket &bucket, + OutputIterator result) { + BuilderV p0, p1, as; + float dx1, dy1, dx2, dy2, max; + int i, flag, asum, csum, index, x, y, v0, v1, v, even; + + // First see if the polygon is just a triangle + int num_verts = prim.get_num_verts(); + if (num_verts == 3) { + PrimType new_prim(prim); + new_prim.set_type(BPT_tri); + *result++ = new_prim; + + return true; + + } else if (num_verts < 3) { + // Or if it's a degenerate polygon. + return false; + } + + // calculate signed areas + as[0] = 0.0; + as[1] = 0.0; + as[2] = 0.0; + + for (i = 0; i < num_verts; i++) { + p0 = prim.get_vertex(i).get_coord_value(bucket); + p1 = prim.get_vertex((i + 1) % num_verts).get_coord_value(bucket); + as[0] += p0[0] * p1[1] - p0[1] * p1[0]; + as[1] += p0[0] * p1[2] - p0[2] * p1[0]; + as[2] += p0[1] * p1[2] - p0[2] * p1[1]; + } + + /* select largest signed area */ + max = 0.0; + index = 0; + for (i = 0; i < 3; i++) { + if (as[i] >= 0.0) { + if (as[i] > max) { + max = as[i]; + index = i; + flag = 1; + } + } else { + as[i] = -as[i]; + if (as[i] > max) { + max = as[i]; + index = i; + flag = 0; + } + } + } + + /* pointer offsets */ + switch (index) { + case 0: + x = 0; + y = 1; + break; + + case 1: + x = 0; + y = 2; + break; + + case 2: + x = 1; + y = 2; + break; + } + + /* concave check */ + p0 = prim.get_vertex(0).get_coord_value(bucket); + p1 = prim.get_vertex(1).get_coord_value(bucket); + dx1 = p1[x] - p0[x]; + dy1 = p1[y] - p0[y]; + p0 = p1; + p1 = prim.get_vertex(2).get_coord_value(bucket); + + dx2 = p1[x] - p0[x]; + dy2 = p1[y] - p0[y]; + asum = ((dx1 * dy2 - dx2 * dy1 >= 0.0) ? 1 : 0); + + for (i = 0; i < num_verts - 1; i++) { + p0 = p1; + p1 = prim.get_vertex((i+3) % num_verts).get_coord_value(bucket); + + dx1 = dx2; + dy1 = dy2; + dx2 = p1[x] - p0[x]; + dy2 = p1[y] - p0[y]; + csum = ((dx1 * dy2 - dx2 * dy1 >= 0.0) ? 1 : 0); + + if (csum ^ asum) { + return decomp_concave(prim, bucket, result, flag, x, y); + } + } + + v0 = 0; + v1 = 1; + v = num_verts - 1; + + even = 1; + + /* + * Convert to triangles only. Do not fan out from a single vertex + * but zigzag into triangle strip. + */ + for (i = 0; i < num_verts - 2; i++) { + if (even) { + PrimType new_prim(prim); + new_prim.set_type(BPT_tri); + new_prim.clear_vertices(); + new_prim.add_vertex(prim.get_vertex(v0)); + new_prim.add_vertex(prim.get_vertex(v1)); + new_prim.add_vertex(prim.get_vertex(v)); + *result++ = new_prim; + v0 = v1; + v1 = v; + v = v0 + 1; + } else { + PrimType new_prim(prim); + new_prim.set_type(BPT_tri); + new_prim.clear_vertices(); + new_prim.add_vertex(prim.get_vertex(v1)); + new_prim.add_vertex(prim.get_vertex(v0)); + new_prim.add_vertex(prim.get_vertex(v)); + *result++ = new_prim; + v0 = v1; + v1 = v; + v = v0 - 1; + } + + even = !even; + } + + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: expand_polys +// Description: Identifies a single polygon as a triangle, quad, or +// higher-order polygon, and writes it into the result +// list. +//////////////////////////////////////////////////////////////////// +template +static bool +expand_polys(PrimType &prim, BuilderBucket &, + OutputIterator result) { + switch (prim.get_num_verts()) { + case 0: + case 1: + case 2: + return false; + + case 3: + prim.set_type(BPT_tri); + break; + + case 4: + prim.set_type(BPT_quad); + break; + + default: + prim.set_type(BPT_poly); + } + + *result++ = prim; + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: expand_points +// Description: Expands a light points primitive into its individual +// component points, with one point per primitive. +//////////////////////////////////////////////////////////////////// +template +static bool +expand_points(const PrimType &prim, BuilderBucket &, + OutputIterator result) { + // Each vertex goes in its own primitive. + + int num_verts = prim.get_num_verts(); + for (int i = 0; i < num_verts; i++) { + PrimType new_prim(prim); + new_prim.clear_vertices(); + new_prim.add_vertex(prim.get_vertex(i)); + *result++ = new_prim; + } + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: expand_lines +// Description: Expands a linestrip primitive into its component line +// primitives. +//////////////////////////////////////////////////////////////////// +template +static bool +expand_lines(const PrimType &prim, BuilderBucket &, + OutputIterator result) { + // Each line segment goes in its own primitive. This breaks up the + // linestrips already defined; we'll re-strip them later if the + // generate-tstrips flag is enabled. + + int num_verts = prim.get_num_verts(); + for (int i = 1; i < num_verts; i++) { + PrimType new_prim(prim); + new_prim.clear_vertices(); + new_prim.add_vertex(prim.get_vertex(i-1)); + new_prim.add_vertex(prim.get_vertex(i)); + *result++ = new_prim; + } + return true; +} + + + +//////////////////////////////////////////////////////////////////// +// Function: expand +// Description: Receives a single primitive as a BuilderPrim or +// BuilderPrimI object, as input by the user. Does some +// initial processing on the primitive to verify +// internal consistency (for instance, that a quad has +// four vertices), and returns a new BuilderPrim or +// series of BuilderPrim objects, suitable for building +// with. +// +// More than one primitive might be returned because +// higher-order polygons may be broken up into +// triangles, and linestrips and points are broken into +// their component pieces. The output primitives are +// written into the STL container defined by result. +//////////////////////////////////////////////////////////////////// +template +bool +expand(const PrimType &prim, BuilderBucket &bucket, OutputIterator result) { + // Make a copy of the prim so we can fiddle with it. + PrimType new_prim = prim; + + switch (new_prim.get_type()) { + case BPT_poly: + case BPT_tri: + case BPT_quad: + // These three types are all treated the same, as polygons. We + // don't entirely trust the user to match the polygon type with + // the number of verts, so we'll do it ourselves later. + new_prim.remove_doubled_verts(true); + + if (!bucket._subdivide_polys || + (bucket._mesh && new_prim.get_num_verts() <= 4)) { + // If we're meshing, we'd like to send quads through without + // subdividing. The mesher can take advantage of the extra + // information (and will eventually produce tris anyway). + return expand_polys(new_prim, bucket, result); + } else { + // If we're not meshing, we'll break them into tris now. + return triangulate_poly(new_prim, bucket, result); + } + + case BPT_point: + new_prim.remove_doubled_verts(false); + return expand_points(new_prim, bucket, result); + + case BPT_line: + new_prim.remove_doubled_verts(false); + return expand_lines(new_prim, bucket, result); + + default: + builder_cat.error() << "Unknown prim type\n"; + return false; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: build_geoms +// Description: Accepts a list of BuilderPrim or BuilderPrimI +// objects, defined by the iterators first and last, and +// creates corresponding geometry for them in the +// indicated GeomNode. +//////////////////////////////////////////////////////////////////// +template +static int +build_geoms(InputIterator first, InputIterator last, + BuilderBucket &bucket, GeomNode *geom_node, + PrimType *) { + if (first==last) { + return 0; + } + + // By the time we get here, we have a list of primitives that all have + // the same properties: + + // 1. The BuilderBucket. + // 2. The indexed/nonindexed type. + // 3. The primitive type (polygon, line, point). + // 4. The pixel size. + + // The binding of normals, colors, or texcoords: per-vertex, + // per-prim, or per-component, will generally be the same across all + // primitives, but it might not always be the same, because in + // certain special cases the mesher might have changed these + // properties when it built tristrips. + + typedef TYPENAME PrimType::VType VType; + typedef TYPENAME PrimType::NType NType; + typedef TYPENAME PrimType::TType TType; + typedef TYPENAME PrimType::CType CType; + + // We need to determine the common binding type for all primitives. + // For a given attribute, say normals, there are at most five cases, + // in the order of priority: + // + // 1. If at least one primitive in the list does not have normals, + // the binding will be G_OFF. + // + // 2. If at least one primitive has per-vertex normals, the binding + // will be G_PER_VERTEX. + // + // 3. If at least one primitive has per-component normals, the + // binding will be G_PER_COMPONENT. + // + // 4. If none of the first three apply, it follows that all + // primitives have an overall normal. If any primitive's + // overall normal differs from any other, the binding will be + // G_PER_PRIM. + // + // 5. If none of the above apply, the binding will be G_OVERALL. + // + // An exception to the above is for texcoords, which is either G_OFF + // (by rule 1) or G_PER_VERTEX. + + GeomBindType bind_normals = G_OVERALL; + GeomBindType bind_colors = G_OVERALL; + GeomBindType bind_texcoords = G_PER_VERTEX; + + NType overall_normal; + CType overall_color; + bool first_normal = true; + bool first_color = true; + + InputIterator i; + for (i = first; i != last; ++i) { + + // Normals. + + // Test rule 1. + if (!(*i).has_any_normal()) { + bind_normals = G_OFF; + } else if (bind_normals != G_OFF) { + // Test rule 2. + if ((*i).has_vertex_normal()) { + bind_normals = G_PER_VERTEX; + } else if (bind_normals != G_PER_VERTEX) { + // Test rule 3. + if ((*i).has_component_normal()) { + bind_normals = G_PER_COMPONENT; + } else if (bind_normals != G_PER_COMPONENT) { + // Test rule 4. + nassertr((*i).has_overall_normal(), 0); + if (first_normal) { + overall_normal = (*i).get_normal(); + first_normal = false; + } else if ( !((*i).get_normal() == overall_normal)) { + bind_normals = G_PER_PRIM; + } + } + } + } + + // Colors. + + // Test rule 1. + if (!(*i).has_any_color()) { + bind_colors = G_OFF; + } else if (bind_colors != G_OFF) { + // Test rule 2. + if ((*i).has_vertex_color()) { + bind_colors = G_PER_VERTEX; + } else if (bind_colors != G_PER_VERTEX) { + // Test rule 3. + if ((*i).has_component_color()) { + bind_colors = G_PER_COMPONENT; + } else if (bind_colors != G_PER_COMPONENT) { + // Test rule 4. + nassertr((*i).has_overall_color(), 0); + if (first_color) { + overall_color = (*i).get_color(); + first_color = false; + } else if ( !((*i).get_color() == overall_color)) { + bind_colors = G_PER_PRIM; + } + } + } + } + + // Texcoords. + + // Test rule 1. + if (!(*i).has_any_texcoord()) { + bind_texcoords = G_OFF; + } + } + + // Determine the primitive type and build the lengths array, if needed. + PTA_int lengths; + bool want_lengths = false; + int j; + + Geom *geom = NULL; + BuilderPrimType type = (*first).get_type(); + switch (type) { + case BPT_poly: + geom = new GeomPolygon; + want_lengths = true; + break; + + case BPT_tristrip: + geom = new GeomTristrip; + want_lengths = true; + break; + + case BPT_trifan: + geom = new GeomTrifan; + want_lengths = true; + break; + + case BPT_line: + geom = new GeomLine; + break; + + case BPT_point: + geom = new GeomPoint; + break; + + case BPT_tri: + geom = new GeomTri; + break; + + case BPT_quad: + geom = new GeomQuad; + break; + + default: + builder_cat.fatal() << "Invalid primitive type.\n"; + abort(); + } + + if (geom == NULL) { + builder_cat.error() << "Unsupported primitive type " << type << "\n"; + return 0; + } + + // Count up the number of prims we're actually building. + int num_prims = 0; + for (i = first; i != last; ++i) { + if ((*i).is_valid()) { + num_prims++; + } + } + + if (num_prims==0) { + builder_cat.error() << "All primitives were invalid!\n"; + return 0; + } + + if (want_lengths) { + lengths = PTA_int(num_prims); + j = 0; + for (i = first; i != last; ++i) { + if ((*i).is_valid()) { + lengths[j++] = (*i).get_num_verts(); + } + } + nassertr(j == num_prims, 0); + } + + // Now build up some arrays. + PTA(VType) coords(0); + PTA(NType) normals(0); + PTA(TType) texcoords(0); + PTA(CType) colors(0); + + int total_verts = 0; + int total_components = 0; + + int v, num_verts; + int c, num_components; + for (i = first; i != last; ++i) { + if ((*i).is_valid()) { + num_verts = (*i).get_num_verts(); + total_verts += num_verts; + for (v = 0; v < num_verts; v++) { + coords.push_back((*i).get_vertex(v).get_coord()); + + if (bind_normals == G_PER_VERTEX) { + normals.push_back((*i).get_vertex(v).get_normal()); + } + if (bind_texcoords == G_PER_VERTEX) { + texcoords.push_back((*i).get_vertex(v).get_texcoord()); + } + if (bind_colors == G_PER_VERTEX) { + colors.push_back((*i).get_vertex(v).get_color()); + } + } + + num_components = (*i).get_num_components(); + total_components += num_components; + for (c = 0; c < num_components; c++) { + if (bind_normals == G_PER_COMPONENT) { + normals.push_back((*i).get_component(c).get_normal()); + } + if (bind_colors == G_PER_COMPONENT) { + colors.push_back((*i).get_component(c).get_color()); + } + } + + if (bind_normals == G_PER_PRIM) { + normals.push_back((*i).get_normal()); + } + if (bind_colors == G_PER_PRIM) { + colors.push_back((*i).get_color()); + } + } + } + + if (bind_normals == G_OVERALL) { + normals.push_back(overall_normal); + } + if (bind_colors == G_OVERALL) { + colors.push_back(overall_color); + } + + // Now add all the stuff to our Geom. + + geom->set_num_prims(num_prims); + + if (lengths != (int *)NULL) { + geom->set_lengths(lengths); + } + + PrimType::fill_geom(geom, coords, + bind_normals, normals, + bind_texcoords, texcoords, + bind_colors, colors, + bucket, num_prims, + total_components, total_verts); + + /* + if ((*first).has_pixel_size()) { + // Again, we only have to test the first one in the list for a + // pixel_size attribute. If this one has it, then they all have + // the same value. + geom->setPntSize((*first).get_pixel_size()); + geom->setLineWidth((*first).get_pixel_size()); + } + */ + + // geom->setGState((pfGeoState *)bucket.getGState()); + // geom->setDrawBin(bucket._drawBin); + // geom->setDrawOrder(bucket._drawOrder); + + Geom *new_geom = bucket.done_geom(geom); + if (new_geom != (Geom *)NULL) { + geom_node->add_geom(new_geom); + } + + return 1; +} + + +///////////////////////////////////////////////////////////////////// +// Class : PrimByType +// Description : An STL function object to sort primitives in order by +// type. +//////////////////////////////////////////////////////////////////// +template +class PrimByType { +public: + int operator () (const PrimType &p1, const PrimType &p2) const { + return p1.get_type() < p2.get_type(); + } +}; + + +//////////////////////////////////////////////////////////////////// +// Function: __mesh_and_build +// Description: The implementation of mesh_and_build(), below. This +// extra function call is just to allow mesh_and_build() +// to infer the PrimType (BuilderPrim or BuilderPrimI) +// from the iterator's value type, and template on that. +//////////////////////////////////////////////////////////////////// +template +static int +__mesh_and_build(InputIterator first, InputIterator last, + BuilderBucket &bucket, GeomNode *geom_node, + PrimType *) { + if (first==last) { + return 0; + } + + typedef vector Prims; + Prims prims; + BuilderBucket *local_bucket = NULL; + BuilderBucket *bucket_ptr = &bucket; + + if (bucket._mesh) { + // Send all the prims through the mesher. First, make a copy of + // the bucket so the mesher can modify it if it wants. + local_bucket = bucket.make_copy(); + bucket_ptr = local_bucket; + MesherTempl mesher(local_bucket); + + for (InputIterator ii = first; ii != last; ++ii) { + mesher.add_prim(*ii); + } + mesher.mesh(); + PrimType prim; + prim = mesher.getPrim(); + while (prim.get_num_verts() > 0) { + prims.push_back(prim); + prim = mesher.getPrim(); + } + + } else { + // Send the prims through without meshing. + copy(first, last, back_inserter(prims)); + } + + // Now we have an array of prims which all share the same + // properties, except possibly type. Sort them by type and send + // them to build_geoms. + sort(prims.begin(), prims.end(), PrimByType()); + + int count = 0; + if (!prims.empty()) { + Prims::iterator pi, last_pi; + pi = prims.begin(); + last_pi = pi; + for (++pi; pi != prims.end(); ++pi) { + if ((*pi).get_type() != (*last_pi).get_type()) { + count += build_geoms(last_pi, pi, *bucket_ptr, geom_node, (PrimType*)0); + last_pi = pi; + } + } + count += build_geoms(last_pi, pi, *bucket_ptr, geom_node, (PrimType*)0); + } + + if (local_bucket!=NULL) { + delete local_bucket; + } + + // Finally, if the user so requested, create some visualization for + // the normals. +#ifdef SUPPORT_SHOW_NORMALS + if (bucket._show_normals) { + BuilderNormalVisualizer bnv(bucket); + for (InputIterator ii = first; ii != last; ++ii) { + bnv.add_prim(*ii); + } + bnv.show_normals(geom_node); + } +#endif + + return count; +} + + +//////////////////////////////////////////////////////////////////// +// Function: mesh_and_build +// Description: Accepts a list of BuilderPrim or BuilderPrimI +// objects, defined by the iterators first and list, +// runs them through the mesher if specified by the +// bucket, and builds them into the indicated GeomNode. +//////////////////////////////////////////////////////////////////// +template +int +mesh_and_build(InputIterator first, InputIterator last, + BuilderBucket &bucket, GeomNode *geom_node, + value_type *value_type_ptr) { + return __mesh_and_build(first, last, bucket, geom_node, value_type_ptr); +} + + +//////////////////////////////////////////////////////////////////// +// Function: split +// Description: Splits an STL list into two other lists, according to +// the return value from pred. +//////////////////////////////////////////////////////////////////// +template +OutputIterator split(InputIterator first, InputIterator last, + OutputIterator true_result, OutputIterator false_result, + Predicate pred) { + while (first != last) { + if (pred(*first)) { + *true_result++ = *first++; + } else { + *false_result++ = *first++; + } + } + return true_result; +} + diff --git a/panda/src/builder/builderFuncs.h b/panda/src/builder/builderFuncs.h new file mode 100644 index 0000000000..31c3691ae2 --- /dev/null +++ b/panda/src/builder/builderFuncs.h @@ -0,0 +1,66 @@ +// Filename: builderFuncs.h +// Created by: drose (09Sep97) +// +//////////////////////////////////////////////////////////////////// +#ifndef BUILDERFUNCS_H +#define BUILDERFUNCS_H + +#include + +#include + +#include + +class BuilderBucket; +class GeomNode; + + +//////////////////////////////////////////////////////////////////// +// Function: expand +// Description: Receives a single primitive as a BuilderPrim or +// BuilderPrimI object, as input by the user. Does some +// initial processing on the primitive to verify +// internal consistency (for instance, that a quad has +// four vertices), and returns a new BuilderPrim or +// series of BuilderPrim objects, suitable for building +// with. +// +// More than one primitive might be returned because +// higher-order polygons may be broken up into +// triangles, and linestrips and points are broken into +// their component pieces. The output primitives are +// written into the STL container defined by result. +//////////////////////////////////////////////////////////////////// +template +bool +expand(const PrimType &prim, BuilderBucket &bucket, + OutputIterator result); + + +//////////////////////////////////////////////////////////////////// +// Function: mesh_and_build +// Description: Accepts a list of BuilderPrim or BuilderPrimI +// objects, defined by the iterators first and list, +// runs them through the mesher if specified by the +// bucket, and builds them into the indicated GeomNode. +//////////////////////////////////////////////////////////////////// +template +int +mesh_and_build(InputIterator first, InputIterator last, + BuilderBucket &bucket, GeomNode *geom_node); + + + +//////////////////////////////////////////////////////////////////// +// Function: split +// Description: Splits an STL list into two other lists, according to +// the return value from pred. +//////////////////////////////////////////////////////////////////// +template +OutputIterator split(InputIterator first, InputIterator last, + OutputIterator true_result, OutputIterator false_result, + Predicate pred); + +#include "builderFuncs.I" + +#endif diff --git a/panda/src/builder/builderMisc.cxx b/panda/src/builder/builderMisc.cxx new file mode 100644 index 0000000000..10e7c72ab2 --- /dev/null +++ b/panda/src/builder/builderMisc.cxx @@ -0,0 +1,37 @@ +// Filename: builderMisc.cxx +// Created by: drose (18Sep97) +// +//////////////////////////////////////////////////////////////////// + +#include "builderMisc.h" +#include "builderTypes.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: make_random_color +// Description: Chooses a reasonable random color. +//////////////////////////////////////////////////////////////////// +void +make_random_color(Colorf &color) { + LVector3f rgb; + float len; + do { + for (int i = 0; i < 3; i++) { + rgb[i] = (double)rand() / (double)RAND_MAX; + } + len = length(rgb); + + // Repeat until we have a color that's not too dark or too light. + } while (len < .1 || len > 1.5); + +#ifdef WIN32_VC + color.set(rgb[0], rgb[1], rgb[2], + 0.25 + 0.75 * (double)rand() / (double)RAND_MAX); +#else + color.set(rgb[0], rgb[1], rgb[2], + 0.25 + 0.75 * (double)random() / (double)RAND_MAX); +#endif +} + diff --git a/panda/src/builder/builderMisc.h b/panda/src/builder/builderMisc.h new file mode 100644 index 0000000000..5cb31aeff6 --- /dev/null +++ b/panda/src/builder/builderMisc.h @@ -0,0 +1,18 @@ +// Filename: builderMisc.h +// Created by: drose (18Sep97) +// +//////////////////////////////////////////////////////////////////// +#ifndef BUILDERMISC_H +#define BUILDERMISC_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Function: make_random_color +// Description: Chooses a reasonable random color. +//////////////////////////////////////////////////////////////////// +void make_random_color(Colorf &color); + +#endif diff --git a/panda/src/builder/builderNormalVisualizer.I b/panda/src/builder/builderNormalVisualizer.I new file mode 100644 index 0000000000..c187457c92 --- /dev/null +++ b/panda/src/builder/builderNormalVisualizer.I @@ -0,0 +1,11 @@ +// Filename: builderNormalVisualizer.I +// Created by: drose (08Sep99) +// +//////////////////////////////////////////////////////////////////// + +INLINE BuilderNormalVisualizer:: +BuilderNormalVisualizer(BuilderBucket &bucket) : _bucket(bucket) { + _num_vertices = 0; + _net_vertex._v.set(0.0, 0.0, 0.0); +} + diff --git a/panda/src/builder/builderNormalVisualizer.cxx b/panda/src/builder/builderNormalVisualizer.cxx new file mode 100644 index 0000000000..95aeb886cc --- /dev/null +++ b/panda/src/builder/builderNormalVisualizer.cxx @@ -0,0 +1,65 @@ +// Filename: builderNormalVisualizer.cxx +// Created by: drose (08Sep99) +// +//////////////////////////////////////////////////////////////////// + +#ifdef SUPPORT_SHOW_NORMALS + +#include "builderNormalVisualizer.h" +#include "builderFuncs.h" + +void BuilderNormalVisualizer:: +add_prim(const BuilderPrim &prim) { + if (prim.has_overall_normal()) { + // Average up all the vertex values to get the primitive center. + BuilderV net_vertex; + net_vertex._v.set(0.0, 0.0, 0.0); + int num_verts = prim.get_num_verts(); + for (int i = 0; i < num_verts; i++) { + net_vertex._v += prim.get_vertex(i).get_coord(); + } + net_vertex._v /= num_verts; + add_normal(net_vertex, prim.get_normal()); + + } else if (prim.has_vertex_normal()) { + // Each vertex gets its own normal. + int num_verts = prim.get_num_verts(); + for (int i = 0; i < num_verts; i++) { + add_normal(prim.get_vertex(i).get_coord(), prim.get_vertex(i).get_normal()); + } + } else if (prim.has_component_normal()) { + // Each component gets its own normal. We don't presently handle + // this, as it should only happen when the user passes + // already-meshed tristrips into the builder, an unusual + // situation. + } +} + +void BuilderNormalVisualizer:: +add_prim(const BuilderPrimI &prim) { + BuilderPrim prim_ni; + prim_ni.nonindexed_copy(prim, _bucket); + add_prim(prim_ni); +} + +void BuilderNormalVisualizer:: +show_normals(GeomNode *node) { + // Ok, now we've got a bunch of normals saved up; create some geometry. + mesh_and_build(_lines.begin(), _lines.end(), _bucket, node, (BuilderPrim *)0); +} + +void BuilderNormalVisualizer:: +add_normal(const BuilderV ¢er, const BuilderN &normal) { + BuilderV to = center._v + normal._v * _bucket._normal_scale; + + BuilderPrim line; + line.set_type(BPT_line); + line.set_color(_bucket._normal_color); + line.add_vertex(BuilderVertex(center)); + line.add_vertex(BuilderVertex(to)); + + _lines.push_back(line); +} + +#endif + diff --git a/panda/src/builder/builderNormalVisualizer.h b/panda/src/builder/builderNormalVisualizer.h new file mode 100644 index 0000000000..beb77333cb --- /dev/null +++ b/panda/src/builder/builderNormalVisualizer.h @@ -0,0 +1,51 @@ +// Filename: builderNormalVisualizer.h +// Created by: drose (08Sep99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BUILDERNORMALVISUALIZER_H +#define BUILDERNORMALVISUALIZER_H + +#ifdef SUPPORT_SHOW_NORMALS + +#include + +#include "builderBucket.h" +#include "builderAttrib.h" +#include "builderVertex.h" +#include "builderPrim.h" + +#include + +/////////////////////////////////////////////////////////////////// +// Class : BuilderNormalVisualizer +// Description : A useful class for collecting information about +// vertices and their associated normals as geometry is +// built, so that its normals may be visualized via +// renderable geometry. This is activated by the +// _show_normals flag in the BuilderProperties. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG BuilderNormalVisualizer { +public: + INLINE BuilderNormalVisualizer(BuilderBucket &bucket); + + void add_prim(const BuilderPrim &prim); + void add_prim(const BuilderPrimI &prim); + + void show_normals(GeomNode *node); + +private: + void add_normal(const BuilderV ¢er, const BuilderN &normal); + + BuilderBucket &_bucket; + + BuilderV _net_vertex; + int _num_vertices; + vector _lines; +}; + +#include "builderNormalVisualizer.I" + +#endif // SUPPORT_SHOW_NORMALS + +#endif diff --git a/panda/src/builder/builderPrim.cxx b/panda/src/builder/builderPrim.cxx new file mode 100644 index 0000000000..c87b7eca52 --- /dev/null +++ b/panda/src/builder/builderPrim.cxx @@ -0,0 +1,194 @@ +// Filename: builderPrim.cxx +// Created by: drose (10Sep97) +// +//////////////////////////////////////////////////////////////////// + +#include "builderPrim.h" + +#include +#include + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrim::nonindexed_copy +// Access: Public +// Description: Makes a nonindexed copy of the given indexed prim, by +// looking up the current values of the indexed +// coordinates in the given bucket. +//////////////////////////////////////////////////////////////////// +BuilderPrim &BuilderPrim:: +nonindexed_copy(const BuilderPrimTempl ©, + const BuilderBucket &bucket) { + clear(); + + set_type(copy.get_type()); + + if (copy.has_normal()) { + nassertr(bucket.get_normals() != (Normalf *)NULL, *this); + set_normal(bucket.get_normals()[copy.get_normal()]); + } + if (copy.has_color()) { + nassertr(bucket.get_colors() != (Colorf *)NULL, *this); + set_color(bucket.get_colors()[copy.get_color()]); + } + if (copy.has_pixel_size()) { + set_pixel_size(copy.get_pixel_size()); + } + + int num_verts = copy.get_num_verts(); + int i; + for (i = 0; i < num_verts; i++) { + const BuilderVertexI &cv = copy.get_vertex(i); + BuilderVertex v; + if (cv.has_coord()) { + v.set_coord(cv.get_coord_value(bucket)); + } + + if (cv.has_normal()) { + v.set_normal(cv.get_normal_value(bucket)); + } + + if (cv.has_texcoord()) { + v.set_texcoord(cv.get_texcoord_value(bucket)); + } + + if (cv.has_color()) { + v.set_color(cv.get_color_value(bucket)); + } + + if (cv.has_pixel_size()) { + v.set_pixel_size(cv.get_pixel_size()); + } + add_vertex(v); + } + return *this; +} + + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrim::fill_geom +// Access: Public +// Description: Fills up the attribute values of a Geom with the +// indicated arrays. This creates a nonindexed Geom. +//////////////////////////////////////////////////////////////////// +void BuilderPrim:: +fill_geom(Geom *geom, const PTA_BuilderV &v_array, + GeomBindType n_attr, const PTA_BuilderN &n_array, + GeomBindType t_attr, const PTA_BuilderTC &t_array, + GeomBindType c_attr, const PTA_BuilderC &c_array, + const BuilderBucket &, int, int, int) { + + // WARNING! This is questionable practice. We have a + // PTA_BuilderV etc.; since a BuilderV is just a proxy + // to a Vertexf, we can get away with casting this to a + // PTA_Vertexf. + + geom->set_coords((PTA_Vertexf &)v_array, G_PER_VERTEX); + + if (n_attr != G_OFF) { + geom->set_normals((PTA_Normalf &)n_array, n_attr); + } + + if (t_attr != G_OFF) { + geom->set_texcoords((PTA_TexCoordf &)t_array, t_attr); + } + + if (c_attr != G_OFF) { + geom->set_colors((PTA_Colorf &)c_array, c_attr); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimI::fill_geom +// Access: Public +// Description: Fills up the attribute values of a Geom with the +// indicated arrays. This creates an indexed Geom. +//////////////////////////////////////////////////////////////////// +void BuilderPrimI:: +fill_geom(Geom *geom, const PTA_ushort &v_array, + GeomBindType n_attr, PTA_ushort n_array, + GeomBindType t_attr, PTA_ushort t_array, + GeomBindType c_attr, PTA_ushort c_array, + const BuilderBucket &bucket, + int num_prims, int num_components, int num_verts) { + PTA_Vertexf v_data = bucket.get_coords(); + PTA_Normalf n_data = bucket.get_normals(); + PTA_TexCoordf t_data = bucket.get_texcoords(); + PTA_Colorf c_data = bucket.get_colors(); + + // Make sure the data pointers are NULL if the attribute is off. + if (n_attr == G_OFF) { + n_data = NULL; + n_array = NULL; + } + if (t_attr == G_OFF) { + t_data = NULL; + t_array = NULL; + } + if (c_attr == G_OFF) { + c_data = NULL; + c_array = NULL; + } + + int n_len = + (n_attr==G_PER_VERTEX) ? num_verts : + (n_attr==G_PER_COMPONENT) ? num_components : + (n_attr==G_PER_PRIM) ? num_prims : + (n_attr==G_OVERALL) ? 1 : 0; + int t_len = + (t_attr==G_PER_VERTEX) ? num_verts : + (t_attr==G_PER_COMPONENT) ? num_components : + (t_attr==G_PER_PRIM) ? num_prims : + (t_attr==G_OVERALL) ? 1 : 0; + int c_len = + (c_attr==G_PER_VERTEX) ? num_verts : + (c_attr==G_PER_COMPONENT) ? num_components : + (c_attr==G_PER_PRIM) ? num_prims : + (c_attr==G_OVERALL) ? 1 : 0; + + // See if we can share some of the index lists. + if (n_attr != G_OFF && + memcmp(v_array, n_array, sizeof(ushort) * n_len)==0) { + n_array = v_array; + } + if (t_attr != G_OFF) { + if (memcmp(v_array, t_array, sizeof(ushort) * t_len)==0) { + t_array = v_array; + } else if (t_len <= n_len && + memcmp(n_array, t_array, sizeof(ushort) * t_len)==0) { + t_array = n_array; + } + } + if (c_attr != G_OFF) { + if (memcmp(v_array, c_array, sizeof(ushort) * c_len)==0) { + c_array = v_array; + } else if (c_len <= n_len && + memcmp(n_array, c_array, sizeof(ushort) * c_len)==0) { + c_array = n_array; + } else if (c_len <= t_len && + memcmp(t_array, c_array, sizeof(ushort) * c_len)==0) { + c_array = t_array; + } + } + + geom->set_coords(v_data, G_PER_VERTEX, v_array); + + if (n_attr != G_OFF) { + geom->set_normals(n_data, n_attr, n_array); + } + + if (t_attr != G_OFF) { + geom->set_texcoords(t_data, t_attr, t_array); + } + + if (c_attr != G_OFF) { + geom->set_colors(c_data, c_attr, c_array); + } +} + diff --git a/panda/src/builder/builderPrim.h b/panda/src/builder/builderPrim.h new file mode 100644 index 0000000000..0ee46180ad --- /dev/null +++ b/panda/src/builder/builderPrim.h @@ -0,0 +1,119 @@ +// Filename: builderPrim.h +// Created by: drose (09Sep97) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BUILDERPRIM_H +#define BUILDERPRIM_H + +/////////////////////////////////////////////////////////////////// +// +// BuilderPrim, BuilderPrimI +// +// The basic class for passing primitives (polygons, etc.) to the +// builder. See the comments at the beginning of builder.h and +// builderVertex.h. +// +// A BuilderPrim has a few attributes of its own--color and +// normal--which are defined in builderAttrib.h (which it inherits +// from). It also has a collection of vertices, represented as +// BuilderVertex objects, each of which can have its own attributes as +// well. Any additional attributes, such as texture, lighting, etc., +// are considered to be external to the primitive, and are defined in +// the BuilderBucket object. +// +// BuilderPrimI is exactly like BuilderPrim, except that it represents +// an indexed primitive. A BuilderPrimI keeps its collection of +// vertices as BuilderVertexI's, which store their values as index +// numbers into an array rather than as actual coordinate values. The +// arrays themselves are stored in the BuilderBucket. +// +// In fact, BuilderPrim and BuilderPrimI are both instantiations of +// the same template object, BuilderPrimTempl, with different vertex +// types (BuilderVertex and BuilderVertexI, respectively). +// +// It is this templating that drives most of the code in this package. +// A lot of stuff in the builder tool, and everything in the mesher +// tool, is templated on the BuilderPrim type, so the same code is +// used to support both indexed and nonindexed primitives. +// +// +// In addition to storing the primitives--individual polygons, +// generally--as passed in by user code, BuilderPrim objects can store +// the compound primitives that might have been generated by the +// mesher, like triangle strips. In this case, in addition to an +// array of vertices, it has an array of component attributes, which +// store the attributes specific to each individual component +// (e.g. the normal of each triangle in a triangle strip). +// +/////////////////////////////////////////////////////////////////// + + + +#include + +#include "builderPrimTempl.h" +#include "builderBucket.h" +#include "pta_BuilderV.h" +#include "pta_BuilderN.h" +#include "pta_BuilderTC.h" +#include "pta_BuilderC.h" + +#include +#include + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, BuilderPrimTempl); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, BuilderPrimTempl); + +///////////////////////////////////////////////////////////////////// +// Class : BuilderPrim +// Description : The basic class for passing nonindexed primitives +// to the builder. See the comments at the the head of +// this file, and in builder.h. +// +// Look in builderPrimTempl.h and builderAttribTempl.h +// for most of the interface to BuilderPrim. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG BuilderPrim : public BuilderPrimTempl { +public: + BuilderPrim() {} + + BuilderPrim &nonindexed_copy(const BuilderPrimTempl ©, + const BuilderBucket &bucket); + + static void fill_geom(Geom *geom, const PTA_BuilderV &v_array, + GeomBindType n_attr, const PTA_BuilderN &n_array, + GeomBindType t_attr, const PTA_BuilderTC &t_array, + GeomBindType c_attr, const PTA_BuilderC &c_array, + const BuilderBucket &bucket, + int num_prims, int num_components, int num_verts); +}; + + +/////////////////////////////////////////////////////////////////// +// Class : BuilderPrimI +// Description : The basic class for passing indexed primitives +// to the builder. +// +// Look in builderPrimTempl.h and builderAttribTempl.h +// for most of the interface to BuilderPrimI. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG BuilderPrimI : public BuilderPrimTempl { +public: + BuilderPrimI() {} + + static void fill_geom(Geom *geom, const PTA_ushort &v_array, + GeomBindType n_attr, PTA_ushort n_array, + GeomBindType t_attr, PTA_ushort t_array, + GeomBindType c_attr, PTA_ushort c_array, + const BuilderBucket &bucket, + int num_prims, int num_components, int num_verts); +}; + + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/builder/builderPrimTempl.I b/panda/src/builder/builderPrimTempl.I new file mode 100644 index 0000000000..49ed78d70a --- /dev/null +++ b/panda/src/builder/builderPrimTempl.I @@ -0,0 +1,1025 @@ +// Filename: builderPrimTempl.I +// Created by: drose (11Sep97) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderPrimTempl:: +BuilderPrimTempl() { + clear(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderPrimTempl:: +BuilderPrimTempl(const BuilderPrimTempl ©) { + (*this) = copy; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderPrimTempl &BuilderPrimTempl:: +operator = (const BuilderPrimTempl ©) { + DAttrib::operator = (copy); + _verts = copy._verts; + _components = copy._components; + _type = copy._type; + _overall = copy._overall; + + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::has_normal +// Access: Public +// Description: Returns true if set_normal() has been called on the +// primitive. This is unrelated to the normal values +// that may or may not override from the vertices. Also +// see has_overall_normal(). +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +has_normal() const { + return DAttrib::has_normal(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::has_color +// Access: Public +// Description: Returns true if set_color() has been called on the +// primitive. This is unrelated to the color values +// that may or may not override from the vertices. Also +// see has_overall_color(). +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +has_color() const { + return DAttrib::has_color(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::has_pixel_size +// Access: Public +// Description: Returns true if set_pixel_size() has been called on +// the primitive. This is unrelated to the pixel_size +// values that may or may not override from the +// vertices. Also see has_overall_pixel_size(). +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +has_pixel_size() const { + return DAttrib::has_pixel_size(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::has_overall_normal +// Access: Public +// Description: Returns true if the primitive has a single, overall +// normal value. This can happen because of one of: (a) +// the primitive had a normal value assigned to it +// directly, and its individual vertices and components +// did not; (b) each vertex was assigned the same normal +// value; (c) each component was assigned the same normal +// value. +// +// If has_overall_normal() returns true, then get_normal() +// will return the overall normal value. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +has_overall_normal() const { + if ((_overall & BAF_overall_updated) == 0) { + ((BuilderPrimTempl *)this)->update_overall_attrib(); + } + return (_overall & BAF_overall_normal)!=0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::has_overall_color +// Access: Public +// Description: Returns true if the primitive has a single, overall +// color value. This can happen because of one of: (a) +// the primitive had a color value assigned to it +// directly, and its individual vertices and components +// did not; (b) each vertex was assigned the same color +// value; (c) each component was assigned the same color +// value. +// +// If has_overall_color() returns true, then get_color() +// will return the overall color value. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +has_overall_color() const { + if ((_overall & BAF_overall_updated) == 0) { + ((BuilderPrimTempl *)this)->update_overall_attrib(); + } + return (_overall & BAF_overall_color)!=0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::has_overall_pixel_size +// Access: Public +// Description: Returns true if the primitive has a single, overall +// pixel_size value. This can happen because of one of: +// (a) the primitive had a pixel_size value assigned to +// it directly, and its individual vertices and +// components did not; (b) each vertex was assigned the +// same pixel_size value; (c) each component was +// assigned the same pixel_size value. +// +// If has_overall_pixel_size() returns true, then +// get_pixel_size() will return the overall pixel_size +// value. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +has_overall_pixel_size() const { + if ((_overall & BAF_overall_updated) == 0) { + ((BuilderPrimTempl *)this)->update_overall_attrib(); + } + return (_overall & BAF_overall_pixel_size)!=0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::has_vertex_normal +// Access: Public +// Description: Returns true if each of the primitive's vertices has +// a different normal value set. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +has_vertex_normal() const { + if ((_overall & BAF_overall_updated) == 0) { + ((BuilderPrimTempl *)this)->update_overall_attrib(); + } + return (_overall & BAF_vertex_normal)!=0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::has_vertex_color +// Access: Public +// Description: Returns true if each of the primitive's vertices has +// a different color value set. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +has_vertex_color() const { + if ((_overall & BAF_overall_updated) == 0) { + ((BuilderPrimTempl *)this)->update_overall_attrib(); + } + return (_overall & BAF_vertex_color)!=0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::has_vertex_texcoord +// Access: Public +// Description: Returns true if each of the primitive's vertices has +// a texcoord value set. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +has_vertex_texcoord() const { + if ((_overall & BAF_overall_updated) == 0) { + ((BuilderPrimTempl *)this)->update_overall_attrib(); + } + return (_overall & BAF_vertex_texcoord)!=0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::has_vertex_pixel_size +// Access: Public +// Description: Returns true if each of the primitive's vertices has +// a different pixel_size value set. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +has_vertex_pixel_size() const { + if ((_overall & BAF_overall_updated) == 0) { + ((BuilderPrimTempl *)this)->update_overall_attrib(); + } + return (_overall & BAF_vertex_pixel_size)!=0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::has_component_normal +// Access: Public +// Description: Returns true if the prim is a composite primitive +// like a tristrip, and its individual components each +// have a different normal value set. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +has_component_normal() const { + if ((_overall & BAF_overall_updated) == 0) { + ((BuilderPrimTempl *)this)->update_overall_attrib(); + } + return (_overall & BAF_component_normal)!=0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::has_component_color +// Access: Public +// Description: Returns true if the prim is a composite primitive +// like a tristrip, and its individual components each +// have a different color value set. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +has_component_color() const { + if ((_overall & BAF_overall_updated) == 0) { + ((BuilderPrimTempl *)this)->update_overall_attrib(); + } + return (_overall & BAF_component_color)!=0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::has_component_pixel_size +// Access: Public +// Description: Returns true if the prim is a composite primitive +// like a tristrip, and its individual components each +// have a different pixel_size value set. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +has_component_pixel_size() const { + if ((_overall & BAF_overall_updated) == 0) { + ((BuilderPrimTempl *)this)->update_overall_attrib(); + } + return (_overall & BAF_component_pixel_size)!=0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::has_any_normal +// Access: Public +// Description: Returns true if the prim has a normal value set +// at any level. That is, it returns true if any of +// has_overall_normal(), has_vertex_normal(), or +// has_component_normal() is true. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +has_any_normal() const { + return has_overall_normal() || has_vertex_normal() || has_component_normal(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::has_any_color +// Access: Public +// Description: Returns true if the prim has a color value set at any +// level. That is, it returns true if any of +// has_overall_color(), has_vertex_color(), or +// has_component_color() is true. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +has_any_color() const { + return has_overall_color() || has_vertex_color() || has_component_color(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::has_any_texcoord +// Access: Public +// Description: Returns true if the prim has a texcoord value set +// at any level. Since texture coordinates can only be +// set on vertices, this is the same thing as +// has_vertex_texcoord(). +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +has_any_texcoord() const { + return has_vertex_texcoord(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::has_any_pixel_size +// Access: Public +// Description: Returns true if the prim has a pixel_size value set +// at any level. That is, it returns true if any of +// has_overall_pixel_size(), has_vertex_pixel_size(), or +// has_component_pixel_size() is true. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +has_any_pixel_size() const { + return has_overall_pixel_size() || has_vertex_pixel_size() || + has_component_pixel_size(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::clear +// Access: Public +// Description: Resets the BuilderPrim to its initial, default state. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderPrimTempl &BuilderPrimTempl:: +clear() { + DAttrib::clear(); + _type = BPT_poly; + return clear_vertices(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::clear_vertices +// Access: Public +// Description: Removes all the vertices that have been added to the +// BuilderPrim, without otherwise affecting its +// properties. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderPrimTempl &BuilderPrimTempl:: +clear_vertices() { + _verts.clear(); + _components.clear(); + _overall = 0; + + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::set_attrib +// Access: Public +// Description: Copies the polygon attributes, color and normal, from +// the indicated attribute structure (which might be +// another BuilderPrim). +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderPrimTempl &BuilderPrimTempl:: +set_attrib(const DAttrib &attrib) { + DAttrib::operator = (attrib); + _overall = 0; + + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::get_type +// Access: Public +// Description: Indicates the type of primitive represented by the +// BuilderPrim. See set_type(). +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderPrimType BuilderPrimTempl:: +get_type() const { + return _type; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::set_type +// Access: Public +// Description: Indicates the type of primitive represented by the +// BuilderPrim. Normally, the user should set this to +// one of BPT_poly, BPT_point, or BPT_line. However, +// other types are possible, and are created by the +// mesher, including triangle strips, quad strips, and +// triangle fans. +// +// Setting this value changes the interpretation of the +// vertices, for instance, a triangle strip is defined +// by vertices that zigzag back and forth between the +// triangles. Note that when defining a composite +// primitive such as a triangle strip, it is also +// necessary to call add_component() the correct number +// of times to match the calls to add_vertex(), +// according to the primitive type. See +// add_component(). +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderPrimTempl &BuilderPrimTempl:: +set_type(BuilderPrimType t) { + _type = t; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::get_normal +// Access: Public +// Description: Returns the value previously set by set_normal(), or +// the overall normal value common to all vertices. It +// is an error to call this without first testing that +// one of has_normal() or has_overall_normal() is true. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderPrimTempl::NType BuilderPrimTempl:: +get_normal() const { + return DAttrib::get_normal(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::set_normal +// Access: Public +// Description: Sets the flat normal associated with the primitive. +// If each of the primitive's vertices also has a normal +// set, the vertex normals will override. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderPrimTempl &BuilderPrimTempl:: +set_normal(const NType &n) { + DAttrib::set_normal(n); + _overall = 0; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::get_color +// Access: Public +// Description: Returns the value previously set by set_color(), or +// the overall color value common to all vertices. It +// is an error to call this without first testing that +// one of has_color() or has_overall_color() is true. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderPrimTempl::CType BuilderPrimTempl:: +get_color() const { + return DAttrib::get_color(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::set_color +// Access: Public +// Description: Sets the flat color associated with the primitive. +// If each of the primitive's vertices also has a color +// set, the vertex colors will override. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderPrimTempl &BuilderPrimTempl:: +set_color(const CType &c) { + DAttrib::set_color(c); + _overall = 0; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::get_pixel_size +// Access: Public +// Description: Returns the value previously set by set_pixel_size(), +// or the overall pixel_size value common to all +// vertices. It is an error to call this without first +// testing that one of has_pixel_size() or +// has_overall_pixel_size() is true. +//////////////////////////////////////////////////////////////////// +template +INLINE float BuilderPrimTempl:: +get_pixel_size() const { + return DAttrib::get_pixel_size(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::set_pixel_size +// Access: Public +// Description: Sets the line thickness (for a polygon or line) or +// size (for a point) in pixels. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderPrimTempl &BuilderPrimTempl:: +set_pixel_size(float s) { + DAttrib::set_pixel_size(s); + _overall = 0; + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::add_vertex +// Access: Public +// Description: Adds a new vertex to the BuilderPrim. The vertex +// data is copied into the prim's internal structure. +// Vertices should be added in counterclockwise order, +// when viewed from the front. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderPrimTempl &BuilderPrimTempl:: +add_vertex(const Vertex &v) { + _overall = 0; + _verts.push_back(v); + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::get_num_verts +// Access: Public +// Description: Returns the number of vertices in the primitive. +//////////////////////////////////////////////////////////////////// +template +INLINE int BuilderPrimTempl:: +get_num_verts() const { + return _verts.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::get_vertex +// Access: Public +// Description: Returns a reference to the nth vertex of the +// primitive, where 0 <= n < get_num_verts(). This +// reference may be modified directly, e.g. to set a +// vertex normal on a vertex after it has been added to +// the primitive. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderPrimTempl::Vertex &BuilderPrimTempl:: +get_vertex(int n) { + nassertr(n >= 0 && n < _verts.size(), *(new Vertex)); + return _verts[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::get_vertex (const) +// Access: Public +// Description: Returns the nth vertex of the primitive, where 0 <= n +// < get_num_verts(). +//////////////////////////////////////////////////////////////////// +template +INLINE const BuilderPrimTempl::Vertex &BuilderPrimTempl:: +get_vertex(int n) const { + nassertr(n >= 0 && n < _verts.size(), *(new Vertex)); + return _verts[n]; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::add_component +// Access: Public +// Description: Normally, this function is only used by the mesher +// code; user code generally should not need to call it. +// +// In addition to storing a simple polygon, a +// BuilderPrim can store a composite primitive, like a +// triangle strip. In this case, it is useful to store +// the attributes (like color and normal) associated +// with each component. +// +// When get_type() is one of the composite types, it is +// the responsibility of the caller to call +// add_component() once for each component, where the +// number of components is defined by the type and the +// number of vertices (e.g. for a triangle strip, the +// number of components is the number of vertices - 2). +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderPrimTempl &BuilderPrimTempl:: +add_component(const DAttrib &attrib) { + _overall = 0; + _components.push_back(attrib); + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::get_num_components +// Access: Public +// Description: Returns the number of times add_component() has been +// called. This should be, but is not necessarily, the +// same as the number of components in the composite +// primitive (e.g. a triangle strip). See +// add_component(). +//////////////////////////////////////////////////////////////////// +template +INLINE int BuilderPrimTempl:: +get_num_components() const { + return _components.size(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::get_component +// Access: Public +// Description: Returns a refernece to the attribute structure +// associated with the nth component, where 0 <= n < +// get_num_components(). This reference may be modified +// directly, e.g. to change the color of a polygon after +// it has been added to the tristrip. See +// add_component() and get_num_components(). +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderPrimTempl::DAttrib &BuilderPrimTempl:: +get_component(int n) { + nassertr(n >= 0 && n < _components.size(), *(new DAttrib)); + return _components[n]; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::get_component (const) +// Access: Public +// Description: Returns the attributes associated with the nth +// component, where 0 <= n < get_num_components(). See +// add_component() and get_num_components(). +//////////////////////////////////////////////////////////////////// +template +INLINE const BuilderPrimTempl::DAttrib &BuilderPrimTempl:: +get_component(int n) const { + nassertr(n >= 0 && n < _components.size(), *(new DAttrib)); + return _components[n]; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::Ordering operator +// Access: Public +// Description: Returns true if this primitive should come before the +// other one, in the bin ordering. This is used to sort +// polygons into groups which can be tristripped +// together, similar to the same operation on the +// BuilderBucket, except that this works at a finer +// level of detail (i.e. on prims within the same +// bucket). +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderPrimTempl:: +operator < (const BuilderPrimTempl &other) const { + int sv1 = sort_value(); + int sv2 = other.sort_value(); + + // Normally, we compare only based on the integer sort_value(). + // However, other things being equal, if two prims have a different + // pixel_size then they sort differently. + + // We can ignore per-vertex pixel_size, since there's no way to + // render that anyway. + + return sv1 < sv2 || + (sv1==sv2 && has_pixel_size() && get_pixel_size() < other.get_pixel_size()); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::sort_value +// Access: Protected +// Description: Returns a number for grouping primitives, such that +// only primitives that share the same number can be +// tristripped together. +//////////////////////////////////////////////////////////////////// +template +INLINE int BuilderPrimTempl:: +sort_value() const { + // If a polygon is missing a normal, color, or uv, it shouldn't be + // stripped along with polygons that have normals, colors, or uv's. + // Furthermore, if one polygon has vertex normals and another is + // flat shaded, they shouldn't be stripped together. + return + ((has_vertex_normal()!=0) << 5) | + ((has_vertex_color()!=0) << 4) | + ((has_any_texcoord()!=0) << 3) | + ((has_any_normal()!=0) << 2) | + ((has_any_color()!=0) << 1) | + ((has_pixel_size()!=0) << 0); +} + + +//////////////////////////////////////////////////////////////////// +// Class: SameCoord +// Description: An STL function object that identifies vertices as +// equivalent when they have the same coordinate value, +// regardless of their attribute values like texture +// coordinates. This is used in remove_doubled_verts(). +//////////////////////////////////////////////////////////////////// +template +class SameCoord { +public: + SameCoord() {} + bool operator () (const VTX &a, const VTX &b) const { + return a.get_coord() == b.get_coord(); + } +}; + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::remove_doubled_verts +// Access: Public +// Description: Removes consecutive identical vertices from the prim +// definition. These are meaningless and only confuse +// polygon subdividing and meshing. +// +// If closed is true, this also removes vertices doubled +// at the beginning and end (as if the list of vertices +// were implicitly closed, as it is for a polygon). +//////////////////////////////////////////////////////////////////// +template +void BuilderPrimTempl:: +remove_doubled_verts(int closed) { + SameCoord sc; + Verts::iterator new_end = unique(_verts.begin(), _verts.end(), sc); + _verts.erase(new_end, _verts.end()); + + if (closed) { + // Then, if this is a polygon (which will be closed anyway), + // remove the vertex from the end if it's a repeat of the + // beginning. + while (!_verts.empty() && sc(_verts.back(), _verts.front())) { + _verts.pop_back(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::is_valid +// Access: Public +// Description: Returns true if the primitive is well-defined and has +// the right number of vertices for its primitive type, +// and all of its vertices are valid. +//////////////////////////////////////////////////////////////////// +template +bool BuilderPrimTempl:: +is_valid() const { + int num_verts = get_num_verts(); + for (int i = 0; i < num_verts; i++) { + if (!get_vertex(i).is_valid()) { + return false; + } + } + + switch (get_type()) { + case BPT_poly: + return num_verts >= 3; + + case BPT_point: + return num_verts >= 1; + + case BPT_line: + return num_verts >= 2; + + case BPT_tri: + return num_verts == 3; + + case BPT_tristrip: + case BPT_trifan: + return num_verts >= 3; + + case BPT_quad: + return num_verts == 4; + + default: + return false; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::output +// Access: Public +// Description: Formats the prim for output in some sensible way. +//////////////////////////////////////////////////////////////////// +template +ostream &BuilderPrimTempl:: +output(ostream &out) const { + int num_verts = get_num_verts(); + + out << get_type() << ", " << num_verts << " vertices:\n"; + + for (int i = 0; i < num_verts; i++) { + out << " " << i << ". " << get_vertex(i) << "\n"; + } + + if (has_overall_normal()) { + out << "normal = " << get_normal() << "\n"; + } + if (has_overall_color()) { + out << "color = " << get_color() << "\n"; + } + if (has_overall_pixel_size()) { + out << "pixel_size = " << get_pixel_size() << "\n"; + } + + return out; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::update_overall_attrib +// Access: Protected +// Description: Examines the primitive and all of its vertices and +// components to determine the amount of commonality of +// each attribute, and updates the bits in _overall to +// indicate this. If an overall attribute is found, the +// primitive attribute value is set to that common +// value. +//////////////////////////////////////////////////////////////////// +template +void BuilderPrimTempl:: +update_overall_attrib() { + int num_verts = get_num_verts(); + int num_components = get_num_components(); + + NType common_normal; + CType common_color; + float common_pixel_size; + + bool has_common_normal = false; + bool has_common_color = false; + bool has_common_pixel_size = false; + + bool has_component_normal = false; + bool has_component_color = false; + bool has_component_pixel_size = false; + + bool has_vertex_normal = false; + bool has_vertex_color = false; + bool has_vertex_texcoord = false; + bool has_vertex_pixel_size = false; + + int i; + if (num_verts > 0) { + has_vertex_texcoord = true; + for (i = 0; i < num_verts && has_vertex_texcoord; i++) { + has_vertex_texcoord = get_vertex(i).has_texcoord(); + } + + if (get_vertex(0).has_normal()) { + common_normal = get_vertex(0).get_normal(); + has_common_normal = true; + has_vertex_normal = true; + + for (i = 1; i < num_verts; i++) { + Vertex &vertex = get_vertex(i); + has_vertex_normal = + (has_vertex_normal && vertex.has_normal()); + if (!vertex.has_normal() || + !(vertex.get_normal() == common_normal)) { + has_common_normal = false; + } + } + } + if (get_vertex(0).has_color()) { + common_color = get_vertex(0).get_color(); + has_common_color = true; + has_vertex_color = true; + + for (i = 1; i < num_verts; i++) { + Vertex &vertex = get_vertex(i); + has_vertex_color = + (has_vertex_color && vertex.has_color()); + if (!vertex.has_color() || + !(vertex.get_color() == common_color)) { + has_common_color = false; + } + } + } + if (get_vertex(0).has_pixel_size()) { + common_pixel_size = get_vertex(0).get_pixel_size(); + has_common_pixel_size = true; + has_vertex_pixel_size = true; + + for (i = 1; i < num_verts; i++) { + Vertex &vertex = get_vertex(i); + has_vertex_pixel_size = + (has_vertex_pixel_size && vertex.has_pixel_size()); + if (!vertex.has_pixel_size() || + !(vertex.get_pixel_size() == common_pixel_size)) { + has_common_pixel_size = false; + } + } + } + } + + if (num_components > 0) { + if (!has_vertex_normal && get_component(0).has_normal()) { + common_normal = get_component(0).get_normal(); + has_common_normal = true; + has_component_normal = true; + + for (i = 1; i < num_components; i++) { + DAttrib &component = get_component(i); + has_component_normal = + (has_component_normal && component.has_normal()); + if (!component.has_normal() || + !(component.get_normal() == common_normal)) { + has_common_normal = false; + } + } + } + if (!has_vertex_color && get_component(0).has_color()) { + common_color = get_component(0).get_color(); + has_common_color = true; + has_component_color = true; + + for (i = 1; i < num_components; i++) { + DAttrib &component = get_component(i); + has_component_color = + (has_component_color && component.has_color()); + if (!component.has_color() || + !(component.get_color() == common_color)) { + has_common_color = false; + } + } + } + if (!has_vertex_pixel_size && get_component(0).has_pixel_size()) { + common_pixel_size = get_component(0).get_pixel_size(); + has_common_pixel_size = true; + has_component_pixel_size = true; + + for (i = 1; i < num_components; i++) { + DAttrib &component = get_component(i); + has_component_pixel_size = + (has_component_pixel_size && component.has_pixel_size()); + if (!component.has_pixel_size() || + !(component.get_pixel_size() == common_pixel_size)) { + has_common_pixel_size = false; + } + } + } + } + + _overall = BAF_overall_updated; + + if (has_common_normal) { + // The primitive has one overall normal, or each of the vertices + // has the same normal. + _overall |= BAF_overall_normal; + DAttrib::set_normal(common_normal); + + } else if (has_component_normal) { + // Each component primitive has a different normal. + _overall |= BAF_component_normal; + + } else if (has_vertex_normal) { + // Each vertex has a different normal. + _overall |= BAF_vertex_normal; + + } else if (has_normal()) { + // Well, none of the above, but the prim itself has a normal. + _overall |= BAF_overall_normal; + } + + + if (has_common_color) { + // The primitive has one overall color, or each of the vertices + // has the same color. + _overall |= BAF_overall_color; + DAttrib::set_color(common_color); + + } else if (has_component_color) { + // Each component primitive has a different color. + _overall |= BAF_component_color; + + } else if (has_vertex_color) { + // Each vertex has a different color. + _overall |= BAF_vertex_color; + + } else if (has_color()) { + // None of the above, but the prim itself has a color. + _overall |= BAF_overall_color; + } + + if (has_vertex_texcoord) { + // Each vertex has a texture coordinate. + _overall |= BAF_vertex_texcoord; + } + + if (has_common_pixel_size) { + // The primitive has one overall pixel size, or each of the vertices + // has the same pixel size. + _overall |= BAF_overall_pixel_size; + DAttrib::set_pixel_size(common_pixel_size); + + } else if (has_component_pixel_size) { + // Each component primitive has a different pixel size. + _overall |= BAF_component_pixel_size; + + } else if (has_vertex_pixel_size) { + // Each vertex has a different pixel size. + _overall |= BAF_vertex_pixel_size; + + } else if (has_pixel_size()) { + // The prim itself has a pixel size. + _overall |= BAF_overall_pixel_size; + } +} diff --git a/panda/src/builder/builderPrimTempl.h b/panda/src/builder/builderPrimTempl.h new file mode 100644 index 0000000000..895cc2fe4d --- /dev/null +++ b/panda/src/builder/builderPrimTempl.h @@ -0,0 +1,143 @@ +// Filename: builderPrimTempl.h +// Created by: drose (11Sep97) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BUILDERPRIMTEMPL_H +#define BUILDERPRIMTEMPL_H + +#include + +#include "builderVertex.h" +#include "builderAttrib.h" +#include "builderTypes.h" + +#include + + +///////////////////////////////////////////////////////////////////// +// Class : BuilderPrimTempl +// Description : The main body of BuilderPrim and BuilderPrimI. This +// is a template class on vertex type, which must be +// either BuilderVertex or BuilderVertexI; these classes +// are themselves template classes on vertex type, +// texcoord type, color type, etc. +//////////////////////////////////////////////////////////////////// +template +class BuilderPrimTempl : public VTX::Attrib { +public: + typedef VTX Vertex; + typedef TYPENAME VTX::VType VType; + typedef TYPENAME VTX::NType NType; + typedef TYPENAME VTX::TType TType; + typedef TYPENAME VTX::CType CType; + typedef TYPENAME VTX::Attrib DAttrib; + + INLINE BuilderPrimTempl(); + INLINE BuilderPrimTempl(const BuilderPrimTempl ©); + INLINE BuilderPrimTempl &operator = (const BuilderPrimTempl ©); + + void remove_doubled_verts(int closed); + bool is_valid() const; + + // has_normal() etc. is true if the primitive has a normal, as + // assigned by the user. This is unrelated to the vertices or + // component primitives that may or may not also have normals. + INLINE bool has_normal() const; + INLINE bool has_color() const; + INLINE bool has_pixel_size() const; + + // The following has_* functions are based on information derived + // from examining the vertices and the component primitives that + // make up this primitive. + + // has_overall_normal() etc. is true if the primitive has a single, + // overall normal shared among all vertices and all component + // primitives, or if its normal was assigned directly via + // set_normal(). For a polygon, this is the polygon normal. For a + // tristrip, this means all triangles share the same normal. + INLINE bool has_overall_normal() const; + INLINE bool has_overall_color() const; + INLINE bool has_overall_pixel_size() const; + + // has_vertex_normal() etc. is true if each vertex in the primitive + // has its own normal. It is not true if any vertex does not have a + // normal. + INLINE bool has_vertex_normal() const; + INLINE bool has_vertex_color() const; + INLINE bool has_vertex_texcoord() const; + INLINE bool has_vertex_pixel_size() const; + + // has_component_normal() can only be true for aggregate primitive + // types like tristrips. In that case, it is true if each + // individual component (e.g. each triangle of the tristrip) has its + // own normal. + INLINE bool has_component_normal() const; + INLINE bool has_component_color() const; + INLINE bool has_component_pixel_size() const; + + // In the above, only one of has_overall_normal(), + // has_vertex_normal(), and has_component_normal() can be true for a + // given primitive. For convenience, the following functions return + // true if any of the above is true: + + INLINE bool has_any_normal() const; + INLINE bool has_any_color() const; + INLINE bool has_any_texcoord() const; + INLINE bool has_any_pixel_size() const; + + INLINE BuilderPrimTempl &clear(); + INLINE BuilderPrimTempl &clear_vertices(); + + INLINE BuilderPrimTempl &set_attrib(const DAttrib &attrib); + + INLINE BuilderPrimType get_type() const; + INLINE BuilderPrimTempl &set_type(BuilderPrimType t); + + INLINE NType get_normal() const; + INLINE BuilderPrimTempl &set_normal(const NType &n); + + INLINE CType get_color() const; + INLINE BuilderPrimTempl &set_color(const CType &c); + + INLINE float get_pixel_size() const; + INLINE BuilderPrimTempl &set_pixel_size(float s); + + INLINE BuilderPrimTempl &add_vertex(const Vertex &v); + + INLINE int get_num_verts() const; + INLINE Vertex &get_vertex(int n); + INLINE const Vertex &get_vertex(int n) const; + + INLINE BuilderPrimTempl &add_component(const DAttrib &attrib); + INLINE int get_num_components() const; + INLINE DAttrib &get_component(int n); + INLINE const DAttrib &get_component(int n) const; + + INLINE bool operator < (const BuilderPrimTempl &other) const; + + ostream &output(ostream &out) const; + +protected: + INLINE int sort_value() const; + void update_overall_attrib(); + + typedef vector Verts; + typedef vector Components; + + Verts _verts; + Components _components; + BuilderPrimType _type; + int _overall; +}; + +template +INLINE ostream &operator << (ostream &out, + const BuilderPrimTempl &prim) { + return prim.output(out); +} + + +#include "builderPrimTempl.I" + +#endif diff --git a/panda/src/builder/builderProperties.cxx b/panda/src/builder/builderProperties.cxx new file mode 100644 index 0000000000..ab5e4a1b6c --- /dev/null +++ b/panda/src/builder/builderProperties.cxx @@ -0,0 +1,116 @@ +// Filename: builderProperties.cxx +// Created by: drose (17Sep97) +// +//////////////////////////////////////////////////////////////////// + +#include "builderProperties.h" + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderProperties::Ordering operator +// Access: Public +// Description: Defines an arbitrary ordering among different +// properties, and groups identical sets of properties +// together. This operator is used for the more +// important task of grouping BuilderBuckets together; +// see the comments for the similar function in +// builderBucket.cxx. +//////////////////////////////////////////////////////////////////// +bool BuilderProperties:: +operator < (const BuilderProperties &other) const { + int sv1 = sort_value(); + int sv2 = other.sort_value(); + + if (sv1 != sv2) { + return sv1 < sv2; + } + + if (_coplanar_threshold != other._coplanar_threshold) { + return _coplanar_threshold < other._coplanar_threshold; + } + + if (_max_tfan_angle != other._max_tfan_angle) { + return _max_tfan_angle < other._max_tfan_angle; + } + + if (_min_tfan_tris != other._min_tfan_tris) { + return _min_tfan_tris < other._min_tfan_tris; + } + + if (_show_normals) { + if (_normal_scale != other._normal_scale) { + return _normal_scale < other._normal_scale; + } + if (_normal_color != other._normal_color) { + return _normal_color < other._normal_color; + } + } + + return false; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::output +// Access: Public +// Description: Outputs the properties meaningfully. +//////////////////////////////////////////////////////////////////// +void BuilderProperties:: +output(ostream &out) const { + if (_mesh) { + out << "T-Mesh using Mesher\n"; + + if (_show_tstrips) { + out << "Color individual tristrips\n"; + } else if (_show_qsheets) { + out << "Color individual quadsheets\n"; + } else if (_show_quads) { + out << "Color individual quads\n"; + } + if (_retesselate_coplanar) { + out << "Retesselate coplanar triangles when needed; threshold = " + << _coplanar_threshold << "\n"; + } + } + if (_subdivide_polys) { + out << "Subdivide polygons into tris.\n"; + } + if (_consider_fans) { + out << "Look for possible triangle fans with max per-triangle angle of " + << _max_tfan_angle << " degrees.\n"; + if (_min_tfan_tris==0) { + out << "Do not create tfans"; + } else { + out << "Do not create tfans smaller than " << _min_tfan_tris << " tris"; + } + if (_unroll_fans) { + out << "; retesselate to tstrips.\n"; + } else { + out << ".\n"; + } + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderPrimTempl::sort_value +// Access: Protected +// Description: Returns a number for grouping properties. This is +// used as a helper function to the ordering operator, +// above. It simply collects all the booleans together +// into a single number. +//////////////////////////////////////////////////////////////////// +int BuilderProperties:: +sort_value() const { + return + ((_mesh!=0) << 8) | + ((_show_tstrips!=0) << 7) | + ((_show_qsheets!=0) << 6) | + ((_show_quads!=0) << 5) | + ((_show_normals!=0) << 4) | + ((_retesselate_coplanar!=0) << 3) | + ((_unroll_fans!=0) << 2) | + ((_subdivide_polys!=0) << 1) | + ((_consider_fans!=0) << 0); +} diff --git a/panda/src/builder/builderProperties.h b/panda/src/builder/builderProperties.h new file mode 100644 index 0000000000..0e7d7f6fbc --- /dev/null +++ b/panda/src/builder/builderProperties.h @@ -0,0 +1,115 @@ +// Filename: builderProperties.h +// Created by: drose (17Sep97) +// +//////////////////////////////////////////////////////////////////// +#ifndef BUILDERPROPERTIES_H +#define BUILDERPROPERTIES_H + +#include + +#include "builderTypes.h" + +#ifndef WIN32_VC +#include +#endif + + + +//////////////////////////////////////////////////////////////////// +// Class : BuilderProperties +// Description : A class which defines several parameters used to +// control specific behavior of the builder and mesher. +// BuilderBucket inherits from this class, so each of +// these properties may be set directly on a +// BuilderBucket to control the geometry made with just +// that bucket. There may in this way be several +// different sets of properties in effect at a given +// time. +// +// The initial values for these are set in the private +// constructor to BuilderBucket, at the bottom of +// builderBucket.cxx. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG BuilderProperties { +public: + bool operator < (const BuilderProperties &other) const; + void output(ostream &out) const; + + // If this is true, the mesher will be invoked to break up large + // polygons and build triangles into tristrips wherever possible. + bool _mesh; + + // If this is true, a pair of adjacent coplanar triangles that form + // a quad may be replaced with a pair of triangles forming the same + // quad but with the opposite diagonal, if this will help the + // building of tristrips. + bool _retesselate_coplanar; + + // If this is true, a coplanar fan may be treated as a single large + // polygon, and then subdivided into a single tristrip, instead of + // treating it as a fan. This can sometimes help form longer + // continuous tristrips. + bool _unroll_fans; + + // The following three flags serve to illustrate the mesher's + // effectiveness by coloring geometry. + + // This colors each created triangle strip with a random color. The + // first triangle of each strip is a little darker than the rest of + // the strip. + bool _show_tstrips; + + // This colors each rectangular group of quads--called a quadsheet + // by the mesher--with a random color. The mesher always creates + // linear tristrips across whatever quadsheets it can identify. + bool _show_qsheets; + + // This colors quads blue, and doesn't mesh them further. Since a + // large part of the mesher's algorithm is reassembling adjacent + // triangles into quads, it's sometimes helpful to see where it + // thinks the best quads lie. + bool _show_quads; + + // This shows normals created by creating little colored line + // segments to represent each normal. + bool _show_normals; + double _normal_scale; + BuilderC _normal_color; + + // If this is true, large polygons will be subdivided into + // triangles. Otherwise, they will remain large polygons. + bool _subdivide_polys; + + // This is the flatness tolerance below which two polygons will be + // considered coplanar. Making it larger makes it easier for the + // mesher to reverse triangle diagonals to achieve a good mesh, at + // the expense of precision of the surface. + double _coplanar_threshold; + + // True if fans are to be considered at all. Sometimes making fans + // is more trouble than they're worth, since they tend to get in the + // way of long tristrips. + bool _consider_fans; + + // The maximum average angle of the apex of each triangle involved + // in a fan. If the triangles are more loosely packed than this, + // don't consider putting them into a fan. + double _max_tfan_angle; + + // The minimum number of triangles to be involved in a fan. Setting + // this number lower results in more fans, probably at the expense + // of tristrips. However, setting it to zero means no tfans will be + // built (although fans may still be unrolled into tstrips if + // _unroll_fans is true). + int _min_tfan_tris; + +protected: + int sort_value() const; +}; + +INLINE ostream &operator << (ostream &out, const BuilderProperties &props) { + props.output(out); + return out; +} + +#endif diff --git a/panda/src/builder/builderTypes.cxx b/panda/src/builder/builderTypes.cxx new file mode 100644 index 0000000000..d732bf9d58 --- /dev/null +++ b/panda/src/builder/builderTypes.cxx @@ -0,0 +1,102 @@ +// Filename: builderTypes.cxx +// Created by: drose (11Sep97) +// +//////////////////////////////////////////////////////////////////// + +#include "builderTypes.h" + +#include + +ostream &operator << (ostream &out, BuilderAttribFlags baf) { + const char *space = ""; + if (baf & BAF_coord) { + out << space << "coord"; + space = " "; + } + if (baf & BAF_normal) { + out << space << "normal"; + space = " "; + } + if (baf & BAF_texcoord) { + out << space << "texcoord"; + space = " "; + } + if (baf & BAF_color) { + out << space << "color"; + space = " "; + } + if (baf & BAF_pixel_size) { + out << space << "pixel_size"; + space = " "; + } + if (baf & BAF_overall_updated) { + out << space << "overall_updated"; + space = " "; + } + if (baf & BAF_overall_normal) { + out << space << "overall_normal"; + space = " "; + } + if (baf & BAF_overall_color) { + out << space << "overall_color"; + space = " "; + } + if (baf & BAF_overall_pixel_size) { + out << space << "overall_pixel_size"; + space = " "; + } + if (baf & BAF_vertex_normal) { + out << space << "vertex_normal"; + space = " "; + } + if (baf & BAF_vertex_texcoord) { + out << space << "vertex_texcoord"; + space = " "; + } + if (baf & BAF_vertex_color) { + out << space << "vertex_color"; + space = " "; + } + if (baf & BAF_vertex_pixel_size) { + out << space << "vertex_pixel_size"; + space = " "; + } + if (baf & BAF_component_normal) { + out << space << "component_normal"; + space = " "; + } + if (baf & BAF_component_color) { + out << space << "component_color"; + space = " "; + } + if (baf & BAF_component_pixel_size) { + out << space << "component_pixel_size"; + space = " "; + } + return out; +} + + +ostream &operator << (ostream &out, BuilderPrimType bpt) { + switch (bpt) { + case BPT_poly: + return out << "poly"; + case BPT_point: + return out << "point"; + case BPT_line: + return out << "line"; + case BPT_tri: + return out << "tri"; + case BPT_tristrip: + return out << "tristrip"; + case BPT_trifan: + return out << "trifan"; + case BPT_quad: + return out << "quad"; + case BPT_quadstrip: + return out << "quadstrip"; + } + nassertr(false, out); + return out << "(**invalid**)"; +} + diff --git a/panda/src/builder/builderTypes.h b/panda/src/builder/builderTypes.h new file mode 100644 index 0000000000..da2c53b560 --- /dev/null +++ b/panda/src/builder/builderTypes.h @@ -0,0 +1,221 @@ +// Filename: builderTypes.h +// Created by: drose (11Sep97) +// +//////////////////////////////////////////////////////////////////// +#ifndef BUILDERTYPES_H +#define BUILDERTYPES_H + +#include + +#include +#include + +#ifndef WIN32_VC +#include +#endif + +static const float nearly_zero = 0.0001; + +// The BuilderVec classes are a series of proxies around Vertexf, +// Normalf, TexCoordf, and Colorf. They're useful for building +// collections of these vertex values, and provide handy things like +// (almost) equivalence operators and sorting operators. + +// The BuilderVec's each have a special constructor with a single int. +// These constructors create an instance of the vector with all values +// initialized to zero. This is a cheat to create a uniform way to create +// a zero-valued VType, CType, or TType without knowing whether the type +// is indexed (a ushort) or nonindexed (a BuilderVec). + +class EXPCL_PANDAEGG BuilderTC { +public: + BuilderTC() {} + BuilderTC(int) : _v(0.0, 0.0) {} + BuilderTC(const TexCoordf &v) : _v(v) {} + BuilderTC(const TexCoordd &v) : _v(v[0], v[1]) {} + BuilderTC(const BuilderTC ©) : _v(copy._v) {} + + operator TexCoordf & () { + return _v; + } + + operator const TexCoordf & () const { + return _v; + } + + float operator [] (int n) const { return _v[n]; } + float &operator [] (int n) { return _v[n]; } + + BuilderTC &operator = (const BuilderTC ©) { + _v = copy._v; + return *this; + } + bool operator == (const BuilderTC &other) const { + return _v.almost_equal(other._v, nearly_zero); + } + + // The < operator is simply for ordering vectors in a sorted + // container; it has no useful mathematical meaning. + bool operator < (const BuilderTC &other) const { + return (_v.compare_to(other._v) < 0); + } + TexCoordf _v; +}; + +class EXPCL_PANDAEGG BuilderV { +public: + BuilderV() {} + BuilderV(int) : _v(0.0, 0.0, 0.0) {} + BuilderV(const Vertexf &v) : _v(v) {} + BuilderV(const Vertexd &v) : _v(v[0], v[1], v[2]) {} + BuilderV(const BuilderV ©) : _v(copy._v) {} + + operator Vertexf & () { + return _v; + } + + operator const Vertexf & () const { + return _v; + } + + float operator [] (int n) const { return _v[n]; } + float &operator [] (int n) { return _v[n]; } + + BuilderV &operator = (const BuilderV ©) { + _v = copy._v; + return *this; + } + bool operator == (const BuilderV &other) const { + return _v.almost_equal(other._v, nearly_zero); + } + bool operator < (const BuilderV &other) const { + return (_v.compare_to(other._v) < 0); + } + Vertexf _v; +}; + +class EXPCL_PANDAEGG BuilderN { +public: + BuilderN() {} + BuilderN(int) : _v(0.0, 0.0, 0.0) {} + BuilderN(const Normalf &v) : _v(v) {} + BuilderN(const Normald &v) : _v(v[0], v[1], v[2]) {} + BuilderN(const BuilderN ©) : _v(copy._v) {} + + operator Normalf & () { + return _v; + } + + operator const Normalf & () const { + return _v; + } + + float operator [] (int n) const { return _v[n]; } + float &operator [] (int n) { return _v[n]; } + + BuilderN &operator = (const BuilderN ©) { + _v = copy._v; + return *this; + } + bool operator == (const BuilderN &other) const { + return _v.almost_equal(other._v, nearly_zero); + } + bool operator < (const BuilderN &other) const { + return (_v.compare_to(other._v) < 0); + } + Normalf _v; +}; + +class EXPCL_PANDAEGG BuilderC { +public: + BuilderC() {} + BuilderC(int) : _v(0.0, 0.0, 0.0, 0.0) {} + BuilderC(const Colorf &v) : _v(v) {} + BuilderC(const Colord &v) : _v(v[0], v[1], v[2], v[3]) {} + BuilderC(const BuilderC ©) : _v(copy._v) {} + + operator Colorf & () { + return _v; + } + + operator const Colorf & () const { + return _v; + } + + float operator [] (int n) const { return _v[n]; } + float &operator [] (int n) { return _v[n]; } + + BuilderC &operator = (const BuilderC ©) { + _v = copy._v; + return *this; + } + bool operator != (const BuilderC &other) const { + return !(*this == other); + } + bool operator == (const BuilderC &other) const { + return _v.almost_equal(other._v, nearly_zero); + } + bool operator < (const BuilderC &other) const { + return (_v.compare_to(other._v) < 0); + } + Colorf _v; +}; + +INLINE ostream &operator << (ostream &out, const BuilderTC &v) { + return out << "(" << v[0] << " " << v[1] << ")"; +} + +INLINE ostream &operator << (ostream &out, const BuilderV &v) { + return out << "(" << v[0] << " " << v[1] << " " << v[2] << ")"; +} + +INLINE ostream &operator << (ostream &out, const BuilderN &v) { + return out << "(" << v[0] << " " << v[1] << " " << v[2] << ")"; +} + +INLINE ostream &operator << (ostream &out, const BuilderC &v) { + return out << "(" << v[0] << " " << v[1] << " " << v[2] << " " + << v[3] << ")"; +} + +enum BuilderAttribFlags { + BAF_coord = 0x00001, + BAF_normal = 0x00002, + BAF_texcoord = 0x00004, + BAF_color = 0x00008, + BAF_pixel_size = 0x00010, + + BAF_overall_updated = 0x00100, + BAF_overall_normal = 0x00200, + BAF_overall_color = 0x00400, + BAF_overall_pixel_size = 0x00800, + + BAF_vertex_normal = 0x01000, + BAF_vertex_texcoord = 0x02000, + BAF_vertex_color = 0x04000, + BAF_vertex_pixel_size = 0x08000, + + BAF_component_normal = 0x10000, + BAF_component_color = 0x20000, + BAF_component_pixel_size = 0x04000, +}; + +ostream &operator << (ostream &out, BuilderAttribFlags baf); + +enum BuilderPrimType { + BPT_poly, + BPT_point, + BPT_line, + + // The following types are generated internally by the builder and + // mesher. Normally they will not be seen by the user. + BPT_tri, + BPT_tristrip, + BPT_trifan, + BPT_quad, + BPT_quadstrip, +}; + +ostream &operator << (ostream &out, BuilderPrimType bpt); + +#endif diff --git a/panda/src/builder/builderVertex.I b/panda/src/builder/builderVertex.I new file mode 100644 index 0000000000..155a72ef74 --- /dev/null +++ b/panda/src/builder/builderVertex.I @@ -0,0 +1,265 @@ +// Filename: builderVertex.I +// Created by: drose (18Sep97) +// +//////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertex::set_coord_value +// Access: Public +// Description: Reassigns the vertex coordinate, without knowing +// whether the vertex is indexed or nonindexed. A +// nonindexed vertex will look up the index in the array +// and store the resulting value, while an indexed +// vertex will just store the index number (which +// assumes the array is the same one it's indexing on). +//////////////////////////////////////////////////////////////////// +INLINE void BuilderVertex:: +set_coord_value(const BuilderV *array, ushort index) { + set_coord(array[index]); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertex::set_normal_value +// Access: Public +// Description: Reassigns the vertex normal, without knowing whether +// the vertex is indexed or nonindexed. A nonindexed +// vertex will look up the index in the array and store +// the resulting value, while an indexed vertex will +// just store the index number (which assumes the array +// is the same one it's indexing on). +//////////////////////////////////////////////////////////////////// +INLINE void BuilderVertex:: +set_normal_value(const BuilderN *array, ushort index) { + set_normal(array[index]); +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertex::set_texcoord_value +// Access: Public +// Description: Reassigns the vertex texture coordinate, without +// knowing whether the vertex is indexed or nonindexed. +// A nonindexed vertex will look up the index in the +// array and store the resulting value, while an indexed +// vertex will just store the index number (which +// assumes the array is the same one it's indexing on). +//////////////////////////////////////////////////////////////////// +INLINE void BuilderVertex:: +set_texcoord_value(const BuilderTC *array, ushort index) { + set_texcoord(array[index]); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertex::set_color_value +// Access: Public +// Description: Reassigns the vertex color, without knowing whether +// the vertex is indexed or nonindexed. A nonindexed +// vertex will look up the index in the array and store +// the resulting value, while an indexed vertex will +// just store the index number (which assumes the array +// is the same one it's indexing on). +//////////////////////////////////////////////////////////////////// +INLINE void BuilderVertex:: +set_color_value(const BuilderC *array, ushort index) { + set_color(array[index]); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertex::get_coord_value +// Access: Public +// Description: Returns the actual coordinate value of the vertex, +// whether it is indexed or nonindexed. Normally, the +// value returned by get_coord(), which will be either a +// BuilderV or a ushort, is sufficient, but there are +// occasional times when it is necessary to get the +// actual location in space of the vertex position (for +// instance, to subdivide a concave polygon). +// +// This function returns the actual coordinate value. +// For a nonindexed vertex, its return value is the same +// as get_coord(); for an indexed vertex, it looks up +// the vertex's index in the bucket's coord array, and +// returns that value. +// +// Note that this makes some perhaps unwarranted +// assumptions about indexed geometry; specifically, +// that its value is valid at creation time, and that it +// won't change too drastically during runtime. +//////////////////////////////////////////////////////////////////// +INLINE BuilderV BuilderVertex:: +get_coord_value(const BuilderBucket &) const { + return get_coord(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertex::get_normal_value +// Access: Public +// Description: Returns the actual normal value of the vertex, +// whether it is indexed or nonindexed. See +// get_coord_value(). +//////////////////////////////////////////////////////////////////// +INLINE BuilderN BuilderVertex:: +get_normal_value(const BuilderBucket &) const { + return get_normal(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertex::get_texcoord_value +// Access: Public +// Description: Returns the actual texture coordinate value of the +// vertex, whether it is indexed or nonindexed. See +// get_coord_value(). +//////////////////////////////////////////////////////////////////// +INLINE BuilderTC BuilderVertex:: +get_texcoord_value(const BuilderBucket &) const { + return get_texcoord(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertex::get_color_value +// Access: Public +// Description: Returns the actual color value of the vertex, +// whether it is indexed or nonindexed. See +// get_coord_value(). +//////////////////////////////////////////////////////////////////// +INLINE BuilderC BuilderVertex:: +get_color_value(const BuilderBucket &) const { + return get_color(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertex::set_coord_value +// Access: Public +// Description: Reassigns the vertex coordinate, without knowing +// whether the vertex is indexed or nonindexed. A +// nonindexed vertex will look up the index in the array +// and store the resulting value, while an indexed +// vertex will just store the index number (which +// assumes the array is the same one it's indexing on). +//////////////////////////////////////////////////////////////////// +INLINE void BuilderVertexI:: +set_coord_value(const BuilderV *, ushort index) { + set_coord(index); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertex::set_normal_value +// Access: Public +// Description: Reassigns the vertex normal, without knowing whether +// the vertex is indexed or nonindexed. A nonindexed +// vertex will look up the index in the array and store +// the resulting value, while an indexed vertex will +// just store the index number (which assumes the array +// is the same one it's indexing on). +//////////////////////////////////////////////////////////////////// +INLINE void BuilderVertexI:: +set_normal_value(const BuilderN *, ushort index) { + set_normal(index); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertex::set_texcoord_value +// Access: Public +// Description: Reassigns the vertex texture coordinate, without +// knowing whether the vertex is indexed or nonindexed. +// A nonindexed vertex will look up the index in the +// array and store the resulting value, while an indexed +// vertex will just store the index number (which +// assumes the array is the same one it's indexing on). +//////////////////////////////////////////////////////////////////// +INLINE void BuilderVertexI:: +set_texcoord_value(const BuilderTC *, ushort index) { + set_texcoord(index); +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertex::set_color_value +// Access: Public +// Description: Reassigns the vertex color, without knowing whether +// the vertex is indexed or nonindexed. A nonindexed +// vertex will look up the index in the array and store +// the resulting value, while an indexed vertex will +// just store the index number (which assumes the array +// is the same one it's indexing on). +//////////////////////////////////////////////////////////////////// +INLINE void BuilderVertexI:: +set_color_value(const BuilderC *, ushort index) { + set_color(index); +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertex::get_coord_value +// Access: Public +// Description: Returns the actual coordinate value of the vertex, +// whether it is indexed or nonindexed. Normally, the +// value returned by get_coord(), which will be either a +// BuilderV or a ushort, is sufficient, but there are +// occasional times when it is necessary to get the +// actual location in space of the vertex position (for +// instance, to subdivide a concave polygon). +// +// This function returns the actual coordinate value. +// For a nonindexed vertex, its return value is the same +// as get_coord(); for an indexed vertex, it looks up +// the vertex's index in the bucket's coord array, and +// returns that value. +// +// Note that this makes some perhaps unwarranted +// assumptions about indexed geometry; specifically, +// that its value is valid at creation time, and that it +// won't change too drastically during runtime. +//////////////////////////////////////////////////////////////////// +INLINE BuilderV BuilderVertexI:: +get_coord_value(const BuilderBucket &bucket) const { + nassertr(bucket.get_coords() != (Vertexf *)NULL, BuilderV()); + return bucket.get_coords()[get_coord()]; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertex::get_normal_value +// Access: Public +// Description: Returns the actual normal value of the vertex, +// whether it is indexed or nonindexed. See +// get_coord_value(). +//////////////////////////////////////////////////////////////////// +INLINE BuilderN BuilderVertexI:: +get_normal_value(const BuilderBucket &bucket) const { + nassertr(bucket.get_normals() != (Normalf *)NULL, BuilderN()); + return bucket.get_normals()[get_normal()]; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertex::get_texcoord_value +// Access: Public +// Description: Returns the actual texture coordinate value of the +// vertex, whether it is indexed or nonindexed. See +// get_coord_value(). +//////////////////////////////////////////////////////////////////// +INLINE BuilderTC BuilderVertexI:: +get_texcoord_value(const BuilderBucket &bucket) const { + nassertr(bucket.get_texcoords() != (TexCoordf *)NULL, BuilderTC()); + return bucket.get_texcoords()[get_texcoord()]; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertex::get_color_value +// Access: Public +// Description: Returns the actual color value of the vertex, +// whether it is indexed or nonindexed. See +// get_coord_value(). +//////////////////////////////////////////////////////////////////// +INLINE BuilderC BuilderVertexI:: +get_color_value(const BuilderBucket &bucket) const { + nassertr(bucket.get_colors() != (Colorf *)NULL, BuilderC()); + return bucket.get_colors()[get_color()]; +} diff --git a/panda/src/builder/builderVertex.cxx b/panda/src/builder/builderVertex.cxx new file mode 100644 index 0000000000..502fb5e768 --- /dev/null +++ b/panda/src/builder/builderVertex.cxx @@ -0,0 +1,12 @@ +// Filename: builderVertex.cxx +// Created by: drose (11May00) +// +//////////////////////////////////////////////////////////////////// + +#include "builderAttrib.h" +#include "builderVertex.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/builder/builderVertex.h b/panda/src/builder/builderVertex.h new file mode 100644 index 0000000000..acd4fe2ac8 --- /dev/null +++ b/panda/src/builder/builderVertex.h @@ -0,0 +1,119 @@ +// Filename: builderVertex.h +// Created by: drose (18Sep97) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BUILDERVERTEX_H +#define BUILDERVERTEX_H + +/////////////////////////////////////////////////////////////////// +// +// BuilderVertex, BuilderVertexI +// +// The basic class for passing vertex values to the builder. See the +// comments at the beginning of builder.h and builderPrim.h. +// +// Like BuilderPrim, the BuilderVertex and BuilderVertexI classes are +// actually two different instantiations of the same template class, +// BuilderVertexTempl. The difference is in the types of the +// attribute values for the four kinds of attributes: vertices +// (coords), normals, texture coordinates, and colors. BuilderVertex +// specifies Coordf, Normalf, TexCoordf, and Colorf for each of these +// (actually, it's BuilderV, BuilderN, BuilderTC, and BuilderC, which +// are simply wrappers around the above types), while BuilderVertexI +// specifies ushort for all of them. +// +// It is this templating that drives the whole indexed/nonindexed +// support in this package and in the mesher. The two kinds of +// BuilderVertex are designed to present largely the same interface, +// regardless of whether its component values are actual vector +// values, or simply index numbers. Lots of things, therefore, can +// template on the BuilderPrim type (which in turn termplates on the +// BuilderVertex type) and thus easily support both indexed and +// nonindexed geometry. +// +/////////////////////////////////////////////////////////////////// + +#include + +#include "builderAttrib.h" +#include "builderVertexTempl.h" +#include "builderBucket.h" + +// We need to define this temporary macro so we can pass a parameter +// containing a comma through the macro. +#define BUILDERVERTEXTEMPL_BUILDERV BuilderVertexTempl +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, BUILDERVERTEXTEMPL_BUILDERV); +#define BUILDERVERTEXTEMPL_USHORT BuilderVertexTempl +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, BUILDERVERTEXTEMPL_USHORT); + +///////////////////////////////////////////////////////////////////// +// Class : BuilderVertex +// Description : The basic class for passing nonindexed vertices to +// the builder. See the comments at the the head of +// this file, and in builder.h. +// +// Look in builderVertexTempl.h and builderAttribTempl.h +// for most of the interface to BuilderVertex. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG BuilderVertex + : public BuilderVertexTempl { +public: + typedef BuilderAttrib Attrib; + + BuilderVertex() {} + BuilderVertex(const BuilderV &c) : + BuilderVertexTempl(c) {} + + INLINE void set_coord_value(const BuilderV *array, ushort index); + INLINE void set_normal_value(const BuilderN *array, ushort index); + INLINE void set_texcoord_value(const BuilderTC *array, ushort index); + INLINE void set_color_value(const BuilderC *array, ushort index); + + INLINE BuilderV get_coord_value(const BuilderBucket &bucket) const; + INLINE BuilderN get_normal_value(const BuilderBucket &bucket) const; + INLINE BuilderTC get_texcoord_value(const BuilderBucket &bucket) const; + INLINE BuilderC get_color_value(const BuilderBucket &bucket) const; + +}; + + +///////////////////////////////////////////////////////////////////// +// Class : BuilderVertexI +// Description : The basic class for passing indexed vertices to the +// builder. See the comments at the the head of this +// file, and in builder.h. +// +// Look in builderVertexTempl.h and builderAttribTempl.h +// for most of the interface to BuilderVertex. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG BuilderVertexI + : public BuilderVertexTempl { +public: + typedef BuilderAttribI Attrib; + + BuilderVertexI() {} + BuilderVertexI(ushort c) : + BuilderVertexTempl(c) {} + + INLINE void set_coord_value(const BuilderV *array, ushort index); + INLINE void set_normal_value(const BuilderN *array, ushort index); + INLINE void set_texcoord_value(const BuilderTC *array, ushort index); + INLINE void set_color_value(const BuilderC *array, ushort index); + + INLINE BuilderV get_coord_value(const BuilderBucket &bucket) const; + INLINE BuilderN get_normal_value(const BuilderBucket &bucket) const; + INLINE BuilderTC get_texcoord_value(const BuilderBucket &bucket) const; + INLINE BuilderC get_color_value(const BuilderBucket &bucket) const; +}; + +#include "builderVertex.I" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif + + diff --git a/panda/src/builder/builderVertexTempl.I b/panda/src/builder/builderVertexTempl.I new file mode 100644 index 0000000000..2ecea213c0 --- /dev/null +++ b/panda/src/builder/builderVertexTempl.I @@ -0,0 +1,291 @@ +// Filename: builderVertexTempl.I +// Created by: drose (11Sep97) +// +//////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderVertexTempl:: +BuilderVertexTempl() { +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::Constructor (with VType) +// Access: Public +// Description: Initializes the vertex coordinate with an initial +// value. A handy constructor. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderVertexTempl:: +BuilderVertexTempl(const VType &c) { + set_coord(c); +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderVertexTempl:: +BuilderVertexTempl(const BuilderVertexTempl ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderVertexTempl &BuilderVertexTempl:: +operator = (const BuilderVertexTempl ©) { + BuilderAttribTempl::operator = (copy); + _coord = copy._coord; + _texcoord = copy._texcoord; + _pixel_size = copy._pixel_size; + + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::is_valid +// Access: Public +// Description: Returns true if the vertex is valid, i.e. if it has a +// vertex coordinate. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderVertexTempl:: +is_valid() const { + return has_coord(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::clear +// Access: Public +// Description: Resets the vertex to its initial, empty state. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderVertexTempl &BuilderVertexTempl:: +clear() { + BuilderAttribTempl::clear(); + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::has_coord +// Access: Public +// Description: Returns true if the vertex has a vertex coordinate. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderVertexTempl:: +has_coord() const { + return _flags & BAF_coord; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::get_coord +// Access: Public +// Description: Returns the vertex's coordinate. It is an error to +// call this without first verifying that has_coord() is +// true. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderVertexTempl::VType BuilderVertexTempl:: +get_coord() const { + nassertr(has_coord(), _coord); + return _coord; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::set_coord +// Access: Public +// Description: Resets the vertex's coordinate. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderVertexTempl &BuilderVertexTempl:: +set_coord(const VType &c) { + _flags |= BAF_coord; + _coord = c; + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::set_normal +// Access: Public +// Description: Resets the vertex's normal. This is overridden from +// BuilderAttrib just so we can typecast the return +// value to BuilderVertex. The other functions, +// has_normal() and get_normal(), are inherited +// untouched from BuilderAttrib. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderVertexTempl &BuilderVertexTempl:: +set_normal(const NType &n) { + BuilderAttribTempl::set_normal(n); + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::has_texcoord +// Access: Public +// Description: Returns true if the vertex has a texture coordinate. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BuilderVertexTempl:: +has_texcoord() const { + return (_flags & BAF_texcoord) != 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::set_texcoord +// Access: Public +// Description: Resets the vertex's texture coordinate. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderVertexTempl &BuilderVertexTempl:: +set_texcoord(const TType &t) { + _flags |= BAF_texcoord; + _texcoord = t; + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::get_texcoord +// Access: Public +// Description: Returns the vertex's texture coordinate. It is an +// error to call this without first verifying that +// has_texcoord() is true. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderVertexTempl::TType BuilderVertexTempl:: +get_texcoord() const { + nassertr(has_texcoord(), _texcoord); + return _texcoord; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::set_color +// Access: Public +// Description: Resets the vertex's color. This is overridden from +// BuilderAttrib just so we can typecast the return +// value to BuilderVertex. The other functions, +// has_color() and get_color(), are inherited +// untouched from BuilderAttrib. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderVertexTempl &BuilderVertexTempl:: +set_color(const CType &c) { + BuilderAttribTempl::set_color(c); + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::set_pixel_size +// Access: Public +// Description: Resets the vertex's pixel_size. This is overridden +// from BuilderAttrib just so we can typecast the return +// value to BuilderVertex. The other functions, +// has_pixel_size() and get_pixel_size(), are inherited +// untouched from BuilderAttrib. +//////////////////////////////////////////////////////////////////// +template +INLINE BuilderVertexTempl &BuilderVertexTempl:: +set_pixel_size(float s) { + BuilderAttribTempl::set_pixel_size(s); + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::operator == +// Access: Public +// Description: Assigns an ordering to the vertices. This is used by +// the Mesher to group identical vertices. This assumes +// that all vertices in the locus of consideration will +// share the same state: with or without normals, +// texcoords, etc. +//////////////////////////////////////////////////////////////////// +template +bool BuilderVertexTempl:: +operator == (const BuilderVertexTempl &other) const { + if (has_coord() && !(_coord == other._coord)) + return false; + + if (has_texcoord() && !(_texcoord == other._texcoord)) + return false; + + return BuilderAttribTempl::operator == (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::operator < +// Access: Public +// Description: Assigns an ordering to the vertices. This is used by +// the Mesher to group identical vertices. This assumes +// that all vertices to be meshed together must share +// the same state: with or without normals, texcoords, +// etc. +//////////////////////////////////////////////////////////////////// +template +bool BuilderVertexTempl:: +operator < (const BuilderVertexTempl &other) const { + if (has_coord() && !(_coord == other._coord)) + return _coord < other._coord; + + if (has_texcoord() && !(_texcoord == other._texcoord)) + return _texcoord < other._texcoord; + + return BuilderAttribTempl::operator < (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: BuilderVertexTempl::output +// Access: Public +// Description: Formats the vertex for output in some sensible way. +//////////////////////////////////////////////////////////////////// +template +ostream &BuilderVertexTempl:: +output(ostream &out) const { + if (this!=NULL) { + if (has_coord()) { + out << get_coord(); + } + + /* + if (has_normal()) { + out << " normal " << get_normal(); + } + + if (has_texcoord()) { + out << " texcoord " << get_texcoord(); + } + + if (has_color()) { + out << " color " << get_color(); + } + + if (has_pixel_size()) { + out << " pixel_size " << get_pixel_size(); + } + */ + } + return out; +} diff --git a/panda/src/builder/builderVertexTempl.h b/panda/src/builder/builderVertexTempl.h new file mode 100644 index 0000000000..36d3676b70 --- /dev/null +++ b/panda/src/builder/builderVertexTempl.h @@ -0,0 +1,73 @@ +// Filename: builderVertexTempl.h +// Created by: drose (09Sep97) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BUILDERVERTEXTEMPL_H +#define BUILDERVERTEXTEMPL_H + +#include + +#include "builderTypes.h" +#include "builderAttribTempl.h" + +#include + + +///////////////////////////////////////////////////////////////////// +// Class : BuilderVertexTempl +// Description : The main body of BuilderVertex and BuilderVertexI. +// This is a template class on each of the four +// attribute types: vertex coordinates, normal, texture +// coordinates, and color. See builderVertex.h. +//////////////////////////////////////////////////////////////////// +template +class BuilderVertexTempl : public BuilderAttribTempl { +public: + typedef VT VType; + typedef NT NType; + typedef TT TType; + typedef CT CType; + + INLINE BuilderVertexTempl(); + INLINE BuilderVertexTempl(const VType &c); + INLINE BuilderVertexTempl(const BuilderVertexTempl ©); + INLINE BuilderVertexTempl &operator = (const BuilderVertexTempl ©); + + INLINE bool is_valid() const; + INLINE BuilderVertexTempl &clear(); + + INLINE bool has_coord() const; + INLINE VType get_coord() const; + INLINE BuilderVertexTempl &set_coord(const VType &c); + + INLINE BuilderVertexTempl &set_normal(const NType &c); + + INLINE bool has_texcoord() const; + INLINE TType get_texcoord() const; + INLINE BuilderVertexTempl &set_texcoord(const TType &t); + + INLINE BuilderVertexTempl &set_color(const CType &c); + + INLINE BuilderVertexTempl &set_pixel_size(float s); + + bool operator == (const BuilderVertexTempl &other) const; + bool operator < (const BuilderVertexTempl &other) const; + + ostream &output(ostream &out) const; + +protected: + VType _coord; + TType _texcoord; +}; + +template +INLINE ostream &operator << (ostream &out, + const BuilderVertexTempl &vertex) { + return vertex.output(out); +} + + +#include "builderVertexTempl.I" + +#endif diff --git a/panda/src/builder/config_builder.cxx b/panda/src/builder/config_builder.cxx new file mode 100644 index 0000000000..8f05f58a4a --- /dev/null +++ b/panda/src/builder/config_builder.cxx @@ -0,0 +1,14 @@ +// Filename: config_builder.cxx +// Created by: drose (29Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_builder.h" + +#include + +Configure(config_builder); +NotifyCategoryDef(builder, ""); + +ConfigureFn(config_builder) { +} diff --git a/panda/src/builder/config_builder.h b/panda/src/builder/config_builder.h new file mode 100644 index 0000000000..492da3a5fb --- /dev/null +++ b/panda/src/builder/config_builder.h @@ -0,0 +1,14 @@ +// Filename: config_builder.h +// Created by: drose (29Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_BUILDER_H +#define CONFIG_BUILDER_H + +#include +#include + +NotifyCategoryDecl(builder, EXPCL_PANDAEGG, EXPTP_PANDAEGG); + +#endif diff --git a/panda/src/builder/mesher.cxx b/panda/src/builder/mesher.cxx new file mode 100644 index 0000000000..dff48b2edc --- /dev/null +++ b/panda/src/builder/mesher.cxx @@ -0,0 +1,11 @@ +// Filename: mesher.cxx +// Created by: drose (11May00) +// +//////////////////////////////////////////////////////////////////// + +#include "mesher.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/builder/mesher.h b/panda/src/builder/mesher.h new file mode 100644 index 0000000000..e87c9b33ad --- /dev/null +++ b/panda/src/builder/mesher.h @@ -0,0 +1,42 @@ +// Filename: mesher.h +// Created by: drose (17Sep97) +// +//////////////////////////////////////////////////////////////////// +#ifndef MESHER_H +#define MESHER_H + +#include + +#include "mesherFanMaker.h" +#include "mesherEdge.h" +#include "mesherStrip.h" +#include "mesherTempl.h" +#include "builderPrim.h" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, MesherFanMaker); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, MesherEdge); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, MesherStrip); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, MesherTempl); + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, MesherFanMaker); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, MesherEdge); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, MesherStrip); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, MesherTempl); + +class EXPCL_PANDAEGG Mesher : public MesherTempl { +public: + Mesher(BuilderBucket *bucket) : MesherTempl(bucket) {} +}; + +class EXPCL_PANDAEGG MesherI : public MesherTempl { +public: + MesherI(BuilderBucket *bucket) : MesherTempl(bucket) {} +}; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif + diff --git a/panda/src/builder/mesherEdge.I b/panda/src/builder/mesherEdge.I new file mode 100644 index 0000000000..2e1cc41490 --- /dev/null +++ b/panda/src/builder/mesherEdge.I @@ -0,0 +1,143 @@ +// Filename: mesherEdge.I +// Created by: drose (15Sep97) +// +//////////////////////////////////////////////////////////////////// + + + +template +INLINE MesherEdge:: +MesherEdge(const Vertex *a, const Vertex *b) : _a(a), _b(b) { + _opposite = NULL; +} + +template +INLINE MesherEdge:: +MesherEdge(const MesherEdge ©) : + _a(copy._a), + _b(copy._b), + _strips(copy._strips), + _opposite(copy._opposite) +{ +} + +template +INLINE bool MesherEdge:: +contains_vertex(const Vertex *v) const { + return (_a==v || _b==v); +} + + +template +INLINE bool MesherEdge:: +matches(const MesherEdge &other) const { + return (_a == other._a && _b == other._b) || + (_b == other._a && _a == other._b); +} + +template +INLINE MesherEdge *MesherEdge:: +common_ptr() { + return min(this, _opposite); +} + +template +INLINE bool MesherEdge:: +operator == (const MesherEdge &other) const { + return _a == other._a && _b == other._b; +} + +template +INLINE bool MesherEdge:: +operator < (const MesherEdge &other) const { + return _a < other._a || (_a == other._a && _b < other._b); +} + +template +INLINE float MesherEdge:: +compute_length(const BuilderBucket &bucket) const { + LVector3f v = ((const Vertexf &)_a->get_coord_value(bucket) - + (const Vertexf &)_b->get_coord_value(bucket)); + return length(v); +} + +template +INLINE Vertexf MesherEdge:: +compute_box(const BuilderBucket &bucket) const { + LVector3f v = ((const Vertexf &)_a->get_coord_value(bucket) - + (const Vertexf &)_b->get_coord_value(bucket)); + return Vertexf(fabs(v[0]), fabs(v[1]), fabs(v[2])); +} + + +//////////////////////////////////////////////////////////////////// +// Function: MesherEdge::remove +// Access: Public +// Description: Removes an edge from a particular strip. +//////////////////////////////////////////////////////////////////// +template +void MesherEdge:: +remove(Strip *strip) { + strip->_edges.remove(this); + strip->_edges.remove(_opposite); + + _strips.remove(strip); + _opposite->_strips.remove(strip); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: MesherEdge::change_strip +// Access: Public +// Description: Reparents the edge from strip "from" to strip "to". +//////////////////////////////////////////////////////////////////// +template +void MesherEdge:: +change_strip(Strip *from, Strip *to) { + Strips::iterator si; + + for (si = _strips.begin(); si != _strips.end(); ++si) { + if (*si == from) { + *si = to; + } + } + + for (si = _opposite->_strips.begin(); + si != _opposite->_strips.end(); + ++si) { + if (*si == from) { + *si = to; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherEdge::output +// Access: Public +// Description: Formats the edge for output in some sensible way. +//////////////////////////////////////////////////////////////////// +template +ostream &MesherEdge:: +output(ostream &out) const { + out << "Edge [" << *_a << " to " << *_b << "], " + << _strips.size() << " strips:"; + + Strips::const_iterator si; + for (si = _strips.begin(); si != _strips.end(); ++si) { + out << " " << (*si)->_index; + } + + if (_opposite!=NULL) { + out << " opposite " + << _opposite->_strips.size() << " strips:"; + + for (si = _opposite->_strips.begin(); + si != _opposite->_strips.end(); + ++si) { + out << " " << (*si)->_index; + } + } + + return out; +} diff --git a/panda/src/builder/mesherEdge.h b/panda/src/builder/mesherEdge.h new file mode 100644 index 0000000000..b444e0016e --- /dev/null +++ b/panda/src/builder/mesherEdge.h @@ -0,0 +1,62 @@ +// Filename: mesherEdge.h +// Created by: drose (15Sep97) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MESHEREDGE_H +#define MESHEREDGE_H + +#include + +#include "builderBucket.h" + +#include +#include + + +template +class MesherStrip; + +template +class MesherEdge { +public: + typedef PrimType Prim; + typedef TYPENAME PrimType::Vertex Vertex; + typedef MesherStrip Strip; + + INLINE MesherEdge(const Vertex *a, const Vertex *b); + INLINE MesherEdge(const MesherEdge ©); + + void remove(Strip *strip); + void change_strip(Strip *from, Strip *to); + + INLINE bool contains_vertex(const Vertex *v) const; + + INLINE bool matches(const MesherEdge &other) const; + + INLINE MesherEdge *common_ptr(); + + INLINE bool operator == (const MesherEdge &other) const; + INLINE bool operator < (const MesherEdge &other) const; + + INLINE float compute_length(const BuilderBucket &bucket) const; + INLINE Vertexf compute_box(const BuilderBucket &bucket) const; + + ostream &output(ostream &out) const; + + const Vertex *_a, *_b; + + typedef list Strips; + Strips _strips; + MesherEdge *_opposite; +}; + +template +INLINE ostream &operator << (ostream &out, + const MesherEdge &edge) { + return edge.output(out); +} + +#include "mesherEdge.I" + +#endif diff --git a/panda/src/builder/mesherFanMaker.I b/panda/src/builder/mesherFanMaker.I new file mode 100644 index 0000000000..4efe72feda --- /dev/null +++ b/panda/src/builder/mesherFanMaker.I @@ -0,0 +1,312 @@ +// Filename: mesherFanMaker.I +// Created by: drose (21Sep97) +// +//////////////////////////////////////////////////////////////////// + +#include "builderFuncs.h" + +#include +#include + +#include + +template +INLINE bool MesherFanMaker:: +operator < (const MesherFanMaker &other) const { + return _edges.front() < other._edges.front(); +} + + +template +INLINE bool MesherFanMaker:: +operator == (const MesherFanMaker &other) const { + return _edges.front() == other._edges.front(); +} + +template +INLINE bool MesherFanMaker:: +is_empty() const { + return (_edges.empty()); +} + +template +INLINE bool MesherFanMaker:: +is_valid() const { + return (_edges.size() > 2); +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherFanMaker::is_coplanar_with +// Access: Public +// Description: Returns true if the strip and the other strip are +// coplanar. +//////////////////////////////////////////////////////////////////// +template +INLINE bool MesherFanMaker:: +is_coplanar_with(const MesherFanMaker &other) const { + return _planar && other._planar && + _strips.front()->is_coplanar_with(*other._strips.front(), + _bucket->_coplanar_threshold); +} + +template +MesherFanMaker:: +MesherFanMaker(const Vertex *vertex, Strip *tri, Mesher *mesher) { + _vertex = vertex; + _edges.push_back(tri->find_opposite_edge(vertex)); + _strips.push_back(tri); + _planar = tri->_planar; + _mesher = mesher; + _bucket = _mesher->_bucket; +} + +template +bool MesherFanMaker:: +join(MesherFanMaker &other) { + nassertr(_vertex == other._vertex, false); + nassertr(_mesher == other._mesher, false); + nassertr(_bucket == other._bucket, false); + + if (_edges.back()->_b == other._edges.front()->_a) { + _planar = is_coplanar_with(other); + _edges.splice(_edges.end(), other._edges); + _strips.splice(_strips.end(), other._strips); + return true; + } else if (_edges.front()->_a == other._edges.back()->_b) { + _planar = is_coplanar_with(other); + _edges.splice(_edges.begin(), other._edges); + _strips.splice(_strips.begin(), other._strips); + return true; + } else { + return false; + } +} + + +template +float MesherFanMaker:: +compute_angle() const { + // We sum up the angles of each triangle. This is more correct than + // taking the net angle from the first edge to the last (since we + // may not be in a plane). + nassertr(is_valid(), 0.0); + + double angle = 0.0; + Vertexf v0 = _vertex->get_coord_value(*_bucket); + + Edges::const_iterator ei; + for (ei = _edges.begin(); ei != _edges.end(); ++ei) { + Normalf v1 = (Vertexf &)(*ei)->_a->get_coord_value(*_bucket) - v0; + Normalf v2 = (Vertexf &)(*ei)->_b->get_coord_value(*_bucket) - v0; + + v1 = normalize(v1); + v2 = normalize(v2); + angle += acos(dot(v1, v2)); + } + + return angle * 180.0 / MathNumbers::pi; +} + +template +int MesherFanMaker:: +build() { + nassertr(_edges.size() == _strips.size(), 0); + + int num_tris = _edges.size(); + float net_angle = compute_angle(); + float avg_angle = net_angle / num_tris; + + if (avg_angle > _bucket->_max_tfan_angle) { + // The triangles are too loose to justify making a fan; it'll + // probably make a better quadsheet. + return 0; + } + + if (_bucket->_min_tfan_tris==0 || num_tris < _bucket->_min_tfan_tris) { + // Oops, not enough triangles to justify a fan. + if (!_bucket->_unroll_fans) { + return 0; + } + + // However, we could (maybe) make it a few tristrips! + + // Each section of the fan which is made up of coplanar tris with + // identical properties may be retesselated into a tristrip. What + // a sneaky trick! To do this, we must first identify each such + // qualifying section. + + // We define a seam as the edge between any two tris which are + // noncoplanar or which do not share identical properties. Then + // we can send each piece between the seams to unroll(). + + Strips::iterator si, last_si; + Edges::iterator ei, last_ei; + + // First, rotate the fan so it begins at a seam. We do this so we + // won't be left out with part of one piece at the beginning and + // also at the end. + si = _strips.begin(); + last_si = si; + ei = _edges.begin(); + last_ei = ei; + int found_seam = false; + + for (++si, ++ei; si != _strips.end() && !found_seam; ++si, ++ei) { + nassertr(ei != _edges.end(), 0); + if ( !((*si)->_prims.front() == (*last_si)->_prims.front()) || + !(*si)->is_coplanar_with(*(*last_si), _bucket->_coplanar_threshold)) { + // Here's a seam. Break the fan here. + found_seam = true; + _edges.splice(_edges.begin(), _edges, ei, _edges.end()); + _strips.splice(_strips.begin(), _strips, si, _strips.end()); + } + } + + // Now break the fan up along its seams and unroll each piece + // separately. + si = _strips.begin(); + last_si = si; + ei = _edges.begin(); + last_ei = ei; + + int count = 0; + for (++si, ++ei; si != _strips.end(); ++si, ++ei) { + nassertr(ei != _edges.end(), 0); + if ( !((*si)->_prims.front() == (*last_si)->_prims.front()) || + !(*si)->is_coplanar_with(*(*last_si), _bucket->_coplanar_threshold)) { + // Here's the end of a run of matching pieces. + count += unroll(last_si, si, last_ei, ei); + last_si = si; + last_ei = ei; + } + } + count += unroll(last_si, si, last_ei, ei); + + return count; + + } else { + Strip new_fan; + new_fan._type = BPT_trifan; + new_fan._verts.push_back(_vertex); + + new_fan._verts.push_back(_edges.front()->_a); + Edges::iterator ei; + for (ei = _edges.begin(); ei != _edges.end(); ++ei) { + new_fan._verts.push_back((*ei)->_b); + } + + Strips::iterator si; + for (si = _strips.begin(); si != _strips.end(); ++si) { + new_fan._prims.splice(new_fan._prims.end(), (*si)->_prims); + (*si)->remove_all_edges(); + (*si)->_verts.clear(); + (*si)->_status = MS_dead; + } + + // If we'd built our list of edges and strips right, this sum should + // come out so that there are two more vertices than triangles in + // the new fan. + nassertr(new_fan._verts.size() == new_fan._prims.size() + 2, 0); + + // Now we've built a fan, and it won't be able to mate with + // anything else, so add it to the done list. + _mesher->_done.push_back(new_fan); + } + + return 1; +} + + + +template +int MesherFanMaker:: +unroll(Strips::iterator strip_begin, Strips::iterator strip_end, + Edges::iterator edge_begin, Edges::iterator edge_end) { + Edges::iterator ei; + Strips::iterator si; + + int num_tris = 0; + for (ei = edge_begin; ei != edge_end; ++ei) { + num_tris++; + } + + if (num_tris < 3) { + // Don't even bother. + return 0; + } + + Prim poly; + + // Now we build an n-sided polygon. We'll decompose it into tris + // in a second. + poly.set_type(BPT_poly); + poly.set_attrib((*strip_begin)->_prims.front()); + + ei = edge_end; + --ei; + if ( !((*ei)->_b == (*edge_begin)->_a)) { + // If the fan is less than a full circle, we need to keep the + // hub vertex and initial vertex in the poly. Otherwise, we'll + // discard them. + poly.add_vertex(*_vertex); + poly.add_vertex(*(*edge_begin)->_a); + } + + for (ei = edge_begin; ei != edge_end; ++ei) { + poly.add_vertex(*(*ei)->_b); + } + + int result = true; + + if (_bucket->_show_quads) { + // If we're showing quads, also show retesselated triangles. + _mesher->add_prim(poly, MO_fanpoly); + + } else { + // Now decompose the new polygon into triangles. + vector tris; + result = expand(poly, *_bucket, back_inserter(tris)); + + if (result) { + // Now add each triangle back into the mesher. + vector::iterator ti; + + for (ti = tris.begin(); ti != tris.end(); ++ti) { + _mesher->add_prim(*ti); + } + } + } + + if (result) { + // Now that we've created a new poly, kill off all the old ones. + for (si = strip_begin; si != strip_end; ++si) { + (*si)->remove_all_edges(); + (*si)->_verts.clear(); + (*si)->_prims.clear(); + (*si)->_status = MS_dead; + } + return 1; + } else { + return 0; + } + +} + +template +ostream &MesherFanMaker:: +output(ostream &out) const { + out << *_vertex << ":["; + if (!_edges.empty()) { + Edges::const_iterator ei; + for (ei = _edges.begin(); ei != _edges.end(); ++ei) { + out << " " << *(*ei)->_a; + } + out << " " << *_edges.back()->_b; + } + out << " ]"; + + if (_planar) { + out << " (planar)"; + } + return out; +} diff --git a/panda/src/builder/mesherFanMaker.h b/panda/src/builder/mesherFanMaker.h new file mode 100644 index 0000000000..6da683fd2e --- /dev/null +++ b/panda/src/builder/mesherFanMaker.h @@ -0,0 +1,75 @@ +// Filename: mesherFanMaker.h +// Created by: drose (21Sep97) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MESHERFANMAKER_H +#define MESHERFANMAKER_H + +#ifdef SUPPORT_FANS + +#include + +#include "builderAttrib.h" +#include "builderVertex.h" +#include "builderBucket.h" +#include "mesherEdge.h" +#include "mesherStrip.h" + +#include + + +template +class MesherTempl; + +template +class MesherFanMaker { +public: + typedef PrimType Prim; + typedef TYPENAME PrimType::Vertex Vertex; + typedef TYPENAME PrimType::DAttrib FAttrib; + typedef MesherEdge Edge; + typedef MesherStrip Strip; + typedef MesherTempl Mesher; + + typedef list Edges; + typedef list Strips; + + MesherFanMaker() {} + MesherFanMaker(const Vertex *vertex, Strip *tri, Mesher *mesher); + + INLINE bool operator < (const MesherFanMaker &other) const; + INLINE bool operator == (const MesherFanMaker &other) const; + + INLINE bool is_empty() const; + INLINE bool is_valid() const; + INLINE bool is_coplanar_with(const MesherFanMaker &other) const; + + bool join(MesherFanMaker &other); + float compute_angle() const; + + int build(); + int unroll(Strips::iterator strip_begin, Strips::iterator strip_end, + Edges::iterator edge_begin, Edges::iterator edge_end); + + ostream &output(ostream &out) const; + + + const Vertex *_vertex; + Edges _edges; + Strips _strips; + int _planar; + BuilderBucket *_bucket; + Mesher *_mesher; +}; + +template +ostream &operator << (ostream &out, const MesherFanMaker &fe) { + return fe.output(out); +} + +#include "mesherFanMaker.I" + +#endif // SUPPORT_FANS + +#endif diff --git a/panda/src/builder/mesherStrip.I b/panda/src/builder/mesherStrip.I new file mode 100644 index 0000000000..c32c66f083 --- /dev/null +++ b/panda/src/builder/mesherStrip.I @@ -0,0 +1,1625 @@ +// Filename: mesherStrip.I +// Created by: drose (16Sep97) +// +//////////////////////////////////////////////////////////////////// + +#include "config_builder.h" + +template +INLINE MesherStrip:: +MesherStrip(const MesherStrip ©) : + _edges(copy._edges), + _prims(copy._prims), + _verts(copy._verts), + _type(copy._type), + _index(copy._index), + _status(copy._status), + _planar(copy._planar), + _plane_normal(copy._plane_normal), + _plane_offset(copy._plane_offset), + _row_id(copy._row_id) +{ +} + + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::is_coplanar_with +// Access: Public +// Description: Returns true if the strip and the other strip are +// coplanar. +//////////////////////////////////////////////////////////////////// +template +INLINE bool MesherStrip:: +is_coplanar_with(const MesherStrip &other, float threshold) const { + return (coplanarity(other) <= threshold); +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::coplanarity +// Access: Public +// Description: Returns the degree to which the two strips are +// coplanar. 0.0 is exactly coplanar; numbers somewhat +// larger than zero indicate less coplanar. 1.0 is +// at right angles; 2.0 is exactly backfacing. If +// either strip is not itself planar, 3.0 is returned. +//////////////////////////////////////////////////////////////////// +template +INLINE float MesherStrip:: +coplanarity(const MesherStrip &other) const { + if (_planar && other._planar) { + return 1.0 - dot(_plane_normal, other._plane_normal); + } else { + return 3.0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::type_category +// Access: Public +// Description: Returns an integer which gives a heuristic about the +// similarity of different strip types. In general, +// closer numbers are more similar. +//////////////////////////////////////////////////////////////////// +template +INLINE int MesherStrip:: +type_category() const { + switch (_type) { + case BPT_tri: + return 1; + + case BPT_tristrip: + return 2; + + case BPT_quad: + case BPT_quadstrip: + return 5; + + default: + return 10; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::rotate_forward +// Access: Public +// Description: Rotates a triangle or quad by bringing its second +// vertex to the front. +//////////////////////////////////////////////////////////////////// +template +INLINE void MesherStrip:: +rotate_forward() { + _verts.push_back(_verts.front()); + _verts.pop_front(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::rotate_back +// Access: Public +// Description: Rotates a triangle or quad by bringing its second +// vertex to the front. +//////////////////////////////////////////////////////////////////// +template +INLINE void MesherStrip:: +rotate_back() { + _verts.push_front(_verts.back()); + _verts.pop_back(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::get_head_edge +// Access: Public +// Description: Returns an Edge which represents the leading edge in +// the quadstrip or tristrip. This Edge will not have +// pointer equality with any shared Edge. +//////////////////////////////////////////////////////////////////// +template +INLINE MesherStrip::Edge MesherStrip:: +get_head_edge() const { + Verts::const_iterator vi = _verts.begin(); + return Edge(_verts.front(), *++vi); +} + + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::get_tail_edge +// Access: Public +// Description: Returns an Edge which represents the trailing edge in +// the quadstrip or tristrip. This Edge will not have +// pointer equality with any shared Edge. +//////////////////////////////////////////////////////////////////// +template +INLINE MesherStrip::Edge MesherStrip:: +get_tail_edge() const { + Verts::const_reverse_iterator vi = _verts.rbegin(); + return Edge(*++vi, _verts.back()); +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::operator == +// Access: Public +// Description: Defines equality for strips. This actually tests +// only pointer equality; it's used only when removing a +// strip from the list. +//////////////////////////////////////////////////////////////////// +template +INLINE bool MesherStrip:: +operator == (const MesherStrip &other) const { + return this == &other; +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +MesherStrip:: +MesherStrip(const PrimType &prim, int index, const BuilderBucket &bucket) { + _index = index; + _row_id = 0; + _status = MS_alive; + _origin = MO_unknown; + prim.has_overall_normal(); // Force the prim to update its overall flags. + _type = prim.get_type(); + + // We save only the prim's overall properties in the _prims + // array. The vertices get re-added later by Mesher::add_prim(). + _prims.push_back(prim); + + if (_type == BPT_poly) { + switch (prim.get_num_verts()) { + case 3: + _type = BPT_tri; + break; + + case 4: + _type = BPT_quad; + break; + } + } + + if (_type == BPT_quad) { + // A quad has two internal triangles; we therefore push the prim + // properties twice. + _prims.push_back(prim); + } + + _planar = false; + + if (prim.get_num_verts() >= 3) { + // However, we will examine the vertices to determine the plane equation. + Vertexf p1 = prim.get_vertex(0).get_coord_value(bucket); + Vertexf p2 = prim.get_vertex(1).get_coord_value(bucket); + Vertexf p3 = prim.get_vertex(2).get_coord_value(bucket); + _plane_normal = cross(p1-p2, p2-p3); + float l = length(_plane_normal); + + if (l != 0.0) { + _plane_normal /= l; + _planar = true; + _plane_offset = -dot(_plane_normal, p1); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::make_prim +// Access: Public +// Description: Creates a PrimType element corresponding to the strip +// represented by this node. +//////////////////////////////////////////////////////////////////// +template +PrimType MesherStrip:: +make_prim(const BuilderBucket &bucket) { + Prim p; + BuilderPrimType dest_type; + + switch (_type) { + case BPT_quad: + dest_type = bucket._show_quads ? BPT_poly : BPT_tristrip; + break; + + case BPT_tristrip: + case BPT_quadstrip: + dest_type = BPT_tristrip; + break; + + case BPT_trifan: + dest_type = BPT_trifan; + break; + + default: + dest_type = _type; + } + + if (dest_type != BPT_tristrip && dest_type != BPT_trifan) { + // The easy case: a simple primitive. + p.set_attrib(_prims.front()); + Verts::iterator vi; + for (vi = _verts.begin(); vi != _verts.end(); ++vi) { + p.add_vertex(**vi); + } + p.set_type(dest_type); + + } else { + // The harder case: a tristrip of some kind. + convert_to_type(dest_type); + p.set_attrib(_prims.front()); + + BuilderPrimType type = dest_type; + + // Now store all the vertices, as well as each individual + // triangle's attributes. + Verts::iterator vi; + Prims::iterator pi; + pi = _prims.begin(); + int count = 0; + for (vi = _verts.begin(); + vi != _verts.end() && pi != _prims.end(); + ++vi) { + Vertex v = **vi; + + if (++count >= 3) { + // Beginning with the third vertex, we increment pi. Thus, the + // first two vertices stand alone, then each vertex beginning + // with the third completes a triangle. + p.add_component(*pi); + ++pi; + } + p.add_vertex(v); + } + + p.set_type(type); + + // If either of these fail, there weren't num_prims + 2 vertices in + // the tristrip! + nassertr(vi==_verts.end(), p); + nassertr(pi==_prims.end(), p); + } + + return p; +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::measure_sheet +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void MesherStrip:: +measure_sheet(const Edge *edge, int new_row, int &num_prims, int &num_rows, + int first_row_id, int this_row_id, int this_row_distance) { + if (new_row) { + // If we would create a new row by stepping here, we won't stay if + // there was any other row already defined here. + if (_row_id >= first_row_id) { + return; + } + } else { + // On the other hand, if this is a continuation of the current + // row, we'll stay if the other row had to travel farther to get + // here. + if (_row_id >= first_row_id && _row_distance <= this_row_distance) { + return; + } + } + + num_prims += _prims.size(); + if (new_row) { + ++num_rows; + this_row_id = first_row_id + num_rows - 1; + } + + _row_id = this_row_id; + + Edges::iterator ei; + Edge::Strips::iterator si; + + if (_type == BPT_quad) { + // If this is a quad, it has four neighbors: two in the direction + // we are testing, and two in an orthagonal direction. + + const Vertex *a = edge->_a; + const Vertex *b = edge->_b; + + // We use these vertices to differentiate the edges that run in + // our primary direction from those in the secondary direction. + // For each edge, we count the number of vertices that the edge + // shares with our starting edge. There are then three cases: + + // (a) The edge shares two vertices. It is the direction we came + // from; forget it. + + // (b) The edge shares one vertex. It is at right angles to our + // starting edge. This is the primary direction if new_row is + // true, and the secondary direction if new_row is false. + + // (c) The edge shares no vertices. It is directly opposite our + // starting edge. This is the primary direction if new_row is + // false, and the secondary direction if new_row is true. + + + // Here's a silly little for loop that executes the following code + // twice: once with secondary == 0, and once with secondary == 1. + // This is because we want to find all the primary edges first, + // and then all the secondary edges. + + for (int secondary = 0; secondary <= 1; secondary++) { + // How many common vertices are we looking for this pass (see + // above)? + + int want_count; + if (secondary) { + want_count = new_row ? 0 : 1; + } else { + want_count = new_row ? 1 : 0; + } + + for (ei = _edges.begin(); ei != _edges.end(); ++ei) { + int common_verts = + ((*ei)->_a == a || (*ei)->_a == b) + + ((*ei)->_b == a || (*ei)->_b == b); + + if (common_verts == want_count) { + // Here's the edge. Look at all its connections. Hopefully, + // there will only be one besides ourselves, but there may be + // more. Pick the best. + + Edge::Strips &strips = (*ei)->_strips; + MesherStrip *mate = NULL; + for (si = strips.begin(); si != strips.end(); ++si) { + if ((*si)->_row_id < first_row_id) { + if (mate==NULL || pick_sheet_mate(**si, *mate)) { + mate = *si; + } + } + } + if (mate!=NULL) { + mate->measure_sheet(*ei, secondary, num_prims, num_rows, + first_row_id, this_row_id, + this_row_distance + secondary); + } + } + } + } + + } else { + // Otherwise, this is not a quad. It's certainly not a triangle, + // because we've built all the single triangles already. + nassertv(_type != BPT_tri); + + // Therefore, it must be a tristrip or quadstrip. + nassertv(_type == BPT_tristrip || _type == BPT_quadstrip); + + // Since it's a strip, it only has two neighbors: the one we came + // from, and the other one. Find the other one. + + for (ei = _edges.begin(); ei != _edges.end(); ++ei) { + if (!(*ei)->matches(*edge)) { + // Here's the edge. Same drill as above. + + Edge::Strips &strips = (*ei)->_strips; + MesherStrip *mate = NULL; + for (si = strips.begin(); si != strips.end(); ++si) { + if ((*si)->_row_id < first_row_id) { + if (mate==NULL || pick_sheet_mate(**si, *mate)) { + mate = *si; + } + } + } + if (mate!=NULL) { + mate->measure_sheet(*ei, false, num_prims, num_rows, + first_row_id, this_row_id, this_row_distance); + } + } + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::cut_sheet +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void MesherStrip:: +cut_sheet(int first_row_id, int do_mate, const BuilderBucket &bucket) { + Edges::iterator ei; + Edge::Strips::iterator si; + + // First, start the process going on any neighbors that belong to a + // later row. (We must do these first, because we'll change our + // neighbor list when we start to mate.) + + // We need to build a temporary list of neighbors first, because + // calling cut_sheet() recursively will start things mating, and + // could damage our edge list. + + typedef list StripPtrs; + StripPtrs strip_ptrs; + + for (ei = _edges.begin(); ei != _edges.end(); ++ei) { + Edge::Strips &strips = (*ei)->_strips; + for (si = strips.begin(); si != strips.end(); ++si) { + if ((*si)->_row_id > _row_id) { + // Here's a different row in the sheet! + strip_ptrs.push_back(*si); + } + } + } + + // Now walk the temporary list and do some damage. We pass do_mate + // = true to each of these neighbors, because as far as we know, + // they're the first nodes of a particular row. + StripPtrs::iterator spi; + for (spi = strip_ptrs.begin(); spi != strip_ptrs.end(); ++spi) { + if ((*spi)->_status == MS_alive) { + (*spi)->cut_sheet(first_row_id, true, bucket); + } + } + + + if (do_mate && _status == MS_alive) { + // Now mate like a bunny until we don't have any more eligible mates. + int not_any; + do { + not_any = true; + + ei = _edges.begin(); + while (ei != _edges.end() && not_any) { + Edge::Strips &strips = (*ei)->_strips; + si = strips.begin(); + while (si != strips.end() && not_any) { + if (*si != this && (*si)->_row_id == _row_id) { + // Here's one! + not_any = false; + MesherStrip *mate = *si; + + // We also recurse on these guys so they can spread the + // word to their own neighbors. This time we don't need + // to build a temporary list, because we'll be restarting + // from the beginning of our edge list after we do this. + // We also pass do_mate = false to these guys because + // we're the ones doing the mating here. + mate->cut_sheet(first_row_id, false, bucket); + + if (_status == MS_alive && mate->_status == MS_alive) { + // Now mate. This will either succeed or fail. It ought + // to succeed, but if it doesn't, no harm done; it will + // simply remove the common edge and return. We'll go + // around again and not encounter this neighbor next time. + mate_pieces(*ei, *this, *mate, bucket); + } + } + if (not_any) { + ++si; + } + } + if (not_any) { + ++ei; + } + } + } while (!not_any); + + // All done. Mark this one as down for the count. + _row_id = -first_row_id; + } +} + + + + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::mate +// Access: Public +// Description: Finds a neighboring strip and joins up with it to +// make a larger strip. Returns true if mating was +// successful or at least possible, false if the strip +// has no neighbors. +//////////////////////////////////////////////////////////////////// +template +bool MesherStrip:: +mate(const BuilderBucket &bucket) { + // We must walk through the list of our neighbors and choose our + // best mate. + nassertr(_status == MS_alive, false); + + MesherStrip *mate; + Edge *common_edge; + + if (!find_ideal_mate(mate, common_edge, bucket)) { + // We have no more eligible neighbors. Call us done. + _status = MS_done; + + return false; + } + + nassertr(!mate->_prims.empty(), false); + nassertr(!mate->_verts.empty(), false); + + mate_pieces(common_edge, *this, *mate, bucket); + + // Whether the mate failed or not, the strip still (probably) has + // other neighbors to consider. Return true regardless. + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::find_ideal_mate +// Access: Public +// Description: Searches our neighbors for the most suitable mate. +// Returns true if one is found, false if we have no +// neighbors. +//////////////////////////////////////////////////////////////////// +template +bool MesherStrip:: +find_ideal_mate(MesherStrip *&mate, Edge *&common_edge, + const BuilderBucket &bucket) { + Edges::iterator ei; + + mate = NULL; + common_edge = NULL; + + for (ei = _edges.begin(); ei != _edges.end(); ++ei) { + Edge::Strips &strips = (*ei)->_strips; + Edge::Strips::iterator si; + for (si = strips.begin(); si != strips.end(); ++si) { + if (*si != this) { + if (mate==NULL || pick_mate(**si, *mate, **ei, *common_edge, + bucket)) { + mate = *si; + common_edge = *ei; + } + } + } + } + + return (mate!=NULL); +} + + + + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::mate_pieces +// Access: Public, Static +// Description: Connects two pieces of arbitrary type, if possible. +// Returns true if successful, false if failure. +//////////////////////////////////////////////////////////////////// +template +bool MesherStrip:: +mate_pieces(Edge *common_edge, MesherStrip &front, MesherStrip &back, + const BuilderBucket &bucket) { + nassertr(front._status == MS_alive, false); + nassertr(back._status == MS_alive, false); + nassertr(&front != &back, false); + + bool success = true; + // remove_sides tracks whether we want to remove all but the leading + // edges of the newly joined piece if we succeed. + bool remove_sides = true; + + bool is_coplanar = front.is_coplanar_with(back, bucket._coplanar_threshold); + + if (front._type==BPT_tri && back._type==BPT_tri) { + + if (is_coplanar && bucket._retesselate_coplanar && + front._prims.front() == back._prims.front() && + convex_quad(common_edge, front, back, bucket)) { + + // If we're joining two equivalent coplanar triangles, call it a + // quad. + front._type = BPT_quad; + + // We add one additional vertex for the new triangle, the one + // vertex we didn't already share. + const Vertex *new_vert = back.find_uncommon_vertex(common_edge); + + // Now we just need to find the right place to insert it. It + // belongs in the middle of the common edge, i.e. after the first + // vertex that is on the common edge and before the second vertex. + Verts::iterator a = front._verts.begin(); + Verts::iterator b = a; + ++b; + + if (common_edge->contains_vertex(*a)) { + if (common_edge->contains_vertex(*b)) { + // It goes between a and b. + front._verts.insert(b, new_vert); + } else { + // It goes at the end. + front._verts.push_back(new_vert); + } + } else { + // It goes between b and c. + ++b; + front._verts.insert(b, new_vert); + } + + front._prims.splice(front._prims.end(), back._prims); + back._verts.clear(); + + // We leave all four surrounding edges for now, since the quad + // might still be joined up in any direction. + remove_sides = false; + + } else { + // Otherwise, connect the two tris into a tristrip. + front._type = BPT_tristrip; + + const Vertex *new_vert = back.find_uncommon_vertex(common_edge); + front.rotate_to_back(common_edge); + + front._verts.push_back(new_vert); + front._prims.splice(front._prims.end(), back._prims); + back._verts.clear(); + } + + } else if ((front._type==BPT_quad || front._type==BPT_quadstrip) && + (back._type==BPT_quad || back._type==BPT_quadstrip)) { + // Joining two quads, two quadstrips, or a quad and a quadstrip. + // This makes another quadstrip. + + // We expect this to succeed every time with quadstrips. + success = mate_strips(common_edge, front, back, BPT_quadstrip); + + if (!success) { + // Although it might fail in rare circumstances (specifically, + // if the two strips we attempted to join were backfacing to + // each other). If so, remove the adjoining edge so these two + // don't get tried again. + common_edge->remove(&front); + common_edge->remove(&back); + } + + } else { + + // Otherwise. This might be two tristrips, a quad and a tristrip, + // a triangle and a quad, a triangle and a tristrip, a triangle + // and a quadstrip, or a tristrip and a quadstrip. In any case, + // we'll end up with a tristrip. + + // This might fail if the tristrips don't match polarity. + success = mate_strips(common_edge, front, back, BPT_tristrip); + + if (!success) { + // If it does fail, we'll try reversing the connection. This + // makes sense if we are joining a tri or tristrip to a quad or + // quadstrip, which might fail in one direction but succeed in + // the other. + success = mate_strips(common_edge, back, front, BPT_tristrip); + + if (success) { + // Yay! Now return all the stuff to front. + front._verts.splice(front._verts.end(), back._verts); + front._prims.splice(front._prims.end(), back._prims); + } else { + // A miserable failure. Never try to join these two again. + + common_edge->remove(&front); + common_edge->remove(&back); + } + } + } + + if (success) { + front.combine_edges(back, remove_sides); + if (!remove_sides) { + // If we didn't want to remove the side edges, at least remove + // the join edge, which is now internal. + common_edge->remove(&front); + } + + nassertr(back._prims.empty(), false); + nassertr(back._verts.empty(), false); + + // Strip back is no more. + back._status = MS_dead; + + // The result is planar if and only if we joined two coplanar + // pieces. + front._planar = is_coplanar; + front._origin = MO_mate; + } + + return success; +} + + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::mate_strips +// Access: Public, Static +// Description: Stitches two strips together, producing in "front" a +// new strip of the indicated type (quadstrip or +// tristrip). The front strip stores the result, and +// the back strip is emptied on success. +// +// Returns true if successful, false if failure +// (generally because of incorrect polarity of +// tristrips), in which case nothing has changed (or at +// least, not much). +//////////////////////////////////////////////////////////////////// +template +bool MesherStrip:: +mate_strips(Edge *common_edge, MesherStrip &front, MesherStrip &back, + BuilderPrimType type) { + // If we start with a quad or tri, rotate the vertices around so we + // start with the common edge. + if (front._type==BPT_tri || front._type==BPT_quad) { + front.rotate_to_back(common_edge); + } + if (back._type==BPT_tri || back._type==BPT_quad) { + back.rotate_to_front(common_edge); + } + + bool reverse_front = common_edge->matches(front.get_head_edge()); + bool reverse_back = !common_edge->matches(back.get_head_edge()); + + bool invert_front = false; + bool invert_back = false; + + if (reverse_front && front.is_odd()) { + // If we're going to reverse the front strip, we have to be + // careful. This will also reverse the facing direction if it has + // an odd number of prims. + if (!front.can_invert()) { + return false; + } + invert_front = true; + } + + if (must_invert(front, back, reverse_back, type)) { + if (!back.can_invert()) { + return false; + } + invert_back = true; + back.invert(); + } + + if (invert_front) { + front.invert(); + } + + if (reverse_front) { + reverse(front._verts.begin(), front._verts.end()); + reverse(front._prims.begin(), front._prims.end()); + } + + if (reverse_back) { + reverse(back._verts.begin(), back._verts.end()); + reverse(back._prims.begin(), back._prims.end()); + } + + bool will_reverse = front.would_reverse_tail(type); + bool is_headtotail = (front.get_tail_edge() == back.get_head_edge()); + if (will_reverse == is_headtotail) { + // Instead of crapping out, for now we'll just recover and carry on. + // builder_cat.info() << "Recovering from attempt to join backfacing strips.\n"; + if (reverse_back) { + reverse(back._verts.begin(), back._verts.end()); + reverse(back._prims.begin(), back._prims.end()); + } + if (invert_back) { + back.invert(); + } + if (reverse_front) { + reverse(front._verts.begin(), front._verts.end()); + reverse(front._prims.begin(), front._prims.end()); + } + if (invert_front) { + front.invert(); + } + return false; + } + + front.convert_to_type(type); + back.convert_to_type(type); + + /* + if (! (front.get_tail_edge() == back.get_head_edge()) ) { + builder_cat.error() + << "\nFailure, trying to connect " << front + << "\nto " << back + << "\nreverse_front = " << reverse_front + << " reverse_back = " << reverse_back + << " invert_front = " << invert_front + << "\n"; + Edges::iterator ei; + + nout << "\nFront edges:\n"; + for (ei = front._edges.begin(); ei != front._edges.end(); ++ei) { + nout << **ei << "\n"; + } + + nout << "\nBack edges:\n"; + for (ei = back._edges.begin(); ei != back._edges.end(); ++ei) { + nout << **ei << "\n"; + } + } + */ + + // If this assertion fails, we were misinformed about our ability to + // join these two strips. Either the must_invert() call returned the + // incorrect value, or our edge-detection logic failed and we + // attempted to join two oppositely-facing strips. + //nassertr(front.get_tail_edge() == back.get_head_edge(), false); + + front._verts.pop_back(); + front._verts.pop_back(); + front._verts.splice(front._verts.end(), back._verts); + front._prims.splice(front._prims.end(), back._prims); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::must_invert +// Access: Public, Static +// Description: Returns false if the strips can be mated as they +// currently are. Returns true if the back strip must +// be inverted first. +//////////////////////////////////////////////////////////////////// +template +bool MesherStrip:: +must_invert(const MesherStrip &front, const MesherStrip &back, + bool will_reverse_back, BuilderPrimType type) { + bool invert = false; + + if ((front._type == BPT_quad || front._type == BPT_quadstrip) && + type == BPT_tristrip) { + // If we'll be converting from quads to tris, the tail edge of the + // front strip will always be even. + + } else if (front.is_odd()) { + // Otherwise, we have to flip if the tail edge is odd. + invert = !invert; + } + + if (will_reverse_back) { + // With the back strip, we don't care about what will happen to + // its tail edge when we convert it, but we do care what happens + // to its front edge if we reverse it. + if (back.is_odd()) { + // Specifically, the front edge will be reversed when the strip + // is reversed only if the strip is odd. + invert = !invert; + } + } + + return invert; +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::convex_quad +// Access: Public, Static +// Description: Returns true if the quad that would be formed by +// connecting coplanar tris front and back along +// common_edge is convex, false otherwise. +//////////////////////////////////////////////////////////////////// +template +bool MesherStrip:: +convex_quad(Edge *common_edge, MesherStrip &front, MesherStrip &back, + const BuilderBucket &bucket) { + // Find the edge from the apex of one triangle to the apex of the + // other. This is the "other" diagonal of the quad-to-be, other + // than the common_edge. + const Vertex *a = front.find_uncommon_vertex(common_edge); + const Vertex *b = back.find_uncommon_vertex(common_edge); + nassertr(a!=NULL && b!=NULL, false); + + Vertexf a3, b3, c3, d3; + a3 = a->get_coord_value(bucket)._v; + b3 = b->get_coord_value(bucket)._v; + + c3 = common_edge->_a->get_coord_value(bucket)._v; + d3 = common_edge->_b->get_coord_value(bucket)._v; + + // Project both edges into the 2-d axis plane most nearly + // perpendicular to the normal. We're assuming both tris have the + // same normal. + + nassertr(front._planar, false); + + LVector3f &n = front._plane_normal; + int xi, yi; + + // Find the largest dimension of the normal. + if (fabs(n[0]) > fabs(n[1])) { + if (fabs(n[0]) > fabs(n[2])) { + xi = 1; + yi = 2; + } else { + xi = 0; + yi = 1; + } + } else { + if (fabs(n[1]) > fabs(n[2])) { + xi = 0; + yi = 2; + } else { + xi = 0; + yi = 1; + } + } + + LVecBase2 a2, b2, c2, d2; + a2.set(a3[xi], a3[yi]); + b2.set(b3[xi], b3[yi]); + c2.set(c3[xi], c3[yi]); + d2.set(d3[xi], d3[yi]); + + // Now (c2-d2) is the common edge, and (a2-b2) is the new edge. The + // quad is convex iff (c2-d2) intersects (a2-b2). We actually only + // need to test whether (c2-d2) intersects the infinite line passing + // through (a2-b2). + + // The equation for the infinite line containing (a2-b2): + // Ax + By + C = 0 + double A = (b2[1] - a2[1]); + double B = (a2[0] - b2[0]); + double C = -(A*b2[0] + B*b2[1]); + + // The parametric equations for the line segment (c2-d2): + // x = c2[0] + (d2[0]-c2[0])t + // y = c2[1] + (d2[1]-c2[1])t + + // Solved for t: + double t = - ((A*c2[0] + B*c2[1]) + C) / (A*(d2[0]-c2[0]) + B*(d2[1]-c2[1])); + + // Now the lines intersect if t is in [0, 1]. + return (0.0 <= t && t <= 1.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::count_neighbors +// Access: Public +// Description: Returns the number of neighbors the strip shares. +//////////////////////////////////////////////////////////////////// +template +int MesherStrip:: +count_neighbors() const { + int count = 0; + Edges::const_iterator ei; + + for (ei = _edges.begin(); ei != _edges.end(); ++ei) { + count += (*ei)->_strips.size(); + } + return count; +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::show_neighbors +// Access: Public +// Description: Writes all the neighbor indexes to the ostream. +//////////////////////////////////////////////////////////////////// +template +ostream &MesherStrip:: +show_neighbors(ostream &out) const { + Edges::const_iterator ei; + Edge::Strips::const_iterator si; + + for (ei = _edges.begin(); ei != _edges.end(); ++ei) { + for (si = (*ei)->_strips.begin(); + si != (*ei)->_strips.end(); + ++si) { + out << " " << (*si)->_index; + } + } + return out; +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::find_uncommon_vertex +// Access: Public +// Description: Returns the first vertex found that is not shared by +// the given edge. +//////////////////////////////////////////////////////////////////// +template +const MesherStrip::Vertex *MesherStrip:: +find_uncommon_vertex(const Edge *edge) const { + const Vertex *a = edge->_a; + const Vertex *b = edge->_b; + + Edges::const_iterator ei; + for (ei = _edges.begin(); ei != _edges.end(); ++ei) { + Edge *e = (*ei); + + if (e->_a != a && e->_a != b) { + return e->_a; + } else if (e->_b != a && e->_b != b) { + return e->_b; + } + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::find_opposite_edge +// Access: Public +// Description: Returns the first edge found that does not contain +// the given vertex. In a tri, this will be the edge +// opposite the given vertex. +//////////////////////////////////////////////////////////////////// +template +const MesherStrip::Edge *MesherStrip:: +find_opposite_edge(const Vertex *vertex) const { + Edges::const_iterator ei; + for (ei = _edges.begin(); ei != _edges.end(); ++ei) { + Edge *e = (*ei); + if (!e->contains_vertex(vertex)) { + return e; + } + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::find_opposite_edge +// Access: Public +// Description: Returns the first edge found that shares no vertices +// with the given edge. In a quad, this will be the +// edge opposite the given edge. +//////////////////////////////////////////////////////////////////// +template +const MesherStrip::Edge *MesherStrip:: +find_opposite_edge(const Edge *edge) const { + const Vertex *a = edge->_a; + const Vertex *b = edge->_b; + + Edges::const_iterator ei; + for (ei = _edges.begin(); ei != _edges.end(); ++ei) { + Edge *e = (*ei); + if (!e->contains_vertex(a) && !e->contains_vertex(b)) { + return e; + } + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::find_adjacent_edge +// Access: Public +// Description: Returns the first edge found that shares exactly one +// vertex with the given edge. In a quad, this will be +// one of two edges adjacent to the given edge. +//////////////////////////////////////////////////////////////////// +template +const MesherStrip::Edge *MesherStrip:: +find_adjacent_edge(const Edge *edge) const { + const Vertex *a = edge->_a; + const Vertex *b = edge->_b; + + Edges::const_iterator ei; + for (ei = _edges.begin(); ei != _edges.end(); ++ei) { + Edge *e = (*ei); + if (e->contains_vertex(a) != e->contains_vertex(b)) { + return e; + } + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::rotate_to_front +// Access: Public +// Description: Rotates a triangle or quad so that the given edge is +// first in the vertex list. +//////////////////////////////////////////////////////////////////// +template +void MesherStrip:: +rotate_to_front(const Edge *edge) { + const Vertex *a = edge->_a; + const Vertex *b = edge->_b; + + // See if we're already there. + if (_verts.front() == a || _verts.front() == b) { + Verts::iterator vi = _verts.begin(); + ++vi; + if (*vi == a || *vi == b) { + // Yes! + return; + } + + // No, we must be right on the line. Roll back one. + rotate_back(); + + } else { + // Roll forward until it comes into view. + int num_verts = _verts.size(); + while (_verts.front() != a && _verts.front() != b) { + // Make sure either vertex exists. + num_verts--; + nassertv(num_verts > 0); + rotate_forward(); + } + } + + // Now make sure the edge actually exists. + Verts::iterator vi = _verts.begin(); + ++vi; + nassertv(*vi == a || *vi == b); +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::rotate_to_back +// Access: Public +// Description: Rotates a triangle or quad so that the given edge is +// last in the vertex list. +//////////////////////////////////////////////////////////////////// +template +void MesherStrip:: +rotate_to_back(const Edge *edge) { + const Vertex *a = edge->_a; + const Vertex *b = edge->_b; + + // See if we're already there. + if (_verts.back() == a || _verts.back() == b) { + Verts::reverse_iterator vi = _verts.rbegin(); + ++vi; + if (*vi == a || *vi == b) { + // Yes! + return; + } + + // No, we must be right on the line. Roll forward one. + rotate_forward(); + + } else { + // Roll backward until it comes into view. + int num_verts = _verts.size(); + while (_verts.back() != a && _verts.back() != b) { + // Make sure either vertex exists. + num_verts--; + nassertv(num_verts > 0); + rotate_back(); + } + } + + // Now make sure the edge actually exists. + Verts::reverse_iterator vi = _verts.rbegin(); + ++vi; + nassertv(*vi == a || *vi == b); +} + + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::can_invert +// Access: Public +// Description: Returns true if the strip can be inverted (reverse +// its facing direction). Generally, this is true for +// quadstrips and false for tristrips. +//////////////////////////////////////////////////////////////////// +template +bool MesherStrip:: +can_invert() const { + return (_type==BPT_quadstrip || _type==BPT_quad); +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::invert +// Access: Public +// Description: Reverses the facing of a quadstrip by reversing pairs +// of vertices. Returns true if successful, false if +// failure (for instance, on a tristrip). +//////////////////////////////////////////////////////////////////// +template +bool MesherStrip:: +invert() { + if (!can_invert()) { + return false; + } + Verts::iterator vi, vi2; + vi = _verts.begin(); + while (vi != _verts.end()) { + vi2 = vi; + ++vi2; + nassertr(vi2 != _verts.end(), false); + + // Exchange vi and vi2 + const Vertex *t = *vi2; + *vi2 = *vi; + *vi = t; + + // Increment + vi = vi2; + ++vi; + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::is_odd +// Access: Public +// Description: Returns true if the tristrip or quadstrip contains an +// odd number of pieces. +//////////////////////////////////////////////////////////////////// +template +bool MesherStrip:: +is_odd() const { + if (_type==BPT_quadstrip || _type==BPT_quad) { + // If a quadstrip has a multiple of four vertices, it has an + // odd number of quads. + return (_verts.size()%4 == 0); + } else { + // If a tristrip has an odd number of vertices, it has an odd + // number of tris. + return (_verts.size()%2 == 1); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::would_reverse_tail +// Access: Public +// Description: Returns true if convert_to_type() would reverse the +// tail edge of the given strip, false otherwise. +//////////////////////////////////////////////////////////////////// +template +bool MesherStrip:: +would_reverse_tail(BuilderPrimType wantType) const { + bool reverse = false; + + if (_type==wantType) { + return false; + } + if (wantType==BPT_tristrip) { + switch (_type) { + case BPT_tri: + case BPT_tristrip: + break; + + case BPT_quad: + case BPT_quadstrip: + // When we convert a quadstrip to a tristrip, we reverse the + // tail edge if we have a multiple of four verts. + reverse = (_verts.size()%4==0); + break; + + default: + builder_cat.fatal() << "Invalid conversion!\n"; + abort(); + break; + } + + } else if (wantType==BPT_quadstrip) { + switch (_type) { + case BPT_quad: + case BPT_quadstrip: + break; + + case BPT_tri: + case BPT_tristrip: + // We don't convert tristrips to quadstrips; fall through. + + default: + builder_cat.fatal() << "Invalid conversion!\n"; + abort(); + break; + } + } + + return reverse; +} + + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::convert_to_type +// Access: Public +// Description: Converts the MesherStrip from whatever form it +// is--triangle, quad, or quadstrip--into a tristrip or +// quadstrip. +//////////////////////////////////////////////////////////////////// +template +void MesherStrip:: +convert_to_type(BuilderPrimType wantType) { + Verts::iterator vi, vi2; + int even; + + if (_type==wantType) { + return; + } + if (wantType==BPT_tristrip) { + switch (_type) { + case BPT_tri: + case BPT_tristrip: + break; + + case BPT_quad: + case BPT_quadstrip: + // To convert from quad/quadstrip to tristrip, we reverse every other + // pair of vertices. + + vi = _verts.begin(); + even = 0; + while (vi != _verts.end()) { + vi2 = vi; + ++vi2; + nassertv(vi2 != _verts.end()); + + // vi and vi2 are a pair. Should we reverse them? + if (even) { + const Vertex *t = *vi2; + *vi2 = *vi; + *vi = t; + } + + // Increment + vi = vi2; + ++vi; + even = !even; + } + break; + + default: + builder_cat.fatal() << "Invalid conversion!\n"; + abort(); + } + + } else if (wantType==BPT_quadstrip) { + switch (_type) { + case BPT_quad: + case BPT_quadstrip: + break; + + case BPT_tri: + case BPT_tristrip: + // We don't convert tristrips to quadstrips; fall through. + + default: + builder_cat.fatal() << "Invalid conversion!\n"; + abort(); + } + } + + _type = wantType; +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::combine_edges +// Access: Public +// Description: Removes the edges from the given strip and appends +// them to our own. If removeSides is true, then +// removes all the edges except the head and the tail. +//////////////////////////////////////////////////////////////////// +template +void MesherStrip:: +combine_edges(MesherStrip &other, int removeSides) { + Edges::iterator ei; + for (ei = other._edges.begin(); ei != other._edges.end(); ++ei) { + (*ei)->change_strip(&other, this); + } + + _edges.splice(_edges.end(), other._edges); + + if (removeSides) { + // Identify the head and tail edges so we can remove everything + // else. + Edge head = get_head_edge(); + Edge tail = get_tail_edge(); + + if (!is_odd()) { + // If the strip is odd, its true tail edge is the inverse of its + // actual edge. + tail = Edge(tail._b, tail._a); + } + + Edges junk_edges; + + Edges::iterator next_ei; + ei = _edges.begin(); + while (ei != _edges.end()) { + next_ei = ei; + ++next_ei; + + // Is this edge to be saved or is it fodder? + if (!(**ei == head) && !(**ei == tail)) { + // Fodder! But we can't remove it right away, because this + // will upset the current list; instead, we'll splice it to + // junk_edges. + junk_edges.splice(junk_edges.end(), _edges, ei); + } + ei = next_ei; + } + + // Now we can safely remove all the to-be-junked edges. + for (ei = junk_edges.begin(); ei != junk_edges.end(); ++ei) { + (*ei)->remove(this); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::remove_all_edges +// Access: Public +// Description: Removes all active edges from the strip. This +// effectively renders it ineligible to mate with +// anything else. +//////////////////////////////////////////////////////////////////// +template +void MesherStrip:: +remove_all_edges() { + // First, move all the edges to a safe place so we can traverse the + // list without it changing on us. + Edges junk_edges; + junk_edges.splice(junk_edges.end(), _edges); + + // Now we can safely remove all the to-be-junked edges. + Edges::iterator ei; + for (ei = junk_edges.begin(); ei != junk_edges.end(); ++ei) { + (*ei)->remove(this); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::pick_mate +// Access: Public +// Description: Defines an ordering to select neighbors to mate with. +// This compares strip a with strip b and returns true +// if strip a is the preferable choice, false if strip +// b. +//////////////////////////////////////////////////////////////////// +template +bool MesherStrip:: +pick_mate(const MesherStrip &a_strip, const MesherStrip &b_strip, + const Edge &a_edge, const Edge &b_edge, + const BuilderBucket &bucket) const { + // First, try to avoid polluting quads, quadstrips, and tristrips + // with arbitrary triangles. When we mate a tri or tristrip to a + // quadstrip, we end up with a tristrip that may be less versatile + // than the original quadstrip. Better to avoid this if we can. + // Try to choose a mate that more closely matches our own type. + int a_cat = a_strip.type_category(); + int b_cat = b_strip.type_category(); + if (a_cat != b_cat) { + int me_cat = type_category(); + return abs(a_cat - me_cat) < abs(b_cat - me_cat); + } + + // Now, if we're connecting two tris, try to connect them up so they + // make good quads. + if (_type == BPT_tri && a_strip._type == BPT_tri && + b_strip._type == BPT_tri) { + + // This will depend on both coplanarity and edge length. We can't + // use just one or the other, because some tris are nearly + // isosceles, and some have more than one coplanar neighbor. + // Hopefully the combination of both factors will zero us in on + // the correct neighbor first. + + double a_coplanar = coplanarity(a_strip); + double b_coplanar = coplanarity(b_strip); + double coplanar_diff = a_coplanar - b_coplanar; + + double a_length = a_edge.compute_length(bucket); + double b_length = b_edge.compute_length(bucket); + double length_diff = (a_length - b_length) / (a_length + b_length); + + // These weights were chosen empirically to yield fairly good results. + double sum = 4.0 * coplanar_diff - 1.0 * length_diff; + return sum < 0; + } + + // Then, get the smallest strip. + if (a_strip._prims.size() != b_strip._prims.size()) { + return a_strip._prims.size() < b_strip._prims.size(); + } + + // Finally, get the strip with the fewest neighbors. + return a_strip.count_neighbors() < b_strip.count_neighbors(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::pick_sheet_mate +// Access: Public +// Description: Defines an ordering to select neighbors to follow +// when measuring out a quadsheet. This is only called +// when three or more prims share a single edge, which +// should be rarely--generally only when coplanar polys +// are going on. +//////////////////////////////////////////////////////////////////// +template +bool MesherStrip:: +pick_sheet_mate(const MesherStrip &a_strip, const MesherStrip &b_strip) const { + // First, try to get the poly which is closest to our own normal. + if (_planar && a_strip._planar && b_strip._planar) { + float a_diff = dot(_plane_normal, a_strip._plane_normal); + float b_diff = dot(_plane_normal, b_strip._plane_normal); + + if (fabs(a_diff - b_diff) > 0.0001) { + return a_diff > b_diff; + } + } + + // Then, pick the one that's most like our own type. + int a_cat = a_strip.type_category(); + int b_cat = b_strip.type_category(); + if (a_cat != b_cat) { + int me_cat = type_category(); + return abs(a_cat - me_cat) < abs(b_cat - me_cat); + } + + // Oh, just pick any old one. + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: MesherStrip::output +// Access: Public +// Description: Formats the vertex for output in some sensible way. +//////////////////////////////////////////////////////////////////// +template +ostream &MesherStrip:: +output(ostream &out) const { + switch (_status) { + case MS_alive: + break; + + case MS_dead: + out << "Dead "; + break; + + case MS_done: + out << "Done "; + break; + + default: + out << "Unknown status "; + } + + switch (_type) { + case BPT_tri: + out << "Tri"; + break; + + case BPT_quad: + out << "Quad"; + break; + + case BPT_tristrip: + out << "TriStrip"; + break; + + case BPT_trifan: + out << "TriFan"; + break; + + case BPT_quadstrip: + out << "QuadStrip"; + break; + + default: + out << "Unknown"; + } + + if (_planar) { + out << " (planar)"; + } + + out << " " << _index << " ["; + + Verts::const_iterator vi; + for (vi = _verts.begin(); vi != _verts.end(); vi++) { + out << " " << **vi; + } + out << " ]: " << _prims.size() + << " prims, " << count_neighbors() << " neighbors"; + + show_neighbors(out); + + out << " edges"; + Edges::const_iterator ei; + for (ei = _edges.begin(); ei != _edges.end(); ei++) { + out << " " << (void *)(*ei); + } + + return out << "."; +} + diff --git a/panda/src/builder/mesherStrip.h b/panda/src/builder/mesherStrip.h new file mode 100644 index 0000000000..36a3ce5213 --- /dev/null +++ b/panda/src/builder/mesherStrip.h @@ -0,0 +1,133 @@ +// Filename: mesherStrip.h +// Created by: drose (16Sep97) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MESHERSTRIP_H +#define MESHERSTRIP_H + +#include + +#include "builderTypes.h" +#include "builderBucket.h" + +#include +#include + + +template +class MesherEdge; + +template +class MesherTempl; + +enum MesherStripStatus { + MS_alive, MS_dead, MS_done, MS_paired +}; + +enum MesherStripOrigin { + MO_unknown, MO_user, MO_firstquad, MO_fanpoly, MO_mate +}; + +template +class MesherStrip { +public: + typedef PrimType Prim; + typedef TYPENAME PrimType::Vertex Vertex; + typedef TYPENAME PrimType::DAttrib SAttrib; + typedef MesherEdge Edge; + + MesherStrip() {} + MesherStrip(const PrimType &prim, int index, const BuilderBucket &bucket); + INLINE MesherStrip(const MesherStrip ©); + + Prim make_prim(const BuilderBucket &bucket); + + void measure_sheet(const Edge *edge, int new_row, + int &num_prims, int &num_rows, + int first_row_id, int this_row_id, + int this_row_distance); + void cut_sheet(int first_row_id, int do_mate, + const BuilderBucket &bucket); + + bool mate(const BuilderBucket &bucket); + bool find_ideal_mate(MesherStrip *&mate, Edge *&common_edge, + const BuilderBucket &bucket); + static bool mate_pieces(Edge *common_edge, MesherStrip &front, + MesherStrip &back, const BuilderBucket &bucket); + static bool mate_strips(Edge *common_edge, MesherStrip &front, + MesherStrip &back, BuilderPrimType type); + static bool must_invert(const MesherStrip &front, const MesherStrip &back, + bool will_reverse_back, BuilderPrimType type); + static bool convex_quad(Edge *common_edge, MesherStrip &front, + MesherStrip &back, const BuilderBucket &bucket); + + int count_neighbors() const; + ostream &show_neighbors(ostream &out) const; + + INLINE bool is_coplanar_with(const MesherStrip &other, float threshold) const; + INLINE float coplanarity(const MesherStrip &other) const; + INLINE int type_category() const; + + const Vertex *find_uncommon_vertex(const Edge *edge) const; + const Edge *find_opposite_edge(const Vertex *vertex) const; + const Edge *find_opposite_edge(const Edge *edge) const; + const Edge *find_adjacent_edge(const Edge *edge) const; + + INLINE void rotate_forward(); + INLINE void rotate_back(); + void rotate_to_front(const Edge *edge); + void rotate_to_back(const Edge *edge); + bool can_invert() const; + bool invert(); + + INLINE Edge get_head_edge() const; + INLINE Edge get_tail_edge() const; + + bool is_odd() const; + bool would_reverse_tail(BuilderPrimType wantType) const; + void convert_to_type(BuilderPrimType wantType); + + void combine_edges(MesherStrip &other, int removeSides); + void remove_all_edges(); + + // ptr equality + INLINE bool operator == (const MesherStrip &other) const; + + bool pick_mate(const MesherStrip &a_strip, const MesherStrip &b_strip, + const Edge &a_edge, const Edge &b_edge, + const BuilderBucket &bucket) const; + + bool pick_sheet_mate(const MesherStrip &a_strip, + const MesherStrip &b_strip) const; + + ostream &output(ostream &out) const; + + typedef list Prims; + typedef list Edges; + typedef list Verts; + + Prims _prims; + Edges _edges; + Verts _verts; + + BuilderPrimType _type; + int _index; + MesherStripStatus _status; + + int _planar; + LVector3f _plane_normal; + float _plane_offset; + int _row_id, _row_distance; + int _origin; +}; + +template +INLINE ostream &operator << (ostream &out, + const MesherStrip &strip) { + return strip.output(out); +} + +#include "mesherStrip.I" + +#endif diff --git a/panda/src/builder/mesherTempl.I b/panda/src/builder/mesherTempl.I new file mode 100644 index 0000000000..857470a594 --- /dev/null +++ b/panda/src/builder/mesherTempl.I @@ -0,0 +1,661 @@ +// Filename: mesherTempl.I +// Created by: drose (15Sep97) +// +//////////////////////////////////////////////////////////////////// + +#include "mesher.h" +#include "builderMisc.h" +#include "mesherStrip.h" +#include "mesherFanMaker.h" +#include "config_builder.h" + +#include +#ifndef PENV_WIN32 +#include +#endif + + +template +MesherTempl:: +MesherTempl(BuilderBucket *bucket) { + _bucket = bucket; + _stripIndex = 0; + _next_strip = _done.end(); +} + +template +int MesherTempl:: +add_prim(const Prim &prim, MesherStripOrigin origin) { + if (!prim.is_valid()) { + return false; + } + + // Define an initial strip (probably of length 1) for the prim. + Strip temp_strip(prim, _stripIndex++, *_bucket); + Strips &list = choose_strip_list(temp_strip); + list.push_back(temp_strip); + Strip &strip = list.back(); + strip._origin = origin; + + int i; + int num_verts = prim.get_num_verts(); + + const Vertex **vptrs = (const Vertex **)alloca(num_verts * sizeof(Vertex *)); + EdgePtrs **eptrs = (EdgePtrs **)alloca(num_verts * sizeof(EdgePtrs *)); + + // Get the common vertex pointers for the primitive's vertices. + for (i = 0; i < num_verts; i++) { + Verts::iterator n = + _verts.insert(Verts::value_type(prim.get_vertex(i), EdgePtrs())).first; + vptrs[i] = &(*n).first; + eptrs[i] = &(*n).second; + + strip._verts.push_back(vptrs[i]); + } + + // Now identify the common edges. + if (prim.get_type() == BPT_tri || prim.get_type() == BPT_quad) { + // Polygons of arbitrary size don't get meshed, and so therefore + // don't have any edges in common with anything else. Only tris + // and quads can be meshed. (The builder normally breaks up + // larger polygons into tris, though the user can choose to defeat + // this.) + + for (i = 0; i < num_verts; i++) { + // Define an inner and outer edge. A polygon shares an edge with a + // neighbor only when one of its inner edges matches a neighbor's + // outer edge (and vice-versa). + Edge inner(vptrs[i], vptrs[(i+1) % num_verts]); + Edge outer(vptrs[(i+1) % num_verts], vptrs[i]); + + // Add it to the list and get its common pointer. + Edge &inner_ref = (Edge &)*_edges.insert(inner).first; + Edge &outer_ref = (Edge &)*_edges.insert(outer).first; + + // Tell the edges about each other. + inner_ref._opposite = &outer_ref; + outer_ref._opposite = &inner_ref; + + // Associate the common edge to the strip. + strip._edges.push_back(&inner_ref); + + // Associate the strip, as well as the original prim, to the edge. + outer_ref._strips.push_back(&strip); + + // Associate the common edge with the vertices that share it. + // Edge *edge_ptr = inner_ref.common_ptr(); + eptrs[i]->insert(&outer_ref); + eptrs[(i+1) % num_verts]->insert(&outer_ref); + } + } + return true; +} + + +template +void MesherTempl:: +mesh() { + if (_bucket->_consider_fans) { + find_fans(); + } + + // First, we try to make all the best quads we can. + if (_bucket->_retesselate_coplanar) { + make_quads(); + } + + // Then, we do the rest of the tris. + meshList(_tris); + + if (_bucket->_show_quads) { + // If we're showing quads, we shouldn't do any more meshing. + Strips::iterator si; + for (si = _quads.begin(); si != _quads.end(); ++si) { + if ((*si)._status == MS_alive) { + (*si)._status = MS_done; + } + } + for (si = _strips.begin(); si != _strips.end(); ++si) { + if ((*si)._status == MS_alive) { + (*si)._status = MS_done; + } + } + } + + // Then, build quads into sheets where possible. + build_sheets(); + + // Pick up any quads that might have been left behind. + meshList(_quads); + + // Finally, do the longer strips. + meshList(_strips); + + // Get ready to walk through the results. + _next_strip = _done.begin(); +} + + +template +PrimType MesherTempl:: +getPrim() { + if (_next_strip == _done.end()) { + // End of the list, return a primitive with no vertices. + finalize(); + return Prim(); + } + + Strip &strip = (*_next_strip++); + BuilderPrimType orig_type = strip._type; + Prim prim = strip.make_prim(*_bucket); + + if (_bucket->_show_tstrips) { + // If we have _show_tstrips enabled, it means we need to color + // every primitive according to which, if any, tristrip it is in. + + // We use the _colors array--and later make a copy in shared + // memory for the bucket to point to--in case the primitives are + // indexed. If the primitives are nonindexed, we'll still + // allocate the _colors array and its copy in shared memory, but + // it will be deleted when the bucket destructs. + + if (_colors.empty()) { + // We need one entry for not-a-tristrip, indicated by white. + _colors.push_back(Colorf(0.85, 0.85, 0.85, 1.0)); + } + + ushort i1, i2; + Colorf color1, color2;; + + switch (prim.get_type()) { + case BPT_tristrip: + case BPT_trifan: + make_random_color(color2); + color1 = (color2 * 0.8); // somewhat darker. + i1 = _colors.size(); + i2 = i1+1; + _colors.push_back(color1); + _colors.push_back(color2); + break; + + default: + // not-a-tristrip. + i1 = i2 = 0; + } + + // Now i1 and i2 index into the colors array to indicate the color + // for the first triangle and the rest of the primitive, + // respectively. + int num_components = prim.get_num_components(); + if (num_components > 0) { + prim.get_component(0).set_color_value(&_colors[0], i1); + for (int i = 1; i < num_components; i++) { + prim.get_component(i).set_color_value(&_colors[0], i2); + } + } else { + prim.set_color_value(&_colors[0], i1); + } + + } else if (_bucket->_show_qsheets) { + // _show_qsheets means to color every primitive according to + // which, if any, quadsheet it is in. This is a bit easier, + // because the entire primitive gets the same color. + + if (_colors.empty()) { + // We need one entry for not-a-qsheet, indicated by white. + _colors.push_back(Colorf(0.85, 0.85, 0.85, 1.0)); + } + + // Is this a quadsheet? + ushort i1 = 0; + if (strip._row_id < 0) { + // Yep! Assign a new color, if it doesn't already have one. + ColorSheetMap::iterator ci = _color_sheets.find(strip._row_id); + + if (ci == _color_sheets.end()) { + Colorf color1; + make_random_color(color1); + i1 = _colors.size(); + _colors.push_back(color1); + _color_sheets[strip._row_id] = i1; + } else { + i1 = (*ci).second; + } + } + + // Now i1 is the color we want to assign to the whole primitive. + // Just set all vertices to the same color. + int num_verts = prim.get_num_verts(); + for (int i = 0; i < num_verts; i++) { + prim.get_vertex(i).set_color_value(&_colors[0], i1); + } + + } else if (_bucket->_show_quads) { + // _show_quads means to show the assembling of tris into quads and fans. + + // We use the following color convention: + + // white: unchanged; as supplied by user. + // dark blue: quads made in the initial pass. These are more certain. + // light blue: quads made in the second pass. These are less certain. + // very light blue: quadstrips. These are unlikely to appear. + // random shades of red: triangles and tristrips. + // green: fans and retesselated fan polygons. + + if (_colors.empty()) { + // We need a handful of entries. + _colors.push_back(Colorf(0.85, 0.85, 0.85, 1.0)); // default: white + _colors.push_back(Colorf(0.0, 0.0, 0.75, 1.0)); // dark blue + _colors.push_back(Colorf(0.4, 0.4, 0.8, 1.0)); // light blue + _colors.push_back(Colorf(0.6, 0.6, 1.0, 1.0)); // very light blue + _colors.push_back(Colorf(0.2, 0.8, 0.2, 1.0)); // green + } + + ushort i1; + Colorf color1; + if (strip._origin == MO_user) { + i1 = 0; + } else if (strip._origin == MO_firstquad) { + i1 = 1; + } else if (strip._origin == MO_fanpoly) { + i1 = 4; + } else { + switch (orig_type) { + case BPT_quad: + i1 = 2; + break; + + case BPT_quadstrip: + i1 = 3; + break; + + case BPT_tristrip: + make_random_color(color1); + // Make it a shade of red. + if (color1[0] < color1[1]) { + float t = color1[0]; + color1[0] = color1[1]; + color1[1] = t; + } + color1[2] = color1[1]; + i1 = _colors.size(); + _colors.push_back(color1); + break; + + case BPT_trifan: + make_random_color(color1); + // Make it a shade of green. + if (color1[0] > color1[1]) { + float t = color1[0]; + color1[0] = color1[1]; + color1[1] = t; + } + color1[2] = color1[0]; + i1 = _colors.size(); + _colors.push_back(color1); + break; + + default: + i1 = 0; + } + } + + // Now i1 is the color we want to assign to the whole primitive. + // Just set all vertices to the same color. + int num_verts = prim.get_num_verts(); + for (int i = 0; i < num_verts; i++) { + prim.get_vertex(i).set_color_value(&_colors[0], i1); + } + } + + return prim; +} + + +template +void MesherTempl:: +finalize() { + if (!_colors.empty()) { + // Create an array in the bucket we might use to add to geoms. + PTA_Colorf colors(_colors.size()); + for (int i = 0; i < _colors.size(); i++) { + colors[i] = _colors[i]._v; + } + _bucket->set_colors(colors); + + _colors.clear(); + _color_sheets.clear(); + } +} + + + +template +void MesherTempl:: +show(ostream &out) { + /* + out << _edges.size() << " edges:\n"; + copy(_edges.begin(), _edges.end(), ostream_iterator(out, "\n")); + */ + + out << _verts.size() << " verts:\n"; + Verts::const_iterator vi; + + for (vi = _verts.begin(); vi != _verts.end(); ++vi) { + const Vertex &v = (*vi).first; + const EdgePtrs &edges = (*vi).second; + out << v << " shares " << count_vert_edges(edges) << " edges:\n"; + EdgePtrs::const_iterator ei; + for (ei = edges.begin(); ei != edges.end(); ++ei) { + if (!(*ei)->_strips.empty() || !(*ei)->_opposite->_strips.empty()) { + out << " " << **ei << "\n"; + } + } + } + + out << _tris.size() << " tris:\n"; + copy(_tris.begin(), _tris.end(), ostream_iterator(out, "\n")); + + out << _quads.size() << " quads:\n"; + copy(_quads.begin(), _quads.end(), ostream_iterator(out, "\n")); + + out << _strips.size() << " strips:\n"; + copy(_strips.begin(), _strips.end(), ostream_iterator(out, "\n")); +} + + +template +int MesherTempl:: +count_vert_edges(const EdgePtrs &edges) const { + int count = 0; + EdgePtrs::const_iterator ei; + for (ei = edges.begin(); ei != edges.end(); ++ei) { + count += (!(*ei)->_strips.empty() || !(*ei)->_opposite->_strips.empty()); + } + return count; +} + +template +list::Strip> &MesherTempl:: +choose_strip_list(const Strip &strip) { + switch (strip._status) { + case MS_done: + return _done; + + case MS_dead: + return _dead; + + case MS_alive: + switch (strip._type) { + case BPT_tri: + return _tris; + + case BPT_quad: + return _quads; + + default: + return _strips; + } + + default: + builder_cat.fatal() << "Invalid strip status!\n"; + abort(); + } + + return _strips; // Unreachable; this is just to make the compiler happy. +} + + +template +void MesherTempl:: +build_sheets() { + int first_row_id = 1; + + // First, move all the quads to our own internal list. + Strips pre_sheeted; + pre_sheeted.splice(pre_sheeted.end(), _quads); + + while (!pre_sheeted.empty()) { + // Pick the first quad on the list. + + Strips::iterator best = pre_sheeted.begin(); + + // If the row_id is negative, we've already built a sheet out of + // this quad. Leave it alone. We also need to leave it be if it + // has no available edges. + if ((*best)._row_id >= 0 && + (*best)._status == MS_alive && + !(*best)._edges.empty()) { + // There are two possible sheets we could make from this quad, + // in two different orientations. Measure them both and figure + // out which one is best. + + const Edge *edge_a = (*best)._edges.front(); + const Edge *edge_b = (*best).find_adjacent_edge(edge_a); + + int num_prims_a = 0; + int num_rows_a = 0; + int first_row_id_a = first_row_id; + (*best).measure_sheet(edge_a, true, num_prims_a, num_rows_a, + first_row_id_a, 0, 0); + first_row_id += num_rows_a; + double avg_length_a = (double)num_prims_a / (double)num_rows_a; + + int num_prims_b = 0; + int num_rows_b = 0; + int first_row_id_b = first_row_id; + double avg_length_b; + if (edge_b != NULL) { + (*best).measure_sheet(edge_b, true, num_prims_b, num_rows_b, + first_row_id_b, 0, 0); + first_row_id += num_rows_b; + avg_length_b = (double)num_prims_b / (double)num_rows_b; + } + + // Which sheet is better? + if (edge_b != NULL && avg_length_b >= avg_length_a) { + // Sheet b. That's easy. + (*best).cut_sheet(first_row_id_b, true, *_bucket); + + } else { + // Nope, sheet a is better. This is a bit of a nuisance + // because we've unfortunately wiped out the information we + // stored when we measured sheet a. We'll have to do it + // again. + + num_prims_a = 0; + num_rows_a = 0; + first_row_id_a = first_row_id; + (*best).measure_sheet(edge_a, true, num_prims_a, num_rows_a, + first_row_id_a, 0, 0); + first_row_id += num_rows_a; + + // Now we can cut it. + (*best).cut_sheet(first_row_id_a, true, *_bucket); + } + } + + // Now put it somewhere. We'll never see this quad again in + // build_sheets(). + Strips &list = choose_strip_list(*best); + list.splice(list.end(), pre_sheeted, best); + } +} + + +template +void MesherTempl:: +find_fans() { +#ifdef SUPPORT_FANS + // Consider all vertices. Any vertex with over a certain number of + // edges connected to it is eligible to become a fan. + + Verts::iterator vi; + + for (vi = _verts.begin(); vi != _verts.end(); ++vi) { + EdgePtrs &edges = (*vi).second; + + // 14 is the magic number of edges. 12 edges or fewer are likely + // to be found on nearly every vertex in a quadsheet (six edges + // times two, one each way). We don't want to waste time fanning + // out each vertex of a quadsheet, and we don't want to break up + // the quadsheets anyway. We bump this up to 14 because some + // quadsheets are defined with triangles flipped here and there. + if (edges.size() > 6) { + const Vertex &v = (*vi).first; + + // Build up a list of far fan edges. + typedef vector FanMakers; + + FanMakers fans; + + EdgePtrs::iterator ei; + Edge::Strips::iterator si; + for (ei = edges.begin(); ei != edges.end(); ++ei) { + for (si = (*ei)->_strips.begin(); + si != (*ei)->_strips.end(); + ++si) { + Strip *strip = *si; + if (strip->_type == BPT_tri) { + fans.push_back(FanMaker(&v, strip, this)); + } + } + } + + // Sort the fans list by edge pointers, and remove duplicates. + sort(fans.begin(), fans.end()); + fans.erase(unique(fans.begin(), fans.end()), + fans.end()); + + FanMakers::iterator fi, fi2; + + // Now pull out connected edges. + int joined_any; + do { + joined_any = false; + for (fi = fans.begin(); fi != fans.end(); ++fi) { + if (!(*fi).is_empty()) { + fi2 = fi; + for (++fi2; fi2 != fans.end(); ++fi2) { + if (!(*fi2).is_empty()) { + joined_any = (*fi).join(*fi2); + } + } + } + } + } while (joined_any); + + for (fi = fans.begin(); fi != fans.end(); ++fi) { + if ((*fi).is_valid()) { + (*fi).build(); + } + } + } + } +#endif +} + + + +//////////////////////////////////////////////////////////////////// +// Function: MesherTempl::make_quads +// Access: Public +// Description: Attempts to join up all the single tris to its +// neighbor and reconstruct a pattern of quads, suitable +// for making into quadsheets or at least quadstrips. +//////////////////////////////////////////////////////////////////// +template +void MesherTempl:: +make_quads() { + // Ideally, we want to match tris across their hypotenuse to make a + // pattern of quads. (This assumes that we are working with a + // triangulated mesh pattern, of course. If we have some other + // pattern of tris, all bets are off and it doesn't really matter + // anyway.) + + // First, we'll find all the tris that have no doubt about their + // ideal mate, and pair them up right away. The others we'll get to + // later. This way, the uncertain matches won't pollute the quad + // alignment for everyone else. + + typedef pair Pair; + typedef pair Matched; + typedef vector SoulMates; + + SoulMates soulmates; + + Strip *tri, *mate, *mate2; + Edge *common_edge, *common_edge2; + + Strips::iterator si; + for (si = _tris.begin(); si != _tris.end(); ++si) { + tri = &(*si); + + if (tri->_status == MS_alive) { + if (tri->find_ideal_mate(mate, common_edge, *_bucket)) { + // Does our chosen mate want us too? + if (mate->_type == BPT_tri && mate->_status == MS_alive && + mate->find_ideal_mate(mate2, common_edge2, *_bucket) && + mate2 == tri) { + // Hooray! + soulmates.push_back(Matched(Pair(tri, mate), common_edge)); + // We'll temporarily mark the two tris as paired. + tri->_status = MS_paired; + mate->_status = MS_paired; + } + } + } + } + + // Now that we've found all the tris that are sure about each other, + // mate them. + SoulMates::iterator mi; + for (mi = soulmates.begin(); mi != soulmates.end(); ++mi) { + tri = (*mi).first.first; + mate = (*mi).first.second; + common_edge = (*mi).second; + + nassertv(tri->_status == MS_paired); + nassertv(mate->_status == MS_paired); + tri->_status = MS_alive; + mate->_status = MS_alive; + + Strip::mate_pieces(common_edge, *tri, *mate, *_bucket); + tri->_origin = MO_firstquad; + } + + // Now move all the strips off the tri list that no longer belong. + Strips::iterator next; + si = _tris.begin(); + while (si != _tris.end()) { + next = si; + ++next; + + Strips &list = choose_strip_list(*si); + if (&list != &_tris) { + list.splice(list.end(), _tris, si); + } + + si = next; + } +} + +template +void MesherTempl:: +meshList(Strips &strips) { + while (!strips.empty()) { + // Pick the first strip on the list. + + Strips::iterator best = strips.begin(); + + if ((*best)._status == MS_alive) { + (*best).mate(*_bucket); + } + + // Put the strip back on the end of whichever list it wants. This + // might be the same list, if the strip is still alive, or it + // might be _done or _dead. + Strips &list = choose_strip_list(*best); + list.splice(list.end(), strips, best); + } +} diff --git a/panda/src/builder/mesherTempl.h b/panda/src/builder/mesherTempl.h new file mode 100644 index 0000000000..d26c59c562 --- /dev/null +++ b/panda/src/builder/mesherTempl.h @@ -0,0 +1,77 @@ +// Filename: mesher.h +// Created by: drose (15Sep97) +// +//////////////////////////////////////////////////////////////////// +#ifndef MESHERTEMPL_H +#define MESHERTEMPL_H + +#include + +#include "builderBucket.h" +#include "mesherEdge.h" +#include "mesherStrip.h" + +#include +#include +#include +#include + +template +class MesherFanMaker; + +template +class MesherTempl { +public: + typedef PrimType Prim; + typedef TYPENAME PrimType::Vertex Vertex; + typedef TYPENAME PrimType::DAttrib MAttrib; + typedef MesherEdge Edge; + typedef MesherStrip Strip; + typedef MesherFanMaker FanMaker; + + MesherTempl(BuilderBucket *bucket); + + int add_prim(const Prim &prim, MesherStripOrigin origin = MO_user); + void mesh(); + Prim getPrim(); + + void finalize(); + + void show(ostream &out); + +protected: + typedef list Strips; + typedef set > Edges; + typedef set > EdgePtrs; + typedef map > Verts; + + // This is used for show-tstrips. + typedef vector Colors; + // And this is used for show-qsheets. + typedef map > ColorSheetMap; + + int count_vert_edges(const EdgePtrs &edges) const; + list &choose_strip_list(const Strip &strip); + + void build_sheets(); + void find_fans(); + void make_quads(); + void meshList(Strips &strips); + + Strips _tris, _quads, _strips; + Strips _dead, _done; + Verts _verts; + Edges _edges; + int _stripIndex; + BuilderBucket *_bucket; + Strips::iterator _next_strip; + Colors _colors; + ColorSheetMap _color_sheets; + + friend class MesherStrip; + friend class MesherFanMaker; +}; + +#include "mesherTempl.I" + +#endif diff --git a/panda/src/builder/pta_BuilderC.cxx b/panda/src/builder/pta_BuilderC.cxx new file mode 100644 index 0000000000..de9fa8ea10 --- /dev/null +++ b/panda/src/builder/pta_BuilderC.cxx @@ -0,0 +1,11 @@ +// Filename: pta_BuilderC.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "pta_BuilderC.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/builder/pta_BuilderC.h b/panda/src/builder/pta_BuilderC.h new file mode 100644 index 0000000000..9855066941 --- /dev/null +++ b/panda/src/builder/pta_BuilderC.h @@ -0,0 +1,37 @@ +// Filename: pta_BuilderC.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PTA_BUILDERC_H +#define PTA_BUILDERC_H + +#include + +#include "vector_BuilderC.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PTA_BuilderC +// Description : A pta of BuilderCs. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a pta of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the pta again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, RefCountObj); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, PointerToBase >); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, PointerToArray) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, ConstPointerToArray) + +typedef PointerToArray PTA_BuilderC; +typedef ConstPointerToArray CPTA_BuilderC; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/builder/pta_BuilderN.cxx b/panda/src/builder/pta_BuilderN.cxx new file mode 100644 index 0000000000..ebd86762e8 --- /dev/null +++ b/panda/src/builder/pta_BuilderN.cxx @@ -0,0 +1,11 @@ +// Filename: pta_BuilderN.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "pta_BuilderN.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/builder/pta_BuilderN.h b/panda/src/builder/pta_BuilderN.h new file mode 100644 index 0000000000..9535c29a08 --- /dev/null +++ b/panda/src/builder/pta_BuilderN.h @@ -0,0 +1,37 @@ +// Filename: pta_BuilderN.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PTA_BUILDERN_H +#define PTA_BUILDERN_H + +#include + +#include "vector_BuilderN.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PTA_BuilderN +// Description : A pta of BuilderNs. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a pta of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the pta again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, RefCountObj); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, PointerToBase >); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, PointerToArray) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, ConstPointerToArray) + +typedef PointerToArray PTA_BuilderN; +typedef ConstPointerToArray CPTA_BuilderN; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/builder/pta_BuilderTC.cxx b/panda/src/builder/pta_BuilderTC.cxx new file mode 100644 index 0000000000..7ef3df926f --- /dev/null +++ b/panda/src/builder/pta_BuilderTC.cxx @@ -0,0 +1,11 @@ +// Filename: pta_BuilderTC.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "pta_BuilderTC.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/builder/pta_BuilderTC.h b/panda/src/builder/pta_BuilderTC.h new file mode 100644 index 0000000000..dc7f3bdab6 --- /dev/null +++ b/panda/src/builder/pta_BuilderTC.h @@ -0,0 +1,37 @@ +// Filename: pta_BuilderTC.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PTA_BUILDERTC_H +#define PTA_BUILDERTC_H + +#include + +#include "vector_BuilderTC.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PTA_BuilderTC +// Description : A pta of BuilderTCs. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a pta of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the pta again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, RefCountObj); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, PointerToBase >); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, PointerToArray) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, ConstPointerToArray) + +typedef PointerToArray PTA_BuilderTC; +typedef ConstPointerToArray CPTA_BuilderTC; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/builder/pta_BuilderV.cxx b/panda/src/builder/pta_BuilderV.cxx new file mode 100644 index 0000000000..878a899af2 --- /dev/null +++ b/panda/src/builder/pta_BuilderV.cxx @@ -0,0 +1,11 @@ +// Filename: pta_BuilderV.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "pta_BuilderV.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/builder/pta_BuilderV.h b/panda/src/builder/pta_BuilderV.h new file mode 100644 index 0000000000..55f5b62f02 --- /dev/null +++ b/panda/src/builder/pta_BuilderV.h @@ -0,0 +1,37 @@ +// Filename: pta_BuilderV.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PTA_BUILDERV_H +#define PTA_BUILDERV_H + +#include + +#include "vector_BuilderV.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PTA_BuilderV +// Description : A pta of BuilderVs. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a pta of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the pta again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, RefCountObj); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, PointerToBase >); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, PointerToArray) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, ConstPointerToArray) + +typedef PointerToArray PTA_BuilderV; +typedef ConstPointerToArray CPTA_BuilderV; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/builder/test_builder.cxx b/panda/src/builder/test_builder.cxx new file mode 100644 index 0000000000..3cd9723e55 --- /dev/null +++ b/panda/src/builder/test_builder.cxx @@ -0,0 +1,199 @@ +// Filename: tb.cxx +// Created by: drose (09Sep97) +// +//////////////////////////////////////////////////////////////////// + +#include "builder.h" +#include "builderPrim.h" +#include "mesher.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class polygon : public BuilderPrimI { +public: +}; + +extern int hedface_vt_len; +extern Vertexf hedface_vt[]; +extern polygon polys[]; +extern int num_polys; + + +class ReportGeoms : + public TraverserVisitor { +public: + bool reached_node(Node *node, const NullAttributeWrapper &, NullLevelState &); +}; + +bool ReportGeoms:: +reached_node(Node *node, const NullAttributeWrapper &, NullLevelState &) { + if (node->is_of_type(GeomNode::get_class_type())) { + GeomNode *geomNode = (GeomNode *)node; + nout << "\n" << *geomNode << ", " << geomNode->get_num_geoms() + << " geoms:\n"; + int num_geoms = geomNode->get_num_geoms(); + for (int i = 0; i < num_geoms; i++) { + dDrawable *draw = geomNode->get_geom(i); + Geom *geom = DCAST(Geom, draw); + nout << *geom << ":\n"; + geom->output_verbose(nout); + } + + } else { + nout << *node << "\n"; + } + return true; +} + +int +main(void) { + +#if 1 + NamedNode *root = new NamedNode("root"); + + Builder builder; + + PTA_Vertexf v_array(hedface_vt_len); + int i; + for (i = 0; i < hedface_vt_len; i++) { + v_array[i] = hedface_vt[i]; + } + PTA_Colorf c_array(num_polys); + for (i = 0; i < num_polys; i++) { + c_array[i].set(1.0, 1.0, 1.0, 1.0); + } + + BuilderBucket bucket; + bucket._node = root; + bucket.set_coords(v_array); + bucket.set_colors(c_array); + bucket._mesh = true; + + for (i = 0; i < num_polys; i++) { + polys[i].set_color(i); + // nout << "Adding polygon " << i << ": " << polys[i] << "\n"; + builder.add_prim_nonindexed(bucket, polys[i]); + } + + ReportGeoms rg; + + GeomNode *gn = builder.build(); + nout << "Built " << (void *)gn << "\n"; + if (gn != NULL) { + NullLevelState level_state; + rg.reached_node(gn, NullAttributeWrapper(), level_state); + } + + nout << "\nTraversing root:\n"; + df_traverse(root, rg, NullAttributeWrapper(), NullLevelState(), + RenderRelation::get_class_type()); + +#else + Builder builder; + BuilderPrimI prim; + + BuilderBucket bucket; + + ushort p = 0; + int xi, yi; + xi = 4; + yi = xi; + + int x, y; + PTA_Vertexf coords(xi * yi); + PTA_Normalf normals(xi * yi); + PTA_Colorf colors(3); + + double xv, yv, zv; + for (y = 0; y < yi; y++) { + for (x = 0; x < xi; x++) { + xv = (double)x / (double)(xi-1) - 0.5; + yv = (double)y / (double)(yi-1) - 0.5; + + //zv = (y <= x) ? 0.0 : ((float)(y-x)*(y-x) / (float)(yi)); + //zv = sqrt(1.0 - (xv*xv + yv*yv)); + //zv = sqrt(1.0 - (xv*xv + yv*yv)); + //zv = sqrt(max(0.25 - (xv*xv + yv*yv), 0.0)); + zv = 0.0; + + p = y*xi + x; + coords[p].set(xv, yv, zv); + } + } + + for (y = 0; y < yi-1; y++) { + for (x = 0; x < xi-1; x++) { + p = y*xi + x; + pfVec3 p1 = coords[p+xi]; + pfVec3 p2 = coords[p]; + pfVec3 p3 = coords[p+1]; + normals[p] = normalize(cross(p1-p2, p2-p3)); + } + } + + colors[0].set(1.0, 1.0, 1.0, 1.0); + colors[1].set(1.0, 0.0, 0.0, 1.0); + colors[2].set(0.0, 0.0, 1.0, 1.0); + + bucket.set_coords(coords); + bucket.set_normals(normals); + bucket.set_colors(colors); + + bucket.setAttr(PFSTATE_FRONTMTL, new pfMaterial); + bucket.setMode(PFSTATE_ENLIGHTING, PF_ON); + + bucket._mesh = true; + bucket._max_tfan_angle = 60; + bucket._show_tstrips = true; + bucket._retesselate_coplanar = true; + builder.add_bucket(bucket); + + nout << "Adding polygons.\n"; + for (y = 0; y < yi-1; y++) { + for (x = 0; x < xi-1; x++) { + p = y*xi + x; + BuilderVertexI bv0(p), bv1(p+1), bv4(p+xi), bv5(p+xi+1); + if (p==0) { + builder.add_prim(BuilderPrimI(bv0, bv5, bv4).set_color(1)); + builder.add_prim(BuilderPrimI(bv1, bv5, bv0).set_color(1)); + } else if (p==5) { + builder.add_prim(BuilderPrimI(bv0, bv5, bv4).set_color(1)); + builder.add_prim(BuilderPrimI(bv1, bv5, bv0).set_color(1)); + } else if (p==1 || p==4) { + builder.add_prim(BuilderPrimI(bv0, bv1, bv4).set_color(1)); + builder.add_prim(BuilderPrimI(bv1, bv5, bv4).set_color(1)); + } else { + builder.add_prim(BuilderPrimI(bv0, bv5, bv4).set_color(0)); + builder.add_prim(BuilderPrimI(bv1, bv5, bv0).set_color(0)); + } + + } + } + + nout << "Building.\n"; + pfNode *root = builder.build_all_buckets(); + + if (root!=NULL) { + report_geosets(root); + } + + pfdStoreFile(root, "builder.pfa"); + +#endif + + return 0; +} + diff --git a/panda/src/builder/test_builder_data.cxx b/panda/src/builder/test_builder_data.cxx new file mode 100644 index 0000000000..358eb2507c --- /dev/null +++ b/panda/src/builder/test_builder_data.cxx @@ -0,0 +1,705 @@ +// Filename: tbData.cxx +// Created by: drose (11Sep97) +// +//////////////////////////////////////////////////////////////////// + +#include "builderPrim.h" + +int hedface_vt_len = 337; +Vertexf hedface_vt[337] = { + Vertexf(), + Vertexf(0.045221, 6.59701, 0.374149), + Vertexf(0.051493, 6.53504, 0.41213), + Vertexf(-0.004945, 6.54228, 0.460135), + Vertexf(-0.006027, 6.59713, 0.428656), + Vertexf(-0.163023, 6.4847, 0.375736), + Vertexf(-0.204045, 6.4752, 0.347943), + Vertexf(-0.18025, 6.50566, 0.329401), + Vertexf(-0.159739, 6.51041, 0.343297), + Vertexf(-0.041928, 6.54233, 0.460079), + Vertexf(-0.040616, 6.59717, 0.428604), + Vertexf(0.116272, 6.4844, 0.376158), + Vertexf(0.119393, 6.45869, 0.408606), + Vertexf(0.057765, 6.47306, 0.45011), + Vertexf(-0.003863, 6.48744, 0.491613), + Vertexf(0.157357, 6.47482, 0.348488), + Vertexf(0.181021, 6.44431, 0.367102), + Vertexf(-0.098236, 6.5352, 0.411904), + Vertexf(-0.091699, 6.59716, 0.373943), + Vertexf(0.113151, 6.51012, 0.343709), + Vertexf(0.05625, 6.68185, 0.38825), + Vertexf(0.052935, 6.65809, 0.388478), + Vertexf(0.038183, 6.65814, 0.391172), + Vertexf(0.038209, 6.68243, 0.390851), + Vertexf(-0.08114, 6.70556, 0.357325), + Vertexf(-0.074757, 6.70668, 0.327237), + Vertexf(-0.06435, 6.75423, 0.318492), + Vertexf(-0.076311, 6.74108, 0.348446), + Vertexf(0.056607, 6.65772, 0.361073), + Vertexf(0.056632, 6.68102, 0.360765), + Vertexf(0.050611, 6.65837, 0.333519), + Vertexf(-0.105274, 6.67711, 0.384106), + Vertexf(-0.102005, 6.6582, 0.384442), + Vertexf(-0.10409, 6.65786, 0.358346), + Vertexf(-0.104068, 6.6782, 0.358076), + Vertexf(0.133694, 6.50533, 0.329875), + Vertexf(-0.056482, 6.84607, 0.309156), + Vertexf(-0.053944, 6.80178, 0.309746), + Vertexf(-0.088991, 6.83803, 0.302363), + Vertexf(-0.07393, 6.90532, 0.300343), + Vertexf(0.028345, 6.90521, 0.300498), + Vertexf(0.043257, 6.83789, 0.302563), + Vertexf(0.008112, 6.80171, 0.30984), + Vertexf(0.010745, 6.846, 0.309258), + Vertexf(-0.094717, 6.80544, 0.300714), + Vertexf(-0.194166, 6.81436, 0.278684), + Vertexf(-0.162377, 6.84458, 0.287335), + Vertexf(-0.103944, 6.82835, 0.296707), + Vertexf(-0.204143, 6.94657, 0.295928), + Vertexf(-0.201354, 6.95975, 0.298222), + Vertexf(-0.169019, 6.95635, 0.305305), + Vertexf(-0.178088, 6.94025, 0.302407), + Vertexf(-0.087574, 6.65823, 0.388024), + Vertexf(-0.087552, 6.67872, 0.387753), + Vertexf(-0.039303, 6.65201, 0.397128), + Vertexf(0.051495, 6.70678, 0.35635), + Vertexf(0.033453, 6.70736, 0.358952), + Vertexf(-0.036388, 6.44535, 0.478493), + Vertexf(-0.023597, 6.43031, 0.477217), + Vertexf(-0.098546, 6.43648, 0.44227), + Vertexf(-0.100206, 6.44582, 0.442143), + Vertexf(-0.164024, 6.44429, 0.404327), + Vertexf(-0.163195, 6.43863, 0.404403), + Vertexf(-0.227839, 6.44475, 0.366484), + Vertexf(0.11709, 6.44598, 0.404725), + Vertexf(0.13215, 6.4488, 0.39385), + Vertexf(0.168279, 6.44653, 0.364598), + Vertexf(0.038949, 6.65899, 0.336169), + Vertexf(-0.007109, 6.65198, 0.397178), + Vertexf(0.046712, 6.70598, 0.324792), + Vertexf(0.036091, 6.73257, 0.383482), + Vertexf(0.027241, 6.74335, 0.351049), + Vertexf(-0.024001, 6.23599, 0.606704), + Vertexf(-0.024029, 6.21889, 0.613174), + Vertexf(-0.046614, 6.21572, 0.610968), + Vertexf(-0.051099, 6.234, 0.60249), + Vertexf(0.003109, 6.23394, 0.602571), + Vertexf(-0.001441, 6.21567, 0.611036), + Vertexf(-0.042134, 6.20409, 0.627544), + Vertexf(-0.039909, 6.2022, 0.648599), + Vertexf(-0.024097, 6.19903, 0.644253), + Vertexf(-0.024073, 6.19793, 0.627118), + Vertexf(-0.024129, 6.18605, 0.656217), + Vertexf(-0.010583, 6.1849, 0.660713), + Vertexf(-0.008288, 6.20216, 0.648647), + Vertexf(-0.02414, 6.18435, 0.662939), + Vertexf(-0.024101, 6.20376, 0.650817), + Vertexf(-0.085163, 6.65912, 0.335982), + Vertexf(-0.024152, 6.1629, 0.656118), + Vertexf(-0.035442, 6.16465, 0.655261), + Vertexf(-0.037687, 6.18493, 0.660672), + Vertexf(-0.024131, 6.15434, 0.63571), + Vertexf(-0.033164, 6.15576, 0.63633), + Vertexf(-0.012855, 6.16463, 0.655295), + Vertexf(-0.015095, 6.15574, 0.636358), + Vertexf(-0.02403, 6.20933, 0.606675), + Vertexf(-0.005995, 6.20405, 0.627598), + Vertexf(-0.024063, 6.20715, 0.627798), + Vertexf(-0.024144, 6.1681, 0.653605), + Vertexf(-0.003116, 6.4359, 0.174903), + Vertexf(-0.043102, 6.43593, 0.174842), + Vertexf(-0.043515, 6.48837, 0.174148), + Vertexf(-0.002372, 6.48835, 0.174209), + Vertexf(0.115485, 6.41793, 0.441265), + Vertexf(0.049948, 6.38957, 0.515453), + Vertexf(0.051474, 6.43632, 0.442496), + Vertexf(0.11625, 6.44229, 0.404773), + Vertexf(0.11793, 6.44966, 0.404678), + Vertexf(0.054838, 6.455, 0.442254), + Vertexf(0.053156, 6.44566, 0.442375), + Vertexf(-0.215724, 6.44831, 0.36377), + Vertexf(-0.178985, 6.44921, 0.393528), + Vertexf(0.132116, 6.43478, 0.376846), + Vertexf(0.081514, 6.43627, 0.276983), + Vertexf(0.125673, 6.433, 0.277093), + Vertexf(-0.023729, 6.39172, 0.536235), + Vertexf(-0.16259, 6.41625, 0.440871), + Vertexf(0.081667, 6.43492, 0.175042), + Vertexf(0.042326, 6.43548, 0.174976), + Vertexf(0.042875, 6.48598, 0.174308), + Vertexf(0.082021, 6.48349, 0.1744), + Vertexf(-0.010775, 6.44532, 0.478531), + Vertexf(-0.179019, 6.43519, 0.376525), + Vertexf(-0.128071, 6.43656, 0.276666), + Vertexf(-0.088566, 6.43707, 0.276718), + Vertexf(-0.120423, 6.43613, 0.41066), + Vertexf(-0.179061, 6.48408, 0.349586), + Vertexf(-0.128102, 6.48581, 0.276014), + Vertexf(-0.166981, 6.48194, 0.276007), + Vertexf(0.073698, 6.44983, 0.427624), + Vertexf(0.073664, 6.43581, 0.410621), + Vertexf(0.13215, 6.4488, 0.39385), + Vertexf(-0.053819, 6.48884, 0.416828), + Vertexf(-0.043668, 6.48972, 0.276088), + Vertexf(-0.088788, 6.48758, 0.276049), + Vertexf(-0.12075, 6.48629, 0.383705), + Vertexf(0.007052, 6.45585, 0.460591), + Vertexf(0.00726, 6.48879, 0.416675), + Vertexf(-0.054026, 6.45591, 0.460743), + Vertexf(0.132646, 6.483, 0.349918), + Vertexf(0.081868, 6.48484, 0.276341), + Vertexf(0.042723, 6.48733, 0.276249), + Vertexf(0.074482, 6.48596, 0.383667), + Vertexf(0.120223, 6.48035, 0.276458), + Vertexf(0.168279, 6.44653, 0.364598), + Vertexf(-0.053173, 6.45076, 0.460301), + Vertexf(-0.053207, 6.43674, 0.443297), + Vertexf(0.006154, 6.43669, 0.443142), + Vertexf(0.006188, 6.45071, 0.460146), + Vertexf(-0.088413, 6.43573, 0.174777), + Vertexf(-0.043255, 6.43728, 0.276783), + Vertexf(-0.002525, 6.4897, 0.27615), + Vertexf(0.11279, 6.41322, 0.399904), + Vertexf(0.181156, 6.44226, 0.277053), + Vertexf(0.112895, 6.40928, 0.325903), + Vertexf(-0.091652, 6.37645, 0.374546), + Vertexf(-0.159678, 6.40957, 0.325491), + Vertexf(-0.159784, 6.41351, 0.399492), + Vertexf(-0.091728, 6.38228, 0.4325), + Vertexf(0.042174, 6.43683, 0.276917), + Vertexf(-0.008253, 6.46035, 0.47983), + Vertexf(0.007052, 6.45585, 0.460591), + Vertexf(-0.054026, 6.45591, 0.460743), + Vertexf(-0.038879, 6.46038, 0.479784), + Vertexf(-0.04324, 6.48748, 0.491554), + Vertexf(0.006188, 6.45071, 0.460146), + Vertexf(0.073698, 6.44983, 0.427624), + Vertexf(-0.120389, 6.45015, 0.427664), + Vertexf(-0.053173, 6.45076, 0.460301), + Vertexf(-0.140225, 7.00983, 0.321446), + Vertexf(-0.123119, 7.01151, 0.328341), + Vertexf(-0.134749, 6.97962, 0.323871), + Vertexf(-0.142948, 6.98128, 0.319329), + Vertexf(0.18119, 6.42198, 0.240696), + Vertexf(0.112929, 6.389, 0.289545), + Vertexf(-0.023958, 6.27715, 0.605697), + Vertexf(-0.023963, 6.25473, 0.593607), + Vertexf(0.007645, 6.2539, 0.604398), + Vertexf(0.012162, 6.27018, 0.615932), + Vertexf(0.044667, 6.35602, 0.338395), + Vertexf(-0.023594, 6.32304, 0.387244), + Vertexf(-0.023627, 6.34332, 0.423602), + Vertexf(0.044634, 6.3763, 0.374752), + Vertexf(-0.101866, 6.45517, 0.442017), + Vertexf(-0.120958, 6.45335, 0.427621), + Vertexf(-0.179269, 6.45115, 0.393502), + Vertexf(-0.164852, 6.44996, 0.404251), + Vertexf(-0.127919, 6.43522, 0.174725), + Vertexf(-0.172614, 6.43334, 0.174683), + Vertexf(-0.166828, 6.4806, 0.174066), + Vertexf(-0.12795, 6.48446, 0.174073), + Vertexf(-0.172766, 6.43469, 0.276624), + Vertexf(-0.023673, 6.35105, 0.465508), + Vertexf(0.044558, 6.38213, 0.432706), + Vertexf(-0.166306, 6.45899, 0.408174), + Vertexf(0.114721, 6.39357, 0.477756), + Vertexf(-0.068907, 6.93005, 0.3295), + Vertexf(-0.081264, 6.91456, 0.321684), + Vertexf(0.021212, 6.29084, 0.620743), + Vertexf(-0.02399, 6.26675, 0.620987), + Vertexf(-0.023978, 6.28932, 0.628239), + Vertexf(-0.060115, 6.27026, 0.615822), + Vertexf(-0.069134, 6.29094, 0.620607), + Vertexf(-0.023865, 6.32255, 0.574454), + Vertexf(0.048421, 6.34283, 0.588411), + Vertexf(0.074274, 6.45303, 0.427583), + Vertexf(-0.161986, 6.39386, 0.477338), + Vertexf(-0.096133, 6.34298, 0.588192), + Vertexf(-0.178985, 6.44921, 0.393528), + Vertexf(-0.120389, 6.45015, 0.427664), + Vertexf(-0.091619, 6.35617, 0.338189), + Vertexf(0.077674, 7.01129, 0.328644), + Vertexf(0.08925, 6.97938, 0.324209), + Vertexf(0.035634, 6.91444, 0.32186), + Vertexf(0.023287, 6.92995, 0.329639), + Vertexf(-0.088636, 6.48623, 0.174108), + Vertexf(0.010745, 6.846, 0.309258), + Vertexf(0.015997, 6.92072, 0.308277), + Vertexf(0.094448, 7.01026, 0.32246), + Vertexf(0.15589, 6.95937, 0.298762), + Vertexf(0.123526, 6.95604, 0.305746), + Vertexf(0.097137, 6.98214, 0.319113), + Vertexf(0.030275, 6.31191, 0.617283), + Vertexf(-0.023949, 6.3137, 0.626308), + Vertexf(-0.02394, 6.29401, 0.605607), + Vertexf(-0.023916, 6.30851, 0.599061), + Vertexf(-0.003269, 6.43725, 0.276843), + Vertexf(0.091554, 6.96985, 0.299337), + Vertexf(0.08196, 6.97015, 0.302846), + Vertexf(-0.078141, 6.31202, 0.61712), + Vertexf(0.070384, 7.00207, 0.307281), + Vertexf(-0.227704, 6.44269, 0.276436), + Vertexf(-0.156456, 6.53612, 0.310859), + Vertexf(0.11003, 6.53584, 0.311261), + Vertexf(0.132438, 6.45006, 0.393834), + Vertexf(-0.097339, 6.38973, 0.515231), + Vertexf(0.132438, 6.45006, 0.393834), + Vertexf(0.074274, 6.45303, 0.427583), + Vertexf(0.120375, 6.479, 0.174517), + Vertexf(0.125825, 6.43165, 0.175152), + Vertexf(0.028345, 6.90521, 0.300498), + Vertexf(-0.159645, 6.38929, 0.289133), + Vertexf(-0.104773, 6.47324, 0.449864), + Vertexf(-0.215724, 6.44831, 0.36377), + Vertexf(-0.131591, 6.96754, 0.300267), + Vertexf(-0.161684, 6.94711, 0.283964), + Vertexf(-0.129814, 6.99298, 0.301599), + Vertexf(-0.115784, 7.00226, 0.307), + Vertexf(-0.061573, 6.9208, 0.30816), + Vertexf(-0.167969, 6.93295, 0.282982), + Vertexf(-0.040067, 6.66458, 0.505563), + Vertexf(-0.039454, 6.62437, 0.477676), + Vertexf(-0.087687, 6.65527, 0.461334), + Vertexf(-0.088272, 6.72793, 0.378401), + Vertexf(-0.088393, 6.70869, 0.445015), + Vertexf(-0.098863, 6.70395, 0.353678), + Vertexf(-0.056482, 6.84607, 0.309156), + Vertexf(-0.041096, 6.73064, 0.482186), + Vertexf(0.038076, 6.65896, 0.462433), + Vertexf(0.035976, 6.71327, 0.446101), + Vertexf(0.08381, 6.99315, 0.302152), + Vertexf(0.1486, 6.95015, 0.277399), + Vertexf(-0.00726, 6.62434, 0.477725), + Vertexf(-0.008959, 6.66524, 0.505601), + Vertexf(0.02867, 6.70656, 0.327393), + Vertexf(-0.179269, 6.45115, 0.393502), + Vertexf(0.134358, 6.93999, 0.302389), + Vertexf(0.159456, 6.94608, 0.296165), + Vertexf(0.018391, 6.75414, 0.318617), + Vertexf(-0.22767, 6.42241, 0.240078), + Vertexf(-0.069554, 7.00945, 0.310645), + Vertexf(-0.115784, 7.00226, 0.307), + Vertexf(0.163425, 6.80702, 0.199637), + Vertexf(-0.023861, 6.35313, 0.595253), + Vertexf(-0.161684, 6.94711, 0.283964), + Vertexf(-0.131591, 6.96754, 0.300267), + Vertexf(-0.127415, 6.97038, 0.30253), + Vertexf(-0.127415, 6.97038, 0.30253), + Vertexf(-0.07393, 6.90532, 0.300343), + Vertexf(0.148469, 6.8287, 0.279006), + Vertexf(0.124864, 6.92982, 0.283536), + Vertexf(-0.208912, 6.80741, 0.199075), + Vertexf(-0.147148, 6.64856, 0.281136), + Vertexf(-0.14701, 6.64735, 0.189092), + Vertexf(-0.02399, 6.22995, 0.594216), + Vertexf(-0.061573, 6.9208, 0.30816), + Vertexf(-0.046619, 6.90315, 0.320843), + Vertexf(-0.041466, 6.79966, 0.369073), + Vertexf(-0.013462, 6.80239, 0.375073), + Vertexf(0.023846, 7.00935, 0.310786), + Vertexf(0.070384, 7.00207, 0.307281), + Vertexf(-0.194019, 6.95051, 0.276882), + Vertexf(-0.011997, 6.73258, 0.482204), + Vertexf(-0.096505, 6.65755, 0.332384), + Vertexf(-0.092479, 6.70507, 0.32359), + Vertexf(0.055599, 6.80587, 0.299473), + Vertexf(0.059185, 6.82536, 0.29725), + Vertexf(-0.194037, 6.93438, 0.277095), + Vertexf(-0.194019, 6.95051, 0.276882), + Vertexf(-0.167969, 6.93295, 0.282982), + Vertexf(-0.19415, 6.82906, 0.278489), + Vertexf(-0.156387, 6.53551, 0.264837), + Vertexf(-0.024129, 6.15856, 0.637607), + Vertexf(0.1486, 6.95015, 0.277399), + Vertexf(-0.194037, 6.93438, 0.277095), + Vertexf(-0.120958, 6.45335, 0.427621), + Vertexf(0.148583, 6.93434, 0.277608), + Vertexf(-0.013559, 6.78726, 0.428727), + Vertexf(-0.041572, 6.78462, 0.42872), + Vertexf(-0.156318, 6.5349, 0.218815), + Vertexf(0.148453, 6.81399, 0.279201), + Vertexf(0.101077, 6.6483, 0.281511), + Vertexf(0.101215, 6.64708, 0.189467), + Vertexf(0.110099, 6.53523, 0.26524), + Vertexf(0.110168, 6.53462, 0.219218), + Vertexf(0.08196, 6.97015, 0.302846), + Vertexf(0.091554, 6.96985, 0.299337), + Vertexf(0.116237, 6.94682, 0.284384), + Vertexf(0.116702, 6.84429, 0.287756), + Vertexf(0.124864, 6.92982, 0.283536), + Vertexf(0.148469, 6.8287, 0.279006), + Vertexf(0.015997, 6.92072, 0.308277), + Vertexf(0.000968, 6.9031, 0.320915), + Vertexf(-0.022854, 7.0094, 0.310715), + Vertexf(-0.023987, 6.25354, 0.609743), + Vertexf(-0.023889, 6.31842, 0.587859), + Vertexf(0.039346, 6.33018, 0.605966), + Vertexf(-0.023908, 6.33618, 0.614913), + Vertexf(-0.096133, 6.34298, 0.588192), + Vertexf(-0.087138, 6.33031, 0.605775), + Vertexf(-0.023865, 6.32255, 0.574454), + Vertexf(0.048421, 6.34283, 0.588411), + Vertexf(-0.023861, 6.35313, 0.595253), + Vertexf(0.148583, 6.93434, 0.277608), + Vertexf(-0.055597, 6.25397, 0.604303), + Vertexf(0.116237, 6.94682, 0.284384), + Vertexf(-0.19415, 6.82906, 0.278489), +}; + +//#define VVAL(i) hedface_vt[i] +#define VVAL(i) i + +class polygon : public BuilderPrimI { +public: + polygon(int i, int j, int k) { + add_vertex(VVAL(i)); + add_vertex(VVAL(j)); + add_vertex(VVAL(k)); + } + polygon(int i, int j, int k, int l) { + add_vertex(VVAL(i)); + add_vertex(VVAL(j)); + add_vertex(VVAL(k)); + add_vertex(VVAL(l)); + } + polygon(int i, int j, int k, int l, int m) { + add_vertex(VVAL(i)); + add_vertex(VVAL(j)); + add_vertex(VVAL(k)); + add_vertex(VVAL(l)); + add_vertex(VVAL(m)); + } + polygon(int i, int j, int k, int l, int m, int n) { + add_vertex(VVAL(i)); + add_vertex(VVAL(j)); + add_vertex(VVAL(k)); + add_vertex(VVAL(l)); + add_vertex(VVAL(m)); + add_vertex(VVAL(n)); + } + polygon(int i, int j, int k, int l, int m, int n, int o) { + add_vertex(VVAL(i)); + add_vertex(VVAL(j)); + add_vertex(VVAL(k)); + add_vertex(VVAL(l)); + add_vertex(VVAL(m)); + add_vertex(VVAL(n)); + add_vertex(VVAL(o)); + } +}; + +polygon polys[] = { + polygon(4, 3, 2, 1), + polygon(8, 7, 6, 5), + polygon(10, 9, 3, 4), + polygon(2, 13, 12, 11), + polygon(3, 14, 13, 2), + polygon(11, 12, 16, 15), + polygon(18, 17, 9, 10), + polygon(8, 5, 17, 18), + polygon(1, 2, 11, 19), + polygon(23, 22, 21, 20), + polygon(27, 26, 25, 24), + polygon(30, 29, 28), + polygon(20, 21, 28, 29), + polygon(34, 33, 32, 31), + polygon(19, 11, 15, 35), + polygon(39, 38, 37, 36), + polygon(43, 42, 41, 40), + polygon(47, 46, 45, 44), + polygon(51, 50, 49, 48), + polygon(54, 53, 52), + polygon(31, 32, 52, 53), + polygon(56, 23, 20, 55), + polygon(55, 20, 29), + /* + polygon(60, 59, 58, 57), + polygon(63, 62, 61), + polygon(16, 66, 65, 64), + polygon(68, 67, 22), + polygon(30, 69, 55, 29), + polygon(71, 70, 23, 56), + polygon(75, 74, 73, 72), + polygon(72, 73, 77, 76), + polygon(81, 80, 79, 78), + polygon(84, 83, 82, 80), + polygon(86, 85, 83, 84), + polygon(52, 87, 54), + polygon(90, 89, 88, 85), + polygon(89, 92, 91, 88), + polygon(88, 91, 94, 93), + polygon(77, 96, 81, 95), + polygon(78, 79, 86, 97), + polygon(80, 82, 90, 79), + polygon(82, 98, 89, 90), + polygon(102, 101, 100, 99), + polygon(106, 105, 104, 103), + polygon(108, 107, 12, 13), + polygon(1, 67, 68, 4), + polygon(109, 105, 106, 64), + polygon(61, 111, 110, 63), + polygon(114, 113, 112), + polygon(104, 105, 58, 115), + polygon(62, 63, 116), + polygon(120, 119, 118, 117), + polygon(121, 58, 105, 109), + polygon(125, 124, 123, 122), + polygon(128, 127, 126), + polygon(65, 112, 130, 129), + polygon(135, 134, 133, 132), + polygon(138, 132, 137, 136), + polygon(142, 141, 140, 139), + polygon(66, 143, 114), + polygon(148, 147, 146, 145), + polygon(150, 100, 149, 124), + polygon(133, 101, 102, 151), + polygon(154, 153, 16, 152), + polygon(158, 157, 156, 155), + polygon(113, 117, 118, 159), + polygon(163, 138, 136, 160), + polygon(73, 97, 96, 77), + polygon(96, 84, 80, 81), + polygon(61, 62, 59, 60), + polygon(22, 23, 68), + polygon(9, 164, 14, 3), + polygon(109, 129, 148, 121), + polygon(57, 145, 167, 60), + polygon(172, 171, 170, 169), + polygon(174, 173, 153, 154), + polygon(178, 177, 176, 175), + polygon(182, 181, 180, 179), + polygon(186, 185, 184, 183), + polygon(190, 189, 188, 187), + polygon(122, 123, 191), + polygon(139, 140, 143), + polygon(193, 192, 181, 182), + polygon(183, 184, 138, 163), + polygon(126, 127, 134, 135), + polygon(194, 63, 186), + polygon(60, 167, 111, 61), + polygon(151, 102, 119, 141), + polygon(152, 16, 195), + polygon(66, 114, 112, 65), + polygon(141, 119, 120, 140), + polygon(170, 171, 197, 196), + polygon(200, 199, 178, 198), + polygon(202, 201, 199, 200), + polygon(204, 203, 192, 193), + polygon(136, 137, 142, 205), + polygon(207, 206, 157, 158), + polygon(167, 125, 122, 111), + polygon(191, 188, 189, 128), + polygon(128, 189, 190, 127), + polygon(210, 180, 181, 155), + polygon(214, 213, 212, 211), + polygon(206, 63, 157), + polygon(124, 149, 187, 123), + polygon(134, 215, 101, 133), + polygon(127, 190, 215, 134), + polygon(214, 217, 43), + polygon(221, 220, 219, 218), + polygon(223, 200, 198, 222), + polygon(224, 175, 201, 202), + polygon(222, 198, 224, 225), + polygon(159, 118, 99, 226), + polygon(226, 99, 100, 150), + polygon(123, 187, 188, 191), + polygon(221, 212, 228, 227), + polygon(229, 202, 200, 223), + polygon(158, 192, 203, 207), + polygon(198, 178, 175, 224), + polygon(217, 214, 211, 230), + polygon(157, 63, 231, 156), + polygon(225, 224, 202, 229), + polygon(63, 110, 185, 186), + polygon(121, 148, 145, 57), + polygon(64, 65, 129, 109), + polygon(13, 14, 160, 108), + polygon(8, 232, 7), + polygon(19, 233, 67, 1), + polygon(234, 66, 16), + polygon(62, 116, 235, 59), + polygon(149, 100, 101, 215), + polygon(205, 142, 139, 234), + polygon(145, 146, 125, 167), + polygon(160, 136, 205, 108), + polygon(108, 205, 234, 107), + polygon(117, 239, 238, 120), + polygon(103, 16, 106), + polygon(234, 139, 143, 66), + polygon(114, 239, 117, 113), + polygon(228, 212, 213, 40), + polygon(241, 210, 155, 156), + polygon(186, 183, 242, 194), + polygon(18, 87, 232, 8), + polygon(64, 106, 16), + polygon(57, 58, 121), + polygon(143, 238, 239, 114), + polygon(40, 213, 43), + polygon(111, 122, 191, 110), + polygon(129, 130, 147, 148), + polygon(112, 113, 159, 130), + polygon(147, 226, 150, 146), + polygon(146, 150, 124, 125), + polygon(172, 50, 245, 244), + polygon(169, 170, 247, 246), + polygon(247, 170, 196, 248), + polygon(249, 245, 50, 51), + polygon(252, 251, 250), + polygon(24, 53, 253, 27), + polygon(253, 53, 252, 254), + polygon(34, 31, 255), + polygon(36, 196, 197), + polygon(67, 30, 28, 21, 22), + polygon(250, 257, 254, 252), + polygon(128, 110, 191), + polygon(23, 70, 259, 258), + polygon(130, 159, 226, 147), + polygon(218, 219, 261, 260), + polygon(248, 196, 36), + polygon(263, 262, 258), + polygon(264, 69, 30, 67), + polygon(110, 128, 126, 185), + polygon(68, 262, 251, 54), + polygon(63, 206, 116), + polygon(267, 219, 220, 266), + polygon(53, 54, 251, 252), + polygon(258, 262, 68, 23), + polygon(264, 268, 71, 56), + polygon(104, 204, 195, 103), + polygon(107, 16, 12), + polygon(115, 273, 204, 104), + polygon(276, 244, 245, 46, 47, 38, 39), + polygon(39, 197, 171, 276), + polygon(235, 207, 273, 115), + polygon(280, 279, 266), + polygon(283, 282, 45, 281), + polygon(177, 76, 284, 176), + polygon(16, 107, 234), + polygon(153, 233, 35, 15), + polygon(282, 87, 25), + polygon(287, 286, 248, 36), + polygon(215, 190, 187, 149), + polygon(288, 42, 43), + polygon(250, 263, 292, 257), + polygon(255, 294, 293, 34), + polygon(24, 25, 294, 255), + polygon(296, 41, 42, 295), + polygon(258, 259, 292, 263), + polygon(56, 55, 69, 264), + polygon(43, 213, 214), + polygon(48, 49, 291, 297), + polygon(300, 45, 46, 245, 249), + polygon(140, 120, 238, 143), + polygon(301, 232, 87), + polygon(287, 37, 26), + polygon(83, 93, 98, 82), + polygon(79, 90, 85, 86), + polygon(98, 302, 92, 89), + polygon(87, 293, 294, 25), + polygon(255, 31, 53, 24), + polygon(281, 45, 300, 297, 291), + polygon(95, 81, 78, 74), + polygon(185, 126, 135, 184), + polygon(241, 269, 210), + polygon(248, 286, 270, 247), + polygon(267, 279, 306), + polygon(17, 242, 164, 9), + polygon(5, 194, 242, 17), + polygon(26, 37, 44, 45, 282, 25), + polygon(301, 87, 282), + polygon(287, 308, 307, 288), + polygon(283, 269, 309), + polygon(311, 310, 295, 42, 268, 264), + polygon(268, 42, 288), + polygon(173, 174, 179), + polygon(231, 63, 6), + polygon(272, 310, 311, 312), + polygon(314, 313, 233, 153, 173), + polygon(164, 163, 160, 14), + polygon(40, 41, 296, 318, 317, 227, 228), + polygon(314, 173, 312), + polygon(197, 39, 36), + polygon(132, 133, 151, 137), + polygon(74, 78, 97, 73), + polygon(33, 34, 293), + polygon(317, 318, 310, 279, 280), + polygon(85, 88, 93, 83), + polygon(97, 86, 84, 96), + polygon(308, 257, 292, 307), + polygon(35, 233, 19), + polygon(259, 307, 292), + polygon(33, 293, 87, 52, 32), + polygon(184, 135, 132, 138), + polygon(93, 94, 302, 98), + polygon(156, 231, 269, 241), + polygon(269, 231, 232, 301, 309), + polygon(230, 289, 322, 217), + polygon(70, 288, 307, 259), + polygon(137, 151, 141, 142), + polygon(264, 67, 311), + polygon(286, 322, 289, 323, 270), + polygon(291, 246, 247), + polygon(262, 263, 250, 251), + polygon(230, 260, 261), + polygon(257, 308, 254), + polygon(154, 152, 193, 182), + polygon(313, 314, 312, 311), + polygon(155, 181, 192, 158), + polygon(115, 58, 59, 235), + polygon(174, 154, 182, 179), + polygon(287, 288, 322, 286), + polygon(231, 6, 7, 232), + polygon(6, 63, 194, 5), + polygon(103, 195, 16), + polygon(254, 308, 287, 253), + polygon(116, 206, 207, 235), + polygon(67, 233, 313), + polygon(4, 68, 54, 10), + polygon(99, 118, 119, 102), + polygon(322, 288, 43, 217), + polygon(36, 37, 287), + polygon(199, 324, 177, 178), + polygon(326, 222, 225, 325), + polygon(302, 94, 91, 92), + polygon(183, 163, 164, 242), + polygon(327, 223, 222, 326), + polygon(203, 325, 329, 207), + polygon(152, 195, 204, 193), + polygon(204, 326, 325, 203), + polygon(284, 95, 74, 75), + polygon(15, 16, 153), + polygon(329, 229, 223, 327), + polygon(76, 77, 95, 284), + polygon(313, 311, 67), + polygon(282, 283, 309, 301), + polygon(207, 329, 327, 273), + polygon(273, 327, 326, 204), + polygon(261, 306, 279, 310, 272), + polygon(334, 75, 72, 324), + polygon(324, 72, 76, 177), + polygon(175, 176, 334, 201), + polygon(325, 225, 229, 329), + polygon(201, 334, 324, 199), + polygon(176, 284, 75, 334), + polygon(71, 268, 288, 70), + polygon(27, 253, 287, 26), + polygon(10, 54, 87, 18), + polygon(230, 211, 218, 260), + polygon(211, 212, 221, 218), + polygon(317, 220, 221, 227), + polygon(220, 317, 280, 266), + polygon(279, 267, 266), + polygon(261, 219, 267, 306), + polygon(310, 318, 296, 295), + polygon(291, 49, 169, 246), + polygon(49, 50, 172, 169), + polygon(276, 171, 172, 244), + polygon(300, 48, 297), + polygon(300, 51, 48), + polygon(300, 249, 51), + polygon(37, 38, 47, 44), + */ +}; + +int num_polys = sizeof(polys) / sizeof(polygon); diff --git a/panda/src/builder/vector_BuilderC.cxx b/panda/src/builder/vector_BuilderC.cxx new file mode 100644 index 0000000000..6040192343 --- /dev/null +++ b/panda/src/builder/vector_BuilderC.cxx @@ -0,0 +1,11 @@ +// Filename: vector_BuilderC.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_BuilderC.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/builder/vector_BuilderC.h b/panda/src/builder/vector_BuilderC.h new file mode 100644 index 0000000000..bd39b58e36 --- /dev/null +++ b/panda/src/builder/vector_BuilderC.h @@ -0,0 +1,32 @@ +// Filename: vector_BuilderC.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_BUILDERC_H +#define VECTOR_BUILDERC_H + +#include + +#include "builderTypes.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_BuilderC +// Description : A vector of BuilderCs. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a vector of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, std::vector) +typedef vector vector_BuilderC; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/builder/vector_BuilderN.cxx b/panda/src/builder/vector_BuilderN.cxx new file mode 100644 index 0000000000..5c37dbf298 --- /dev/null +++ b/panda/src/builder/vector_BuilderN.cxx @@ -0,0 +1,11 @@ +// Filename: vector_BuilderN.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_BuilderN.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/builder/vector_BuilderN.h b/panda/src/builder/vector_BuilderN.h new file mode 100644 index 0000000000..1f15090a02 --- /dev/null +++ b/panda/src/builder/vector_BuilderN.h @@ -0,0 +1,32 @@ +// Filename: vector_BuilderN.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_BUILDERN_H +#define VECTOR_BUILDERN_H + +#include + +#include "builderTypes.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_BuilderN +// Description : A vector of BuilderNs. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a vector of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, std::vector) +typedef vector vector_BuilderN; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/builder/vector_BuilderTC.cxx b/panda/src/builder/vector_BuilderTC.cxx new file mode 100644 index 0000000000..05d2aa1461 --- /dev/null +++ b/panda/src/builder/vector_BuilderTC.cxx @@ -0,0 +1,11 @@ +// Filename: vector_BuilderTC.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_BuilderTC.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/builder/vector_BuilderTC.h b/panda/src/builder/vector_BuilderTC.h new file mode 100644 index 0000000000..9aa9bb7550 --- /dev/null +++ b/panda/src/builder/vector_BuilderTC.h @@ -0,0 +1,32 @@ +// Filename: vector_BuilderTC.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_BUILDERTC_H +#define VECTOR_BUILDERTC_H + +#include + +#include "builderTypes.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_BuilderTC +// Description : A vector of BuilderTCs. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a vector of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, std::vector) +typedef vector vector_BuilderTC; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/builder/vector_BuilderV.cxx b/panda/src/builder/vector_BuilderV.cxx new file mode 100644 index 0000000000..3063ccd75c --- /dev/null +++ b/panda/src/builder/vector_BuilderV.cxx @@ -0,0 +1,11 @@ +// Filename: vector_BuilderV.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_BuilderV.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/builder/vector_BuilderV.h b/panda/src/builder/vector_BuilderV.h new file mode 100644 index 0000000000..721082f2b8 --- /dev/null +++ b/panda/src/builder/vector_BuilderV.h @@ -0,0 +1,32 @@ +// Filename: vector_BuilderV.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_BUILDERV_H +#define VECTOR_BUILDERV_H + +#include + +#include "builderTypes.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_BuilderV +// Description : A vector of BuilderVs. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a vector of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, std::vector) +typedef vector vector_BuilderV; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/chan/Sources.pp b/panda/src/chan/Sources.pp new file mode 100644 index 0000000000..32a6c7e68b --- /dev/null +++ b/panda/src/chan/Sources.pp @@ -0,0 +1,43 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET chan + #define LOCAL_LIBS \ + putil linmath mathutil event graph sgattrib + + #define SOURCES \ + animBundle.I animBundle.cxx animBundle.h animBundleNode.I \ + animBundleNode.cxx animBundleNode.h animChannel.I animChannel.cxx \ + animChannel.h animChannelBase.I animChannelBase.cxx \ + animChannelBase.h animChannelMatrixXfmTable.I \ + animChannelMatrixXfmTable.cxx animChannelMatrixXfmTable.h \ + animChannelScalarTable.I animChannelScalarTable.cxx \ + animChannelScalarTable.h animControl.I animControl.N \ + animControl.cxx animControl.h animControlCollection.I \ + animControlCollection.cxx animControlCollection.h animGroup.I \ + animGroup.cxx animGroup.h auto_bind.cxx auto_bind.h config_chan.cxx \ + config_chan.h movingPartBase.I movingPartBase.cxx movingPartBase.h \ + movingPartMatrix.I movingPartMatrix.cxx movingPartMatrix.h \ + movingPartScalar.I movingPartScalar.cxx movingPartScalar.h \ + partBundle.I partBundle.N partBundle.cxx partBundle.h \ + partBundleNode.I partBundleNode.cxx partBundleNode.h partGroup.I \ + partGroup.cxx partGroup.h vector_PartGroupStar.cxx \ + vector_PartGroupStar.h + + #define INSTALL_HEADERS \ + animBundle.I animBundle.h animBundleNode.I animBundleNode.h \ + animChannel.I animChannel.h animChannelBase.I animChannelBase.h \ + animChannelFixed.I animChannelFixed.h animChannelMatrixXfmTable.I \ + animChannelMatrixXfmTable.h animChannelScalarTable.I \ + animChannelScalarTable.h animControl.I animControl.h \ + animControlCollection.I animControlCollection.h animGroup.I \ + animGroup.h auto_bind.h movingPart.I movingPart.h movingPartBase.I \ + movingPartBase.h movingPartMatrix.I movingPartMatrix.h \ + movingPartScalar.I movingPartScalar.h partBundle.I partBundle.h \ + partBundleNode.I partBundleNode.h partGroup.I partGroup.h \ + vector_PartGroupStar.h + + #define IGATESCAN all + +#end lib_target + diff --git a/panda/src/chan/animBundle.I b/panda/src/chan/animBundle.I new file mode 100644 index 0000000000..53ba089501 --- /dev/null +++ b/panda/src/chan/animBundle.I @@ -0,0 +1,53 @@ +// Filename: animBundle.I +// Created by: drose (21Feb99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundle::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AnimBundle:: +AnimBundle(const string &name, float fps, int num_frames) : AnimGroup(name) { + _fps = fps; + _num_frames = num_frames; + _root = this; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundle::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AnimBundle:: +AnimBundle(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundle::get_base_frame_rate +// Access: Public +// Description: Returns the ideal number of frames per second of the +// animation, when it is running at normal speed. This +// may not be the same as the actual playing frame rate, +// as it might have been adjusted through +// set_play_rate() on the AnimControl object. See +// AnimControl::get_effective_frame_rate(). +//////////////////////////////////////////////////////////////////// +INLINE double AnimBundle:: +get_base_frame_rate() const { + return _fps; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundle::get_num_frames +// Access: Public +// Description: Returns the number of frames of animation, or 0 if +// the animation has no fixed number of frames. +//////////////////////////////////////////////////////////////////// +INLINE int AnimBundle:: +get_num_frames() const { + return _num_frames; +} + + diff --git a/panda/src/chan/animBundle.cxx b/panda/src/chan/animBundle.cxx new file mode 100644 index 0000000000..4bbf1f1ac0 --- /dev/null +++ b/panda/src/chan/animBundle.cxx @@ -0,0 +1,86 @@ +// Filename: animBundle.cxx +// Created by: drose (21Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "animBundle.h" + +#include +#include +#include +#include +#include + +TypeHandle AnimBundle::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundle::output +// Access: Public, Virtual +// Description: Writes a one-line description of the bundle. +//////////////////////////////////////////////////////////////////// +void AnimBundle:: +output(ostream &out) const { + out << get_type() << " " << get_name() << ", " << get_num_frames() + << " frames at " << get_base_frame_rate() << " fps"; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundle::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void AnimBundle:: +write_datagram(BamWriter *manager, Datagram &me) +{ + AnimGroup::write_datagram(manager, me); + me.add_float64(_fps); + me.add_uint16(_num_frames); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundle::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void AnimBundle:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + AnimGroup::fillin(scan, manager); + _fps = scan.get_float64(); + _num_frames = scan.get_uint16(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundle::make_AnimBundle +// Access: Protected +// Description: Factory method to generate a AnimBundle object +//////////////////////////////////////////////////////////////////// +TypedWriteable* AnimBundle:: +make_AnimBundle(const FactoryParams ¶ms) +{ + AnimBundle *me = new AnimBundle; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundle::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a AnimBundle object +//////////////////////////////////////////////////////////////////// +void AnimBundle:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_AnimBundle); +} + diff --git a/panda/src/chan/animBundle.h b/panda/src/chan/animBundle.h new file mode 100644 index 0000000000..b27a869f23 --- /dev/null +++ b/panda/src/chan/animBundle.h @@ -0,0 +1,72 @@ +// Filename: animBundle.h +// Created by: drose (21Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANIMBUNDLE_H +#define ANIMBUNDLE_H + +#include + +#include "animGroup.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : AnimBundle +// Description : This is the root of an AnimChannel hierarchy. It +// knows the frame rate and number of frames of all the +// channels in the hierarchy (which must all match). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA AnimBundle : public AnimGroup { +public: + INLINE AnimBundle(const string &name, float fps, int num_frames); + + INLINE double get_base_frame_rate() const; + INLINE int get_num_frames() const; + + virtual void output(ostream &out) const; + +protected: + INLINE AnimBundle(void); + + float _fps; + int _num_frames; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + + static TypedWriteable *make_AnimBundle(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + AnimGroup::init_type(); + register_type(_type_handle, "AnimBundle", + AnimGroup::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +inline ostream &operator <<(ostream &out, const AnimBundle &bundle) { + bundle.output(out); + return out; +} + + +#include "animBundle.I" + +#endif diff --git a/panda/src/chan/animBundleNode.I b/panda/src/chan/animBundleNode.I new file mode 100644 index 0000000000..a2a426bcba --- /dev/null +++ b/panda/src/chan/animBundleNode.I @@ -0,0 +1,60 @@ +// Filename: animBundleNode.I +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AnimBundleNode:: +AnimBundleNode(AnimBundle *bundle) : + NamedNode(bundle->get_name()), + _bundle(bundle) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleNode::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AnimBundleNode:: +AnimBundleNode(void) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleNode::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AnimBundleNode:: +AnimBundleNode(const AnimBundleNode ©) : + NamedNode(copy), + _bundle(copy._bundle) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleNode::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void AnimBundleNode:: +operator = (const AnimBundleNode ©) { + NamedNode::operator = (copy); + _bundle = copy._bundle; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleNode::get_bundle +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AnimBundle *AnimBundleNode:: +get_bundle() const { + return _bundle; +} diff --git a/panda/src/chan/animBundleNode.cxx b/panda/src/chan/animBundleNode.cxx new file mode 100644 index 0000000000..f45851a5af --- /dev/null +++ b/panda/src/chan/animBundleNode.cxx @@ -0,0 +1,102 @@ +// Filename: animBundleNode.cxx +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "animBundleNode.h" +#include +#include +#include +#include + +TypeHandle AnimBundleNode::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleNode::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Node that is a shallow copy +// of this one. It will be a different Node pointer, +// but its internal data may or may not be shared with +// that of the original Node. +//////////////////////////////////////////////////////////////////// +Node *AnimBundleNode:: +make_copy() const { + return new AnimBundleNode(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleNode::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void AnimBundleNode:: +write_datagram(BamWriter *manager, Datagram &me) +{ + NamedNode::write_datagram(manager, me); + manager->write_pointer(me, _bundle); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleNode::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void AnimBundleNode:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + NamedNode::fillin(scan, manager); + manager->read_pointer(scan, this); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleNode::complete_pointers +// Access: Public +// Description: Takes in a vector of pointes to TypedWriteable +// objects that correspond to all the requests for +// pointers that this object made to BamReader. +//////////////////////////////////////////////////////////////////// +int AnimBundleNode:: +complete_pointers(vector_typedWriteable &plist, BamReader* manager) +{ + int start = NamedNode::complete_pointers(plist, manager); + _bundle = DCAST(AnimBundle, plist[start]); + return start+1; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleNode::make_AnimBundleNode +// Access: Protected +// Description: Factory method to generate a AnimBundleNode object +//////////////////////////////////////////////////////////////////// +TypedWriteable* AnimBundleNode:: +make_AnimBundleNode(const FactoryParams ¶ms) +{ + AnimBundleNode *me = new AnimBundleNode; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleNode::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a AnimBundleNode object +//////////////////////////////////////////////////////////////////// +void AnimBundleNode:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_AnimBundleNode); +} + + + diff --git a/panda/src/chan/animBundleNode.h b/panda/src/chan/animBundleNode.h new file mode 100644 index 0000000000..0e9ca6f85c --- /dev/null +++ b/panda/src/chan/animBundleNode.h @@ -0,0 +1,68 @@ +// Filename: animBundleNode.h +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANIMBUNDLENODE_H +#define ANIMBUNDLENODE_H + +#include + +#include "animBundle.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : AnimBundleNode +// Description : This is a node that contains a pointer to an +// AnimBundle. Its primary raison d'être is to allow +// the AnimBundles to be stored in the scene graph, so +// they may conveniently be loaded as egg files, for +// instance. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA AnimBundleNode : public NamedNode { +public: + INLINE AnimBundleNode(AnimBundle *bundle); + INLINE AnimBundleNode(const AnimBundleNode ©); + INLINE void operator = (const AnimBundleNode ©); + + virtual Node *make_copy() const; + INLINE AnimBundle *get_bundle() const; + +private: + PT(AnimBundle) _bundle; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + virtual int complete_pointers(vector_typedWriteable &plist, + BamReader *manager); + + static TypedWriteable *make_AnimBundleNode(const FactoryParams ¶ms); + +protected: + INLINE AnimBundleNode(void); + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NamedNode::init_type(); + register_type(_type_handle, "AnimBundleNode", + NamedNode::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "animBundleNode.I" + +#endif diff --git a/panda/src/chan/animChannel.I b/panda/src/chan/animChannel.I new file mode 100644 index 0000000000..b0226b9c6d --- /dev/null +++ b/panda/src/chan/animChannel.I @@ -0,0 +1,106 @@ +// Filename: animChannel.I +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + +template +TypeHandle AnimChannel::_type_handle; + +// We don't need to explicitly call AnimChannel::init_type(), because +// it is an abstract class and therefore must have derived objects. +// Its derived objects will call init_type() for us. + + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannel::Protected constructor +// Access: Protected +// Description: Don't use this constructor. It exists only so that +// AnimChannelFixed may define itself outside of the +// hierarchy. Normally, an AnimChannel must be created +// as part of a hierarchy. +//////////////////////////////////////////////////////////////////// +template +INLINE AnimChannel:: +AnimChannel(const string &name) + : AnimChannelBase(name) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannel::Constructor +// Access: Public +// Description: This is the normal constructor, which automatically +// places the AnimChannel in the previously-created +// hierarchy. +//////////////////////////////////////////////////////////////////// +template +INLINE AnimChannel:: +AnimChannel(AnimGroup *parent, const string &name) + : AnimChannelBase(parent, name) { +} + +#ifdef WIN32_VC +//////////////////////////////////////////////////////////////////// +// Function: AnimChannel::get_value +// Access: Public, Virtual +// Description: Gets the value of the channel at the indicated frame. +// This is a pure virtual function and normally would +// not need a function body, except that VC++ seems to +// be unhappy about instantiating the template without +// it. +// +// However, GCC seems to get confused when it *is* +// defined. So this whole thing is protected within an +// ifdef. +//////////////////////////////////////////////////////////////////// +template +void AnimChannel:: +get_value(int, AnimChannel::ValueType &) { +} +#endif + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannel::get_value_no_scale +// Access: Public, Virtual +// Description: Returns the value associated with the current frame, +// with no scale components. This only makes sense for +// a matrix-type channel, although for fiddly technical +// reasons the function exists for all channels. +//////////////////////////////////////////////////////////////////// +template +void AnimChannel:: +get_value_no_scale(int frame, ValueType &value) { + get_value(frame, value); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannel::get_scale +// Access: Public, Virtual +// Description: Returns the x, y, and z scale components associated +// with the current frame. As above, this only makes +// sense for a matrix-type channel. +//////////////////////////////////////////////////////////////////// +template +void AnimChannel:: +get_scale(int, float scale[3]) { + scale[0] = 1.0; + scale[1] = 1.0; + scale[2] = 1.0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannel::get_value_type +// Access: Public, Virtual +// Description: Returns the TypeHandle associated with the ValueType +// we return. This is provided to allow a bit of +// run-time checking that joints and channels are +// matching properly in type. +//////////////////////////////////////////////////////////////////// +template +TypeHandle AnimChannel:: +get_value_type() const { + return get_type_handle(ValueType); +} + + diff --git a/panda/src/chan/animChannel.cxx b/panda/src/chan/animChannel.cxx new file mode 100644 index 0000000000..7ab540de95 --- /dev/null +++ b/panda/src/chan/animChannel.cxx @@ -0,0 +1,12 @@ +// Filename: animChannel.cxx +// Created by: drose (11May00) +// +//////////////////////////////////////////////////////////////////// + +#include "animChannel.h" + + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/chan/animChannel.h b/panda/src/chan/animChannel.h new file mode 100644 index 0000000000..1174b89ee8 --- /dev/null +++ b/panda/src/chan/animChannel.h @@ -0,0 +1,120 @@ +// Filename: animChannel.h +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANIMCHANNEL_H +#define ANIMCHANNEL_H + +#include + +#include "animChannelBase.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : AnimChannel +// Description : This template class is the parent class for all kinds +// of AnimChannels that return different values. +//////////////////////////////////////////////////////////////////// +template +class EXPCL_PANDA AnimChannel : public AnimChannelBase { +protected: + // The default constructor is protected: don't try to create an + // AnimChannel without a parent. To create an AnimChannel hierarchy, + // you must first create an AnimBundle, and use that to create any + // subsequent children. + INLINE AnimChannel(const string &name = ""); + +public: + typedef TYPENAME SwitchType::ValueType ValueType; + + INLINE AnimChannel(AnimGroup *parent, const string &name); + + virtual void get_value(int frame, ValueType &value)=0; + + // These two functions only have meaning for matrix types. + virtual void get_value_no_scale(int frame, ValueType &value); + virtual void get_scale(int frame, float scale[3]); + // The second parameter above should really by LVector3f instead of + // float[3], but there seems to be a compiler bug in EGCS that + // doesn't like that. So we have this kludge for now. + + virtual TypeHandle get_value_type() const; + + //This class has no Read/Write functions as it is abstract + //and defines no new data + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + AnimChannelBase::init_type(); + register_type(_type_handle, SwitchType::get_channel_type_name(), + AnimChannelBase::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + + +// And now we instantiate a few useful types. + +class EXPCL_PANDA ACMatrixSwitchType { +public: + typedef LMatrix4f ValueType; + static const char *get_channel_type_name() { return "AnimChannelMatrix"; } + static const char *get_fixed_channel_type_name() { return "AnimChannelMatrixFixed"; } + static const char *get_part_type_name() { return "MovingPart"; } + static void write_datagram(Datagram &dest, ValueType& me) + { + me.write_datagram(dest); + } + static void read_datagram(DatagramIterator &source, ValueType& me) + { + me.read_datagram(source); + } +}; + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, AnimChannel); +typedef AnimChannel AnimChannelMatrix; + + +class EXPCL_PANDA ACScalarSwitchType { +public: + typedef float ValueType; + static const char *get_channel_type_name() { return "AnimChannelScalar"; } + static const char *get_fixed_channel_type_name() { return "AnimChannelScalarFixed"; } + static const char *get_part_type_name() { return "MovingPart"; } + static void write_datagram(Datagram &dest, ValueType& me) + { + dest.add_float64(me); + } + static void read_datagram(DatagramIterator &source, ValueType& me) + { + me = source.get_float64(); + } +}; + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, AnimChannel); +typedef AnimChannel AnimChannelScalar; + + +#include "animChannel.I" + + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif + + + diff --git a/panda/src/chan/animChannelBase.I b/panda/src/chan/animChannelBase.I new file mode 100644 index 0000000000..7f2f588c2d --- /dev/null +++ b/panda/src/chan/animChannelBase.I @@ -0,0 +1,35 @@ +// Filename: animChannelBase.I +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelBase::Protected constructor +// Access: Protected +// Description: Don't use this constructor. It exists only so that +// AnimChannelFixed may define itself outside of the +// hierarchy. Normally, an AnimChannel must be created +// as part of a hierarchy. +//////////////////////////////////////////////////////////////////// +INLINE AnimChannelBase:: +AnimChannelBase(const string &name) + : AnimGroup(name) +{ + _last_frame = -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelBase::Constructor +// Access: Public +// Description: This is the normal constructor, which automatically +// places the AnimChannel in the previously-created +// hierarchy. +//////////////////////////////////////////////////////////////////// +INLINE AnimChannelBase:: +AnimChannelBase(AnimGroup *parent, const string &name) + : AnimGroup(parent, name) +{ + _last_frame = -1; +} + + diff --git a/panda/src/chan/animChannelBase.cxx b/panda/src/chan/animChannelBase.cxx new file mode 100644 index 0000000000..cd10169e9f --- /dev/null +++ b/panda/src/chan/animChannelBase.cxx @@ -0,0 +1,66 @@ +// Filename: animChannelBase.cxx +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "animChannelBase.h" +#include +#include +#include +#include + +TypeHandle AnimChannelBase::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelBase::has_changed +// Access: Public, Virtual +// Description: Returns true if the value has changed since the last +// call to has_changed(). last_frame is the frame +// number of the last call; this_frame is the current +// frame number. +//////////////////////////////////////////////////////////////////// +bool AnimChannelBase:: +has_changed(int, int) { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelBase::output +// Access: Public, Virtual +// Description: Writes a one-line description of the channel. +//////////////////////////////////////////////////////////////////// +void AnimChannelBase:: +output(ostream &out) const { + out << get_type() << "(" << get_value_type() << ") " << get_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelBase::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void AnimChannelBase:: +write_datagram(BamWriter *manager, Datagram &me) +{ + AnimGroup::write_datagram(manager, me); + me.add_uint16(_last_frame); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelBase::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void AnimChannelBase:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + AnimGroup::fillin(scan, manager); + _last_frame = scan.get_uint16(); +} + + diff --git a/panda/src/chan/animChannelBase.h b/panda/src/chan/animChannelBase.h new file mode 100644 index 0000000000..6a269eb6e9 --- /dev/null +++ b/panda/src/chan/animChannelBase.h @@ -0,0 +1,71 @@ +// Filename: animChannelBase.h +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANIMCHANNELBASE_H +#define ANIMCHANNELBASE_H + +#include + +#include "animGroup.h" +#include "animControl.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : AnimChannelBase +// Description : Parent class for all animation channels. An +// AnimChannel is an arbitrary function that changes +// over time (actually, over frames), usually defined by +// a table read from an egg file (but possibly computed +// or generated in any other way). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA AnimChannelBase : public AnimGroup { +protected: + // The default constructor is protected: don't try to create an + // AnimChannel without a parent. To create an AnimChannel hierarchy, + // you must first create an AnimBundle, and use that to create any + // subsequent children. + INLINE AnimChannelBase(const string &name = ""); + +public: + INLINE AnimChannelBase(AnimGroup *parent, const string &name); + + virtual bool has_changed(int last_frame, int this_frame); + + virtual TypeHandle get_value_type() const=0; + virtual void output(ostream &out) const; + +protected: + + int _last_frame; + +public: + virtual void write_datagram(BamWriter* manager, Datagram &me); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + AnimGroup::init_type(); + register_type(_type_handle, "AnimChannelBase", + AnimGroup::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "animChannelBase.I" + +#endif diff --git a/panda/src/chan/animChannelFixed.I b/panda/src/chan/animChannelFixed.I new file mode 100644 index 0000000000..334e3c933a --- /dev/null +++ b/panda/src/chan/animChannelFixed.I @@ -0,0 +1,72 @@ +// Filename: animChannelFixed.I +// Created by: drose (24Feb99) +// +//////////////////////////////////////////////////////////////////// + +template +TypeHandle AnimChannelFixed::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelFixed::Constructor +// Access: Public +// Description: This is the flavor of AnimChannelFixed that puts it +// in a hierarchy. +//////////////////////////////////////////////////////////////////// +template +INLINE AnimChannelFixed:: +AnimChannelFixed(AnimGroup *parent, const string &name, + const ValueType &value) + : AnimChannel(parent, name), + _value(value) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelFixed::Constructor +// Access: Public +// Description: This flavor creates an AnimChannelFixed that is not +// in a hierarchy. +//////////////////////////////////////////////////////////////////// +template +INLINE AnimChannelFixed:: +AnimChannelFixed(const string &name, const ValueType &value) + : AnimChannel(name), + _value(value) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelFixed::has_changed +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +bool AnimChannelFixed:: +has_changed(int, int) { + return false; +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelFixed::get_value +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void AnimChannelFixed:: +get_value(int, ValueType &value) { + value = _value; +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelFixed::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void AnimChannelFixed:: +output(ostream &out) const { + AnimChannel::output(out); + out << " = " << _value; +} diff --git a/panda/src/chan/animChannelFixed.h b/panda/src/chan/animChannelFixed.h new file mode 100644 index 0000000000..08eada42c8 --- /dev/null +++ b/panda/src/chan/animChannelFixed.h @@ -0,0 +1,57 @@ +// Filename: animChannelFixed.h +// Created by: drose (24Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANIMCHANNELFIXED_H +#define ANIMCHANNELFIXED_H + +#include + +#include "animChannel.h" + + +//////////////////////////////////////////////////////////////////// +// Class : AnimChannelFixed +// Description : This template class is a special kind of AnimChannel +// that always returns just one fixed value. It is a +// special channel, in that it need not be assigned +// within a hierarchy. It may stand alone, so that it +// may be created on-the-fly for parts that need default +// anims to bind against. +//////////////////////////////////////////////////////////////////// +template +class EXPCL_PANDA AnimChannelFixed : public AnimChannel { +public: + INLINE AnimChannelFixed(AnimGroup *parent, const string &name, const ValueType &value); + INLINE AnimChannelFixed(const string &name, const ValueType &value); + + virtual bool has_changed(int last_frame, int this_frame); + virtual void get_value(int frame, ValueType &value); + + virtual void output(ostream &out) const; + + ValueType _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + AnimChannel::init_type(); + register_type(_type_handle, SwitchType::get_fixed_channel_type_name(), + AnimChannel::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + + +#include "animChannelFixed.I" + +#endif diff --git a/panda/src/chan/animChannelMatrixXfmTable.I b/panda/src/chan/animChannelMatrixXfmTable.I new file mode 100644 index 0000000000..1fbd8fc78b --- /dev/null +++ b/panda/src/chan/animChannelMatrixXfmTable.I @@ -0,0 +1,70 @@ +// Filename: animChannelMatrixXfmTable.I +// Created by: drose (21Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::is_valid_id +// Access: Public, Static +// Description: Returns true if the given letter is one of the nine +// valid table id's. +//////////////////////////////////////////////////////////////////// +INLINE bool AnimChannelMatrixXfmTable:: +is_valid_id(char table_id) { + return get_table_index(table_id) >= 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::has_table +// Access: Public +// Description: Returns true if the indicated subtable has been +// assigned. +//////////////////////////////////////////////////////////////////// +INLINE bool AnimChannelMatrixXfmTable:: +has_table(char table_id) const { + int table_index = get_table_index(table_id); + if (table_index < 0) { + return false; + } + return !(_tables[table_index] == NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::clear_table +// Access: Public +// Description: Removes the indicated table from the definition. +//////////////////////////////////////////////////////////////////// +INLINE void AnimChannelMatrixXfmTable:: +clear_table(char table_id) { + int table_index = get_table_index(table_id); + if (table_index >= 0) { + _tables[table_index] = NULL; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::get_table_id +// Access: Protected, Static +// Description: Returns the table ID associated with the indicated +// table index number. This is the letter 'i', 'j', +// 'k', 'h', 'p', 'r', 'x', 'y', or 'z'. +//////////////////////////////////////////////////////////////////// +INLINE char AnimChannelMatrixXfmTable:: +get_table_id(int table_index) { + return _table_ids[table_index]; +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::get_default_value +// Access: Protected, Static +// Description: Returns the default value the indicated table is +// expected to have in the absence of any data. +//////////////////////////////////////////////////////////////////// +INLINE float AnimChannelMatrixXfmTable:: +get_default_value(int table_index) { + return _default_values[table_index]; +} + diff --git a/panda/src/chan/animChannelMatrixXfmTable.cxx b/panda/src/chan/animChannelMatrixXfmTable.cxx new file mode 100644 index 0000000000..d0c8e089b4 --- /dev/null +++ b/panda/src/chan/animChannelMatrixXfmTable.cxx @@ -0,0 +1,366 @@ +// Filename: animChannelMatrixXfmTable.cxx +// Created by: drose (20Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "animChannelMatrixXfmTable.h" +#include "animBundle.h" +#include "config_chan.h" + +#include +#include +#include +#include +#include +#include + +TypeHandle AnimChannelMatrixXfmTable::_type_handle; + +const char +AnimChannelMatrixXfmTable::_table_ids[AnimChannelMatrixXfmTable::num_tables] = +{ 'i', 'j', 'k', 'h', 'p', 'r', 'x', 'y', 'z' }; + +const float +AnimChannelMatrixXfmTable::_default_values[AnimChannelMatrixXfmTable::num_tables] = +{ 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +AnimChannelMatrixXfmTable:: +AnimChannelMatrixXfmTable(AnimGroup *parent, const string &name) + : AnimChannelMatrix(parent, name) { +} + +///////////////////////////////////////////////////////////// +AnimChannelMatrixXfmTable:: +AnimChannelMatrixXfmTable(void) +{ +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::has_changed +// Access: Public, Virtual +// Description: Returns true if the value has changed since the last +// call to has_changed(). last_frame is the frame +// number of the last call; this_frame is the current +// frame number. +//////////////////////////////////////////////////////////////////// +bool AnimChannelMatrixXfmTable:: +has_changed(int last_frame, int this_frame) { + for (int i = 0; i < num_tables; i++) { + if (_tables[i].size() > 1) { + if (_tables[i][last_frame % _tables[i].size()] != + _tables[i][this_frame % _tables[i].size()]) { + return true; + } + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::get_value +// Access: Public, Virtual +// Description: Gets the value of the channel at the indicated frame. +//////////////////////////////////////////////////////////////////// +void AnimChannelMatrixXfmTable:: +get_value(int frame, LMatrix4f &mat) { + float components[num_tables]; + + for (int i = 0; i < num_tables; i++) { + if (_tables[i].empty()) { + components[i] = get_default_value(i); + } else { + components[i] = _tables[i][frame % _tables[i].size()]; + } + } + + compose_matrix(mat, components); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::get_value_no_scale +// Access: Public, Virtual +// Description: Gets the value of the channel at the indicated frame, +// without any scale information. +//////////////////////////////////////////////////////////////////// +void AnimChannelMatrixXfmTable:: +get_value_no_scale(int frame, LMatrix4f &mat) { + float components[num_tables]; + components[0] = 1.0; + components[1] = 1.0; + components[2] = 1.0; + + for (int i = 3; i < num_tables; i++) { + if (_tables[i].empty()) { + components[i] = get_default_value(i); + } else { + components[i] = _tables[i][frame % _tables[i].size()]; + } + } + + compose_matrix(mat, components); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::get_scale +// Access: Public, Virtual +// Description: Gets the scale value at the indicated frame. +//////////////////////////////////////////////////////////////////// +void AnimChannelMatrixXfmTable:: +get_scale(int frame, float scale[3]) { + for (int i = 0; i < 3; i++) { + if (_tables[i].empty()) { + scale[i] = get_default_value(i); + } else { + scale[i] = _tables[i][frame % _tables[i].size()]; + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::clear_all_tables +// Access: Public +// Description: Removes all the tables from the channel, and resets +// it to its initial state. +//////////////////////////////////////////////////////////////////// +void AnimChannelMatrixXfmTable:: +clear_all_tables() { + for (int i = 0; i < num_tables; i++) { + _tables[i] = NULL; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::set_table +// Access: Public +// Description: Assigns the indicated table. table_id is one of 'i', +// 'j', 'k', for scale, 'h', 'p', 'r', for rotation, and +// 'x', 'y', 'z', for translation. The new table must +// have either zero, one, or get_num_frames() frames. +//////////////////////////////////////////////////////////////////// +void AnimChannelMatrixXfmTable:: +set_table(char table_id, const CPTA_float &table) { + int num_frames = _root->get_num_frames(); + + if (table.size() > 1 && table.size() < num_frames) { + // The new table has an invalid number of frames--it doesn't match + // the bundle's requirement. + return; + } + + int i = get_table_index(table_id); + if (i < 0) { + return; + } + + _tables[i] = table; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::write +// Access: Public, Virtual +// Description: Writes a brief description of the table and all of +// its descendants. +//////////////////////////////////////////////////////////////////// +void AnimChannelMatrixXfmTable:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) + << get_type() << " " << get_name() << " "; + + // Write a list of all the sub-tables that have data. + bool found_any = false; + for (int i = 0; i < num_tables; i++) { + if (!_tables[i].empty()) { + out << get_table_id(i) << _tables[i].size(); + found_any = true; + } + } + + if (!found_any) { + out << "(no data)"; + } + + if (!_children.empty()) { + out << " {\n"; + write_descendants(out, indent_level + 2); + indent(out, indent_level) << "}"; + } + + out << "\n"; +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::get_table_index +// Access: Protected, Static +// Description: Returns the table index number, a value between 0 and +// num_tables, that corresponds to the indicate table +// id. Returns -1 if the table id is invalid. +//////////////////////////////////////////////////////////////////// +int AnimChannelMatrixXfmTable:: +get_table_index(char table_id) { + for (int i = 0; i < num_tables; i++) { + if (table_id == get_table_id(i)) { + return i; + } + } + + return -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void AnimChannelMatrixXfmTable:: +write_datagram(BamWriter *manager, Datagram &me) +{ + AnimChannelMatrix::write_datagram(manager, me); + + me.add_uint8(quantize_bam_channels); + if (!quantize_bam_channels) { + // Write out everything the old way, as floats. + for(int i = 0; i < num_tables; i++) { + me.add_uint16(_tables[i].size()); + for(int j = 0; j < _tables[i].size(); j++) { + me.add_float64(_tables[i][j]); + } + } + + } else { + // Write out everything the compact way, as quantized integers. + + // First, write out the scales. These will be in the range 1/256 .. 255. + int i; + for(i = 0; i < 3; i++) { + me.add_uint16(_tables[i].size()); + for(int j = 0; j < _tables[i].size(); j++) { + me.add_uint16((int)max(min(_tables[i][j]*256.0, 65535.0), 0.0)); + } + } + + // Now, write out the joint angles. These are in the range 0 .. 360. + for(i = 3; i < 6; i++) { + me.add_uint16(_tables[i].size()); + for(int j = 0; j < _tables[i].size(); j++) { + me.add_uint16((unsigned int)(_tables[i][j] * 65536.0 / 360.0)); + } + } + + // And now write out the translations. These are in the range + // -1000 .. 1000. + for(i = 6; i < 9; i++) { + me.add_uint16(_tables[i].size()); + for(int j = 0; j < _tables[i].size(); j++) { + me.add_int16((int)max(min(_tables[i][j] * 32767.0 / 1000.0, 32767.0), -32767.0)); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void AnimChannelMatrixXfmTable:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + AnimChannelMatrix::fillin(scan, manager); + + bool wrote_quantized = false; + if (manager->get_file_minor_ver() >= 1) { + // Version 1 and later: we might have quantized channels. + wrote_quantized = (bool)scan.get_uint8(); + } + + if (!wrote_quantized) { + // Regular floats. + for(int i = 0; i < num_tables; i++) { + int size = scan.get_uint16(); + PTA_float ind_table; + for(int j = 0; j < size; j++) { + ind_table.push_back(scan.get_float64()); + } + _tables[i] = ind_table; + } + + } else { + // Quantized int16's and expand to floats. + int i; + + // First, read in the scales. + for(i = 0; i < 3; i++) { + int size = scan.get_uint16(); + PTA_float ind_table; + for(int j = 0; j < size; j++) { + ind_table.push_back((double)scan.get_uint16() / 256.0); + } + _tables[i] = ind_table; + } + + // Then, read in the joint angles. + for(i = 3; i < 6; i++) { + int size = scan.get_uint16(); + PTA_float ind_table; + for(int j = 0; j < size; j++) { + ind_table.push_back((double)scan.get_uint16() * 360.0 / 65536.0); + } + _tables[i] = ind_table; + } + + // Now read in the translations. + for(i = 6; i < 9; i++) { + int size = scan.get_uint16(); + PTA_float ind_table; + for(int j = 0; j < size; j++) { + ind_table.push_back((double)scan.get_int16() * 1000.0 / 32767.0); + } + _tables[i] = ind_table; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::make_AnimChannelMatrixXfmTable +// Access: Protected +// Description: Factory method to generate a AnimChannelMatrixXfmTable object +//////////////////////////////////////////////////////////////////// +TypedWriteable* AnimChannelMatrixXfmTable:: +make_AnimChannelMatrixXfmTable(const FactoryParams ¶ms) +{ + AnimChannelMatrixXfmTable *me = new AnimChannelMatrixXfmTable; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelMatrixXfmTable::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a AnimChannelMatrixXfmTable object +//////////////////////////////////////////////////////////////////// +void AnimChannelMatrixXfmTable:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_AnimChannelMatrixXfmTable); +} + + diff --git a/panda/src/chan/animChannelMatrixXfmTable.h b/panda/src/chan/animChannelMatrixXfmTable.h new file mode 100644 index 0000000000..7b71517296 --- /dev/null +++ b/panda/src/chan/animChannelMatrixXfmTable.h @@ -0,0 +1,85 @@ +// Filename: animChannelMatrixXfmTable.h +// Created by: drose (20Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANIMCHANNELMATRIXXFMTABLE_H +#define ANIMCHANNELMATRIXXFMTABLE_H + +#include + +#include "animChannel.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : AnimChannelMatrixXfmTable +// Description : An animation channel that issues a matrix each frame, +// read from a table such as might have been read from +// an egg file. The table actually consists of nine +// sub-tables, each representing one component of the +// transform: scale, rotate, translate. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA AnimChannelMatrixXfmTable : public AnimChannelMatrix { +public: + AnimChannelMatrixXfmTable(AnimGroup *parent, const string &name); + + virtual bool has_changed(int last_frame, int this_frame); + virtual void get_value(int frame, LMatrix4f &mat); + virtual void get_value_no_scale(int frame, LMatrix4f &value); + virtual void get_scale(int frame, float scale[3]); + + static INLINE bool is_valid_id(char table_id); + + void clear_all_tables(); + void set_table(char table_id, const CPTA_float &table); + INLINE bool has_table(char table_id) const; + INLINE void clear_table(char table_id); + + virtual void write(ostream &out, int indent_level) const; + +protected: + AnimChannelMatrixXfmTable(void); + INLINE static char get_table_id(int table_index); + static int get_table_index(char table_id); + INLINE static float get_default_value(int table_index); + + enum { num_tables = 9 }; + + CPTA_float _tables[num_tables]; + +private: + static const char _table_ids[num_tables]; + static const float _default_values[num_tables]; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + + static TypedWriteable *make_AnimChannelMatrixXfmTable(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + AnimChannelMatrix::init_type(); + register_type(_type_handle, "AnimChannelMatrixXfmTable", + AnimChannelMatrix::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "animChannelMatrixXfmTable.I" + +#endif diff --git a/panda/src/chan/animChannelScalarTable.I b/panda/src/chan/animChannelScalarTable.I new file mode 100644 index 0000000000..8466f50a78 --- /dev/null +++ b/panda/src/chan/animChannelScalarTable.I @@ -0,0 +1,27 @@ +// Filename: animChannelScalarTable.I +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelScalarTable::has_table +// Access: Public +// Description: Returns true if the data table has been assigned. +//////////////////////////////////////////////////////////////////// +INLINE bool AnimChannelScalarTable:: +has_table() const { + return !(_table == NULL); +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelScalarTable::clear_table +// Access: Public +// Description: Empties the data table. +//////////////////////////////////////////////////////////////////// +INLINE void AnimChannelScalarTable:: +clear_table() { + _table = NULL; +} + diff --git a/panda/src/chan/animChannelScalarTable.cxx b/panda/src/chan/animChannelScalarTable.cxx new file mode 100644 index 0000000000..5b2f2ff823 --- /dev/null +++ b/panda/src/chan/animChannelScalarTable.cxx @@ -0,0 +1,211 @@ +// Filename: animChannelScalarTable.cxx +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "animChannelScalarTable.h" +#include "animBundle.h" +#include "config_chan.h" + +#include +#include +#include +#include +#include + +TypeHandle AnimChannelScalarTable::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelScalarTable::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +AnimChannelScalarTable:: +AnimChannelScalarTable(AnimGroup *parent, const string &name) + : AnimChannelScalar(parent, name) { +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelScalarTable::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +AnimChannelScalarTable:: +AnimChannelScalarTable(void){ +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelScalarTable::has_changed +// Access: Public, Virtual +// Description: Returns true if the value has changed since the last +// call to has_changed(). last_frame is the frame +// number of the last call; this_frame is the current +// frame number. +//////////////////////////////////////////////////////////////////// +bool AnimChannelScalarTable:: +has_changed(int last_frame, int this_frame) { + if (_table.size() > 1) { + if (_table[last_frame % _table.size()] != + _table[this_frame % _table.size()]) { + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelScalarTable::get_value +// Access: Public, Virtual +// Description: Gets the value of the channel at the indicated frame. +//////////////////////////////////////////////////////////////////// +void AnimChannelScalarTable:: +get_value(int frame, float &value) { + if (_table.empty()) { + value = 0.0; + } else { + value = _table[frame % _table.size()]; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelScalarTable::set_table +// Access: Public +// Description: Assigns the data table. +//////////////////////////////////////////////////////////////////// +void AnimChannelScalarTable:: +set_table(const CPTA_float &table) { + int num_frames = _root->get_num_frames(); + + if (table.size() > 1 && table.size() < num_frames) { + // The new table has an invalid number of frames--it doesn't match + // the bundle's requirement. + return; + } + + _table = table; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelScalarTable::write +// Access: Public, Virtual +// Description: Writes a brief description of the table and all of +// its descendants. +//////////////////////////////////////////////////////////////////// +void AnimChannelScalarTable:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) + << get_type() << " " << get_name() << " " << _table.size(); + + if (!_children.empty()) { + out << " {\n"; + write_descendants(out, indent_level + 2); + indent(out, indent_level) << "}"; + } + + out << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelScalarTable::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void AnimChannelScalarTable:: +write_datagram(BamWriter *manager, Datagram &me) +{ + AnimChannelScalar::write_datagram(manager, me); + + me.add_uint8(quantize_bam_channels); + if (!quantize_bam_channels) { + // Write out everything the old way, as floats. + me.add_uint16(_table.size()); + for(int i = 0; i < _table.size(); i++) { + me.add_float64(_table[i]); + } + + } else { + // Write out everything the compact way, as quantized integers. + + // We quantize morphs within the range -100 .. 100. + me.add_uint16(_table.size()); + for(int i = 0; i < _table.size(); i++) { + me.add_int16((int)max(min(_table[i] * 32767.0 / 100.0, 32767.0), -32767.0)); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelScalarTable::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void AnimChannelScalarTable:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + AnimChannelScalar::fillin(scan, manager); + + bool wrote_quantized = false; + if (manager->get_file_minor_ver() >= 1) { + // Version 1 and later: we might have quantized channels. + wrote_quantized = (bool)scan.get_uint8(); + } + + if (!wrote_quantized) { + // Regular floats. + int size = scan.get_uint16(); + PTA_float temp_table; + for(int i = 0; i < size; i++) { + temp_table.push_back(scan.get_float64()); + } + _table = temp_table; + + } else { + // Quantized int16's and expand to floats. + int size = scan.get_uint16(); + PTA_float temp_table; + for(int i = 0; i < size; i++) { + temp_table.push_back((double)scan.get_int16() * 100.0 / 32767.0); + } + _table = temp_table; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelScalarTable::make_AnimChannelScalarTable +// Access: Protected +// Description: Factory method to generate a AnimChannelScalarTable object +//////////////////////////////////////////////////////////////////// +TypedWriteable* AnimChannelScalarTable:: +make_AnimChannelScalarTable(const FactoryParams ¶ms) +{ + AnimChannelScalarTable *me = new AnimChannelScalarTable; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimChannelScalarTable::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a AnimChannelScalarTable object +//////////////////////////////////////////////////////////////////// +void AnimChannelScalarTable:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_AnimChannelScalarTable); +} + + + + diff --git a/panda/src/chan/animChannelScalarTable.h b/panda/src/chan/animChannelScalarTable.h new file mode 100644 index 0000000000..17201af151 --- /dev/null +++ b/panda/src/chan/animChannelScalarTable.h @@ -0,0 +1,70 @@ +// Filename: animChannelScalarTable.h +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANIMCHANNELSCALARTABLE_H +#define ANIMCHANNELSCALARTABLE_H + +#include + +#include "animChannel.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : AnimChannelScalarTable +// Description : An animation channel that issues a scalar each frame, +// read from a table such as might have been read from +// an egg file. The table actually consists of nine +// sub-tables, each representing one component of the +// transform: scale, rotate, translate. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA AnimChannelScalarTable : public AnimChannelScalar { +public: + AnimChannelScalarTable(AnimGroup *parent, const string &name); + + virtual bool has_changed(int last_frame, int this_frame); + virtual void get_value(int frame, float &value); + + void set_table(const CPTA_float &table); + INLINE bool has_table() const; + INLINE void clear_table(); + + virtual void write(ostream &out, int indent_level) const; + +protected: + AnimChannelScalarTable(void); + CPTA_float _table; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + + static TypedWriteable *make_AnimChannelScalarTable(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + AnimChannelScalar::init_type(); + register_type(_type_handle, "AnimChannelScalarTable", + AnimChannelScalar::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "animChannelScalarTable.I" + +#endif diff --git a/panda/src/chan/animControl.I b/panda/src/chan/animControl.I new file mode 100644 index 0000000000..0a531b2602 --- /dev/null +++ b/panda/src/chan/animControl.I @@ -0,0 +1,109 @@ +// Filename: animControl.I +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include + + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::set_play_rate +// Access: Public +// Description: Sets the speed of the animation, relative to its +// "normal" speed. Setting this number to 2.0 plays it +// twice as fast, 0.5 half as fast. -1.0 plays it +// backwards, and 0.0 stops it. The change is actually +// retroactive to the last frame. +// +// If you are going to change the play_rate from a +// positive number to a negative number, or vice-versa, +// you should do this before starting the animation. +// The various flavors of play() and loop() do slightly +// different behavior based on whether play_rate is +// positive or negative. +//////////////////////////////////////////////////////////////////// +INLINE void AnimControl:: +set_play_rate(double play_rate) { + _play_rate = play_rate; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::get_play_rate +// Access: Public +// Description: Returns the current speed of the animation. See +// set_play_rate(). +//////////////////////////////////////////////////////////////////// +INLINE double AnimControl:: +get_play_rate() const { + return _play_rate; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::get_frame_rate +// Access: Public +// Description: Returns the actual frame rate of the animation, based +// on the play_rate (see set_play_rate()) and the +// animation's base frame rate (see +// AnimBundle::get_base_frame_rate()). This is in +// frames per second. +//////////////////////////////////////////////////////////////////// +INLINE double AnimControl:: +get_frame_rate() const { + return get_play_rate() * get_anim()->get_base_frame_rate(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::get_frame +// Access: Public +// Description: Returns the current frame number of the animation. +//////////////////////////////////////////////////////////////////// +INLINE int AnimControl:: +get_frame() const { + return (int)floor(_frame + 0.0001); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::get_num_frames +// Access: Public +// Description: Returns the number of frames of animation. This is +// actually just extracted directly from the AnimBundle; +// the function is duplicated here for convenience. The +// frame number will never be outside the range 0 <= +// frame < get_num_frames(). +//////////////////////////////////////////////////////////////////// +INLINE int AnimControl:: +get_num_frames() const { + return get_anim()->get_num_frames(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::is_playing +// Access: Public +// Description: Returns true if the AnimControl is currently playing, +// false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool AnimControl:: +is_playing() const { + return _playing; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::get_anim +// Access: Public +// Description: Returns the AnimBundle bound in with this +// AnimControl. +//////////////////////////////////////////////////////////////////// +INLINE AnimBundle *AnimControl:: +get_anim() const { + return _anim; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::get_channel_index +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int AnimControl:: +get_channel_index() const { + return _channel_index; +} diff --git a/panda/src/chan/animControl.N b/panda/src/chan/animControl.N new file mode 100644 index 0000000000..a618fba82c --- /dev/null +++ b/panda/src/chan/animControl.N @@ -0,0 +1,3 @@ +ignoreinvolved Action + +forcetype PointerTo diff --git a/panda/src/chan/animControl.cxx b/panda/src/chan/animControl.cxx new file mode 100644 index 0000000000..25bd512ed5 --- /dev/null +++ b/panda/src/chan/animControl.cxx @@ -0,0 +1,647 @@ +// Filename: animControl.cxx +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "animControl.h" +#include "animChannelBase.h" +#include "partBundle.h" +#include "config_chan.h" + +#include +#include + + +TypeHandle AnimControl::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::Constructor +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +AnimControl:: +AnimControl(PartBundle *part, AnimBundle *anim, int channel_index) { + _part = part; + _anim = anim; + _channel_index = channel_index; + + _play_rate = 1.0; + _frame = 0.0; + _as_of_time = 0.0; + _playing = false; + + _marked_frame = -1; +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::play +// Access: Public +// Description: Runs the entire animation from beginning to end and +// stops, throwing the stop event (if it is non-NULL). +//////////////////////////////////////////////////////////////////// +void AnimControl:: +play(const CPT_Event &stop_event) { + assert(get_num_frames() > 0); + + if (get_play_rate() < 0.0) { + play(get_num_frames()-1, 0, stop_event); + } else { + play(0, get_num_frames()-1, stop_event); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::play +// Access: Public +// Description: Runs the animation from the frame "from" to and +// including the frame "to", at which point the +// animation is stopped and the indicated stop event is +// thrown (if it is non-NULL). If the to frame is less +// than the from frame (unless play_rate is negative), +// the animation will wrap around the end and begins +// again at the beginning before the animation stops. +//////////////////////////////////////////////////////////////////// +void AnimControl:: +play(int from, int to, const CPT_Event &stop_event) { + assert(get_num_frames() > 0); + + assert(from >= 0 && from < get_num_frames()); + assert(to >= 0 && to < get_num_frames()); + _as_of_time = ClockObject::get_global_clock()->get_time(); + _playing = true; + + _actions = _user_actions; + if (stop_event != (Event*)0L) { + insert_event_action(_actions, to, stop_event); + } + insert_stop_action(_actions, to); + + get_part()->control_activated(this); + set_frame(from); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::loop +// Access: Public +// Description: Starts the entire animation looping. If restart is +// true, the animation is restarted from the beginning; +// otherwise, it continues from the current frame. +//////////////////////////////////////////////////////////////////// +void AnimControl:: +loop(bool restart) { + assert(get_num_frames() > 0); + + _as_of_time = ClockObject::get_global_clock()->get_time(); + _playing = true; + + _actions = _user_actions; + get_part()->control_activated(this); + + if (restart) { + if (get_play_rate() < 0.0) { + set_frame(get_num_frames() - 1); + } else { + set_frame(0); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::loop +// Access: Public +// Description: Loops the animation from the frame "from" to and +// including the frame "to", indefinitely. If restart +// is true, the animation is restarted from the +// beginning; otherwise, it continues from the current +// frame. +//////////////////////////////////////////////////////////////////// +void AnimControl:: +loop(bool restart, int from, int to) { + assert(get_num_frames() > 0); + + assert(from >= 0 && from < get_num_frames()); + assert(to >= 0 && to < get_num_frames()); + _as_of_time = ClockObject::get_global_clock()->get_time(); + _playing = true; + + _actions = _user_actions; + + if (get_play_rate() < 0.0) { + // If we're playing backward, we need to set up the loop a little + // differently. + + if ((to == 0 && from == get_num_frames()-1) || + (to == from-1)) { + + // In this case, the user has specified to loop over the whole + // range of animation. We don't need a special jump action to + // handle this. + + } else { + // Otherwise, set up a jump action to effect the loop. This isn't + // completely accurate, since it will always jump exactly to the + // first frame of the loop, no matter how many frames past the end + // we'd gotten to, but it should be reasonably close, especially + // if the number of frames in the loop is large enough and/or the + // frame rate is high enough. + insert_jump_action(_actions, (to-1+get_num_frames())%get_num_frames(), + from); + } + } else { + + if ((from == 0 && to == get_num_frames()-1) || + (from == to-1)) { + + // In this case, the user has specified to loop over the whole + // range of animation. We don't need a special jump action to + // handle this. + + } else { + // Otherwise, set up a jump action to effect the loop. This isn't + // completely accurate, since it will always jump exactly to the + // first frame of the loop, no matter how many frames past the end + // we'd gotten to, but it should be reasonably close, especially + // if the number of frames in the loop is large enough and/or the + // frame rate is high enough. + insert_jump_action(_actions, (to+1)%get_num_frames(), from); + } + } + + get_part()->control_activated(this); + if (restart) { + set_frame(from); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::stop +// Access: Public +// Description: Stops a currently playing or looping animation right +// where it is. The animation remains posed at the +// current frame, and no event is thrown. +//////////////////////////////////////////////////////////////////// +void AnimControl:: +stop() { + _playing = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::pose +// Access: Public +// Description: Sets the animation to the indicated frame and holds +// it there. +//////////////////////////////////////////////////////////////////// +void AnimControl:: +pose(int frame) { + assert(get_num_frames() > 0); + + assert(frame >= 0 && frame < get_num_frames()); + _as_of_time = ClockObject::get_global_clock()->get_time(); + _playing = false; + + _actions = _user_actions; + + get_part()->control_activated(this); + set_frame(frame); +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::add_event +// Access: Public +// Description: Adds the indicated event to the list of events that +// will be called whenever the animation reaches the +// indicated frame number. Once added, the event will +// persist until it is removed via remove_event() or +// remove_all_events(). +//////////////////////////////////////////////////////////////////// +void AnimControl:: +add_event(int frame, const CPT_Event &event) { + insert_event_action(_user_actions, frame, event); + if (_playing) { + insert_event_action(_actions, frame, event); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::remove_event +// Access: Public +// Description: Removes all events found that match the indicated +// event name, and returns the number of events +// removed. +//////////////////////////////////////////////////////////////////// +int AnimControl:: +remove_event(const string &event_name) { + Actions new_actions; + + int removed = 0; + + Actions::const_iterator ai; + for (ai = _user_actions.begin(); ai != _user_actions.end(); ++ai) { + const Action &action = (*ai).second; + if (action._type == AT_event && + action._event->get_name() == event_name) { + // Remove this event by not copying it to new_actions. + removed++; + } else { + // Preserve this event. + new_actions.insert(*ai); + } + } + + if (removed != 0) { + _user_actions.swap(new_actions); + + if (_playing) { + new_actions.clear(); + int p_removed = 0; + for (ai = _actions.begin(); ai != _actions.end(); ++ai) { + const Action &action = (*ai).second; + if (action._type == AT_event && + action._event->get_name() == event_name) { + // Remove this event by not copying it to new_actions. + p_removed++; + } else { + // Preserve this event. + new_actions.insert(*ai); + } + } + assert(p_removed == removed); + _actions.swap(new_actions); + } + } + + return removed; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::remove_all_events +// Access: Public +// Description: Removes all user-defined event messages. However, if +// called while an animation is running, this will not +// take effect until the animation is stopped and +// restarted. +//////////////////////////////////////////////////////////////////// +void AnimControl:: +remove_all_events() { + _user_actions.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::get_part +// Access: Public +// Description: Returns the PartBundle bound in with this +// AnimControl. +//////////////////////////////////////////////////////////////////// +PartBundle *AnimControl:: +get_part() const { + return DCAST(PartBundle, _part); +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::advance_time +// Access: Public +// Description: Tells the AnimControl what time it is. This +// recomputes the frame number according to the amount +// of time elapsed since last time. Until this function +// is called, the frame number will not increment. The +// time passed to this function must be nondecreasing; +// it is an error to call it with a value less than a +// previously-passed value. +//////////////////////////////////////////////////////////////////// +void AnimControl:: +advance_time(double time) { + double elapsed_time = time - _as_of_time; + double elapsed_frames = elapsed_time * get_frame_rate(); + _as_of_time = time; + + if (_playing && elapsed_frames != 0.0) { + int orig_frame = get_frame(); + + _frame += elapsed_frames; + double num_frames = (double)get_num_frames(); + + int new_frame = get_frame(); + + // Now call all the actions. + if (elapsed_frames < 0.0) { + // If we're playing the animation backward, we have to check the + // actions in reverse order. + + if (new_frame >= 0) { + do_actions_backward(orig_frame-1, new_frame); + } else { + if (do_actions_backward(orig_frame-1, 0)) { + _frame = _frame - floor(_frame / num_frames) * num_frames; + new_frame = get_frame(); + do_actions_backward(get_num_frames(), new_frame); + } + } + } else { + // Normally, we'll be playing the animation forward. + + if (new_frame < get_num_frames()) { + do_actions_forward(orig_frame+1, new_frame); + } else { + if (do_actions_forward(orig_frame+1, get_num_frames()-1)) { + _frame = _frame - floor(_frame / num_frames) * num_frames; + new_frame = get_frame(); + do_actions_forward(0, new_frame); + } + } + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::channel_has_changed +// Access: Public +// Description: Returns true if the indicated channel value has +// changed since the last call to mark_channels(). +//////////////////////////////////////////////////////////////////// +bool AnimControl:: +channel_has_changed(AnimChannelBase *channel) const { + if (_marked_frame < 0) { + return true; + } + + int this_frame = get_frame(); + + if (this_frame == _marked_frame) { + return false; + } + + return channel->has_changed(_marked_frame, this_frame); +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::mark_channels +// Access: Public +// Description: Marks this point as the point of reference for the +// next call to channel_has_changed(). +//////////////////////////////////////////////////////////////////// +void AnimControl:: +mark_channels() { + _marked_frame = get_frame(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void AnimControl:: +output(ostream &out) const { + out << "AnimControl(" << get_part()->get_name() + << ", " << get_anim()->get_name() << ")"; +} + + + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::insert_event_action +// Access: Private, Static +// Description: Inserts an "event" action at the indicated frame +// number. The event will be thrown as the animation +// reaches the indicated frame number. +//////////////////////////////////////////////////////////////////// +void AnimControl:: +insert_event_action(Actions &actions, int frame, const CPT_Event &event) { + Action new_action; + new_action._type = AT_event; + new_action._event = event; + actions.insert(pair(frame, new_action)); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::insert_stop_action +// Access: Private, Static +// Description: Inserts a "stop" action at the indicated frame +// number. The animation will stop as soon as it +// reaches the indicated frame number, traveling from +// either direction. +//////////////////////////////////////////////////////////////////// +void AnimControl:: +insert_stop_action(Actions &actions, int frame) { + Action new_action; + new_action._type = AT_stop; + actions.insert(pair(frame, new_action)); +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::insert_jump_action +// Access: Private, Static +// Description: Inserts a "jump" action at the indicated frame +// number. When the animation reaches the indicated +// frame number, it will jump to the indicated jump_to +// frame. It will not seem to spend any time at the +// reached frame number. +//////////////////////////////////////////////////////////////////// +void AnimControl:: +insert_jump_action(Actions &actions, int frame, int jump_to) { + Action new_action; + new_action._type = AT_jump; + new_action._jump_to = jump_to; + actions.insert(pair(frame, new_action)); +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::set_frame +// Access: Private +// Description: Sets the current frame number to the indicated frame, +// and performs any actions on that frame. +//////////////////////////////////////////////////////////////////// +void AnimControl:: +set_frame(double frame) { + _frame = frame; + int iframe = get_frame(); + do_actions_forward(iframe, iframe); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::do_actions_forward +// Access: Private +// Description: Calls each of the actions, in turn, on the timeline +// between the indicated "from" frame and the indicated +// "to" frame, inclusive. If any of the actions +// specifies to stop the animation, the animation is +// stopped, the current frame number is set to the point +// of stopping, no further actions are called, and false +// is returned. Otherwise, true is returned. +//////////////////////////////////////////////////////////////////// +bool AnimControl:: +do_actions_forward(int from, int to) { + if (to >= from) { + Actions::const_iterator lower = _actions.lower_bound(from); + Actions::const_iterator upper = _actions.lower_bound(to+1); + + int sequence_frame = -1; + const Action *sequence_action; + + Actions::const_iterator ai; + for (ai = lower; ai != upper; ++ai) { + int frame = (*ai).first; + const Action &action = (*ai).second; + + if (sequence_frame != -1 && frame > sequence_frame) { + // We encountered an action that resequenced our frame numbers. + // Now that we've finished evaluating all the other actions that + // occurred in the same frame, evaluate this one action and + // exit. + do_sequence_action(sequence_frame, *sequence_action); + return false; + } + + do_action(frame, action, sequence_frame, sequence_action); + } + + if (sequence_frame != -1) { + do_sequence_action(sequence_frame, *sequence_action); + return false; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::do_actions_backward +// Access: Private +// Description: Calls each of the actions, in turn, on the timeline +// between the indicated "from" frame and the indicated +// "to" frame, in reverse order. If any of the actions +// specifies to stop the animation, the animation is +// stopped, the current frame number is set to the point +// of stopping, no further actions are called, and false +// is returned. Otherwise, true is returned. +//////////////////////////////////////////////////////////////////// +bool AnimControl:: +do_actions_backward(int from, int to) { +#ifndef WIN32_VC + if (from >= to) { +#ifdef WIN32_VC + typedef reverse_iterator Action_reverse_iterator; +#else + typedef reverse_iterator Action_reverse_iterator; +#endif + Action_reverse_iterator lower(_actions.upper_bound(from)); + Action_reverse_iterator upper(_actions.upper_bound(to-1)); + + int sequence_frame = -1; + const Action *sequence_action; + + Action_reverse_iterator ai; + for (ai = lower; ai != upper; ++ai) { + int frame = (*ai).first; + const Action &action = (*ai).second; + + if (sequence_frame != -1 && frame < sequence_frame) { + // We encountered an action that resequenced our frame numbers. + // Now that we've finished evaluating all the other actions that + // occurred in the same frame, evaluate this one action and + // exit. + do_sequence_action(sequence_frame, *sequence_action); + return false; + } + + do_action(frame, action, sequence_frame, sequence_action); + } + + if (sequence_frame != -1) { + do_sequence_action(sequence_frame, *sequence_action); + return false; + } + } +#endif + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::do_action +// Access: Private +// Description: Performs a single action. If the action involves +// some resequencing behavior--stopping, or jumping +// around to a new frame or something--does nothing +// immediately, but instead sets sequence_frame and +// sequence_action to the current frame number and +// action, so they may be executed later (after all the +// other actions this frame have been executed). +//////////////////////////////////////////////////////////////////// +void AnimControl:: +do_action(int frame, const Action &action, + int &sequence_frame, const Action *&sequence_action) { + switch (action._type) { + case AT_stop: + case AT_jump: + sequence_frame = frame; + sequence_action = &action; + break; + + case AT_event: + throw_event(action._event); + break; + + default: + chan_cat.error() << "Invalid action!\n"; + abort(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::do_sequence_action +// Access: Private +// Description: Performs an action that was saved from do_action(), +// above. This action presumably does some mucking +// around with the frame number or something, so we +// needed to do all the other actions associated with +// that frame first. +//////////////////////////////////////////////////////////////////// +void AnimControl:: +do_sequence_action(int, const Action &action) { + switch (action._type) { + case AT_stop: + stop(); + break; + + case AT_jump: + set_frame(action._jump_to); + break; + + case AT_event: + default: + chan_cat.error() << "Invalid sequence action!\n"; + abort(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControl::Action::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void AnimControl::Action:: +output(ostream &out) const { + switch (_type) { + case AT_event: + out << "event " << *_event; + break; + + case AT_stop: + out << "stop"; + break; + + case AT_jump: + out << "jump to " << _jump_to; + break; + + default: + out << "**error**"; + } +} diff --git a/panda/src/chan/animControl.h b/panda/src/chan/animControl.h new file mode 100644 index 0000000000..1a46e6db0e --- /dev/null +++ b/panda/src/chan/animControl.h @@ -0,0 +1,166 @@ +// Filename: animControl.h +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANIMCONTROL_H +#define ANIMCONTROL_H + +#include + +#include "animBundle.h" +#include "partGroup.h" + +#include +#include +#include +#include +#include + +#include +#include + +class PartBundle; +class AnimChannelBase; + +//////////////////////////////////////////////////////////////////// +// Class : AnimControl +// Description : Controls the timing of a character animation. An +// AnimControl object is created for each +// character/bundle binding and manages the state of the +// animation: whether started, stopped, or looping, and +// the current frame number and play rate. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA AnimControl : public ReferenceCount { +public: + AnimControl(PartBundle *part, AnimBundle *anim, int channel_index); + + void play(const CPT_Event &stop_event = NULL); + void play(int from, int to, const CPT_Event &stop_event = NULL); + void loop(bool restart); + void loop(bool restart, int from, int to); + void stop(); + void pose(int frame); + + void add_event(int frame, const CPT_Event &event); + int remove_event(const string &event_name); + void remove_all_events(); + + INLINE void set_play_rate(double play_rate); + INLINE double get_play_rate() const; + INLINE double get_frame_rate() const; + + INLINE int get_frame() const; + INLINE int get_num_frames() const; + + INLINE bool is_playing() const; + + PartBundle *get_part() const; + INLINE AnimBundle *get_anim() const; + +public: + // The following functions aren't really part of the public + // interface; they're just public so we don't have to declare a + // bunch of friends. + + void advance_time(double time); + + bool channel_has_changed(AnimChannelBase *channel) const; + void mark_channels(); + + INLINE int get_channel_index() const; + + void output(ostream &out) const; + +private: + + enum ActionType { + AT_event, + AT_stop, + AT_jump, + }; + +#ifdef WIN32_VC +public: +#endif + + class EXPCL_PANDA Action { + public: + ActionType _type; + CPT_Event _event; + int _jump_to; + + void output(ostream &out) const; + }; + +private: + typedef multimap Actions; + + static void insert_event_action(Actions &actions, int frame, const CPT_Event &event); + static void insert_stop_action(Actions &actions, int frame); + static void insert_jump_action(Actions &actions, int frame, int jump_to); + + void set_frame(double frame); + + bool do_actions_forward(int from, int to); + bool do_actions_backward(int from, int to); + + void do_action(int frame, const Action &action, + int &sequence_frame, const Action *&sequence_action); + void do_sequence_action(int frame, const Action &action); + + Actions _actions; + Actions _user_actions; + + // This is a PT(PartGroup) instead of a PT(PartBundle), just because + // we can't include partBundle.h for circular reasons. But it + // actually keeps a pointer to a PartBundle. + PT(PartGroup) _part; + PT(AnimBundle) _anim; + int _channel_index; + + double _play_rate; + + // We keep a floating-point frame number. This increments fluidly, + // and records honest "between-frame" increments. However, whenever + // we report the current frame number, we just report the integer + // portion. + double _frame; + double _as_of_time; + + bool _playing; + + // This is the frame number as of the last call to mark_channels(). + int _marked_frame; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ReferenceCount::init_type(); + register_type(_type_handle, "AnimControl", + ReferenceCount::get_class_type()); + } + +private: + static TypeHandle _type_handle; + +friend inline ostream &operator << (ostream &, const AnimControl::Action &); +friend class AnimControl::Action; +}; + +inline ostream &operator << (ostream &out, const AnimControl &ac) { + ac.output(out); + return out; +} + +inline ostream &operator << (ostream &out, const AnimControl::Action &action) { + action.output(out); + return out; +} + +#include "animControl.I" + +#endif diff --git a/panda/src/chan/animControlCollection.I b/panda/src/chan/animControlCollection.I new file mode 100644 index 0000000000..3e64e36488 --- /dev/null +++ b/panda/src/chan/animControlCollection.I @@ -0,0 +1,228 @@ +// Filename: animControlCollection.I +// Created by: drose (22Feb00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::set_stop_event +// Access: Public +// Description: Sets the event that will be thrown when the next +// animation that is played eventually comes to a stop. +// Setting this does not affect any stop event that is +// pending from a previously-started animation. +//////////////////////////////////////////////////////////////////// +INLINE void AnimControlCollection:: +set_stop_event(const CPT_Event &stop_event) { + _stop_event = stop_event; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::clear_stop_event +// Access: Public +// Description: Indicates that the next-started animation will not +// throw a stop event when it comes to a stop. This +// does not affect any already-started animations. +//////////////////////////////////////////////////////////////////// +INLINE void AnimControlCollection:: +clear_stop_event() { + _stop_event = (const Event *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::has_stop_event +// Access: Public +// Description: Returns true if a stop event has been established via +// set_stop_event(). If true, this means that +// animations that are to be started in the future will +// throw this event when they stop. However, it does +// not necessarily mean that the currently-playing +// animation will throw this event. +//////////////////////////////////////////////////////////////////// +INLINE bool AnimControlCollection:: +has_stop_event() const { + return (_stop_event != (const Event *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::get_stop_event +// Access: Public +// Description: Returns the event that has been established via +// set_stop_event(). This is the event that will be +// thrown by animations that are started in the future +// when they stop. However, this may or may not be +// associated with any currently-playing animations. +//////////////////////////////////////////////////////////////////// +INLINE CPT_Event AnimControlCollection:: +get_stop_event() const { + return _stop_event; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::play +// Access: Public +// Description: Starts the named animation playing. +//////////////////////////////////////////////////////////////////// +INLINE bool AnimControlCollection:: +play(const string &anim_name) { + AnimControl *control = find_anim(anim_name); + if (control == (AnimControl *)NULL) { + return false; + } + _last_started_control = control; + control->play(_stop_event); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::play +// Access: Public +// Description: Starts the named animation playing. +//////////////////////////////////////////////////////////////////// +INLINE bool AnimControlCollection:: +play(const string &anim_name, int from, int to) { + AnimControl *control = find_anim(anim_name); + if (control == (AnimControl *)NULL) { + return false; + } + _last_started_control = control; + control->play(from, to, _stop_event); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::loop +// Access: Public +// Description: Starts the named animation looping. +//////////////////////////////////////////////////////////////////// +INLINE bool AnimControlCollection:: +loop(const string &anim_name, bool restart) { + AnimControl *control = find_anim(anim_name); + if (control == (AnimControl *)NULL) { + return false; + } + _last_started_control = control; + control->loop(restart); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::loop +// Access: Public +// Description: Starts the named animation looping. +//////////////////////////////////////////////////////////////////// +INLINE bool AnimControlCollection:: +loop(const string &anim_name, bool restart, int from, int to) { + AnimControl *control = find_anim(anim_name); + if (control == (AnimControl *)NULL) { + return false; + } + _last_started_control = control; + control->loop(restart, from, to); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::stop +// Access: Public +// Description: Stops the named animation. +//////////////////////////////////////////////////////////////////// +INLINE bool AnimControlCollection:: +stop(const string &anim_name) { + AnimControl *control = find_anim(anim_name); + if (control == (AnimControl *)NULL) { + return false; + } + control->stop(); + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::pose +// Access: Public +// Description: Sets to a particular frame in the named animation. +//////////////////////////////////////////////////////////////////// +INLINE bool AnimControlCollection:: +pose(const string &anim_name, int frame) { + AnimControl *control = find_anim(anim_name); + if (control == (AnimControl *)NULL) { + return false; + } + _last_started_control = control; + control->pose(frame); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::get_frame +// Access: Public +// Description: Returns the current frame in the named animation, or +// 0 if the animation is not found. +//////////////////////////////////////////////////////////////////// +INLINE int AnimControlCollection:: +get_frame(const string &anim_name) const { + AnimControl *control = find_anim(anim_name); + if (control == (AnimControl *)NULL) { + return 0; + } + return control->get_frame(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::get_frame +// Access: Public +// Description: Returns the current frame in the last-started +// animation. +//////////////////////////////////////////////////////////////////// +INLINE int AnimControlCollection:: +get_frame() const { + if (_last_started_control == (AnimControl *)NULL) { + return 0; + } + return _last_started_control->get_frame(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::is_playing +// Access: Public +// Description: Returns true if the named animation is currently +// playing, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool AnimControlCollection:: +is_playing(const string &anim_name) const { + AnimControl *control = find_anim(anim_name); + if (control == (AnimControl *)NULL) { + return false; + } + return control->is_playing(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::is_playing +// Access: Public +// Description: Returns true if the last-started animation is +// currently playing, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool AnimControlCollection:: +is_playing() const { + if (_last_started_control == (AnimControl *)NULL) { + return false; + } + return _last_started_control->is_playing(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::get_num_frames +// Access: Public +// Description: Returns the total number of frames in the named +// animation, or 0 if the animation is not found. +//////////////////////////////////////////////////////////////////// +INLINE int AnimControlCollection:: +get_num_frames(const string &anim_name) const { + AnimControl *control = find_anim(anim_name); + if (control == (AnimControl *)NULL) { + return 0; + } + return control->get_num_frames(); +} diff --git a/panda/src/chan/animControlCollection.cxx b/panda/src/chan/animControlCollection.cxx new file mode 100644 index 0000000000..57c93767bb --- /dev/null +++ b/panda/src/chan/animControlCollection.cxx @@ -0,0 +1,217 @@ +// Filename: animControlCollection.cxx +// Created by: drose (22Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "animControlCollection.h" + + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::Constructor +// Access: Public +// Description: Returns the AnimControl associated with the given +// name, or NULL if no such control has been associated. +//////////////////////////////////////////////////////////////////// +AnimControlCollection:: +AnimControlCollection() { + _last_started_control = (AnimControl *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +AnimControlCollection:: +~AnimControlCollection() { +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::store_anim +// Access: Public +// Description: Associates the given AnimControl with this collection +// under the given name. The AnimControl will remain +// associated until a new AnimControl is associated with +// the same name later, or until unbind_anim() is called +// with this name. +//////////////////////////////////////////////////////////////////// +void AnimControlCollection:: +store_anim(AnimControl *control, const string &name) { + Controls::iterator ci = _controls.find(name); + if (ci == _controls.end()) { + _controls.insert(Controls::value_type(name, control)); + } else { + if (_last_started_control == (*ci).second) { + _last_started_control = (AnimControl *)NULL; + } + (*ci).second = control; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::find_anim +// Access: Public +// Description: Returns the AnimControl associated with the given +// name, or NULL if no such control has been associated. +//////////////////////////////////////////////////////////////////// +AnimControl *AnimControlCollection:: +find_anim(const string &name) const { + Controls::const_iterator ci = _controls.find(name); + if (ci == _controls.end()) { + return (AnimControl *)NULL; + } + return (*ci).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::unbind_anim +// Access: Public +// Description: Removes the AnimControl associated with the given +// name, if any. Returns true if an AnimControl was +// removed, false if there was no AnimControl with the +// indicated name. +//////////////////////////////////////////////////////////////////// +bool AnimControlCollection:: +unbind_anim(const string &name) { + Controls::iterator ci = _controls.find(name); + if (ci == _controls.end()) { + return false; + } + if (_last_started_control == (*ci).second) { + _last_started_control = (AnimControl *)NULL; + } + _controls.erase(ci); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::get_num_anims +// Access: Public +// Description: Returns the number of AnimControls associated with +// this collection. +//////////////////////////////////////////////////////////////////// +int AnimControlCollection:: +get_num_anims() const { + return _controls.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::clear_anims +// Access: Public +// Description: Disassociates all anims from this collection. +//////////////////////////////////////////////////////////////////// +void AnimControlCollection:: +clear_anims() { + _controls.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::play_all +// Access: Public +// Description: Starts all animations playing. +//////////////////////////////////////////////////////////////////// +void AnimControlCollection:: +play_all() { + Controls::const_iterator ci; + for (ci = _controls.begin(); ci != _controls.end(); ++ci) { + (*ci).second->play(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::play_all +// Access: Public +// Description: Starts all animations playing. +//////////////////////////////////////////////////////////////////// +void AnimControlCollection:: +play_all(int from, int to) { + Controls::const_iterator ci; + for (ci = _controls.begin(); ci != _controls.end(); ++ci) { + (*ci).second->play(from, to); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::loop_all +// Access: Public +// Description: Starts all animations looping. +//////////////////////////////////////////////////////////////////// +void AnimControlCollection:: +loop_all(bool restart) { + Controls::const_iterator ci; + for (ci = _controls.begin(); ci != _controls.end(); ++ci) { + (*ci).second->loop(restart); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::loop_all +// Access: Public +// Description: Starts all animations looping. +//////////////////////////////////////////////////////////////////// +void AnimControlCollection:: +loop_all(bool restart, int from, int to) { + Controls::const_iterator ci; + for (ci = _controls.begin(); ci != _controls.end(); ++ci) { + (*ci).second->loop(restart, from, to); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::stop_all +// Access: Public +// Description: Stops all currently playing animations. Returns true +// if any animations were stopped, false if none were +// playing. +//////////////////////////////////////////////////////////////////// +bool AnimControlCollection:: +stop_all() { + bool any = false; + Controls::const_iterator ci; + for (ci = _controls.begin(); ci != _controls.end(); ++ci) { + if ((*ci).second->is_playing()) { + any = true; + (*ci).second->stop(); + } + } + + return any; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::pose_all +// Access: Public +// Description: Sets all animations to the indicated frame. +//////////////////////////////////////////////////////////////////// +void AnimControlCollection:: +pose_all(int frame) { + Controls::const_iterator ci; + for (ci = _controls.begin(); ci != _controls.end(); ++ci) { + (*ci).second->pose(frame); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimControlCollection::which_anim_playing +// Access: Public +// Description: Returns the name of the bound AnimControl currently +// playing, if any. If more than one AnimControl is +// currently playing, returns all of the names separated +// by spaces. +//////////////////////////////////////////////////////////////////// +string AnimControlCollection:: +which_anim_playing() const { + string result; + + Controls::const_iterator ci; + for (ci = _controls.begin(); ci != _controls.end(); ++ci) { + if ((*ci).second->is_playing()) { + if (!result.empty()) { + result += " "; + } + result += (*ci).first; + } + } + + return result; +} diff --git a/panda/src/chan/animControlCollection.h b/panda/src/chan/animControlCollection.h new file mode 100644 index 0000000000..7d62019321 --- /dev/null +++ b/panda/src/chan/animControlCollection.h @@ -0,0 +1,83 @@ +// Filename: animControlCollection.h +// Created by: drose (22Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANIMCONTROLCOLLECTION_H +#define ANIMCONTROLCOLLECTION_H + +#include + +#include "animControl.h" + +#include +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : AnimControlCollection +// Description : This is a named collection of AnimControl pointers. +// An AnimControl may be added to the collection by +// name. While an AnimControl is associated, its +// reference count is maintained; associating a new +// AnimControl with the same name will decrement the +// previous control's reference count (and possibly +// delete it, unbinding its animation). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA AnimControlCollection { +public: + AnimControlCollection(); + ~AnimControlCollection(); + + void store_anim(AnimControl *control, const string &name); + AnimControl *find_anim(const string &name) const; + bool unbind_anim(const string &name); + + int get_num_anims() const; + void clear_anims(); + + INLINE void set_stop_event(const CPT_Event &stop_event); + INLINE void clear_stop_event(); + INLINE bool has_stop_event() const; + INLINE CPT_Event get_stop_event() const; + + // The following functions are convenience functions that vector + // directly into the AnimControl's functionality by anim name. + + INLINE bool play(const string &anim_name); + INLINE bool play(const string &anim_name, int from, int to); + INLINE bool loop(const string &anim_name, bool restart); + INLINE bool loop(const string &anim_name, bool restart, int from, int to); + INLINE bool stop(const string &anim_name); + INLINE bool pose(const string &anim_name, int frame); + + // These functions operate on all anims at once. + void play_all(); + void play_all(int from, int to); + void loop_all(bool restart); + void loop_all(bool restart, int from, int to); + bool stop_all(); + void pose_all(int frame); + + INLINE int get_frame(const string &anim_name) const; + INLINE int get_frame() const; + + INLINE int get_num_frames(const string &anim_name) const; + + INLINE bool is_playing(const string &anim_name) const; + INLINE bool is_playing() const; + + string which_anim_playing() const; + +private: + typedef map Controls; + Controls _controls; + CPT_Event _stop_event; + AnimControl *_last_started_control; +}; + +#include "animControlCollection.I" + +#endif diff --git a/panda/src/chan/animGroup.I b/panda/src/chan/animGroup.I new file mode 100644 index 0000000000..ea0191100e --- /dev/null +++ b/panda/src/chan/animGroup.I @@ -0,0 +1,4 @@ +// Filename: animGroup.I +// Created by: drose (21Feb99) +// +//////////////////////////////////////////////////////////////////// diff --git a/panda/src/chan/animGroup.cxx b/panda/src/chan/animGroup.cxx new file mode 100644 index 0000000000..242d75c03c --- /dev/null +++ b/panda/src/chan/animGroup.cxx @@ -0,0 +1,240 @@ +// Filename: animGroup.cxx +// Created by: drose (21Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "animGroup.h" +#include "animBundle.h" +#include "config_chan.h" + +#include +#include +#include +#include +#include + +#include + +TypeHandle AnimGroup::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: AnimGroup::Constructor +// Access: Public +// Description: Creates the AnimGroup, and adds it to the indicated +// parent. The only way to delete it subsequently is to +// delete the entire hierarchy. +//////////////////////////////////////////////////////////////////// +AnimGroup:: +AnimGroup(AnimGroup *parent, const string &name) : Namable(name) { + assert(parent != NULL); + + parent->_children.push_back(this); + _root = parent->_root; +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimGroup::get_num_children +// Access: Public +// Description: Returns the number of child nodes of the group. +//////////////////////////////////////////////////////////////////// +int AnimGroup:: +get_num_children() const { + return _children.size(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimGroup::get_child +// Access: Public +// Description: Returns the nth child of the group. +//////////////////////////////////////////////////////////////////// +AnimGroup *AnimGroup:: +get_child(int n) const { + assert(n >= 0 && n < _children.size()); + return _children[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimGroupa::get_value_type +// Access: Public, Virtual +// Description: Returns the TypeHandle associated with the ValueType +// we are concerned with. This is provided to allow a +// bit of run-time checking that joints and channels are +// matching properly in type. +//////////////////////////////////////////////////////////////////// +TypeHandle AnimGroup:: +get_value_type() const { + return TypeHandle::none(); +} + + +// An STL object to sort a list of children into alphabetical order. +class AnimGroupAlphabeticalOrder { +public: + bool operator()(const PT(AnimGroup) &a, const PT(AnimGroup) &b) const { + return a->get_name() < b->get_name(); + } +}; + +//////////////////////////////////////////////////////////////////// +// Function: AnimGroup::sort_descendants +// Access: Public +// Description: Sorts the children nodes at each level of the +// hierarchy into alphabetical order. This should be +// done after creating the hierarchy, to guarantee that +// the correct names will match up together when the +// AnimBundle is later bound to a PlayerRoot. +//////////////////////////////////////////////////////////////////// +void AnimGroup:: +sort_descendants() { + sort(_children.begin(), _children.end(), AnimGroupAlphabeticalOrder()); + + Children::iterator ci; + for (ci = _children.begin(); ci != _children.end(); ++ci) { + (*ci)->sort_descendants(); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimGroup::output +// Access: Public, Virtual +// Description: Writes a one-line description of the group. +//////////////////////////////////////////////////////////////////// +void AnimGroup:: +output(ostream &out) const { + out << get_type() << " " << get_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimGroup::write +// Access: Public, Virtual +// Description: Writes a brief description of the group and all of +// its descendants. +//////////////////////////////////////////////////////////////////// +void AnimGroup:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << *this << " {\n"; + write_descendants(out, indent_level + 2); + indent(out, indent_level) << "}\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimGroup::write_descendants +// Access: Protected +// Description: Writes a brief description of all of the group's +// descendants. +//////////////////////////////////////////////////////////////////// +void AnimGroup:: +write_descendants(ostream &out, int indent_level) const { + Children::const_iterator ci; + + for (ci = _children.begin(); ci != _children.end(); ++ci) { + (*ci)->write(out, indent_level); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimGroup::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void AnimGroup:: +write_datagram(BamWriter *manager, Datagram &me) +{ + me.add_string(get_name()); + //Write out the root + manager->write_pointer(me, this->_root); + me.add_uint16(_children.size()); + for(int i = 0; i < _children.size(); i++) + { + manager->write_pointer(me, _children[i]); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimGroup::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void AnimGroup:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + set_name(scan.get_string()); + manager->read_pointer(scan, this); + _num_children = scan.get_uint16(); + for(int i = 0; i < _num_children; i++) + { + manager->read_pointer(scan, this); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimGroup::complete_pointers +// Access: Public +// Description: Takes in a vector of pointes to TypedWriteable +// objects that correspond to all the requests for +// pointers that this object made to BamReader. +//////////////////////////////////////////////////////////////////// +int AnimGroup:: +complete_pointers(vector_typedWriteable &plist, BamReader*) +{ + nassertr(plist[0] != TypedWriteable::Null, 0); + _root = DCAST(AnimBundle, plist[0]); + for(int i = 1; i < _num_children+1; i++) + { + if (plist[i] == TypedWriteable::Null) + { + chan_cat->warning() << get_type().get_name() + << " Ignoring null child" << endl; + } + else + { + _children.push_back(DCAST(AnimGroup, plist[i])); + } + } + return _num_children+1; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimGroup::make_AnimGroup +// Access: Protected +// Description: Factory method to generate a AnimGroup object +//////////////////////////////////////////////////////////////////// +TypedWriteable* AnimGroup:: +make_AnimGroup(const FactoryParams ¶ms) +{ + AnimGroup *me = new AnimGroup; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimGroup::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a AnimGroup object +//////////////////////////////////////////////////////////////////// +void AnimGroup:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_AnimGroup); +} + + + + + + + diff --git a/panda/src/chan/animGroup.h b/panda/src/chan/animGroup.h new file mode 100644 index 0000000000..14f83fc872 --- /dev/null +++ b/panda/src/chan/animGroup.h @@ -0,0 +1,97 @@ +// Filename: animGroup.h +// Created by: drose (21Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANIMGROUP_H +#define ANIMGROUP_H + +#include + +#include +#include +#include + +class AnimBundle; +class BamReader; + +//////////////////////////////////////////////////////////////////// +// Class : AnimGroup +// Description : This is the base class for AnimChannel and +// AnimBundle. It implements a hierarchy of +// AnimChannels. The root of the hierarchy must be an +// AnimBundle. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA AnimGroup : public TypedWriteableReferenceCount, public Namable { +protected: + // The default constructor is protected: don't try to create an + // AnimGroup without a parent. To create an AnimChannel hierarchy, + // you must first create an AnimBundle, and use that to create any + // subsequent children. + AnimGroup(const string &name = "") : Namable(name) { } + +public: + // This is the normal AnimGroup constructor. + AnimGroup(AnimGroup *parent, const string &name); + + int get_num_children() const; + AnimGroup *get_child(int n) const; + + virtual TypeHandle get_value_type() const; + + void sort_descendants(); + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level) const; + +protected: + void write_descendants(ostream &out, int indent_level) const; + +protected: + typedef vector Children; + Children _children; + AnimBundle *_root; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + virtual int complete_pointers(vector_typedWriteable &plist, + BamReader *manager); + + static TypedWriteable *make_AnimGroup(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +private: + int _num_children; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedWriteableReferenceCount::init_type(); + register_type(_type_handle, "AnimGroup", + TypedWriteableReferenceCount::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +inline ostream &operator << (ostream &out, const AnimGroup &anim) { + anim.output(out); + return out; +} + +#include "animGroup.I" + +#endif + + diff --git a/panda/src/chan/auto_bind.cxx b/panda/src/chan/auto_bind.cxx new file mode 100644 index 0000000000..0dacc5a245 --- /dev/null +++ b/panda/src/chan/auto_bind.cxx @@ -0,0 +1,141 @@ +// Filename: auto_bind.cxx +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "auto_bind.h" +#include "animBundleNode.h" +#include "partBundleNode.h" +#include "config_chan.h" + +#include +#include +#include +#include +#include +#include + +typedef set AnimNodes; +typedef map Anims; + +typedef set PartNodes; +typedef map Parts; + + + +//////////////////////////////////////////////////////////////////// +// Class : CollectNodes +// Description : This is a traverser visitor that locates bundle nodes +// and adds them to their respective maps. +//////////////////////////////////////////////////////////////////// +class CollectNodes : + public TraverserVisitor { +public: + bool reached_node(Node *node, const NullAttributeWrapper &, + NullLevelState &) { + if (node->is_of_type(AnimBundleNode::get_class_type())) { + AnimBundleNode *bn = DCAST(AnimBundleNode, node); + _anims[bn->get_bundle()->get_name()].insert(bn); + + } else if (node->is_of_type(PartBundleNode::get_class_type())) { + PartBundleNode *bn = DCAST(PartBundleNode, node); + _parts[bn->get_bundle()->get_name()].insert(bn); + } + + return true; + } + + Anims _anims; + Parts _parts; +}; + + +//////////////////////////////////////////////////////////////////// +// Function: bind_anims +// Description: A support function for auto_bind(), below. Given a +// set of AnimBundles and a set of PartBundles that all +// share the same name, perform whatever bindings make +// sense. +//////////////////////////////////////////////////////////////////// +static void +bind_anims(const PartNodes &parts, const AnimNodes &anims, + AnimControlCollection &controls, + int hierarchy_match_flags) { + + PartNodes::const_iterator pni; + + for (pni = parts.begin(); pni != parts.end(); ++pni) { + PartBundle *part = (*pni)->get_bundle(); + + AnimNodes::const_iterator ani; + for (ani = anims.begin(); ani != anims.end(); ++ani) { + AnimBundle *anim = (*ani)->get_bundle(); + + if (chan_cat.is_info()) { + chan_cat.info() + << "Attempting to bind " << *part << " to " << *anim << "\n"; + } + + PT(AnimControl) control = + part->bind_anim(anim, hierarchy_match_flags); + if (control != (AnimControl *)NULL) { + controls.store_anim(control, (*ani)->get_name()); + } + + if (chan_cat.is_info()) { + if (control == NULL) { + chan_cat.info() + << "Bind failed.\n"; + } else { + chan_cat.info() + << "Bind succeeded, index " + << control->get_channel_index() << "\n"; + } + } + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: auto_bind +// Description: Walks the scene graph or subgraph beginning at the +// indicated node, and attempts to bind any AnimBundles +// found to their matching PartBundles, when possible. +// +// The list of all resulting AnimControls created is +// filled into controls. +//////////////////////////////////////////////////////////////////// +void auto_bind(Node *root_node, AnimControlCollection &controls, + int hierarchy_match_flags) { + + // First, locate all the bundles in the subgraph. + CollectNodes cn; + df_traverse(root_node, cn, NullAttributeWrapper(), NullLevelState(), + RenderRelation::get_class_type()); + + // Now, match up the bundles by name. + + Anims::const_iterator ai = cn._anims.begin(); + Parts::const_iterator pi = cn._parts.begin(); + + while (ai != cn._anims.end() && pi != cn._parts.end()) { + if ((*ai).first < (*pi).first) { + // Here's an anim with no matching parts. + ++ai; + + } else if ((*pi).first < (*ai).first) { + // And here's a part with no matching anims. + ++pi; + + } else { + // But here we have (at least one) match! + bind_anims((*pi).second, (*ai).second, controls, + hierarchy_match_flags); + ++ai; + ++pi; + } + } +} + + diff --git a/panda/src/chan/auto_bind.h b/panda/src/chan/auto_bind.h new file mode 100644 index 0000000000..760e6402f6 --- /dev/null +++ b/panda/src/chan/auto_bind.h @@ -0,0 +1,30 @@ +// Filename: auto_bind.h +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef AUTO_BIND_H +#define AUTO_BIND_H + +#include + +#include "animControl.h" +#include "animControlCollection.h" + +class Node; + +//////////////////////////////////////////////////////////////////// +// Function: auto_bind +// Description: Walks the scene graph or subgraph beginning at the +// indicated node, and attempts to bind any AnimBundles +// found to their matching PartBundles, when possible. +// +// The list of all resulting AnimControls created is +// filled into controls. +//////////////////////////////////////////////////////////////////// +EXPCL_PANDA void +auto_bind(Node *root_node, AnimControlCollection &controls, + int hierarchy_match_flags = 0); + +#endif + diff --git a/panda/src/chan/config_chan.cxx b/panda/src/chan/config_chan.cxx new file mode 100644 index 0000000000..3c80956d47 --- /dev/null +++ b/panda/src/chan/config_chan.cxx @@ -0,0 +1,69 @@ +// Filename: config_chan.cxx +// Created by: drose (28Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_chan.h" +#include "animBundle.h" +#include "animBundleNode.h" +#include "animChannelBase.h" +#include "animChannelMatrixXfmTable.h" +#include "animChannelScalarTable.h" +#include "animControl.h" +#include "animGroup.h" +#include "movingPartBase.h" +#include "movingPartMatrix.h" +#include "movingPartScalar.h" +#include "partBundle.h" +#include "partBundleNode.h" +#include "partGroup.h" + +#include +#include + +Configure(config_chan); +NotifyCategoryDef(chan, ""); + +// This is normally set true to quantize animation channels to 16-bit +// integer values when writing to a Bam file; a cheesy way to +// hopefully achieve greater compression ratios. +const bool quantize_bam_channels = config_chan.GetBool("quantize-bam-channels", true); + +ConfigureFn(config_chan) { + AnimBundle::init_type(); + AnimBundleNode::init_type(); + AnimChannelBase::init_type(); + AnimChannelMatrixXfmTable::init_type(); + AnimChannelScalarTable::init_type(); + AnimControl::init_type(); + AnimGroup::init_type(); + MovingPartBase::init_type(); + MovingPartMatrix::init_type(); + MovingPartScalar::init_type(); + PartBundle::init_type(); + PartBundleNode::init_type(); + PartGroup::init_type(); + + // This isn't defined in this package, but it *is* essential that it + // be initialized. We have to do it explicitly here since template + // statics don't necessarily resolve very well across dynamic + // libraries. + LMatrix4f::init_type(); + + //Registration of writeable object's creation + //functions with BamReader's factory + PartGroup::register_with_read_factory(); + PartBundle::register_with_read_factory(); + MovingPartMatrix::register_with_read_factory(); + MovingPartScalar::register_with_read_factory(); + + AnimGroup::register_with_read_factory(); + AnimBundle::register_with_read_factory(); + AnimBundleNode::register_with_read_factory(); + AnimChannelMatrixXfmTable::register_with_read_factory(); + AnimChannelScalarTable::register_with_read_factory(); +} + + + + diff --git a/panda/src/chan/config_chan.h b/panda/src/chan/config_chan.h new file mode 100644 index 0000000000..812f5322f1 --- /dev/null +++ b/panda/src/chan/config_chan.h @@ -0,0 +1,17 @@ +// Filename: config_chan.h +// Created by: drose (28Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_CHAN_H +#define CONFIG_CHAN_H + +#include +#include + +// Configure variables for chan package. +NotifyCategoryDecl(chan, EXPCL_PANDA, EXPTP_PANDA); + +EXPCL_PANDA extern const bool quantize_bam_channels; + +#endif diff --git a/panda/src/chan/movingPart.I b/panda/src/chan/movingPart.I new file mode 100644 index 0000000000..90d3f41349 --- /dev/null +++ b/panda/src/chan/movingPart.I @@ -0,0 +1,122 @@ +// Filename: movingPart.I +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "animChannelFixed.h" +#include +#include +#include +#include + +template +TypeHandle MovingPart::_type_handle; + +// We don't need to explicitly call MovingPart::init_type(), because +// it is an abstract class and therefore must have derived objects. +// Its derived objects will call init_type() for us. + + + +//////////////////////////////////////////////////////////////////// +// Function: MovingPart::Copy Constructor +// Access: Protected +// Description: Normally, you'd use make_copy() or copy_subgraph() to +// make a copy of this. +//////////////////////////////////////////////////////////////////// +template +INLINE MovingPart:: +MovingPart(const MovingPart ©) : + MovingPartBase(copy), + _initial_value(copy._initial_value), + _value(copy._value) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: MovingPart::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE MovingPart:: +MovingPart(PartGroup *parent, const string &name, + const ValueType &initial_value) + : MovingPartBase(parent, name), + _initial_value(initial_value), + _value(initial_value) { +} + +//////////////////////////////////////////////////////////////////// +// Function: MovingPart::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE MovingPart:: +MovingPart(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function: MovingPart::get_value_type +// Access: Public, Virtual +// Description: Returns the TypeHandle associated with the ValueType +// we are concerned with. This is provided to allow a +// bit of run-time checking that joints and channels are +// matching properly in type. +//////////////////////////////////////////////////////////////////// +template +TypeHandle MovingPart:: +get_value_type() const { + return get_type_handle(ValueType); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: MovingPart::make_initial_channel +// Access: Public, Virtual +// Description: Creates and returns a new AnimChannel that is not +// part of any hierarchy, but that returns the default +// value associated with this part. +//////////////////////////////////////////////////////////////////// +template +AnimChannelBase *MovingPart:: +make_initial_channel() const { + return new AnimChannelFixed(get_name(), _initial_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: MovingPart::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +template +void MovingPart:: +write_datagram(BamWriter *manager, Datagram &me) +{ + MovingPartBase::write_datagram(manager, me); + SwitchType::write_datagram(me, _value); + SwitchType::write_datagram(me, _initial_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: MovingPart::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +template +void MovingPart:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + MovingPartBase::fillin(scan, manager); + SwitchType::read_datagram(scan, _value); + SwitchType::read_datagram(scan, _initial_value); +} + + + diff --git a/panda/src/chan/movingPart.h b/panda/src/chan/movingPart.h new file mode 100644 index 0000000000..803932b4a6 --- /dev/null +++ b/panda/src/chan/movingPart.h @@ -0,0 +1,69 @@ +// Filename: movingPart.h +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MOVINGPART_H +#define MOVINGPART_H + +#include + +#include "movingPartBase.h" +#include "animChannel.h" + +//////////////////////////////////////////////////////////////////// +// Class : MovingPart +// Description : This is the template instantiation of MovingPartBase, +// on the particular type of value provided by the +// channel. +//////////////////////////////////////////////////////////////////// +template +class EXPCL_PANDA MovingPart : public MovingPartBase { +public: + typedef TYPENAME SwitchType::ValueType ValueType; + typedef AnimChannel ChannelType; + +protected: + INLINE MovingPart(const MovingPart ©); + +public: + INLINE MovingPart(PartGroup *parent, const string &name, + const ValueType &_initial_value); + + virtual TypeHandle get_value_type() const; + virtual AnimChannelBase *make_initial_channel() const; + + ValueType _value; + ValueType _initial_value; + +public: + INLINE virtual void write_datagram(BamWriter* manager, Datagram &me); + +protected: + INLINE MovingPart(void); + INLINE void fillin(DatagramIterator& scan, BamReader* manager); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MovingPartBase::init_type(); + register_type(_type_handle, SwitchType::get_part_type_name(), + MovingPartBase::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "movingPart.I" + +#endif + + + diff --git a/panda/src/chan/movingPartBase.I b/panda/src/chan/movingPartBase.I new file mode 100644 index 0000000000..7e2ac4be7b --- /dev/null +++ b/panda/src/chan/movingPartBase.I @@ -0,0 +1,18 @@ +// Filename: movingPartBase.I +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartBase::Copy Constructor +// Access: Protected +// Description: Normally, you'd use make_copy() or copy_subgraph() to +// make a copy of this. +//////////////////////////////////////////////////////////////////// +INLINE MovingPartBase:: +MovingPartBase(const MovingPartBase ©) : + PartGroup(copy) +{ + // We don't copy the bound channels. +} diff --git a/panda/src/chan/movingPartBase.cxx b/panda/src/chan/movingPartBase.cxx new file mode 100644 index 0000000000..95b5b7ce64 --- /dev/null +++ b/panda/src/chan/movingPartBase.cxx @@ -0,0 +1,176 @@ +// Filename: movingPartBase.cxx +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "movingPartBase.h" +#include "animControl.h" +#include "animChannelBase.h" + +#include + +TypeHandle MovingPartBase::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartBase::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +MovingPartBase:: +MovingPartBase(PartGroup *parent, const string &name) + : PartGroup(parent, name) { +} + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartBase::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +MovingPartBase:: +MovingPartBase(void){ +} + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartBase::write +// Access: Public, Virtual +// Description: Writes a brief description of the channel and all of +// its descendants. +//////////////////////////////////////////////////////////////////// +void MovingPartBase:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << get_value_type() << " " << get_name(); + if (_children.empty()) { + out << "\n"; + } else { + out << " {\n"; + write_descendants(out, indent_level + 2); + indent(out, indent_level) << "}\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartBase::do_update +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void MovingPartBase:: +do_update(PartBundle *root, PartGroup *parent, + bool parent_changed, bool anim_changed) { + bool needs_update = anim_changed; + + // See if any of the channel values have changed since last time. + + PartBundle::control_iterator bci; + for (bci = root->control_begin(); + !needs_update && bci != root->control_end(); + ++bci) { + AnimControl *control = (*bci); + int channel_index = control->get_channel_index(); + assert(channel_index >= 0 && channel_index < _channels.size()); + AnimChannelBase *channel = _channels[channel_index]; + assert(channel != (AnimChannelBase*)0L); + + needs_update = control->channel_has_changed(channel); + } + + if (needs_update) { + // Ok, get the latest value. + get_blend_value(root); + } + + if (parent_changed || needs_update) { + update_internals(parent, needs_update, parent_changed); + } + + // Now recurse. + Children::iterator ci; + for (ci = _children.begin(); ci != _children.end(); ++ci) { + (*ci)->do_update(root, this, parent_changed || needs_update, + anim_changed); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartBase::update_internals +// Access: Public, Virtual +// Description: This is called by do_update() whenever the part or +// some ancestor has changed values. It is a hook for +// derived classes to update whatever cache they may +// have that depends on these. +//////////////////////////////////////////////////////////////////// +void MovingPartBase:: +update_internals(PartGroup *, bool, bool) { +} + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartBase::pick_channel_index +// Access: Protected +// Description: Walks the part hierarchy, looking for a suitable +// channel index number to use. Available index numbers +// are the elements of the holes set, as well as next to +// infinity. +//////////////////////////////////////////////////////////////////// +void MovingPartBase:: +pick_channel_index(list &holes, int &next) const { + // Verify each of the holes. + + list::iterator ii, ii_next; + ii = holes.begin(); + while (ii != holes.end()) { + ii_next = ii; + ++ii_next; + + int hole = (*ii); + assert(hole >= 0 && hole < next); + if (hole < _channels.size() || + _channels[hole] != (AnimChannelBase *)NULL) { + // We can't accept this hole; we're using it! + holes.erase(ii); + } + ii = ii_next; + } + + // Now do we have any more to restrict? + if (next < _channels.size()) { + int i; + for (i = next; i < _channels.size(); i++) { + if (_channels[i] == (AnimChannelBase*)0L) { + // Here's a hole we do have. + holes.push_back(i); + } + } + next = _channels.size(); + } + + PartGroup::pick_channel_index(holes, next); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartBase::bind_hierarchy +// Access: Protected, Virtual +// Description: Binds the indicated anim hierarchy to the part +// hierarchy, at the given channel index number. +//////////////////////////////////////////////////////////////////// +void MovingPartBase:: +bind_hierarchy(AnimGroup *anim, int channel_index) { + while (_channels.size() <= channel_index) { + _channels.push_back((AnimChannelBase*)0L); + } + + assert(_channels[channel_index] == (AnimChannelBase*)0L); + + if (anim == (AnimGroup*)0L) { + // If we're binding to the NULL anim, it means actually to create + // a default AnimChannel that just returns the part's initial + // value. + _channels[channel_index] = make_initial_channel(); + } else { + _channels[channel_index] = DCAST(AnimChannelBase, anim); + } + + PartGroup::bind_hierarchy(anim, channel_index); +} diff --git a/panda/src/chan/movingPartBase.h b/panda/src/chan/movingPartBase.h new file mode 100644 index 0000000000..aca2126083 --- /dev/null +++ b/panda/src/chan/movingPartBase.h @@ -0,0 +1,77 @@ +// Filename: movingPartBase.h +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MOVINGPARTBASE_H +#define MOVINGPARTBASE_H + +#include + +#include "partGroup.h" +#include "partBundle.h" +#include "animChannelBase.h" + +//////////////////////////////////////////////////////////////////// +// Class : MovingPartBase +// Description : This is the base class for a single animatable piece +// that may be bound to one channel (or more, if +// blending is in effect). It corresponds to, for +// instance, a single joint or slider of a character. +// +// MovingPartBase does not have a particular value type. +// See the derived template class, MovingPart, for this. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA MovingPartBase : public PartGroup { +protected: + INLINE MovingPartBase(const MovingPartBase ©); + +public: + MovingPartBase(PartGroup *parent, const string &name); + + virtual TypeHandle get_value_type() const=0; + virtual AnimChannelBase *make_initial_channel() const=0; + virtual void write(ostream &out, int indent_level) const; + + virtual void do_update(PartBundle *root, PartGroup *parent, + bool parent_changed, bool anim_changed); + + virtual void get_blend_value(const PartBundle *root)=0; + virtual void update_internals(PartGroup *parent, bool self_changed, + bool parent_changed); + +protected: + MovingPartBase(void); + + virtual void pick_channel_index(list &holes, int &next) const; + virtual void bind_hierarchy(AnimGroup *anim, int channel_index); + + typedef vector Channels; + Channels _channels; + +public: + + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PartGroup::init_type(); + register_type(_type_handle, "MovingPartBase", + PartGroup::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "movingPartBase.I" + +#endif + + + + diff --git a/panda/src/chan/movingPartMatrix.I b/panda/src/chan/movingPartMatrix.I new file mode 100644 index 0000000000..65ed9fa061 --- /dev/null +++ b/panda/src/chan/movingPartMatrix.I @@ -0,0 +1,37 @@ +// Filename: movingPartMatrix.I +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartMatrix::Copy Constructor +// Access: Protected +// Description: Normally, you'd use make_copy() or copy_subgraph() to +// make a copy of this. +//////////////////////////////////////////////////////////////////// +INLINE MovingPartMatrix:: +MovingPartMatrix(const MovingPartMatrix ©) : + MovingPart(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartMatrix::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MovingPartMatrix:: +MovingPartMatrix(PartGroup *parent, const string &name, + const LMatrix4f &initial_value) + : MovingPart(parent, name, initial_value) { +} + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartMatrix::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MovingPartMatrix:: +MovingPartMatrix(void) { +} diff --git a/panda/src/chan/movingPartMatrix.cxx b/panda/src/chan/movingPartMatrix.cxx new file mode 100644 index 0000000000..eb9002cfac --- /dev/null +++ b/panda/src/chan/movingPartMatrix.cxx @@ -0,0 +1,148 @@ +// Filename: movingPartMatrix.cxx +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "movingPartMatrix.h" + +#include +#include +#include +#include +#include + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif + +TypeHandle MovingPartMatrix::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartMatrix::get_blend_value +// Access: Public +// Description: Attempts to blend the various matrix values +// indicated, and sets the _value member to the +// resulting matrix. +//////////////////////////////////////////////////////////////////// +void MovingPartMatrix:: +get_blend_value(const PartBundle *root) { + const PartBundle::ChannelBlend &blend = root->get_blend_map(); + + if (blend.empty()) { + // No channel is bound; supply the default value. + _value = _initial_value; + + } else if (blend.size() == 1) { + // A single value, the normal case. + AnimControl *control = (*blend.begin()).first; + + int channel_index = control->get_channel_index(); + assert(channel_index >= 0 && channel_index < _channels.size()); + ChannelType *channel = DCAST(ChannelType, _channels[channel_index]); + assert(channel != NULL); + + channel->get_value(control->get_frame(), _value); + + } else { + // A blend of two or more values. + + if (root->get_blend_type() == PartBundle::BT_linear) { + // An ordinary, linear blend. + _value = 0.0; + float net = 0.0; + + PartBundle::ChannelBlend::const_iterator cbi; + for (cbi = blend.begin(); cbi != blend.end(); ++cbi) { + AnimControl *control = (*cbi).first; + float effect = (*cbi).second; + assert(effect != 0.0); + + int channel_index = control->get_channel_index(); + assert(channel_index >= 0 && channel_index < _channels.size()); + ChannelType *channel = DCAST(ChannelType, _channels[channel_index]); + assert(channel != NULL); + + ValueType v; + channel->get_value(control->get_frame(), v); + + _value += v * effect; + net += effect; + } + + assert(net != 0.0); + _value /= net; + + } else if (root->get_blend_type() == PartBundle::BT_normalized_linear) { + // A normalized linear blend. This means we do a linear blend + // without scales, normalize the scale components of the + // resulting matrix to eliminate artificially-introduced scales, + // and then reapply the scales. + + _value = 0.0; + LVector3f scale(0.0, 0.0, 0.0); + float net = 0.0; + + PartBundle::ChannelBlend::const_iterator cbi; + for (cbi = blend.begin(); cbi != blend.end(); ++cbi) { + AnimControl *control = (*cbi).first; + float effect = (*cbi).second; + assert(effect != 0.0); + + int channel_index = control->get_channel_index(); + assert(channel_index >= 0 && channel_index < _channels.size()); + ChannelType *channel = DCAST(ChannelType, _channels[channel_index]); + assert(channel != NULL); + + ValueType v; + channel->get_value_no_scale(control->get_frame(), v); + LVector3f s; + channel->get_scale(control->get_frame(), &s[0]); + + _value += v * effect; + scale += s * effect; + net += effect; + } + + assert(net != 0.0); + _value /= net; + scale /= net; + + // Now rebuild the matrix with the correct scale values. + + LVector3f false_scale, hpr, translate; + decompose_matrix(_value, false_scale, hpr, translate); + compose_matrix(_value, scale, hpr, translate); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartMatrix::make_MovingPartMatrix +// Access: Protected +// Description: Factory method to generate a MovingPartMatrix object +//////////////////////////////////////////////////////////////////// +TypedWriteable* MovingPartMatrix:: +make_MovingPartMatrix(const FactoryParams ¶ms) +{ + MovingPartMatrix *me = new MovingPartMatrix; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartMatrix::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a MovingPartMatrix object +//////////////////////////////////////////////////////////////////// +void MovingPartMatrix:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_MovingPartMatrix); +} diff --git a/panda/src/chan/movingPartMatrix.h b/panda/src/chan/movingPartMatrix.h new file mode 100644 index 0000000000..a368ca9372 --- /dev/null +++ b/panda/src/chan/movingPartMatrix.h @@ -0,0 +1,70 @@ +// Filename: movingPartMatrix.h +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MOVINGPARTMATRIX_H +#define MOVINGPARTMATRIX_H + +#include + +#include "movingPart.h" +#include "animChannel.h" +#include "animChannelFixed.h" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, MovingPart); + +//////////////////////////////////////////////////////////////////// +// Class : MovingPartMatrix +// Description : This is a particular kind of MovingPart that accepts +// a matrix each frame. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA MovingPartMatrix : public MovingPart { +protected: + INLINE MovingPartMatrix(const MovingPartMatrix ©); + +public: + INLINE MovingPartMatrix(PartGroup *parent, const string &name, + const LMatrix4f &initial_value = + LMatrix4f::ident_mat()); + + virtual void get_blend_value(const PartBundle *root); + +protected: + INLINE MovingPartMatrix(void); + +public: + static void register_with_read_factory(void); + + static TypedWriteable *make_MovingPartMatrix(const FactoryParams ¶ms); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MovingPart::init_type(); + AnimChannelFixed::init_type(); + register_type(_type_handle, "MovingPartMatrix", + MovingPart::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "movingPartMatrix.I" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif + + + diff --git a/panda/src/chan/movingPartScalar.I b/panda/src/chan/movingPartScalar.I new file mode 100644 index 0000000000..aac6692d02 --- /dev/null +++ b/panda/src/chan/movingPartScalar.I @@ -0,0 +1,37 @@ +// Filename: movingPartScalar.I +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartScalar::Copy Constructor +// Access: Protected +// Description: Normally, you'd use make_copy() or copy_subgraph() to +// make a copy of this. +//////////////////////////////////////////////////////////////////// +INLINE MovingPartScalar:: +MovingPartScalar(const MovingPartScalar ©) : + MovingPart(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartScalar::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MovingPartScalar:: +MovingPartScalar(PartGroup *parent, const string &name, + const float &initial_value) + : MovingPart(parent, name, initial_value) { +} + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartScalar::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MovingPartScalar:: +MovingPartScalar(void){ +} diff --git a/panda/src/chan/movingPartScalar.cxx b/panda/src/chan/movingPartScalar.cxx new file mode 100644 index 0000000000..ebd03be39d --- /dev/null +++ b/panda/src/chan/movingPartScalar.cxx @@ -0,0 +1,102 @@ +// Filename: movingPartScalar.cxx +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "movingPartScalar.h" +#include +#include +#include +#include + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif + +TypeHandle MovingPartScalar::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartScalar::get_blend_value +// Access: Public +// Description: Attempts to blend the various scalar values +// indicated, and sets the _value member to the +// resulting scalar. +//////////////////////////////////////////////////////////////////// +void MovingPartScalar:: +get_blend_value(const PartBundle *root) { + const PartBundle::ChannelBlend &blend = root->get_blend_map(); + + if (blend.empty()) { + // No channel is bound; supply the default value. + _value = _initial_value; + + } else if (blend.size() == 1) { + // A single value, the normal case. + AnimControl *control = (*blend.begin()).first; + + int channel_index = control->get_channel_index(); + assert(channel_index >= 0 && channel_index < _channels.size()); + ChannelType *channel = DCAST(ChannelType, _channels[channel_index]); + assert(channel != NULL); + + channel->get_value(control->get_frame(), _value); + + } else { + // A blend of two or more values. + _value = 0.0; + float net = 0.0; + + PartBundle::ChannelBlend::const_iterator cbi; + for (cbi = blend.begin(); cbi != blend.end(); ++cbi) { + AnimControl *control = (*cbi).first; + float effect = (*cbi).second; + assert(effect != 0.0); + + int channel_index = control->get_channel_index(); + assert(channel_index >= 0 && channel_index < _channels.size()); + ChannelType *channel = DCAST(ChannelType, _channels[channel_index]); + assert(channel != NULL); + + ValueType v; + channel->get_value(control->get_frame(), v); + + _value += v * effect; + net += effect; + } + + assert(net != 0.0); + _value /= net; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartScalar::make_MovingPartScalar +// Access: Protected +// Description: Factory method to generate a MovingPartScalar object +//////////////////////////////////////////////////////////////////// +TypedWriteable* MovingPartScalar:: +make_MovingPartScalar(const FactoryParams ¶ms) +{ + MovingPartScalar *me = new MovingPartScalar; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: MovingPartScalar::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a MovingPartScalar object +//////////////////////////////////////////////////////////////////// +void MovingPartScalar:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_MovingPartScalar); +} + diff --git a/panda/src/chan/movingPartScalar.h b/panda/src/chan/movingPartScalar.h new file mode 100644 index 0000000000..e983ad778e --- /dev/null +++ b/panda/src/chan/movingPartScalar.h @@ -0,0 +1,69 @@ +// Filename: movingPartScalar.h +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MOVINGPARTSCALAR_H +#define MOVINGPARTSCALAR_H + +#include + +#include "movingPart.h" +#include "animChannel.h" +#include "animChannelFixed.h" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, MovingPart); + +//////////////////////////////////////////////////////////////////// +// Class : MovingPartScalar +// Description : This is a particular kind of MovingPart that accepts +// a scalar each frame. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA MovingPartScalar : public MovingPart { +protected: + INLINE MovingPartScalar(const MovingPartScalar ©); + +public: + INLINE MovingPartScalar(PartGroup *parent, const string &name, + const float &initial_value = 0); + + virtual void get_blend_value(const PartBundle *root); + +protected: + INLINE MovingPartScalar(void); + +public: + static void register_with_read_factory(void); + + static TypedWriteable *make_MovingPartScalar(const FactoryParams ¶ms); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MovingPart::init_type(); + AnimChannelFixed::init_type(); + register_type(_type_handle, "MovingPartScalar", + MovingPart::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "movingPartScalar.I" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif + + + diff --git a/panda/src/chan/partBundle.I b/panda/src/chan/partBundle.I new file mode 100644 index 0000000000..5e76d8b904 --- /dev/null +++ b/panda/src/chan/partBundle.I @@ -0,0 +1,75 @@ +// Filename: partBundle.I +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::get_blend_type +// Access: Public +// Description: Returns the way the character responds to multiple +// animations being bound simultaneously. +//////////////////////////////////////////////////////////////////// +INLINE PartBundle::BlendType PartBundle:: +get_blend_type() const { + return _blend_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::get_node +// Access: Public +// Description: Returns the PartBundleNode associated with this +// PartBundle. +//////////////////////////////////////////////////////////////////// +INLINE PartBundleNode *PartBundle:: +get_node() const { + return _node; +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::control_begin +// Access: Public +// Description: Returns an iterator that can be used to traverse the +// entire set of AnimControls currently in effect. +//////////////////////////////////////////////////////////////////// +INLINE PartBundle::control_iterator PartBundle:: +control_begin() const { + return _blend.begin(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::control_end +// Access: Public +// Description: Returns an iterator that can be used to traverse the +// entire set of AnimControls currently in effect. +//////////////////////////////////////////////////////////////////// +INLINE PartBundle::control_iterator PartBundle:: +control_end() const { + return _blend.end(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::control_size +// Access: Public +// Description: Returns the number of entries between control_begin() +// and control_end(): the number of AnimControls +// currently in effect. +//////////////////////////////////////////////////////////////////// +INLINE PartBundle::control_size_type PartBundle:: +control_size() const { + return _blend.size(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::get_blend_map +// Access: Public +// Description: Returns the entire mapping of AnimControls to blend +// effect. +//////////////////////////////////////////////////////////////////// +INLINE const PartBundle::ChannelBlend &PartBundle:: +get_blend_map() const { + return _blend; +} diff --git a/panda/src/chan/partBundle.N b/panda/src/chan/partBundle.N new file mode 100644 index 0000000000..ff7d90a5a9 --- /dev/null +++ b/panda/src/chan/partBundle.N @@ -0,0 +1,10 @@ +ignoreinvolved BlendType +ignoreinvolved control_iterator +ignoreinvolved control_size_type +ignoreinvolved ChannelBlend +ignoremember control_begin +ignoremember control_end +ignoremember control_size + + + diff --git a/panda/src/chan/partBundle.cxx b/panda/src/chan/partBundle.cxx new file mode 100644 index 0000000000..19ef18d31a --- /dev/null +++ b/panda/src/chan/partBundle.cxx @@ -0,0 +1,463 @@ +// Filename: partBundle.cxx +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "partBundle.h" +#include "animBundle.h" +#include "animControl.h" +#include "config_chan.h" + +#include +#include +#include +#include +#include + +TypeHandle PartBundle::_type_handle; + + + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::Copy Constructor +// Access: Protected +// Description: Normally, you'd use make_copy() or copy_subgraph() to +// make a copy of this. +//////////////////////////////////////////////////////////////////// +PartBundle:: +PartBundle(const PartBundle ©) : + PartGroup(copy), + _blend_type(copy._blend_type) +{ + // We don't invoke the AnimControlCollection's copy, or any of the + // bound animations. + _last_control_set = NULL; + _net_blend = 0.0; + _anim_changed = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::Constructor +// Access: Public +// Description: Normally, a PartBundle constructor should not be +// called directly--it will get created when a +// PartBundleNode is created. +//////////////////////////////////////////////////////////////////// +PartBundle:: +PartBundle(const string &name) : PartGroup(name) { + _blend_type = BT_single; + + _last_control_set = NULL; + _net_blend = 0.0; + _anim_changed = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::make_copy +// Access: Public, Virtual +// Description: Allocates and returns a new copy of the node. +// Children are not copied, but see copy_subgraph(). +//////////////////////////////////////////////////////////////////// +PartGroup *PartBundle:: +make_copy() const { + return new PartBundle(*this); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::set_blend_type +// Access: Public +// Description: Defines the way the character responds to multiple +// set_control_effect()). By default, the blend_type is +// BT_single, which disallows multiple animations. In +// BT_single mode, it is not necessary to explicitly set +// the control_effect when starting an animation; +// starting the animation will implicitly remove the +// control_effect from the previous animation and set it +// on the current one. +// +// However, if the blend_type is set to any other value, +// the control_effect must be explicitly set via +// set_control_effect() whenever an animation is to +// affect the character. +// +// See partBundle.h for a description of the meaning of +// each of the BlendType values. +//////////////////////////////////////////////////////////////////// +void PartBundle:: +set_blend_type(BlendType bt) { + if (_blend_type != bt) { + _blend_type = bt; + + if (_blend_type == BT_single && control_size() > 1) { + // If we just changed to a single blend type, i.e. no blending, + // we should eliminate all the AnimControls other than the + // most-recently-added one. + + nassertv(_last_control_set != NULL); + clear_and_stop_except(_last_control_set); + } + + _anim_changed = true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::clear_control_effects +// Access: Public +// Description: Sets the control effect of all AnimControls to zero +// (but does not "stop" the AnimControls). The +// character will no longer be affected by any +// animation, and will return to its original Jesus +// pose. +// +// The AnimControls which are no longer associated will +// not be using any CPU cycles, but they may still be in +// the "playing" state; if they are later reassociated +// with the PartBundle they will resume at their current +// frame as if they'd been running all along. +//////////////////////////////////////////////////////////////////// +void PartBundle:: +clear_control_effects() { + if (!_blend.empty()) { + _blend.clear(); + _net_blend = 0.0; + _anim_changed = true; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::set_control_effect +// Access: Public +// Description: Sets the amount by which the character is affected by +// the indicated AnimControl (and its associated +// animation). Normally, this will only be zero or one. +// Zero indicates the animation does not affect the +// character, and one means it does. +// +// If the blend_type is not BT_single (see +// set_blend_type()), it is possible to have multiple +// AnimControls in effect simultaneously. In this case, +// the effect is a weight that indicates the relative +// importance of each AnimControl to the final +// animation. +//////////////////////////////////////////////////////////////////// +void PartBundle:: +set_control_effect(AnimControl *control, float effect) { + assert(control->get_part() == this); + + if (effect == 0.0) { + // An effect of zero means to eliminate the control. + ChannelBlend::iterator cbi = _blend.find(control); + if (cbi != _blend.end()) { + _blend.erase(cbi); + _anim_changed = true; + } + + } else { + // Otherwise we define it. + + // If we currently have BT_single, we only allow one AnimControl + // at a time. Stop all of the other AnimControls. + if (get_blend_type() == BT_single) { + clear_and_stop_except(control); + } + + if (get_control_effect(control) != effect) { + _blend[control] = effect; + _anim_changed = true; + } + _last_control_set = control; + } + + recompute_net_blend(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::get_control_effect +// Access: Public +// Description: Returns the amount by which the character is affected +// by the indicated AnimControl and its associated +// animation. See set_control_effect(). +//////////////////////////////////////////////////////////////////// +float PartBundle:: +get_control_effect(AnimControl *control) { + assert(control->get_part() == this); + + ChannelBlend::iterator cbi = _blend.find(control); + if (cbi == _blend.end()) { + // The control is not in effect. + return 0.0; + } else { + return (*cbi).second; + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::output +// Access: Public, Virtual +// Description: Writes a one-line description of the bundle. +//////////////////////////////////////////////////////////////////// +void PartBundle:: +output(ostream &out) const { + out << get_type() << " " << get_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::write +// Access: Public, Virtual +// Description: Writes a brief description of the bundle and all of +// its descendants. +//////////////////////////////////////////////////////////////////// +void PartBundle:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) + << get_type() << " " << get_name() << " {\n"; + write_descendants(out, indent_level + 2); + indent(out, indent_level) << "}\n"; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::bind_anim +// Access: Public +// Description: Binds the animation to the bundle, if possible, and +// returns a new AnimControl that can be used to start +// and stop the animation. If the anim hierarchy does +// not match the part hierarchy, returns NULL. +// +// If hierarchy_match_flags is 0, only an exact match is +// accepted; otherwise, it may contain a union of +// PartGroup::HierarchyMatchFlags values indicating +// conditions that will be tolerated (but warnings will +// still be issued). +// +// This flavor of bind_anim() does not associate a name +// with the channel, and the AnimControl is not stored +// within the PartBundle; it is the user's +// responsibility to maintain the pointer. The +// animation will automatically unbind itself when the +// AnimControl destructs (e.g. its reference count goes +// to zero). +//////////////////////////////////////////////////////////////////// +PT(AnimControl) PartBundle:: +bind_anim(AnimBundle *anim, int hierarchy_match_flags) { + if ((hierarchy_match_flags & HMF_ok_wrong_root_name) == 0) { + // Make sure the root names match. + if (get_name() != anim->get_name()) { + if (chan_cat.is_error()) { + chan_cat.error() + << "Root name of part (" << get_name() + << ") does not match that of anim (" << anim->get_name() + << ")\n"; + } + return NULL; + } + } + + if (!check_hierarchy(anim, NULL, hierarchy_match_flags)) { + return NULL; + } + + list holes; + int channel_index = 0; + pick_channel_index(holes, channel_index); + + if (!holes.empty()) { + channel_index = holes.front(); + } + + bind_hierarchy(anim, channel_index); + return new AnimControl(this, anim, channel_index); +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::bind_anim +// Access: Public +// Description: Binds the animation to the bundle, if possible, and +// returns a new AnimControl that can be used to start +// and stop the animation. If the anim hierarchy does +// not match the part hierarchy, returns NULL. +// +// If hierarchy_match_flags is 0, only an exact match is +// accepted; otherwise, it may contain a union of +// PartGroup::HierarchyMatchFlags values indicating +// conditions that will be tolerated (but warnings will +// still be issued). +// +// This flavor of bind_anim() automatically stores the +// bound AnimControl in the PartBundle with the +// indicated name, so that it may later be referenced by +// name. This means that the animation will not be +// unbound until another animation with the same name is +// bound, or it is explicitly unbound with +// unbind_anim(). +// +// The return value is true if the animation was +// successfully bound, false if there was some error. +//////////////////////////////////////////////////////////////////// +bool PartBundle:: +bind_anim(AnimBundle *anim, const string &name, + int hierarchy_match_flags) { + PT(AnimControl) control = bind_anim(anim, hierarchy_match_flags); + if (control == (AnimControl *)NULL) { + return false; + } + + store_anim(control, name); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::advance_time +// Access: Public +// Description: Calls advance_time() on all AnimControls currently +// in effect. +//////////////////////////////////////////////////////////////////// +void PartBundle:: +advance_time(double time) { + ChannelBlend::const_iterator cbi; + for (cbi = _blend.begin(); cbi != _blend.end(); ++cbi) { + AnimControl *control = (*cbi).first; + control->advance_time(time); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::update +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void PartBundle:: +update() { + do_update(this, NULL, false, _anim_changed); + + // Now update all the controls for next time. + ChannelBlend::const_iterator cbi; + for (cbi = _blend.begin(); cbi != _blend.end(); ++cbi) { + AnimControl *control = (*cbi).first; + control->mark_channels(); + } + _anim_changed = false; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::control_activated +// Access: Public, Virtual +// Description: Called by the AnimControl whenever it starts an +// animation. This is just a hook so the bundle can do +// something, if necessary, before the animation starts. +//////////////////////////////////////////////////////////////////// +void PartBundle:: +control_activated(AnimControl *control) { + assert(control->get_part() == this); + + // If (and only if) our blend type is BT_single, which means no + // blending, then starting an animation implicitly enables it. + if (get_blend_type() == BT_single) { + set_control_effect(control, 1.0); + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::recompute_net_blend +// Access: Protected +// Description: Recomputes the total blending amount after a control +// effect has been adjusted. This value must be kept +// up-to-date so we can normalize the blending amounts. +//////////////////////////////////////////////////////////////////// +void PartBundle:: +recompute_net_blend() { + _net_blend = 0.0; + + ChannelBlend::const_iterator bti; + for (bti = _blend.begin(); bti != _blend.end(); ++bti) { + _net_blend += (*bti).second; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::finalize +// Access: Public +// Description: Method to ensure that any necessary clean up tasks +// that have to be performed by this object are performed +//////////////////////////////////////////////////////////////////// +void PartBundle:: +finalize(void) +{ + do_update(this, NULL, true, true); +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::make_PartBundle +// Access: Protected +// Description: Factory method to generate a PartBundle object +//////////////////////////////////////////////////////////////////// +TypedWriteable* PartBundle:: +make_PartBundle(const FactoryParams ¶ms) +{ + PartBundle *me = new PartBundle; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + manager->register_finalize(me); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a PartBundle object +//////////////////////////////////////////////////////////////////// +void PartBundle:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_PartBundle); +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::clear_and_stop_except +// Access: Protected +// Description: Removes and stops all the currently activated +// AnimControls, except for the indicated one. This is +// a special internal function that's only called when +// _blend_type is BT_single, to automatically stop all +// the other currently-executing animations. +//////////////////////////////////////////////////////////////////// +void PartBundle:: +clear_and_stop_except(AnimControl *control) { + double new_net_blend = 0.0; + ChannelBlend new_blend; + bool any_changed = false; + + ChannelBlend::iterator cbi; + for (cbi = _blend.begin(); cbi != _blend.end(); ++cbi) { + AnimControl *ac = (*cbi).first; + if (ac == control) { + // Save this control, but only this one. + new_blend.insert(new_blend.end(), (*cbi)); + new_net_blend += (*cbi).second; + } else { + // Remove and stop this control. + ac->stop(); + any_changed = true; + } + } + + if (any_changed) { + _net_blend = new_net_blend; + _blend.swap(new_blend); + _anim_changed = true; + } +} diff --git a/panda/src/chan/partBundle.h b/panda/src/chan/partBundle.h new file mode 100644 index 0000000000..199ee13c7d --- /dev/null +++ b/panda/src/chan/partBundle.h @@ -0,0 +1,162 @@ +// Filename: partBundle.h +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARTBUNDLE_H +#define PARTBUNDLE_H + +#include + +#include "partGroup.h" +#include "animControl.h" +#include "animControlCollection.h" + +#include +#include + +class AnimBundle; +class PartBundleNode; + +//////////////////////////////////////////////////////////////////// +// Class : PartBundle +// Description : This is the root of a MovingPart hierarchy. It +// defines the hierarchy of moving parts that make up an +// animatable object. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PartBundle : public PartGroup, public AnimControlCollection { +public: + + // This is passed down through the MovingParts during the + // do_update() call to specify the channels that are in effect. + typedef map ChannelBlend; + + // This is the parameter to set_blend_type() and specifies the kind + // of blending operation to be performed when multiple controls are + // in effect simultaneously (see set_control_effect()). + enum BlendType { + + // BT_single means no blending is performed. Only one AnimControl + // is allowed to be set at a time in set_control_effect(). In + // this mode, and this mode only, activating a particular + // AnimControl (via play(), loop(), or pose()) will implicitly set + // the bundle's control_effect to 100% of that particular + // AnimControl, removing any AnimControls previously set. In + // other modes, the control_effect must be set manually. + BT_single, + + // BT_linear does a componentwise average of all blended matrices, + // which is a linear blend. The result of this is that if a + // particular vertex would have been at point P in one animation + // and point Q in another one, it will end up on the line in + // between them in the resulting blend animation. However, this + // tends to stretch and squash limbs in strange and disturbing + // ways. + BT_linear, + + // BT_normalized_linear is a compromise on BT_linear. The matrix + // is blended linearly without the scale component, and the + // blended scale component is applied separately. This keeps all + // of character's body parts in the correct size and shape. + // However, if the hierarchy is disconnected, body parts can fly + // off. It's essential the skeleton hierarchy be completely + // connected to use this blend mode successully. + BT_normalized_linear, + }; + + typedef first_of_pair_iterator control_iterator; + typedef ChannelBlend::size_type control_size_type; + +protected: + // The copy constructor is protected; use make_copy() or copy_subgraph(). + PartBundle(const PartBundle ©); + +public: + PartBundle(const string &name = ""); + virtual PartGroup *make_copy() const; + + void set_blend_type(BlendType bt); + INLINE BlendType get_blend_type() const; + + INLINE PartBundleNode *get_node() const; + + void clear_control_effects(); + void set_control_effect(AnimControl *control, float effect); + float get_control_effect(AnimControl *control); + +public: + // The following functions may be used to traverse the set of + // controls applied to the PartBundle. Beware! These are not safe + // to use outside of PANDA.DLL. + INLINE control_iterator control_begin() const; + INLINE control_iterator control_end() const; + INLINE control_size_type control_size() const; + + INLINE const ChannelBlend &get_blend_map() const; + +public: + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level) const; + + PT(AnimControl) bind_anim(AnimBundle *anim, + int hierarchy_match_flags = 0); + + bool bind_anim(AnimBundle *anim, const string &name, + int hierarchy_match_flags = 0); + +public: + // The following functions aren't really part of the public + // interface; they're just public so we don't have to declare a + // bunch of friends. + + void advance_time(double time); + void update(); + virtual void control_activated(AnimControl *control); + +protected: + void recompute_net_blend(); + void clear_and_stop_except(AnimControl *control); + + BlendType _blend_type; + PartBundleNode *_node; + + AnimControl *_last_control_set; + ChannelBlend _blend; + float _net_blend; + bool _anim_changed; + +public: + static void register_with_read_factory(void); + virtual void finalize(); + + static TypedWriteable *make_PartBundle(const FactoryParams ¶ms); + +public: + + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PartGroup::init_type(); + register_type(_type_handle, "PartBundle", + PartGroup::get_class_type()); + } + +private: + static TypeHandle _type_handle; + + friend class PartBundleNode; +}; + +inline ostream &operator <<(ostream &out, const PartBundle &bundle) { + bundle.output(out); + return out; +} + +#include "partBundle.I" + +#endif diff --git a/panda/src/chan/partBundleNode.I b/panda/src/chan/partBundleNode.I new file mode 100644 index 0000000000..fd14edacaf --- /dev/null +++ b/panda/src/chan/partBundleNode.I @@ -0,0 +1,57 @@ +// Filename: partBundleNode.I +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PartBundleNode::Constructor +// Access: Public +// Description: The PartBundle and its node should be constructed +// together. Generally, the derived classes of +// PartBundleNode will automatically create a PartBundle +// of the appropriate type, and pass it up to this +// constructor. +//////////////////////////////////////////////////////////////////// +INLINE PartBundleNode:: +PartBundleNode(const string &name, PartBundle *bundle) : + NamedNode(name), + _bundle(bundle) +{ + _bundle->_node = this; +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundleNode::Default Constructor +// Access: Protected +// Description: For internal use only. +//////////////////////////////////////////////////////////////////// +INLINE PartBundleNode:: +PartBundleNode(void) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundleNode::Copy Constructor +// Access: Protected +// Description: Use make_copy() or copy_subgraph() to copy one of +// these. Copying a PartBundleNode will always force a +// deep copy of the PartGroup hierarchy. +//////////////////////////////////////////////////////////////////// +INLINE PartBundleNode:: +PartBundleNode(const PartBundleNode ©) : + NamedNode(copy), + _bundle(DCAST(PartBundle, copy._bundle->copy_subgraph())) +{ + _bundle->_node = this; +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundleNode::get_bundle +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PartBundle *PartBundleNode:: +get_bundle() const { + return _bundle; +} diff --git a/panda/src/chan/partBundleNode.cxx b/panda/src/chan/partBundleNode.cxx new file mode 100644 index 0000000000..a1da23b00c --- /dev/null +++ b/panda/src/chan/partBundleNode.cxx @@ -0,0 +1,74 @@ +// Filename: partBundleNode.cxx +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "partBundleNode.h" +#include +#include +#include +#include + +TypeHandle PartBundleNode::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: PartBundleNode::safe_to_flatten +// Access: Public, Virtual +// Description: Returns true if it is generally safe to flatten out +// this particular kind of Node by duplicating +// instances, false otherwise (for instance, a Camera +// cannot be safely flattened, because the Camera +// pointer itself is meaningful). +//////////////////////////////////////////////////////////////////// +bool PartBundleNode:: +safe_to_flatten() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundleNode::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void PartBundleNode:: +write_datagram(BamWriter *manager, Datagram &me) +{ + NamedNode::write_datagram(manager, me); + manager->write_pointer(me, _bundle); +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundleNode::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void PartBundleNode:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + NamedNode::fillin(scan, manager); + manager->read_pointer(scan, this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundleNode::complete_pointers +// Access: Public +// Description: Takes in a vector of pointes to TypedWriteable +// objects that correspond to all the requests for +// pointers that this object made to BamReader. +//////////////////////////////////////////////////////////////////// +int PartBundleNode:: +complete_pointers(vector_typedWriteable &plist, BamReader* manager) +{ + int start = NamedNode::complete_pointers(plist, manager); + _bundle = DCAST(PartBundle, plist[start]); + //Let PartBundleNode tell the PartBundle about itselt + _bundle->_node = this; + return start+1; +} + + diff --git a/panda/src/chan/partBundleNode.h b/panda/src/chan/partBundleNode.h new file mode 100644 index 0000000000..021abf1a7d --- /dev/null +++ b/panda/src/chan/partBundleNode.h @@ -0,0 +1,66 @@ +// Filename: partBundleNode.h +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARTBUNDLENODE_H +#define PARTBUNDLENODE_H + +#include + +#include "partBundle.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PartBundleNode +// Description : This is a node that contains a pointer to an +// PartBundle. Like AnimBundleNode, it exists solely to +// make it easy to store PartBundles in the scene graph. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PartBundleNode : public NamedNode { +public: + INLINE PartBundleNode(const string &name, PartBundle *bundle); + +protected: + INLINE PartBundleNode(); + INLINE PartBundleNode(const PartBundleNode ©); + +public: + virtual bool safe_to_flatten() const; + + INLINE PartBundle *get_bundle() const; + +private: + PT(PartBundle) _bundle; + +public: + virtual void write_datagram(BamWriter* manager, Datagram &me); + virtual int complete_pointers(vector_typedWriteable &plist, + BamReader *manager); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NamedNode::init_type(); + register_type(_type_handle, "PartBundleNode", + NamedNode::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "partBundleNode.I" + +#endif diff --git a/panda/src/chan/partGroup.I b/panda/src/chan/partGroup.I new file mode 100644 index 0000000000..a1166bbc09 --- /dev/null +++ b/panda/src/chan/partGroup.I @@ -0,0 +1,33 @@ +// Filename: partGroup.I +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::Default Constructor +// Access: Protected +// Description: This constructor is only intended for interal use and +// for derived classes. You should normally use the +// non-default constructor, below. +//////////////////////////////////////////////////////////////////// +INLINE PartGroup:: +PartGroup(const string &name) : + Namable(name) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::Copy Constructor +// Access: Protected +// Description: This constructor is only intended for interal use and +// for derived classes. You should normally use the +// make_copy() interface to make copies.. +//////////////////////////////////////////////////////////////////// +INLINE PartGroup:: +PartGroup(const PartGroup ©) : + Namable(copy) +{ + // We don't copy children in the copy constructor. However, + // copy_subgraph() will do this. +} diff --git a/panda/src/chan/partGroup.cxx b/panda/src/chan/partGroup.cxx new file mode 100644 index 0000000000..2d5342fd2f --- /dev/null +++ b/panda/src/chan/partGroup.cxx @@ -0,0 +1,484 @@ +// Filename: partGroup.cxx +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "partGroup.h" +#include "animGroup.h" +#include "config_chan.h" + +#include +#include +#include +#include +#include + +#include + +TypeHandle PartGroup::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::Constructor +// Access: Public +// Description: Creates the PartGroup, and adds it to the indicated +// parent. The only way to delete it subsequently is to +// delete the entire hierarchy. +//////////////////////////////////////////////////////////////////// +PartGroup:: +PartGroup(PartGroup *parent, const string &name) : Namable(name) { + assert(parent != NULL); + + parent->_children.push_back(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PartGroup:: +~PartGroup() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::make_copy +// Access: Public, Virtual +// Description: Allocates and returns a new copy of the node. +// Children are not copied, but see copy_subgraph(). +//////////////////////////////////////////////////////////////////// +PartGroup *PartGroup:: +make_copy() const { + return new PartGroup(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::copy_subgraph +// Access: Public +// Description: Allocates and returns a new copy of this node and of +// all of its children. +//////////////////////////////////////////////////////////////////// +PartGroup *PartGroup:: +copy_subgraph() const { + PartGroup *root = make_copy(); + + if (root->get_type() != get_type()) { + chan_cat.warning() + << "Don't know how to copy " << get_type() << "\n"; + } + + Children::const_iterator ci; + for (ci = _children.begin(); ci != _children.end(); ++ci) { + PartGroup *child = (*ci)->copy_subgraph(); + root->_children.push_back(child); + } + + return root; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::get_num_children +// Access: Public +// Description: Returns the number of child nodes of the group. +//////////////////////////////////////////////////////////////////// +int PartGroup:: +get_num_children() const { + return _children.size(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::get_child +// Access: Public +// Description: Returns the nth child of the group. +//////////////////////////////////////////////////////////////////// +PartGroup *PartGroup:: +get_child(int n) const { + assert(n >= 0 && n < _children.size()); + return _children[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::get_value_type +// Access: Public, Virtual +// Description: Returns the TypeHandle associated with the ValueType +// we are concerned with. This is provided to allow a +// bit of run-time checking that joints and channels are +// matching properly in type. +//////////////////////////////////////////////////////////////////// +TypeHandle PartGroup:: +get_value_type() const { + return TypeHandle::none(); +} + + +// An STL object to sort a list of children into alphabetical order. +class PartGroupAlphabeticalOrder { +public: + bool operator()(const PT(PartGroup) &a, const PT(PartGroup) &b) const { + return a->get_name() < b->get_name(); + } +}; + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::sort_descendants +// Access: Public +// Description: Sorts the children nodes at each level of the +// hierarchy into alphabetical order. This should be +// done after creating the hierarchy, to guarantee that +// the correct names will match up together when the +// AnimBundle is later bound to a PlayerRoot. +//////////////////////////////////////////////////////////////////// +void PartGroup:: +sort_descendants() { + sort(_children.begin(), _children.end(), PartGroupAlphabeticalOrder()); + + Children::iterator ci; + for (ci = _children.begin(); ci != _children.end(); ++ci) { + (*ci)->sort_descendants(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::check_hierarchy +// Access: Public +// Description: Walks the part hierarchy in tandem with the indicated +// anim hierarchy, and returns true if the hierarchies +// match, false otherwise. +// +// If hierarchy_match_flags is 0, only an exact match is +// accepted; otherwise, it may contain a union of +// PartGroup::HierarchyMatchFlags values indicating +// conditions that will be tolerated (but warnings will +// still be issued). +// +// If there is a discrepancy, it is reported to the +// indicated output stream, if it is non-null. +//////////////////////////////////////////////////////////////////// +bool PartGroup:: +check_hierarchy(const AnimGroup *anim, const PartGroup *, + int hierarchy_match_flags) const { + if (anim->get_value_type() != get_value_type()) { + if (chan_cat.is_error()) { + chan_cat.error() + << "Part " << get_name() << " expects type " << get_value_type() + << " while matching anim node has type " << anim->get_value_type() + << ".\n"; + } + return false; + } + + if (chan_cat.is_info()) { + // If we're issuing error messages, check ahead of time if the set + // of children agrees. If it does not, we'll write a one-line + // warning, and then list the set of children that differ. + + bool match = true; + if (anim->get_num_children() != get_num_children()) { + chan_cat.info() + << "Part " << get_name() << " has " << get_num_children() + << " children, while matching anim node has " + << anim->get_num_children() << ":\n"; + match = false; + + } else { + for (int i = 0; match && i < get_num_children(); i++) { + PartGroup *pc = get_child(i); + AnimGroup *ac = anim->get_child(i); + + match = (pc->get_name() == ac->get_name()); + } + if (!match) { + chan_cat.info() + << "Part " << get_name() << " has a different set of children " + << " than matching anim node:\n"; + } + } + if (!match) { + int i = 0, j = 0; + while (i < get_num_children() && + j < anim->get_num_children()) { + PartGroup *pc = get_child(i); + AnimGroup *ac = anim->get_child(j); + + if (pc->get_name() < ac->get_name()) { + chan_cat.info() + << " part has " << pc->get_name() + << ", not in anim.\n"; + i++; + } else if (ac->get_name() < pc->get_name()) { + chan_cat.info() + << " anim has " << ac->get_name() + << ", not in part.\n"; + j++; + } else { + chan_cat.info() + << " part and anim both have " << ac->get_name() << "\n"; + i++; + j++; + } + } + + while (i < get_num_children()) { + PartGroup *pc = get_child(i); + chan_cat.info() + << " part has " << pc->get_name() + << ", not in anim.\n"; + i++; + } + + while (j < anim->get_num_children()) { + AnimGroup *ac = anim->get_child(j); + chan_cat.info() + << " anim has " << ac->get_name() + << ", not in part.\n"; + j++; + } + } + } + + // Now walk the list of children and check the matching + // sub-hierarchies only. + + int i = 0, j = 0; + while (i < get_num_children() && + j < anim->get_num_children()) { + PartGroup *pc = get_child(i); + AnimGroup *ac = anim->get_child(j); + + if (pc->get_name() < ac->get_name()) { + if ((hierarchy_match_flags & HMF_ok_part_extra) == 0) { + return false; + } + i++; + } else if (ac->get_name() < pc->get_name()) { + if ((hierarchy_match_flags & HMF_ok_anim_extra) == 0) { + return false; + } + j++; + } else { + if (!pc->check_hierarchy(ac, this, hierarchy_match_flags)) { + return false; + } + i++; + j++; + } + } + + while (i < get_num_children()) { + PartGroup *pc = get_child(i); + if ((hierarchy_match_flags & HMF_ok_part_extra) == 0) { + return false; + } + i++; + } + + while (j < anim->get_num_children()) { + AnimGroup *ac = anim->get_child(j); + if ((hierarchy_match_flags & HMF_ok_anim_extra) == 0) { + return false; + } + j++; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::write +// Access: Public, Virtual +// Description: Writes a brief description of the group and all of +// its descendants. +//////////////////////////////////////////////////////////////////// +void PartGroup:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) + << get_type() << " " << get_name() << " {\n"; + write_descendants(out, indent_level + 2); + indent(out, indent_level) << "}\n"; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::do_update +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void PartGroup:: +do_update(PartBundle *root, PartGroup *, + bool parent_changed, bool anim_changed) { + Children::iterator ci; + for (ci = _children.begin(); ci != _children.end(); ++ci) { + (*ci)->do_update(root, this, parent_changed, anim_changed); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::write_descendants +// Access: Protected +// Description: Writes a brief description of all of the group's +// descendants. +//////////////////////////////////////////////////////////////////// +void PartGroup:: +write_descendants(ostream &out, int indent_level) const { + Children::const_iterator ci; + + for (ci = _children.begin(); ci != _children.end(); ++ci) { + (*ci)->write(out, indent_level); + } +} + + + + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::pick_channel_index +// Access: Protected, Virtual +// Description: Walks the part hierarchy, looking for a suitable +// channel index number to use. Available index numbers +// are the elements of the holes set, as well as next to +// infinity. +//////////////////////////////////////////////////////////////////// +void PartGroup:: +pick_channel_index(list &holes, int &next) const { + Children::const_iterator ci; + for (ci = _children.begin(); ci != _children.end(); ++ci) { + (*ci)->pick_channel_index(holes, next); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::bind_hierarchy +// Access: Protected, Virtual +// Description: Binds the indicated anim hierarchy to the part +// hierarchy, at the given channel index number. +//////////////////////////////////////////////////////////////////// +void PartGroup:: +bind_hierarchy(AnimGroup *anim, int channel_index) { + int i = 0, j = 0; + int part_num_children = get_num_children(); + int anim_num_children = (anim == NULL) ? 0 : anim->get_num_children(); + + while (i < part_num_children && j < anim_num_children) { + PartGroup *pc = get_child(i); + AnimGroup *ac = anim->get_child(j); + + if (pc->get_name() < ac->get_name()) { + // Here's a part, not in the anim. Bind it to the special NULL + // anim. + pc->bind_hierarchy(NULL, channel_index); + i++; + } else if (ac->get_name() < pc->get_name()) { + j++; + } else { + pc->bind_hierarchy(ac, channel_index); + i++; + j++; + } + } + + // Now pick up any more parts, not in the anim. + while (i < part_num_children) { + PartGroup *pc = get_child(i); + pc->bind_hierarchy(NULL, channel_index); + i++; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void PartGroup:: +write_datagram(BamWriter *manager, Datagram &me) +{ + int i; + me.add_string(get_name()); + me.add_uint16(_children.size()); + for(i = 0; i < _children.size(); i++) + { + manager->write_pointer(me, _children[i]); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void PartGroup:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + int i; + set_name(scan.get_string()); + _num_children = scan.get_uint16(); + for(i = 0; i < _num_children; i++) + { + manager->read_pointer(scan, this); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::complete_pointers +// Access: Public +// Description: Takes in a vector of pointes to TypedWriteable +// objects that correspond to all the requests for +// pointers that this object made to BamReader. +//////////////////////////////////////////////////////////////////// +int PartGroup:: +complete_pointers(vector_typedWriteable &plist, BamReader*) +{ + int i; + for(i = 0; i < _num_children; i++) + { + if (plist[i] == TypedWriteable::Null) + { + chan_cat->warning() << get_type().get_name() + << " Ignoring null PartGroup" << endl; + } + else + { + _children.push_back(DCAST(PartGroup, plist[i])); + } + } + + return _num_children; +} + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::make_PartGroup +// Access: Protected +// Description: Factory method to generate a PartGroup object +//////////////////////////////////////////////////////////////////// +TypedWriteable* PartGroup:: +make_PartGroup(const FactoryParams ¶ms) +{ + PartGroup *me = new PartGroup; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: PartGroup::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a PartGroup object +//////////////////////////////////////////////////////////////////// +void PartGroup:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_PartGroup); +} + diff --git a/panda/src/chan/partGroup.h b/panda/src/chan/partGroup.h new file mode 100644 index 0000000000..52e88c29d2 --- /dev/null +++ b/panda/src/chan/partGroup.h @@ -0,0 +1,119 @@ +// Filename: partGroup.h +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARTGROUP_H +#define PARTGROUP_H + +#include + +#include +#include +#include +#include + +#include + +class AnimControl; +class AnimGroup; +class PartBundle; +class BamReader; + +//////////////////////////////////////////////////////////////////// +// Class : PartGroup +// Description : This is the base class for PartRoot and +// MovingPart. It defines a hierarchy of MovingParts. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PartGroup : public TypedWriteableReferenceCount, public Namable { +public: + // This enum defines bits which may be passed into check_hierarchy() + // and PartBundle::bind_anim() to allow an inexact match of channel + // hierarchies. This specifies conditions that we don't care about + // enforcing. + enum HierarchyMatchFlags { + HMF_ok_part_extra = 0x01, + HMF_ok_anim_extra = 0x02, + HMF_ok_wrong_root_name = 0x04, + }; + +protected: + // The default constructor is protected: don't try to create a + // PartGroup without a parent. To create a PartGroup hierarchy, you + // must first create a PartBundle, and use that to create any + // subsequent children. + INLINE PartGroup(const string &name = ""); + INLINE PartGroup(const PartGroup ©); + +public: + // This is the normal PartGroup constructor. + PartGroup(PartGroup *parent, const string &name); + virtual ~PartGroup(); + + virtual PartGroup *make_copy() const; + PartGroup *copy_subgraph() const; + + int get_num_children() const; + PartGroup *get_child(int n) const; + + virtual TypeHandle get_value_type() const; + + void sort_descendants(); + bool check_hierarchy(const AnimGroup *anim, + const PartGroup *parent, + int hierarchy_match_flags = 0) const; + + virtual void write(ostream &out, int indent_level) const; + + virtual void do_update(PartBundle *root, PartGroup *parent, + bool parent_changed, bool anim_changed); + +protected: + void write_descendants(ostream &out, int indent_level) const; + + virtual void pick_channel_index(list &holes, int &next) const; + virtual void bind_hierarchy(AnimGroup *anim, int channel_index); + + typedef vector Children; + Children _children; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + virtual int complete_pointers(vector_typedWriteable &plist, + BamReader *manager); + + static TypedWriteable *make_PartGroup(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +private: + int _num_children; + +public: + + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedWriteableReferenceCount::init_type(); + register_type(_type_handle, "PartGroup", + TypedWriteableReferenceCount::get_class_type()); + } + +private: + static TypeHandle _type_handle; + + friend class Character; +}; + +#include "partGroup.I" + +#endif + + diff --git a/panda/src/chan/vector_PartGroupStar.cxx b/panda/src/chan/vector_PartGroupStar.cxx new file mode 100644 index 0000000000..197ab432a9 --- /dev/null +++ b/panda/src/chan/vector_PartGroupStar.cxx @@ -0,0 +1,11 @@ +// Filename: vector_PartGroup.cxx +// Created by: drose (06Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_PartGroupStar.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/chan/vector_PartGroupStar.h b/panda/src/chan/vector_PartGroupStar.h new file mode 100644 index 0000000000..e93cdc1207 --- /dev/null +++ b/panda/src/chan/vector_PartGroupStar.h @@ -0,0 +1,33 @@ +// Filename: vector_PartGroupStar.h +// Created by: drose (06Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_PARTGROUPSTAR_H +#define VECTOR_PARTGROUPSTAR_H + +#include + +#include "partGroup.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_PartGroupStar +// Description : A vector of PartGroup pointers. This class is +// defined once here, and exported to PANDA.DLL; other +// packages that want to use a vector of this type +// (whether they need to export it or not) should +// include this header file, rather than defining the +// vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, std::vector) +typedef vector vector_PartGroupStar; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/chancfg/Sources.pp b/panda/src/chancfg/Sources.pp new file mode 100644 index 0000000000..d4bdea435c --- /dev/null +++ b/panda/src/chancfg/Sources.pp @@ -0,0 +1,36 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET chancfg + #define LOCAL_LIBS \ + putil display sgattrib linmath graph sgraph gobj display gsgbase \ + mathutil + + #define SOURCES \ + chancfg.I chancfg.N chancfg.cxx chancfg.h chanlayout.I \ + chanlayout.cxx chanlayout.h chanparse.I chanparse.cxx chanparse.h \ + chansetup.I chansetup.cxx chansetup.h chanwindow.I chanwindow.cxx \ + chanwindow.h + + #define INSTALL_HEADERS \ + chancfg.I chancfg.h chanlayout.I chanlayout.h chansetup.I \ + chansetup.h chanshare.h chanviewport.I chanviewport.h chanwindow.I \ + chanwindow.h + + #define INSTALL_DATA \ + layout_db setup_db window_db + + #define IGATESCAN chancfg.h + +#end lib_target + +#begin test_bin_target + #define TARGET test_chancfg + #define LOCAL_LIBS \ + chancfg putil + + #define SOURCES \ + test_chancfg.cxx + +#end test_bin_target + diff --git a/panda/src/chancfg/chancfg.I b/panda/src/chancfg/chancfg.I new file mode 100644 index 0000000000..fbc05b51f4 --- /dev/null +++ b/panda/src/chancfg/chancfg.I @@ -0,0 +1,4 @@ +// Filename: chancfg.I +// Created by: cary (07Feb99) +// +//////////////////////////////////////////////////////////////////// diff --git a/panda/src/chancfg/chancfg.N b/panda/src/chancfg/chancfg.N new file mode 100644 index 0000000000..45e7d927aa --- /dev/null +++ b/panda/src/chancfg/chancfg.N @@ -0,0 +1,2 @@ +ignorefile chanlayout.h chanlayout.I chansetup.h chansetup.I +ignorefile chanwindow.h chanwindow.I diff --git a/panda/src/chancfg/chancfg.cxx b/panda/src/chancfg/chancfg.cxx new file mode 100644 index 0000000000..6c37653d40 --- /dev/null +++ b/panda/src/chancfg/chancfg.cxx @@ -0,0 +1,534 @@ +// Filename: chancfg.cxx +// Created by: cary (02Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "chancfg.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +Configure(chanconfig); + +ConfigureFn(chanconfig) { +} + +static bool have_read = false; +NotifyCategoryDef(chancfg, ""); + +static void ReadChanConfigData(void) { + if (have_read) + return; + + ResetLayout(); + ResetSetup(); + ResetWindow(); + + DSearchPath path(chanconfig.GetString("ETC_PATH", ".")); + + Filename layoutfname = Filename::text_filename("layout_db"); + if (!layoutfname.resolve_filename(path)) { + chancfg_cat->error() + << "Could not find " << layoutfname << " in " << path << "\n"; + } else { + ifstream ifs; + if (layoutfname.open_read(ifs)) { + if (chancfg_cat->is_debug()) + chancfg_cat->debug() + << "Reading layout database " << layoutfname << endl; + ParseLayout(ifs); + } else { + chancfg_cat->error() + << "Unable to read layout database " << layoutfname << "\n"; + } + } + + Filename setupfname = Filename::text_filename("setup_db"); + if (!setupfname.resolve_filename(path)) { + chancfg_cat->error() + << "Could not find " << setupfname << " in " << path << "\n"; + } else { + ifstream ifs; + if (setupfname.open_read(ifs)) { + if (chancfg_cat->is_debug()) + chancfg_cat->debug() + << "Reading setup database " << setupfname << endl; + ParseSetup(ifs); + } else { + chancfg_cat->error() + << "Unable to read setup database " << setupfname << "\n"; + } + } + + Filename windowfname = Filename::text_filename("window_db"); + if (!windowfname.resolve_filename(path)) { + chancfg_cat->error() + << "Could not find " << windowfname << " in " << path << "\n"; + } else { + ifstream ifs; + if (windowfname.open_read(ifs)) { + if (chancfg_cat->is_debug()) + chancfg_cat->debug() + << "Reading window database " << windowfname << endl; + ParseWindow(ifs); + } else { + chancfg_cat->error() + << "Unable to read window database " << windowfname << "\n"; + } + } +} + +static const bool config_sanity_check = + chanconfig.GetBool("chan-config-sanity-check", false); + +ChanCfgOverrides ChanOverrideNone; + +INLINE bool LayoutDefined(std::string sym) { + return (LayoutDB != (LayoutType *)NULL) && + (LayoutDB->find(sym) != LayoutDB->end()); +} + +INLINE bool SetupDefined(std::string sym) { + return (SetupDB != (SetupType *)NULL) && + (SetupDB->find(sym) != SetupDB->end()); +} + +INLINE bool ConfigDefined(std::string sym) { + return (WindowDB != (WindowType *)NULL) && + (WindowDB->find(sym) != WindowDB->end()); +} + +typedef std::vector SVec; + +bool ChanCheckLayouts(SetupSyms& S) { + if (S.empty()) + return false; + for (SetupSyms::iterator i=S.begin(); i!=S.end(); ++i) + if (!LayoutDefined(*i)) { + chancfg_cat.error() << "no layout called '" << *i << "'" << endl; + return false; + } + return true; +} + +bool ChanCheckSetups(SetupSyms& S) { + if (S.empty()) + return false; + for (SetupSyms::iterator i=S.begin(); i!=S.end(); ++i) { + if (!SetupDefined(*i)) { + chancfg_cat.error() << "no setup called '" << *i << "'" << endl; + return false; + } + if (((*SetupDB)[*i]).getRecurse()) { + SetupSyms a = ((*SetupDB)[*i]).getLayouts(); + if (!ChanCheckLayouts(a)) + return false; + a = ((*SetupDB)[*i]).getSetups(); + if (!ChanCheckSetups(a)) + return false; + } + } + return true; +} + +INLINE ChanViewport ChanScaleViewport(const ChanViewport& parent, + const ChanViewport& child) { + float dx = (parent.right() - parent.left()); + float dy = (parent.top() - parent.bottom()); + ChanViewport v(parent.left() + (dx * child.left()), + parent.left() + (dx * child.right()), + parent.bottom() + (dy * child.bottom()), + parent.bottom() + (dy * child.top())); + return v; +} + +/* +int ChanFindNextHWChan(int offset, const HardwareChannel::HWChanMap& hw_chans) { + int i = offset; + while (hw_chans.find(i) != hw_chans.end()) + ++i; + return i; +} +*/ + +SetupFOV ChanResolveFOV(SetupFOV& fov, float sizeX, float sizeY) { + float horiz = 45.; + float vert; + SetupFOV ret; + switch (fov.getType()) { + case SetupFOV::Horizontal: + horiz = fov.getHoriz(); + if (chancfg_cat->is_debug()) + chancfg_cat->debug() << "ChanResolveFOV:: setting default horiz = " + << horiz << endl; + case SetupFOV::Default: + horiz = chanconfig.GetFloat("fov", horiz); + vert = 2.*rad_2_deg(atan((sizeY/sizeX)*tan(0.5*deg_2_rad(horiz)))); + if (chancfg_cat->is_debug()) + chancfg_cat->debug() << "ChanResolveFOV:: setting horiz = " << horiz + << " and vert = " << vert << endl; + break; + case SetupFOV::Both: + horiz = fov.getHoriz(); + vert = fov.getVert(); + if (chancfg_cat->is_debug()) + chancfg_cat->debug() << "ChanResolveFOV:: setting horiz = " << horiz + << " and vert = " << vert << endl; + break; + } + ret.setFOV(horiz, vert); + return ret; +} + +void ChanEval(GraphicsWindow* win, WindowItem& W, LayoutItem& L, SVec& S, + ChanViewport& V, int hw_offset, int xsize, int ysize, + Node *camera_node, Node *render, bool want_cameras) { + int i = min(L.GetNumRegions(), int(S.size())); + int j; + SVec::iterator k; + for (j=0, k=S.begin(); jget_channel(hw_offset); + } else + chan = win->get_channel((*k).getChan()); + // HW channels always start with the full area of the channel + v = ChanViewport(0., 1., 0., 1.); + } else { + chan = win->get_channel(0); + } + ChanViewport v2(ChanScaleViewport(v, (*k).getViewport())); + PT(GraphicsLayer) layer = chan->make_layer(); + PT(DisplayRegion) dr = + layer->make_display_region(v2.left(), v2.right(), + v2.bottom(), v2.top()); + if (want_cameras && camera_node != (Node *)NULL) { + // now make a camera for it + PT(Camera) cam = new Camera; + dr->set_camera(cam); + SetupFOV fov = (*k).getFOV(); + fov = ChanResolveFOV(fov, xsize*(v2.right()-v2.left()), + ysize*(v2.top()-v2.bottom())); + if (chancfg_cat->is_debug()) { + chancfg_cat->debug() << "ChanEval:: about to compute frustum." + << endl; + chancfg_cat->debug() << "ChanEval:: FOVhoriz = " << fov.getHoriz() + << " FOVvert = " << fov.getVert() << endl; + chancfg_cat->debug() << "ChanEval:: xsize = " << xsize + << " ysize = " << ysize << endl; + } + Frustumf frust; + frust.make_perspective(fov.getHoriz(), fov.getVert(), 1., 10000.); + cam->set_projection(PerspectiveProjection(frust)); + if (chancfg_cat->is_debug()) + chancfg_cat->debug() << "ChanEval:: camera hfov = " + << cam->get_hfov() << " vfov = " + << cam->get_vfov() << endl; + cam->set_scene(render); + + // take care of the orientation + PT(TransformTransition) orient; + + switch ((*k).getOrientation()) { + case SetupItem::Up: + break; + case SetupItem::Down: + orient = new TransformTransition(LMatrix4f::rotate_mat(180., LVector3f::forward())); + break; + case SetupItem::Left: + orient = new TransformTransition(LMatrix4f::rotate_mat(90., LVector3f::forward())); + break; + case SetupItem::Right: + orient = new TransformTransition(LMatrix4f::rotate_mat(-90., LVector3f::forward())); + break; + } + + RenderRelation *tocam = new RenderRelation(camera_node, cam); + if (orient != (TransformTransition *)NULL) { + tocam->set_transition(orient); + } + } + } + } + return; +} + +PT(GraphicsWindow) ChanConfig(GraphicsPipe* pipe, std::string cfg, + Node *camera_node, Node *render, + ChanCfgOverrides& overrides) { + ReadChanConfigData(); + // check to make sure we know everything we need to + if (!ConfigDefined(cfg)) { + chancfg_cat.error() + << "no window configuration called '" << cfg << "'" << endl; + return (GraphicsWindow*)0; + } + WindowItem W = (*WindowDB)[cfg]; + + std::string l = W.getLayout(); + if (!LayoutDefined(l)) { + chancfg_cat.error() + << "No layout called '" << l << "'" << endl; + return (GraphicsWindow*)0; + } + LayoutItem L = (*LayoutDB)[l]; + + SetupSyms s = W.getSetups(); + if (!ChanCheckSetups(s)) + return (GraphicsWindow*)0; + SVec S; + for (SetupSyms::iterator i=s.begin(); i!=s.end(); ++i) + S.push_back((*SetupDB)[(*i)]); + + // get the window data + int sizeX = chanconfig.GetInt("win-width", -1); + int sizeY = chanconfig.GetInt("win-height", -1); + if (overrides.defined(ChanCfgOverrides::SizeX)) + sizeX = overrides.getInt(ChanCfgOverrides::SizeX); + if (overrides.defined(ChanCfgOverrides::SizeY)) + sizeY = overrides.getInt(ChanCfgOverrides::SizeY); + if (sizeX < 0) { + if (sizeY < 0) { + // take the default size + sizeX = W.getSizeX(); + sizeY = W.getSizeY(); + } else { + // verticle size is defined, compute horizontal keeping the aspect from + // the default + sizeX = (W.getSizeX() * sizeY) / W.getSizeY(); + } + } + if (sizeY < 0) { + // horizontal size is defined, compute verticle keeping the aspect from the + // default + sizeY = (W.getSizeY() * sizeX) / W.getSizeX(); + } + + int origX = chanconfig.GetInt("win-origin-x"); + int origY = chanconfig.GetInt("win-origin-y"); + origX = overrides.defined(ChanCfgOverrides::OrigX) ? + overrides.getInt(ChanCfgOverrides::OrigX) : origX; + origY = overrides.defined(ChanCfgOverrides::OrigY) ? + overrides.getInt(ChanCfgOverrides::OrigY) : origY; + + bool border = !chanconfig.GetBool("no-border", !W.getBorder()); + bool fullscreen = chanconfig.GetBool("fullscreen", false); + bool cursor = chanconfig.GetBool("cursor-visible", W.getCursor()); + int want_depth_bits = chanconfig.GetInt("want-depth-bits", 1); + int want_color_bits = chanconfig.GetInt("want-color-bits", 1); + + // visual? nope, that's handled with the mode. + uint mask = 0x0; // ?! this really should come from the win config + mask = overrides.defined(ChanCfgOverrides::Mask) ? + overrides.getUInt(ChanCfgOverrides::Mask) : mask; + std::string title = cfg; + title = overrides.defined(ChanCfgOverrides::Title) ? + overrides.getString(ChanCfgOverrides::Title) : title; + + GraphicsWindow::Properties props; + props._xorg = origX; + props._yorg = origY; + props._xsize = sizeX; + props._ysize = sizeY; + props._title = title; + props._mask = mask; + props._border = border; + props._fullscreen = fullscreen; + props._want_depth_bits = want_depth_bits; + props._want_color_bits = want_color_bits; + + // stereo prep? + // DVR prep? + + bool want_cameras = overrides.defined(ChanCfgOverrides::Cameras) ? + overrides.getBool(ChanCfgOverrides::Cameras) : true; + + // open that sucker + PT(GraphicsWindow) win = pipe->make_window(props); + nassertr(win != (GraphicsWindow *)NULL, NULL); + + // make channels and display regions + ChanViewport V(0., 1., 0., 1.); + ChanEval(win, W, L, S, V, W.getChanOffset()+1, sizeX, sizeY, + camera_node, render, want_cameras); + + // sanity check + if (config_sanity_check) { + nout << "ChanConfig Sanity check:" << endl; + nout << "window - " << (void*)win << endl; + nout << " width = " << win->get_width() << " height = " + << win->get_height() << endl; + nout << " xorig = " << win->get_xorg() << " yorig = " << win->get_yorg() + << endl; + { + int max_channel_index = win->get_max_channel_index(); + for (int c = 0; c < max_channel_index; c++) { + if (win->is_channel_defined(c)) { + GraphicsChannel *chan = win->get_channel(c); + nout << " Chan - " << (void*)chan << endl; + nout << " window = " << (void*)(chan->get_window()) << endl; + nout << " active = " << chan->is_active() << endl;; + + int num_layers = chan->get_num_layers(); + for (int l = 0; l < num_layers; l++) { + GraphicsLayer *layer = chan->get_layer(l); + nout << " Layer - " << (void*)layer << endl; + nout << " channel = " << (void*)(layer->get_channel()) << endl; + nout << " active = " << layer->is_active() << endl;; + + int num_drs = layer->get_num_drs(); + for (int d = 0; d < num_drs; d++) { + DisplayRegion *dr = layer->get_dr(d); + nout << " DR - " << (void*)dr << endl; + nout << " layer = " << (void*)(dr->get_layer()) << endl; + float ll, rr, bb, tt; + dr->get_dimensions(ll, rr, bb, tt); + nout << " (" << ll << " " << rr << " " << bb << " " << tt << ")" + << endl; + nout << " camera = " << (void*)(dr->get_camera()) << endl; + { + Camera* cmm = dr->get_camera(); + if (cmm != (Camera*)0L) { + nout << " active = " << cmm->is_active() << endl;; + int num_cam_drs = cmm->get_num_drs(); + for (int cd = 0; cd < num_cam_drs; cd++) { + nout << " dr = " << (void*)cmm->get_dr(cd) << endl; + } + } + } + nout << " active = " << dr->is_active() << endl; + } + } + } + } + } + } + + return win; +} + + + + + +ChanCfgOverrides::ChanCfgOverrides(void) {} + +ChanCfgOverrides::ChanCfgOverrides(const ChanCfgOverrides& c) : + _fields(c._fields) {} + +ChanCfgOverrides::~ChanCfgOverrides(void) {} + +ChanCfgOverrides& ChanCfgOverrides::operator=(const ChanCfgOverrides& c) +{ + _fields = c._fields; + return *this; +} + +void ChanCfgOverrides::setField(const Field f, const bool v) { + Types t; + t.setBool(v); + _fields[f] = t; +} + +void ChanCfgOverrides::setField(const Field f, const int v) { + Types t; + t.setInt(v); + _fields[f] = t; +} + +void ChanCfgOverrides::setField(const Field f, const unsigned int v) { + Types t; + t.setUInt(v); + _fields[f] = t; +} + +void ChanCfgOverrides::setField(const Field f, const float v) { + Types t; + t.setFloat(v); + _fields[f] = t; +} + +void ChanCfgOverrides::setField(const Field f, const double v) { + Types t; + t.setDouble(v); + _fields[f] = t; +} + +void ChanCfgOverrides::setField(const Field f, const std::string& v) { + Types t; + t.setString(v); + _fields[f] = t; +} + +void ChanCfgOverrides::setField(const Field f, const char* v) { + Types t; + t.setString(v); + _fields[f] = t; +} + +bool ChanCfgOverrides::defined(const Field f) const { + return (_fields.find(f) != _fields.end()); +} + +bool ChanCfgOverrides::getBool(const Field f) const { + Fields::const_iterator i = _fields.find(f); + const Types t((*i).second); + return t.getBool(); +} + +int ChanCfgOverrides::getInt(const Field f) const { + Fields::const_iterator i = _fields.find(f); + const Types t((*i).second); + return t.getInt(); +} + +unsigned int ChanCfgOverrides::getUInt(const Field f) const { + Fields::const_iterator i = _fields.find(f); + const Types t((*i).second); + return t.getUInt(); +} + +float ChanCfgOverrides::getFloat(const Field f) const { + Fields::const_iterator i = _fields.find(f); + const Types t((*i).second); + return t.getFloat(); +} + +double ChanCfgOverrides::getDouble(const Field f) const { + Fields::const_iterator i = _fields.find(f); + const Types t((*i).second); + return t.getDouble(); +} + +std::string ChanCfgOverrides::getString(const Field f) const { + Fields::const_iterator i = _fields.find(f); + const Types t((*i).second); + return t.getString(); +} diff --git a/panda/src/chancfg/chancfg.h b/panda/src/chancfg/chancfg.h new file mode 100644 index 0000000000..7dcd5e9329 --- /dev/null +++ b/panda/src/chancfg/chancfg.h @@ -0,0 +1,97 @@ +// Filename: chancfg.h +// Created by: cary (04Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __CHANCFG_H__ +#define __CHANCFG_H__ + +#include + +#include "chanlayout.h" +#include "chansetup.h" +#include "chanwindow.h" +#include "chanshare.h" + +#include +#include + +#include + +class Node; + +class EXPCL_PANDA ChanCfgOverrides { +public: + enum Field { OrigX, OrigY, SizeX, SizeY, Title, Mask, Cameras }; +private: + class EXPCL_PANDA Types { + private: + bool _b; + int _i; + unsigned int _ui; + float _f; + double _d; + std::string _str; + public: + INLINE Types(void) {} + INLINE Types(const Types& c) : _b(c._b), _i(c._i), _ui(c._ui), _f(c._f), + _d(c._d), _str(c._str) {} + INLINE ~Types(void) {} + INLINE Types& operator=(const Types& c) { + _b = c._b; + _i = c._i; + _ui = c._ui; + _f = c._f; + _d = c._d; + _str = c._str; + return *this; + } + INLINE void setBool(const bool v) { _b = v; } + INLINE bool getBool(void) const { return _b; } + INLINE void setInt(const int v) { _i = v; } + INLINE int getInt(void) const { return _i; } + INLINE void setUInt(const unsigned int v) { _ui = v; } + INLINE unsigned int getUInt(void) const { return _ui; } + INLINE void setFloat(const float v) { _f = v; } + INLINE float getFloat(void) const { return _f; } + INLINE void setDouble(const double v) { _d = v; } + INLINE double getDouble(void) const { return _d; } + INLINE void setString(const std::string& v) { _str = v; } + INLINE std::string getString(void) const { return _str; } + }; + typedef std::map Fields; + Fields _fields; +public: + ChanCfgOverrides(void); + ChanCfgOverrides(const ChanCfgOverrides&); + ~ChanCfgOverrides(void); + + ChanCfgOverrides& operator=(const ChanCfgOverrides&); + + void setField(const Field, const bool); + void setField(const Field, const int); + void setField(const Field, const unsigned int); + void setField(const Field, const float); + void setField(const Field, const double); + void setField(const Field, const std::string&); + void setField(const Field, const char*); + + bool defined(const Field) const; + + bool getBool(const Field) const; + int getInt(const Field) const; + unsigned int getUInt(const Field) const; + float getFloat(const Field) const; + double getDouble(const Field) const; + std::string getString(const Field) const; +}; + +extern ChanCfgOverrides ChanOverrideNone; + +PT(GraphicsWindow) EXPCL_PANDA +ChanConfig(GraphicsPipe*, std::string, Node *camera_node, Node *render, + ChanCfgOverrides& = ChanOverrideNone); + +#include "chancfg.I" + +#endif /* __CHANCFG_H__ */ diff --git a/panda/src/chancfg/chanlayout.I b/panda/src/chancfg/chanlayout.I new file mode 100644 index 0000000000..1d1b031847 --- /dev/null +++ b/panda/src/chancfg/chanlayout.I @@ -0,0 +1,32 @@ +// Filename: chanlayout.I +// Created by: cary (04Feb99) +// +//////////////////////////////////////////////////////////////////// + +INLINE LayoutItem::LayoutItem(void) {} + +INLINE LayoutItem::LayoutItem(int x, int y) : _x(x), _y(y) {} + +INLINE LayoutItem::LayoutItem(const LayoutItem& c) : _x(c._x), _y(c._y), + _regions(c._regions) {} + +INLINE LayoutItem::~LayoutItem(void) {} + +INLINE LayoutItem& LayoutItem::operator=(const LayoutItem& c) { + _x = c._x; + _y = c._y; + _regions = c._regions; + return *this; +} + +INLINE void LayoutItem::AddRegion(const ChanViewport& r) { + _regions.push_back(r); +} + +INLINE int LayoutItem::GetNumRegions(void) { + return _regions.size(); +} + +INLINE const ChanViewport& LayoutItem::operator[](int i) { + return _regions[i]; +} diff --git a/panda/src/chancfg/chanlayout.cxx b/panda/src/chancfg/chanlayout.cxx new file mode 100644 index 0000000000..e5fd789195 --- /dev/null +++ b/panda/src/chancfg/chanlayout.cxx @@ -0,0 +1,113 @@ +// Filename: chanlayout.cxx +// Created by: cary (02Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "chanlayout.h" +#include "chanparse.h" +#include "chanshare.h" + +#include + +LayoutType* LayoutDB = (LayoutType*)0; + +class LayoutParseFunctor : public ChanParseFunctor { +public: + INLINE LayoutParseFunctor(void) : ChanParseFunctor() {} + virtual ~LayoutParseFunctor(void); + + virtual void operator()(std::string); +}; + +LayoutParseFunctor::~LayoutParseFunctor(void) { + return; +} + +typedef std::vector LayoutBoolVec; + +void LayoutParseFunctor::operator()(std::string S) { + std::string sym; + + ChanEatFrontWhite(S); + sym = ChanReadNextWord(S); + ChanCheckScoping(S); + ChanDescope(S); + + int X, Y; + + X = ChanReadNextInt(S); + Y = ChanReadNextInt(S); + + LayoutItem L(X, Y); + LayoutBoolVec mask; + int i=0; + for (; i<(X*Y); ++i) + mask.push_back(false); + + float xfrac = 1. / float(X); + float yfrac = 1. / float(Y); + + while (!S.empty()) { + ChanCheckScoping(S); + i = S.find_first_of(")"); + std::string stmp = S.substr(1, i-1); + S.erase(0, i+1); + ChanEatFrontWhite(S); + + int m, n, start; + + m = ChanReadNextInt(stmp); + n = ChanReadNextInt(stmp); + start = ChanReadNextInt(stmp); + + int x = start % X; + int y = (start - x) / X; + bool ok = true; + + for (int j=y; jis_debug()) { + chancfg_cat->debug() << "parsed a layout called '" << sym << "':" << endl; + chancfg_cat->debug() << " " << L.GetNumRegions() << " regions" << endl; + for (int q=0; qdebug() << " region #" << q << ": (" << v.left() << ", " + << v.right() << ", " << v.bottom() << ", " + << v.top() << ")" << endl; + } + } + (*LayoutDB)[sym] = L; +} + +void ResetLayout() { + if (LayoutDB != (LayoutType *)NULL) { + delete LayoutDB; + } + LayoutDB = new LayoutType; +} + +void ParseLayout(istream& is) { + LayoutParseFunctor l; + ChanParse(is, &l); +} diff --git a/panda/src/chancfg/chanlayout.h b/panda/src/chancfg/chanlayout.h new file mode 100644 index 0000000000..fb9389ef67 --- /dev/null +++ b/panda/src/chancfg/chanlayout.h @@ -0,0 +1,45 @@ +// Filename: chanlayout.h +// Created by: cary (04Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __CHANLAYOUT_H__ +#define __CHANLAYOUT_H__ + +#include + +#include +#include +#include +#include +#include "chanviewport.h" + + +typedef std::vector LayoutRegionVec; + +class LayoutItem { +private: + int _x, _y; + LayoutRegionVec _regions; +public: + INLINE LayoutItem(void); + INLINE LayoutItem(int, int); + INLINE LayoutItem(const LayoutItem&); + INLINE ~LayoutItem(void); + + INLINE LayoutItem& operator=(const LayoutItem&); + INLINE void AddRegion(const ChanViewport&); + INLINE int GetNumRegions(void); + INLINE const ChanViewport& operator[](int); +}; + +typedef std::map LayoutType; + +extern LayoutType* LayoutDB; + +void ResetLayout(); +void ParseLayout(istream&); + +#include "chanlayout.I" + +#endif /* __CHANLAYOUT_H__ */ diff --git a/panda/src/chancfg/chanparse.I b/panda/src/chancfg/chanparse.I new file mode 100644 index 0000000000..4bd46f9bc8 --- /dev/null +++ b/panda/src/chancfg/chanparse.I @@ -0,0 +1,98 @@ +// Filename: chanparse.I +// Created by: cary (06Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include + +INLINE void ChanEatFrontWhite(std::string& S) { + int i = S.find_first_not_of(" \t\r\f\n"); + if ((i != std::string::npos) && (i != 0)) + S.erase(0, i); +} + +INLINE void ChanCheckScoping(std::string& S) { + if (S[0] != '(') { + // error, unscoped text usually indicates a problem + nout << "error, '" << S << "' does not start with a (" << endl; + S.erase(0, S.find_first_of("(")); + } +} + +INLINE void ChanDescope(std::string& S) { + int i = ChanMatchingParen(S); + nassertv(i != -1); + S = S.substr(1, i-2); + ChanEatFrontWhite(S); +} + +INLINE std::string ChanReadNextWord(std::string& S) { + int i = S.find_first_of(" \t\r\f\n"); + std::string stmp = S.substr(0, i); + S.erase(0, i); + ChanEatFrontWhite(S); + return stmp; +} + +INLINE int ChanReadNextInt(std::string& S) { + int i = S.find_first_of(" \t\r\f\n"); + std::string stmp = S.substr(0, i); + S.erase(0, i); + ChanEatFrontWhite(S); + istringstream st(stmp.c_str()); + st >> i; + return i; +} + +INLINE bool ChanReadNextBool(std::string& S) { + int i = S.find_first_of(" \t\r\f\n"); + std::string stmp = S.substr(0, i); + S.erase(0, i); + ChanEatFrontWhite(S); + bool ret = false; + if ((stmp.length() != 2) || (stmp[0] != '#')) { + // error, this isn't gonna be either #t or #f + nout << "error, '" << stmp << "' is not either #t or #f" << endl; + ret = false; + } + if (stmp[1] == 't') + ret = true; + else if (stmp[1] != 'f') { + // error this isn't either #t or #f + nout << "error, '" << stmp << "' is not either #t or #f" << endl; + ret = false; + } + return ret; +} + +// #f or int +INLINE int ChanReadNextIntB(std::string& S) { + int i = S.find_first_of(" \t\r\f\n"); + std::string stmp = S.substr(0, i); + S.erase(0, i); + ChanEatFrontWhite(S); + i = -1; + if (stmp[0] == '#') + if ((stmp.length() != 2)||(stmp[1] != 'f')) { + // error, not #f.. #t is not allowed in this case + nout << "error, '" << stmp << "' is not allowed, only #f or an int" + << endl; + i = -1; + } + else { + istringstream st(stmp.c_str()); + st >> i; + } + return i; +} + +INLINE float ChanReadNextFloat(std::string& S) { + int i = S.find_first_of(" \t\r\f\n"); + std::string stmp = S.substr(0, i); + S.erase(0, i); + ChanEatFrontWhite(S); + istringstream st(stmp.c_str()); + float ret; + st >> ret; + return ret; +} diff --git a/panda/src/chancfg/chanparse.cxx b/panda/src/chancfg/chanparse.cxx new file mode 100644 index 0000000000..2a169874a8 --- /dev/null +++ b/panda/src/chancfg/chanparse.cxx @@ -0,0 +1,140 @@ +// Filename: chanparse.cxx +// Created by: cary (02Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "chanparse.h" + +#include + +const int ChanFileEOF = -1; + +static bool file_done; + +INLINE std::string ChanReadLine(istream& is) { + if (is.eof() || is.fail()) +#ifdef BROKEN_EXCEPTIONS + { + file_done = true; + return ""; + } +#else + throw ChanFileEOF; // all done +#endif /* BROKEN_EXCEPTIONS */ + std::string S; + std::getline(is, S); + int i = S.find_first_not_of(" /t/f/r/n"); + if ((i == std::string::npos) || (S[i] == ';')) + return ChanReadLine(is); // all white space or comment + if (i != 0) + S.erase(0, i); // remove leading white space + i = S.find_first_of(";"); + if (i != std::string::npos) + S.erase(i, std::string::npos); // remove trailing comment + return S; +} + +int ChanMatchingParen(std::string S) { + int i = 1, j = 1; + std::string::iterator k = S.begin(); + + ++k; + while ((k != S.end()) && (i != 0)) { + switch (*k) { + case '(': + ++i; + break; + case ')': + --i; + break; + } + ++j; + ++k; + } + if ((k == S.end()) && (i != 0)) + j = -1; + return j; +} + +void ChanParse(istream& is, ChanParseFunctor* p) { + file_done = false; + + std::string S; +#ifdef BROKEN_EXCEPTIONS + S = ChanReadLine(is); + if (file_done) { + nout << "No data in file to parse" << endl; + return; + } +#else + try { + S = ChanReadLine(is); + } + catch (...) { + nout << "No data in file to parse" << endl; + return; // comment that there was no data in file to parse + } +#endif /* BROKEN_EXCEPTIONS */ + + // now the main loop +#ifdef BROKEN_EXCEPTIONS + { + int i; + while (true) { + if ((!S.empty()) && (S[0] != '(')) { + // error, should have leading paren + nout << "error, no leading paren in '" << S << "'" << endl; + S.erase(0, S.find_first_of("(")); + } + while ((S.empty())||(i = ChanMatchingParen(S)) == -1) { + S += " "; + S += ChanReadLine(is); + if (file_done) { + if (!S.empty() && (!(S == " "))) + // error, trailing text usually indicates an error in the file + nout << "error, trailing text in file S = '" << S << "'" << endl; + return; + } + ChanEatFrontWhite(S); + } + (*p)(S.substr(1, i-2)); + S.erase(0, i); + ChanEatFrontWhite(S); + } + } +#else + try { + int i; + while (true) { + if ((!S.empty()) && (S[0] != '(')) { + // error, should have leading paren + nout << "error, no leading paren in '" << S << "'" << endl; + S.erase(0, S.find_first_of("(")); + } + while ((S.empty())||(i = ChanMatchingParen(S)) == -1) { + S += " "; + S += ChanReadLine(is); + ChanEatFrontWhite(S); + } + (*p)(S.substr(1, i-2)); + S.erase(0, i); + ChanEatFrontWhite(S); + } + } + catch (...) { + if (!S.empty() && (!(S == " "))) + // error, trailing text usually indicates an error in the file + nout << "error, trailing text in file S = '" << S << "'" << endl; + return; + } +#endif /* BROKEN_EXCEPTIONS */ +} + +ChanParseFunctor::~ChanParseFunctor() { + return; +} + +void ChanParseFunctor::operator()(std::string) { + // error, should never be in here + return; +} diff --git a/panda/src/chancfg/chanparse.h b/panda/src/chancfg/chanparse.h new file mode 100644 index 0000000000..5c54de28b4 --- /dev/null +++ b/panda/src/chancfg/chanparse.h @@ -0,0 +1,35 @@ +// Filename: chanparse.h +// Created by: cary (02Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __CHANPARSE_H__ +#define __CHANPARSE_H__ + +#include + +#include + +class EXPCL_PANDA ChanParseFunctor { +public: + INLINE ChanParseFunctor() {}; + virtual ~ChanParseFunctor(); + + virtual void operator()(std::string) = 0; +}; + +int ChanMatchingParen(std::string); +void ChanParse(istream&, ChanParseFunctor*); + +INLINE void ChanEatFrontWhite(std::string&); +INLINE void ChanCheckScoping(std::string&); +INLINE void ChanDescope(std::string&); +INLINE std::string ChanReadNextWord(std::string&); +INLINE int ChanReadNextInt(std::string&); +INLINE bool ChanReadNextBool(std::string&); +INLINE int ChanReadNextIntB(std::string&); +INLINE float ChanReadNextFloat(std::string&); + +#include "chanparse.I" + +#endif /* __CHANPARSE_H__ */ diff --git a/panda/src/chancfg/chansetup.I b/panda/src/chancfg/chansetup.I new file mode 100644 index 0000000000..a3643ebdc3 --- /dev/null +++ b/panda/src/chancfg/chansetup.I @@ -0,0 +1,128 @@ +// Filename: chansetup.I +// Created by: cary (05Feb99) +// +//////////////////////////////////////////////////////////////////// + +INLINE SetupFOV::SetupFOV(void) : _type(Invalid) {} + +INLINE SetupFOV::SetupFOV(const SetupFOV& c) : _type(c._type), + _horiz(c._horiz), + _vert(c._vert) {} + +INLINE SetupFOV::~SetupFOV(void) {} + +INLINE SetupFOV& SetupFOV::operator=(const SetupFOV& c) { + _type = c._type; + _horiz = c._horiz; + _vert = c._vert; + return *this; +} + +INLINE void SetupFOV::setFOV(void) { + _type = Default; +} + +INLINE void SetupFOV::setFOV(const float h) { + _type = Horizontal; + _horiz = h; +} + +INLINE void SetupFOV::setFOV(const float h, const float v) { + _type = Both; + _horiz = h; + _vert = v; +} + +INLINE SetupFOV::FOVType SetupFOV::getType(void) const { + return _type; +} + +INLINE float SetupFOV::getHoriz(void) const { + return _horiz; +} + +INLINE float SetupFOV::getVert(void) const { + return _vert; +} + +INLINE SetupItem::SetupItem(void) : _viewport(0., 1., 0., 1.) {} + +INLINE SetupItem::SetupItem(bool r) : _recurse(r), _viewport(0., 1., 0., 1.) {} + +INLINE SetupItem::SetupItem(const SetupItem& c) : _recurse(c._recurse), + _layouts(c._layouts), + _setups(c._setups), + _stereo(c._stereo), + _hw_chan(c._hw_chan), + _chan(c._chan), + _viewport(c._viewport), + _fov(c._fov), + _orientation(c._orientation) + {} + +INLINE SetupItem::~SetupItem(void) {} + +INLINE SetupItem& SetupItem::operator=(const SetupItem& c) { + _recurse = c._recurse; + _layouts = c._layouts; + _setups = c._setups; + _stereo = c._stereo; + _hw_chan = c._hw_chan; + _chan = c._chan; + _viewport = c._viewport; + _fov = c._fov; + _orientation = c._orientation; + return *this; +} + +INLINE void SetupItem::AddRecurse(std::string& l, std::string& s) { + _layouts.push_back(l); + _setups.push_back(s); +} + +INLINE void SetupItem::setState(const bool st, const bool hw, const int pvc, + const ChanViewport& v, const SetupFOV& f, + const Orientation& o) { + _stereo = st; + _hw_chan = hw; + _chan = pvc; + _viewport = v; + _fov = f; + _orientation = o; +} + +INLINE bool SetupItem::getRecurse(void) const { + return _recurse; +} + +INLINE SetupSyms SetupItem::getLayouts(void) const { + return _layouts; +} + +INLINE SetupSyms SetupItem::getSetups(void) const { + return _setups; +} + +INLINE bool SetupItem::getStereo(void) const { + return _stereo; +} + +INLINE bool SetupItem::getHWChan(void) const { + return _hw_chan; +} + +INLINE int SetupItem::getChan(void) const { + return _chan; +} + +INLINE ChanViewport SetupItem::getViewport(void) const { + return _viewport; +} + +INLINE SetupFOV SetupItem::getFOV(void) const { + return _fov; +} + +INLINE SetupItem::Orientation SetupItem::getOrientation(void) const { + return _orientation; +} diff --git a/panda/src/chancfg/chansetup.cxx b/panda/src/chancfg/chansetup.cxx new file mode 100644 index 0000000000..7709e22860 --- /dev/null +++ b/panda/src/chancfg/chansetup.cxx @@ -0,0 +1,204 @@ +// Filename: chansetup.cxx +// Created by: cary (05Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "chansetup.h" +#include "chanparse.h" +#include "chanshare.h" + +#include + +SetupType* SetupDB = (SetupType*)0; + +class SetupParseFunctor : public ChanParseFunctor { +public: + INLINE SetupParseFunctor(void) : ChanParseFunctor() {} + virtual ~SetupParseFunctor(void); + + virtual void operator()(std::string); +}; + +SetupParseFunctor::~SetupParseFunctor(void) { + return; +} + +void SetupParseFunctor::operator()(std::string S) { + std::string sym; + + ChanEatFrontWhite(S); + sym = ChanReadNextWord(S); + ChanCheckScoping(S); + ChanDescope(S); + + bool recurse = ChanReadNextBool(S); + SetupItem s(recurse); + + if (recurse) { + ChanCheckScoping(S); + int i = S.find_first_of(")"); + std::string layouts = S.substr(1, i-2); + S.erase(0, i+1); + ChanEatFrontWhite(S); + ChanCheckScoping(S); + i = S.find_first_of(")"); + std::string setups = S.substr(1, i-2); + S.erase(0, i+1); + if (!S.empty()) { + // error, there shouldn't be any more here + nout << "error, trailing data in recursive definition '" << S << "'" + << endl; + S.erase(0, std::string::npos); + } + std::string a, b; + while ((!layouts.empty())&&(!setups.empty())) { + a = ChanReadNextWord(layouts); + b = ChanReadNextWord(setups); + s.AddRecurse(a, b); + } + } else { + bool stereo = ChanReadNextBool(S); + bool hw_chan = ChanReadNextBool(S); + int chan_num = ChanReadNextIntB(S); + ChanCheckScoping(S); + int i = S.find_first_of(")"); + std::string stmp = S.substr(1, i-2); + S.erase(0, i+1); + ChanEatFrontWhite(S); + ChanViewport v(ChanReadNextFloat(stmp), ChanReadNextFloat(stmp), + ChanReadNextFloat(stmp), ChanReadNextFloat(stmp)); + if (!stmp.empty()) { + // error, there shouldn't be anything left after eating the viewport + nout << "error, tailing text in viewport spec '" << stmp << "'" << endl; + stmp.erase(0, std::string::npos); + } + SetupFOV fov; + if (S[0] == '#') { + bool b = ChanReadNextBool(S); + if (b) { + // error, #t is not allowed here + nout << "error, cannot have #t for FOV spec" << endl; + b = false; + } + fov.setFOV(); + } else if (S[0] == '(') { + i = S.find_first_of(")"); + stmp = S.substr(1, i-2); + S.erase(0, i+1); + ChanEatFrontWhite(S); + fov.setFOV(ChanReadNextFloat(stmp), ChanReadNextFloat(stmp)); + if (!stmp.empty()) { + // error, there shouldn't be anything left after eating the fov + nout << "error, trailing text after fov spec '" << stmp << "'" << endl; + stmp.erase(0, std::string::npos); + } + } else { + fov.setFOV(ChanReadNextFloat(S)); + } + SetupItem::Orientation orie = SetupItem::Up; + if (!S.empty()) { + stmp = ChanReadNextWord(S); + if (stmp == "up") { + // nothing really to do + orie = SetupItem::Up; + } else if (stmp == "down") { + orie = SetupItem::Down; + } else if (stmp == "left") { + orie = SetupItem::Left; + } else if (stmp == "right") { + orie = SetupItem::Right; + } else { + // error, not a recognized orientation + nout << "error, invalid orientation '" << stmp << "'" << endl; + stmp.erase(0, std::string::npos); + } + } + if (!S.empty()) { + // error, we should have consumed all the data by now + nout << "error, trailing text on setup spec '" << S << "'" << endl; + S.erase(0, std::string::npos); + } + s.setState(stereo, hw_chan, chan_num, v, fov, orie); + } + + if (chancfg_cat->is_debug()) { + chancfg_cat->debug() << "parsed a setup called '" << sym << "':" << endl; + if (s.getRecurse()) { + chancfg_cat->debug() << " is a recursive setup" << endl; + SetupSyms q(s.getLayouts()); + chancfg_cat->debug() << " has " << q.size() << " sub-layouts" << endl; + SetupSyms::iterator r; + for (r=q.begin(); r!=q.end(); ++r) + chancfg_cat->debug() << " sub-layout #" << (r-q.begin()) << ": '" + << *r << "'" << endl; + q = s.getSetups(); + chancfg_cat->debug() << " has " << q.size() << " sub-setups" << endl; + for (r=q.begin(); r!=q.end(); ++r) + chancfg_cat->debug() << " sub-setups #" << (r-q.begin()) << ": '" + << *r << "'" << endl; + } else { + chancfg_cat->debug() << " is not a recursive setup" << endl; + chancfg_cat->debug() << " is" << (s.getStereo()?" ":" not ") + << "a stereo setup" << endl; + chancfg_cat->debug() <<" is" << (s.getHWChan()?" ":" not ") + << "a HW channel setup" << endl; + chancfg_cat->debug() << " prefered HW channel number: " << s.getChan() + << endl; + ChanViewport qv(s.getViewport()); + chancfg_cat->debug() << " sub viewport: (" << qv.left() << ", " + << qv.right() << ", " << qv.bottom() << ", " + << qv.top() << ")" << endl; + SetupFOV qf(s.getFOV()); + switch (qf.getType()) { + case SetupFOV::Invalid: + chancfg_cat->debug() << " FOV is invalid" << endl; + break; + case SetupFOV::Default: + chancfg_cat->debug() << " FOV takes defaults" << endl; + break; + case SetupFOV::Horizontal: + chancfg_cat->debug() << " FOV specifies only horizontal: " + << qf.getHoriz() << endl; + break; + case SetupFOV::Both: + chancfg_cat->debug() << " FOV: " << qf.getHoriz() << " x " + << qf.getVert() << endl; + break; + default: + chancfg_cat->debug() << " FOV is of an unknown type (" << qf.getType() + << ")" << endl; + } + switch (s.getOrientation()) { + case SetupItem::Up: + chancfg_cat->debug() << " setup is oriented Up" << endl; + break; + case SetupItem::Down: + chancfg_cat->debug() << " setup is oriented Down" << endl; + break; + case SetupItem::Left: + chancfg_cat->debug() << " setup is oriented Left" << endl; + break; + case SetupItem::Right: + chancfg_cat->debug() << " setup is oriented Right" << endl; + break; + default: + chancfg_cat->debug() << " setup has an unknown orientation (" + << s.getOrientation() << ")" << endl; + } + } + } + (*SetupDB)[sym] = s; +} + + +void ResetSetup() { + if (SetupDB != (SetupType *)NULL) { + delete SetupDB; + } + SetupDB = new SetupType; +} + +void ParseSetup(istream& is) { + SetupParseFunctor s; + ChanParse(is, &s); +} diff --git a/panda/src/chancfg/chansetup.h b/panda/src/chancfg/chansetup.h new file mode 100644 index 0000000000..4717a38731 --- /dev/null +++ b/panda/src/chancfg/chansetup.h @@ -0,0 +1,87 @@ +// Filename: chansetup.h +// Created by: cary (05Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __CHANSETUP_H__ +#define __CHANSETUP_H__ + +#include + +#include + +#include +#include "chanviewport.h" + +typedef vector_string SetupSyms; + +class SetupFOV { +public: + enum FOVType { Invalid, Default, Horizontal, Both }; +private: + FOVType _type; + float _horiz, _vert; +public: + INLINE SetupFOV(void); + INLINE SetupFOV(const SetupFOV&); + INLINE ~SetupFOV(void); + + INLINE SetupFOV& operator=(const SetupFOV&); + + INLINE void setFOV(void); + INLINE void setFOV(const float); + INLINE void setFOV(const float, const float); + INLINE FOVType getType(void) const; + INLINE float getHoriz(void) const; + INLINE float getVert(void) const; +}; + +class SetupItem { +public: + enum Orientation { Up, Down, Left, Right }; +private: + bool _recurse; + + SetupSyms _layouts; + SetupSyms _setups; + + bool _stereo; + bool _hw_chan; + int _chan; + ChanViewport _viewport; + SetupFOV _fov; + Orientation _orientation; +public: + INLINE SetupItem(void); + INLINE SetupItem(bool); + INLINE SetupItem(const SetupItem&); + INLINE ~SetupItem(void); + + INLINE SetupItem& operator=(const SetupItem&); + + INLINE void AddRecurse(std::string&, std::string&); + + INLINE void setState(const bool, const bool, const int, const ChanViewport&, + const SetupFOV&, const Orientation&); + + INLINE bool getRecurse(void) const; + INLINE SetupSyms getLayouts(void) const; + INLINE SetupSyms getSetups(void) const; + INLINE bool getStereo(void) const; + INLINE bool getHWChan(void) const; + INLINE int getChan(void) const; + INLINE ChanViewport getViewport(void) const; + INLINE SetupFOV getFOV(void) const; + INLINE Orientation getOrientation(void) const; +}; + +typedef std::map SetupType; + +extern SetupType* SetupDB; + +void ResetSetup(); +void ParseSetup(istream&); + +#include "chansetup.I" + +#endif /* __CHANSETUP_H__ */ diff --git a/panda/src/chancfg/chanshare.h b/panda/src/chancfg/chanshare.h new file mode 100644 index 0000000000..ee83d3e847 --- /dev/null +++ b/panda/src/chancfg/chanshare.h @@ -0,0 +1,13 @@ +// Filename: chanshare.h +// Created by: frang (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __CHANSHARE_H__ +#define __CHANSHARE_H__ + +#include "notifyCategoryProxy.h" + +NotifyCategoryDecl(chancfg, EXPCL_PANDA, EXPTP_PANDA); + +#endif /* __CHANSHARE_H__ */ diff --git a/panda/src/chancfg/chanviewport.I b/panda/src/chancfg/chanviewport.I new file mode 100644 index 0000000000..8360dc048b --- /dev/null +++ b/panda/src/chancfg/chanviewport.I @@ -0,0 +1,39 @@ +// Filename: chanviewport.I +// Created by: cary (06Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include + +INLINE ChanViewport::ChanViewport(void) {} + +INLINE ChanViewport::ChanViewport(float l, float r, float b, float t) : + _left(l), _right(r), _bottom(b), _top(t) { + if (_left > _right) + swap(_left, _right); + if (_bottom > _top) + swap(_bottom, _top); +} + +INLINE ChanViewport::ChanViewport(const ChanViewport& c) : _left(c._left), + _right(c._right), + _bottom(c._bottom), + _top(c._top) {} + +INLINE ChanViewport::~ChanViewport(void) {} + +INLINE ChanViewport& ChanViewport::operator=(const ChanViewport& c) { + _left = c._left; + _right = c._right; + _bottom = c._bottom; + _top = c._top; + return *this; +} + +INLINE float ChanViewport::left(void) const { return _left; } + +INLINE float ChanViewport::right(void) const { return _right; } + +INLINE float ChanViewport::bottom(void) const { return _bottom; } + +INLINE float ChanViewport::top(void) const { return _top; } diff --git a/panda/src/chancfg/chanviewport.h b/panda/src/chancfg/chanviewport.h new file mode 100644 index 0000000000..5db1c5d17a --- /dev/null +++ b/panda/src/chancfg/chanviewport.h @@ -0,0 +1,31 @@ +// Filename: chanviewport.h +// Created by: cary (06Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __CHANVIEWPORT_H__ +#define __CHANVIEWPORT_H__ + + +class EXPCL_PANDA ChanViewport { +private: + float _left, _right, _bottom, _top; + + INLINE ChanViewport(void); +public: + INLINE ChanViewport(float, float, float, float); + INLINE ChanViewport(const ChanViewport&); + INLINE ~ChanViewport(void); + INLINE ChanViewport& operator=(const ChanViewport&); + + INLINE float left(void) const; + INLINE float right(void) const; + INLINE float bottom(void) const; + INLINE float top(void) const; +}; + +#include + +#include "chanviewport.I" + +#endif /* __CHANVIEWPORT_H__ */ diff --git a/panda/src/chancfg/chanwindow.I b/panda/src/chancfg/chanwindow.I new file mode 100644 index 0000000000..2e046d01ef --- /dev/null +++ b/panda/src/chancfg/chanwindow.I @@ -0,0 +1,76 @@ +// Filename: chanwindow.I +// Created by: cary (06Feb99) +// +//////////////////////////////////////////////////////////////////// + +INLINE WindowItem::WindowItem(void) {} + +INLINE WindowItem::WindowItem(const bool h, const bool d, const int o, + const std::string& l, const SetupSyms& s, + const int x, const int y, const bool b, + const bool c) : _hw_chans(h), _dvr(d), + _border(b), _cursor(c), + _chan_offset(o), _sizeX(x), + _sizeY(y), _layout(l), + _setups(s) {} + +INLINE WindowItem::WindowItem(const WindowItem& c) : _hw_chans(c._hw_chans), + _dvr(c._dvr), + _border(c._border), + _cursor(c._cursor), + _chan_offset(c._chan_offset), + _sizeX(c._sizeX), + _sizeY(c._sizeY), + _layout(c._layout), + _setups(c._setups) {} + +INLINE WindowItem::~WindowItem(void) {} + +INLINE WindowItem& WindowItem::operator=(const WindowItem& c) { + _hw_chans = c._hw_chans; + _dvr = c._dvr; + _border = c._border; + _cursor = c._cursor; + _chan_offset = c._chan_offset; + _sizeX = c._sizeX; + _sizeY = c._sizeY; + _layout = c._layout; + _setups = c._setups; + return *this; +} + +INLINE bool WindowItem::getHWChans(void) const { + return _hw_chans; +} + +INLINE bool WindowItem::getDVR(void) const { + return _dvr; +} + +INLINE bool WindowItem::getBorder(void) const { + return _border; +} + +INLINE bool WindowItem::getCursor(void) const { + return _cursor; +} + +INLINE int WindowItem::getChanOffset(void) const { + return _chan_offset; +} + +INLINE int WindowItem::getSizeX(void) const { + return _sizeX; +} + +INLINE int WindowItem::getSizeY(void) const { + return _sizeY; +} + +INLINE std::string WindowItem::getLayout(void) const { + return _layout; +} + +INLINE SetupSyms WindowItem::getSetups(void) const { + return _setups; +} diff --git a/panda/src/chancfg/chanwindow.cxx b/panda/src/chancfg/chanwindow.cxx new file mode 100644 index 0000000000..3b978baa85 --- /dev/null +++ b/panda/src/chancfg/chanwindow.cxx @@ -0,0 +1,105 @@ +// Filename: chanwindow.cxx +// Created by: cary (06Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "chanwindow.h" +#include "chanparse.h" +#include "chanshare.h" + +#include + +WindowType* WindowDB = (WindowType*)0; + +class WindowParseFunctor : public ChanParseFunctor { +public: + INLINE WindowParseFunctor(void) : ChanParseFunctor() {} + virtual ~WindowParseFunctor(void); + + virtual void operator()(std::string); +}; + +WindowParseFunctor::~WindowParseFunctor(void) { + return; +} + +void WindowParseFunctor::operator()(std::string S) { + std::string sym; + + ChanEatFrontWhite(S); + sym = ChanReadNextWord(S); + ChanCheckScoping(S); + ChanDescope(S); + + bool hw_chans = ChanReadNextBool(S); + bool dvr = ChanReadNextBool(S); + int hw_chan_offset = ChanReadNextInt(S); + std::string layout = ChanReadNextWord(S); + SetupSyms sv; + { + ChanCheckScoping(S); + int i = S.find_first_of(")"); + std::string stmp = S.substr(1, i-1); + S.erase(0, i+1); + ChanEatFrontWhite(S); + + while (!stmp.empty()) { + std::string stmp2 = ChanReadNextWord(stmp); + sv.push_back(stmp2); + } + } + int X, Y; + { + ChanCheckScoping(S); + int i = S.find_first_of(")"); + std::string stmp = S.substr(1, i-1); + S.erase(0, i+1); + ChanEatFrontWhite(S); + X = ChanReadNextInt(stmp); + Y = ChanReadNextInt(stmp); + } + bool border = ChanReadNextBool(S); + bool cursor = ChanReadNextBool(S); + if (!S.empty()) { + // error, should have consumed all of the data by now + nout << "error, trailing text in window spec '" << S << "'" << endl; + S.erase(0, std::string::npos); + } + + WindowItem W(hw_chans, dvr, hw_chan_offset, layout, sv, X, Y, border, + cursor); + + if (chancfg_cat->is_debug()) { + chancfg_cat->debug() << "parsed a window called '" << sym << "':" << endl; + chancfg_cat->debug() << " do" << (W.getHWChans()?" ":" not ") + << "use HW channels" << endl; + chancfg_cat->debug() << " do" << (W.getDVR()?" ":" not ") << "use DVR" + << endl; + chancfg_cat->debug() << " do" << (W.getBorder()?" ":" not ") + << "have a border" << endl; + chancfg_cat->debug() << " do" << (W.getCursor()?" ":" not ") + << "have a cursor" << endl; + chancfg_cat->debug() << " HW channel offset: " << W.getChanOffset() + << endl; + chancfg_cat->debug() << " prefered window size: (" << W.getSizeX() + << ", " << W.getSizeY() << ")" << endl; + chancfg_cat->debug() << " layout: '" << W.getLayout() << "'" << endl; + chancfg_cat->debug() << " setups:" << endl; + SetupSyms q(W.getSetups()); + for (SetupSyms::iterator r=q.begin(); r!=q.end(); ++r) + chancfg_cat->debug() << " '" << *r << "'" << endl; + } + (*WindowDB)[sym] = W; +} + +void ResetWindow() { + if (WindowDB != (WindowType *)NULL) { + delete WindowDB; + } + WindowDB = new WindowType; +} + +void ParseWindow(istream& is) { + WindowParseFunctor w; + ChanParse(is, &w); +} diff --git a/panda/src/chancfg/chanwindow.h b/panda/src/chancfg/chanwindow.h new file mode 100644 index 0000000000..a7fb09896e --- /dev/null +++ b/panda/src/chancfg/chanwindow.h @@ -0,0 +1,52 @@ +// Filename: chanwindow.h +// Created by: cary (06Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __CHANWINDOW_H__ +#define __CHANWINDOW_H__ + +#include + +#include +#include +#include +#include "chansetup.h" + +class WindowItem { +private: + bool _hw_chans, _dvr, _border, _cursor; + int _chan_offset, _sizeX, _sizeY; + std::string _layout; + SetupSyms _setups; +public: + INLINE WindowItem(void); + INLINE WindowItem(const bool, const bool, const int, const std::string&, + const SetupSyms&, const int, const int, const bool, + const bool); + INLINE WindowItem(const WindowItem&); + INLINE ~WindowItem(void); + + INLINE WindowItem& operator=(const WindowItem&); + + INLINE bool getHWChans(void) const; + INLINE bool getDVR(void) const; + INLINE bool getBorder(void) const; + INLINE bool getCursor(void) const; + INLINE int getChanOffset(void) const; + INLINE int getSizeX(void) const; + INLINE int getSizeY(void) const; + INLINE std::string getLayout(void) const; + INLINE SetupSyms getSetups(void) const; +}; + +typedef std::map WindowType; + +extern WindowType* WindowDB; + +void ResetWindow(); +void ParseWindow(istream&); + +#include "chanwindow.I" + +#endif /* __CHANWINDOW_H__ */ diff --git a/panda/src/chancfg/layout_db b/panda/src/chancfg/layout_db new file mode 100644 index 0000000000..e6be024e6b --- /dev/null +++ b/panda/src/chancfg/layout_db @@ -0,0 +1,217 @@ +;; Filename: layout_db +;; Created by: cary (07Feb99) +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Copyright (C) 1992,93,94,95,96,97 Walt Disney Imagineering, Inc. +;; +;; These coded instructions, statements, data structures and +;; computer programs contain unpublished proprietary information of +;; Walt Disney Imagineering and are protected by Federal copyright +;; law. They may not be disclosed to third parties or copied or +;; duplicated in any form, in whole or in part, without the prior +;; written consent of Walt Disney Imagineering Inc. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; + +;;;*Description +;;; The channel layout database. +;;; +;;; The layout specifies how a region is divided up into sup-regions, and +;;; what order we want to talk about them. +;;; +;;; The format for a layout is: +;;; layout m x n rgn rgn rgn +;;; name dicing spec spec spec +;;; (string (int int (---) (---) (---) ...)) +;;; +;;; * There may be an arbitrary number of region specs, though regions are +;;; not allowed to overlap. If they do, this will be detected and an +;;; error will be thrown. +;;; * 'm' is horizontal, 'n' is verticle +;;; +;;; The format for a region spec is: +;;; m x n lower +;;; size left +;;; (int int int) +;;; +;;; * 'Lower left' is the cell index of the lower left corner of the region. +;;; * Cell indexes are 0-based, and start in the lower left corner of the +;;; parent region/window. +;;; * 'm' is horizontal, 'n' is verticle +;;;. + +;;;*Public + +;; -------- +;; | | +;; | 1 | +;; | | +;; -------- +;; +;; 1x1 +(layout1x1 (1 1)) + +;; -------- +;; | | +;; | 2 | +;; | | +;; |------| +;; | | +;; | 1 | +;; | | +;; -------- +;; +;; 1x2 +(layout1x2 (1 2)) + +;; --------------- +;; | | | +;; | 1 | 2 | +;; | | | +;; --------------- +;; +;; 2x1 +(layout2x1 (2 1)) + +;; --------------- +;; | | | +;; | 3 | 4 | +;; | | | +;; |-------------| +;; | | | +;; | 1 | 2 | +;; | | | +;; --------------- +;; +;; 2x2 +(layout2x2 (2 2)) + +;; --------------- +;; | | +;; | 5 | +;; | | +;; |-------------| +;; | | | +;; | 3 | 4 | +;; | | | +;; |-------------| +;; | | | +;; | 1 | 2 | +;; | | | +;; --------------- +;; +;; 2x3-1+2x2 +(layout2x3-1+2x2 (2 3 (1 1 0) (1 1 1) (1 1 2) (1 1 3) (2 1 4))) + +;; --------------- +;; | | +;; | | +;; | | +;; | 5 | +;; | | +;; | | +;; | | +;; |-------------| +;; | | | +;; | 3 | 4 | +;; | | | +;; |-------------| +;; | | | +;; | 1 | 2 | +;; | | | +;; --------------- +;; +;; 2x4-1+2x2 +(layout2x4-1+2x2 (2 4 (1 1 0) (1 1 1) (1 1 2) (1 1 3) (2 2 4))) + +;; ---------------------- +;; | | | | +;; | 1 | 2 | 3 | +;; | | | | +;; ---------------------- +;; +;; 3x1 +(layout3x1 (3 1)) + +;; ---------------------- +;; | | | | +;; | 4 | 5 | 6 | +;; | | | | +;; |--------------------| +;; | | | | +;; | 1 | 2 | 3 | +;; | | | | +;; ---------------------- +;; +;; 3x2 +(layout3x2 (3 2)) + +;; ----------------------------- +;; | | | | | +;; | 1 | 2 | 3 | 4 | +;; | | | | | +;; ----------------------------- +;; +;; 4x1 +(layout4x1 (4 1)) + +;; ------------------------------------ +;; | | | | | | +;; | 1 | 2 | 3 | 4 | 5 | +;; | | | | | | +;; ------------------------------------ +;; +;; 5x1 +(layout5x1 (5 1)) + +;; ---------------------- +;; | | +;; | 4 | +;; | | +;; |--------------------| +;; | | | | +;; | 1 | 2 | 3 | +;; | | | | +;; ---------------------- +;; +;; 3x2-1+3x1 +(layout3x2-1+3x1 (3 2 (1 1 0) (1 1 1) (1 1 2) (3 1 3))) + +;; ---------------------- +;; | | | | +;; | 3 | 4 | 5 | +;; | | | | +;; |--------------------| +;; | | | | +;; | 6 | 1 | 2 | +;; | | | | +;; ---------------------- +;; +;; 3x2 +(layout3x2-0last (3 2 (1 1 1) (1 1 2) (1 1 3) (1 1 4) (1 1 5))) + +;; ----------------------------- +;; | | | | | +;; | 13 | 14 | 15 | 16 | +;; | | | | | +;; ----------------------------- +;; | | | | | +;; | 9 | 10 | 11 | 12 | +;; | | | | | +;; ----------------------------- +;; | | | | | +;; | 5 | 6 | 7 | 8 | +;; | | | | | +;; ----------------------------- +;; | | | | | +;; | 1 | 2 | 3 | 4 | +;; | | | | | +;; ----------------------------- +;; +;; 4x4 +(layout4x4 (4 4)) + +;; misc + +;;;*Private + diff --git a/panda/src/chancfg/setup_db b/panda/src/chancfg/setup_db new file mode 100644 index 0000000000..1dd223812d --- /dev/null +++ b/panda/src/chancfg/setup_db @@ -0,0 +1,82 @@ +;; Filename: setup_db +;; Created by: cary (07Feb99) +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Copyright (C) 1992,93,94,95,96,97 Walt Disney Imagineering, Inc. +;; +;; These coded instructions, statements, data structures and +;; computer programs contain unpublished proprietary information of +;; Walt Disney Imagineering and are protected by Federal copyright +;; law. They may not be disclosed to third parties or copied or +;; duplicated in any form, in whole or in part, without the prior +;; written consent of Walt Disney Imagineering Inc. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; + +;;;*Description +;;; setup database +;;; +;;; The Setup specifies how a given region is to be configured, or further +;;; divided. +;;; +;;; Abbreviations used below are: +;;; PVC - Pipe Video Chanel +;;; DVR - Dynamic Video Resolution +;;; ofs - offset +;;; rgn - region +;;; +;;; The format for a setup is: +;;; setup sub mono use PVC view- FOV orientation +;;; name rgn ster PVC # port +;;; (string (#f bool bool bool/int (---) bool/float/list [direction])) +;;; -or- +;;; setup sub lay- set- +;;; name rgn outs ups +;;; (string (#t (---) (---))) +;;; +;;; * The 'sub-region' flag indicates whether this region is to be further +;;; subdivided and layed out. This allows an easer way to describe +;;; layouts who have parts who's proportions are relatively prime to other +;;; parts. +;;; * In the case of this being a sub-region setup, the next two elements +;;; are exactly like in the config. A list of layouts and setups for +;;; those layouts. +;;; * In the case of this not being a sub-region setup, the next 4 elements +;;; are the mono/stereo flag, a flag to indicate if this region should +;;; use a PVC, the PVC # (or #f for default), the viewport specification +;;; (allowing you to render to less then the full channel), and lastly +;;; the optional orientation of the channel (without this, the default is +;;; up). +;;; * The orientation can have the valid values of: up, down, left, right. +;;; * The orientation specifies an in-channel rotation. The values of up, +;;; down, left, and right correspond to what edge of the actual channel +;;; the top of the image will be on. +;;; +;;; The format for a viewport is: +;;; left right bottom top +;;; (float float float float) +;;; +;;; * these are fractions in the range 0. - 1. and are relative to the local +;;; region. +;;; * a full region would be (0. 1. 0. 1.) +;;; + +;;;. + +;;;*Public + +(setup-mono-plain (#f #f #f #f (0. 1. 0. 1.) #f)) +(setup-mono-right (#f #f #f #f (0. 1. 0. 1.) #f right)) +(setup-mono-left (#f #f #f #f (0. 1. 0. 1.) #f left)) +(setup-mono-down (#f #f #f #f (0. 1. 0. 1.) #f down)) +(setup-stereo-plain (#f #t #t #f (0. 1. 0. 1.) #f)) +(setup-stereo-right (#f #t #t #f (0. 1. 0. 1.) #f right)) +(setup-stereo-left (#f #t #t #f (0. 1. 0. 1.) #f left)) +(setup-pronto-plain (#f #f #f #f (0. 1. 0.1 0.9) 114.)) +(setup-mono-hmd (#f #f #t #f (0. 1. 0. 1.) (90. 73.74))) +(setup-mono-wide (#f #f #f #f (0. 1. 0. 1.) 180.)) +(setup-mono-wide-vptrim (#f #f #f #f (0. 1. 0.667 1.) 180.)) +(setup-mono-wide-vptrim2 (#f #f #f #f (0. 1. 0.5 1.) 180.)) + +;;;*Private + diff --git a/panda/src/chancfg/test_chancfg.cxx b/panda/src/chancfg/test_chancfg.cxx new file mode 100644 index 0000000000..436393fd50 --- /dev/null +++ b/panda/src/chancfg/test_chancfg.cxx @@ -0,0 +1,10 @@ +// Filename: test_chancfg.cxx +// Created by: shochet (02Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "chancfg.h" + +int main() { + return 0; +} diff --git a/panda/src/chancfg/window_db b/panda/src/chancfg/window_db new file mode 100644 index 0000000000..fc277207aa --- /dev/null +++ b/panda/src/chancfg/window_db @@ -0,0 +1,112 @@ +;; Filename: chan_db +;; Created by: cary (07Feb99) +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Copyright (C) 1992,93,94,95,96,97 Walt Disney Imagineering, Inc. +;; +;; These coded instructions, statements, data structures and +;; computer programs contain unpublished proprietary information of +;; Walt Disney Imagineering and are protected by Federal copyright +;; law. They may not be disclosed to third parties or copied or +;; duplicated in any form, in whole or in part, without the prior +;; written consent of Walt Disney Imagineering Inc. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; + +;;;*Description +;;; The window config database. +;;; +;;; The window config specifies globally how the window is to be configured, +;;; it's default size, which layouts and setups to use, etc. +;;; +;;; Abbreviations used below are: +;;; PVC - Pipe Video Chanel +;;; DVR - Dynamic Video Resolution +;;; ofs - offset +;;; rgn - region +;;; +;;; The format for a config is: +;;; window use use PVC lay- set- default bor- cur- +;;; name PVC DVR ofs outs ups size der sor +;;; (string (bool bool int (---) (---) (int int) bool bool)) +;;; +;;; * In order to have DVR, we must also be using Pipe Video Channels +;;; * If Pipe Video Channels are being used, we do not need the default size +;;; * If Pipe Video Channels are being used, the PVC offset will give the +;;; first chanel number to be used for channels that allow default +;;; assignment (see setup below). +;;; * Setups is an arbitrarilly long list of setup specs. Any region that +;;; does not have a coresponding setup, does not get configured. +;;;. + +;;;*Public + +(single (#f #f 0 layout1x1 (setup-mono-plain) (640 480) #t #t)) +(single-stereo (#f #f 0 layout1x1 (setup-stereo-plain) (640 480) #t #f)) +(single-pronto (#f #f 0 layout1x1 (setup-mono-plain) (720 486) #t #f)) +(single-pronto-widescreen (#f #f 0 layout1x1 (setup-pronto-plain) (720 486) #t + #f)) +(pronto+god (#f #f 0 layout1x2 (setup-mono-plain setup-mono-plain) (720 972) + #t #t)) +(pronto-widescreen+god (#f #f 0 layout1x2 (setup-pronto-plain setup-mono-plain) + (720 972) #t #t)) +(fakecave3 (#f #f 0 layout3x1 (setup-mono-plain setup-mono-plain + setup-mono-plain) (720 320) #t #t)) +(fakecave3+god (#f #f 0 layout3x2-1+3x1 (setup-mono-plain setup-mono-plain + setup-mono-plain setup-mono-wide) + (720 640) #t #t)) +(fakecave5 (#f #f 0 layout5x1 (setup-mono-plain setup-mono-plain + setup-mono-plain setup-mono-plain + setup-mono-plain) (1280 341) #t #t)) +(cave (#t #t 1 layout3x2-0last (setup-mono-left setup-stereo-left + setup-stereo-left setup-stereo-left + setup-mono-left) (1920 960) #t #f)) +(mono-cave (#t #t 1 layout3x2-0last (setup-mono-left setup-mono-left + setup-mono-left setup-mono-left + setup-mono-left) (1920 960) #t #f)) +(supercave (#t #t 1 layout3x2-0last (setup-stereo-plain setup-stereo-plain + setup-stereo-plain setup-stereo-plain + setup-stereo-plain) (1920 960) #t #f)) +(hmd-ship (#t #t 4 layout2x2 (setup-mono-hmd setup-mono-hmd setup-mono-hmd + setup-mono-hmd) (1280 960) #t #f)) +(square-fakehmd (#f #f 0 layout2x2 (setup-mono-hmd setup-mono-hmd + setup-mono-hmd setup-mono-hmd) (1280 960) + #t #f)) +(pirates-fakecave (#f #f 0 layout4x1 (setup-mono-plain setup-mono-plain + setup-mono-plain setup-mono-plain) + (1280 240) #t #t)) +(pirates-small-cave (#t #t 0 layout2x2 (setup-stereo-plain setup-stereo-plain + setup-stereo-plain setup-stereo-plain) + (1280 960) #t #f)) +(pirates-small-cave+demigod (#t #t 0 layout2x3-1+2x2 (setup-stereo-plain + setup-stereo-plain + setup-stereo-plain + setup-stereo-plain + setup-mono-wide-vptrim) + (1280 1440) #t #f)) +(pirates-small-cave+god (#t #t 0 layout2x4-1+2x2 (setup-stereo-plain + setup-stereo-plain + setup-stereo-plain + setup-stereo-plain + setup-mono-wide-vptrim2) + (1280 1920) #t #f)) +(pirates-large-cave (#t #t 0 layout2x2 (setup-stereo-plain setup-stereo-plain + setup-stereo-plain setup-stereo-plain) + (1600 1200) #t #f)) +(multipass-tile (#f #f 0 layout4x4 (setup-mono-plain setup-mono-plain + setup-mono-plain setup-mono-plain + setup-mono-plain setup-mono-plain + setup-mono-plain setup-mono-plain + setup-mono-plain setup-mono-plain + setup-mono-plain setup-mono-plain + setup-mono-plain setup-mono-plain + setup-mono-plain setup-mono-plain) + (720 320) #t #t)) + +;;;*Private + +(square-test (#f #f 0 layout2x2 (setup-mono-plain setup-mono-left + setup-mono-right setup-mono-down) (800 800) + #f #f)) +(two-frame (#f #f 0 layout2x1 (setup-mono-plain setup-mono-plain) (600 300) + #f #f)) diff --git a/panda/src/char/Sources.pp b/panda/src/char/Sources.pp new file mode 100644 index 0000000000..3e35e6188c --- /dev/null +++ b/panda/src/char/Sources.pp @@ -0,0 +1,27 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET char + #define LOCAL_LIBS \ + chan graph sgraph linmath putil event sgattrib mathutil gsgbase \ + pstatclient + + #define SOURCES \ + character.I character.cxx character.h characterJoint.cxx \ + characterJoint.h characterJointBundle.I characterJointBundle.cxx \ + characterJointBundle.h characterSlider.cxx characterSlider.h \ + computedVertices.I computedVertices.cxx computedVertices.h \ + computedVerticesMorph.I computedVerticesMorph.cxx \ + computedVerticesMorph.h config_char.cxx config_char.h \ + dynamicVertices.cxx dynamicVertices.h + + #define INSTALL_HEADERS \ + character.I character.h characterJoint.h characterJointBundle.I \ + characterJointBundle.h characterSlider.h computedVertices.I \ + computedVertices.h computedVerticesMorph.I computedVerticesMorph.h \ + config_char.h dynamicVertices.h + + #define IGATESCAN all + +#end lib_target + diff --git a/panda/src/char/character.I b/panda/src/char/character.I new file mode 100644 index 0000000000..a1c50f5366 --- /dev/null +++ b/panda/src/char/character.I @@ -0,0 +1,50 @@ +// Filename: character.I +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "characterJointBundle.h" + +//////////////////////////////////////////////////////////////////// +// Function: Character::get_bundle +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CharacterJointBundle *Character:: +get_bundle() const { + return DCAST(CharacterJointBundle, PartBundleNode::get_bundle()); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Character::get_computed_vertices +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ComputedVertices *Character:: +get_computed_vertices() const { + return _computed_vertices; +} + +//////////////////////////////////////////////////////////////////// +// Function: Character::get_num_parts +// Access: Public +// Description: Returns the total number of moving parts (e.g. joints +// and sliders) associated with the character. +//////////////////////////////////////////////////////////////////// +INLINE int Character:: +get_num_parts() const { + return _parts.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Character::get_part +// Access: Public +// Description: Returns the nth moving part associated with the +// character. +//////////////////////////////////////////////////////////////////// +INLINE PartGroup *Character:: +get_part(int n) const { + nassertr(n >= 0 && n < _parts.size(), NULL); + return _parts[n]; +} diff --git a/panda/src/char/character.cxx b/panda/src/char/character.cxx new file mode 100644 index 0000000000..54d8af6666 --- /dev/null +++ b/panda/src/char/character.cxx @@ -0,0 +1,484 @@ +// Filename: character.cxx +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "character.h" +#include "characterJoint.h" +#include "computedVertices.h" +#include "config_char.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +TypeHandle Character::_type_handle; +PStatCollector Character::_anim_pcollector = + PStatCollector("Animation", RGBColorf(1,0,1), 30); + + +//////////////////////////////////////////////////////////////////// +// Function: Character::Copy Constructor +// Access: Protected +// Description: Use make_copy() or copy_subgraph() to copy a character. +//////////////////////////////////////////////////////////////////// +Character:: +Character(const Character ©) : + PartBundleNode(copy.get_name(), new CharacterJointBundle(copy.get_name())), + _cv(DynamicVertices::deep_copy(copy._cv)), + _computed_vertices(copy._computed_vertices), + _parts(copy._parts), + _char_pcollector(copy._char_pcollector) +{ + // Now make a copy of the joint/slider hierarchy. We could just use + // the PartBundleNode's copy constructor, but if we do it ourselves + // we can simultaneously update our _parts list. + + copy_joints(get_bundle(), copy.get_bundle()); +} + +//////////////////////////////////////////////////////////////////// +// Function: Character::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Character:: +Character(const string &name) : + PartBundleNode(name, new CharacterJointBundle(name)), + _char_pcollector(_anim_pcollector, + name.empty() ? string("Unnamed Character") : name) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: Character::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Character:: +~Character() { +} + +//////////////////////////////////////////////////////////////////// +// Function: Character::make_copy +// Access: Public, Virtual +// Description: The Character make_copy() function will make a new +// copy of the character, with all of its joints copied, +// and with a new set of dynamic vertex arrays all ready +// to go, but it will not copy any of the original +// Character's geometry, so the new Character won't look +// like much. Use copy_subgraph() to make a full copy +// of the Character. +//////////////////////////////////////////////////////////////////// +Node *Character:: +make_copy() const { + return new Character(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: Character::safe_to_transform +// Access: Public, Virtual +// Description: Returns true if it is generally safe to transform +// this particular kind of Node by calling the xform() +// method, false otherwise. For instance, it's usually +// a bad idea to attempt to xform a Character. +//////////////////////////////////////////////////////////////////// +bool Character:: +safe_to_transform() const { + return false; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Character::app_traverse +// Access: Public, Virtual +// Description: This is called by the App traversal by virtue of the +// character node's being present in the scene graph. +//////////////////////////////////////////////////////////////////// +void Character:: +app_traverse() { + double now = ClockObject::get_global_clock()->get_time(); + get_bundle()->advance_time(now); + + if (char_cat.is_debug()) { + char_cat.debug() << "Animating " << *this << " at time " << now << "\n"; + } + + update(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Character::update +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Character:: +update() { + // Statistics + PStatTimer timer(_char_pcollector); + + // First, update all the joints and sliders. + get_bundle()->update(); + + // Now update the vertices. + if (_computed_vertices != (ComputedVertices *)NULL) { + _computed_vertices->update(this); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Character::copy_joints +// Access: Private +// Description: Recursively walks the joint/slider hierarchy and +// creates a new copy of the hierarchy. +//////////////////////////////////////////////////////////////////// +void Character:: +copy_joints(PartGroup *copy, PartGroup *orig) { + if (copy->get_type() != orig->get_type()) { + char_cat.warning() + << "Don't know how to copy " << orig->get_type() << "\n"; + } + + PartGroup::Children::const_iterator ci; + for (ci = orig->_children.begin(); ci != orig->_children.end(); ++ci) { + PartGroup *orig_child = (*ci); + PartGroup *copy_child = orig_child->make_copy(); + copy->_children.push_back(copy_child); + copy_joints(copy_child, orig_child); + } + + Parts::iterator pi = find(_parts.begin(), _parts.end(), orig); + if (pi != _parts.end()) { + (*pi) = copy; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Character::r_copy_subgraph +// Access: Private, Virtual +// Description: This is a virtual function inherited from Node. It's +// called when a copy_subgraph() operation reaches the +// Character node. In the case of a Character, it's +// overridden to do the all right things to copy the +// dynamic geometry to the new Character. +// +// Note that it includes the parameter inst_map, which +// is a map type, and is not (and cannot be) exported +// from PANDA.DLL. Thus, any derivative of Node that is +// not also a member of PANDA.DLL *cannot* access this +// map. +//////////////////////////////////////////////////////////////////// +Node *Character:: +r_copy_subgraph(TypeHandle graph_type, Node::InstanceMap &) const { + Node *copy = make_copy(); + nassertr(copy != (Node *)NULL, NULL); + if (copy->get_type() != get_type()) { + graph_cat.warning() + << "Don't know how to copy nodes of type " << get_type() << "\n"; + } + + // We assume there will be no instancing going on below the + // Character node. If there is, too bad; it will get flattened out. + + // Now we preempt the node's r_copy_subgraph() operation with our + // own function that keeps track of the old vs. new arcs and also + // updates any Geoms we find with our new dynamic vertices. + + Character *char_copy; + DCAST_INTO_R(char_copy, copy, NULL); + ArcMap arc_map; + char_copy->r_copy_char(char_copy, this, graph_type, this, arc_map); + char_copy->copy_arc_pointers(this, arc_map); + + return copy; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Character::r_copy_char +// Access: Private +// Description: Recursively walks the scene graph hierarchy below the +// Character node, duplicating it while noting the +// orig:copy arc mappings, and also updates any +// GeomNodes found. +//////////////////////////////////////////////////////////////////// +void Character:: +r_copy_char(Node *dest, const Node *source, TypeHandle graph_type, + const Character *from, Character::ArcMap &arc_map) { + if (source->is_of_type(GeomNode::get_class_type())) { + const GeomNode *source_gnode; + GeomNode *dest_gnode; + DCAST_INTO_V(source_gnode, source); + DCAST_INTO_V(dest_gnode, dest); + + dest_gnode->clear(); + int num_geoms = source_gnode->get_num_geoms(); + for (int i = 0; i < num_geoms; i++) { + dDrawable *d = source_gnode->get_geom(i); + if (d->is_of_type(Geom::get_class_type())) { + dest_gnode->add_geom(copy_geom(DCAST(Geom, d), from)); + } else { + dest_gnode->add_geom(d); + } + } + } + + int num_children = source->get_num_children(graph_type); + for (int i = 0; i < num_children; i++) { + NodeRelation *source_arc = source->get_child(graph_type, i); + const Node *source_child = source_arc->get_child(); + nassertv(source_child != (Node *)NULL); + + Node *dest_child; + if (source_child->is_of_type(Character::get_class_type())) { + // We make a special case for nodes of type Character. If we + // encounter one of these, we have a character under a + // character, and the nested character's copy should be called + // instead of ours. + dest_child = source_child->copy_subgraph(graph_type); + + } else { + // Otherwise, we assume that make_copy() will make a suitable + // copy of the node. This does limit the sorts of things we can + // have parented to a Character and expect copy_subgraph() to + // work correctly. Too bad. + dest_child = source_child->make_copy(); + r_copy_char(dest_child, source_child, graph_type, from, arc_map); + } + + NodeRelation *dest_arc = + NodeRelation::create_typed_arc(graph_type, dest, dest_child); + nassertv(dest_arc != (NodeRelation *)NULL); + nassertv(dest_arc->is_exact_type(graph_type)); + + dest_arc->copy_transitions_from(source_arc); + arc_map[source_arc] = dest_arc; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Character::copy_geom +// Access: Private +// Description: Makes a new copy of the Geom with the dynamic vertex +// arrays replaced to reference this character instead +// of the other one. If no arrays have changed, simply +// returns the same Geom. +//////////////////////////////////////////////////////////////////// +PT(Geom) Character:: +copy_geom(Geom *source, const Character *from) { + GeomBindType bind; + PTA_ushort index; + + PTA_Vertexf coords; + PTA_Normalf norms; + PTA_Colorf colors; + PTA_TexCoordf texcoords; + + PT(Geom) dest = source; + + source->get_coords(coords, bind, index); + if (bind != G_OFF && coords == from->_cv._coords) { + if (dest == source) { + dest = source->make_copy(); + } + dest->set_coords(_cv._coords, bind, index); + } + + source->get_normals(norms, bind, index); + if (bind != G_OFF && norms == from->_cv._norms) { + if (dest == source) { + dest = source->make_copy(); + } + dest->set_normals(_cv._norms, bind, index); + } + + source->get_colors(colors, bind, index); + if (bind != G_OFF && colors == from->_cv._colors) { + if (dest == source) { + dest = source->make_copy(); + } + dest->set_colors(_cv._colors, bind, index); + } + + source->get_texcoords(texcoords, bind, index); + if (bind != G_OFF && texcoords == from->_cv._texcoords) { + if (dest == source) { + dest = source->make_copy(); + } + dest->set_texcoords(_cv._texcoords, bind, index); + } + + return dest; +} + +//////////////////////////////////////////////////////////////////// +// Function: Character::copy_arc_pointers +// Access: Public +// Description: Creates _net_transform_arcs and _local_transform_arcs +// as appropriate in each of the character's joints, as +// copied from the other character. +//////////////////////////////////////////////////////////////////// +void Character:: +copy_arc_pointers(const Character *from, const Character::ArcMap &arc_map) { + nassertv(_parts.size() == from->_parts.size()); + for (int i = 0; i < (int)_parts.size(); i++) { + if (_parts[i]->is_of_type(CharacterJoint::get_class_type())) { + nassertv(_parts[i] != from->_parts[i]); + CharacterJoint *source_joint; + CharacterJoint *dest_joint; + DCAST_INTO_V(source_joint, from->_parts[i]); + DCAST_INTO_V(dest_joint, _parts[i]); + + CharacterJoint::ArcList::const_iterator ai; + for (ai = source_joint->_net_transform_arcs.begin(); + ai != source_joint->_net_transform_arcs.end(); + ++ai) { + NodeRelation *source_arc = (*ai); + + ArcMap::const_iterator mi; + mi = arc_map.find(source_arc); + if (mi != arc_map.end()) { + NodeRelation *dest_arc = (*mi).second; + + // Here's an internal joint that the source Character was + // animating directly. We'll animate our corresponding + // joint the same way. + dest_joint->add_net_transform(dest_arc); + } + } + + for (ai = source_joint->_local_transform_arcs.begin(); + ai != source_joint->_local_transform_arcs.end(); + ++ai) { + NodeRelation *source_arc = (*ai); + + ArcMap::const_iterator mi; + mi = arc_map.find(source_arc); + if (mi != arc_map.end()) { + NodeRelation *dest_arc = (*mi).second; + + // Here's an internal joint that the source Character was + // animating directly. We'll animate our corresponding + // joint the same way. + dest_joint->add_local_transform(dest_arc); + } + } + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: Character::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void Character:: +write_datagram(BamWriter *manager, Datagram &me) +{ + PartBundleNode::write_datagram(manager, me); + _cv.write_datagram(manager, me); + manager->write_pointer(me, _computed_vertices); + + me.add_uint16(_parts.size()); + Parts::const_iterator pi; + for (pi = _parts.begin(); pi != _parts.end(); pi++) { + manager->write_pointer(me, (*pi)); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Character::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void Character:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + PartBundleNode::fillin(scan, manager); + _cv.fillin(scan, manager); + manager->read_pointer(scan, this); + + // Read the number of parts to expect in the _parts list, and then + // fill the array up with NULLs. We'll fill in the actual values in + // complete_pointers, later. + int num_parts = scan.get_uint16(); + _parts.clear(); + _parts.reserve(num_parts); + for (int i = 0; i < num_parts; i++) { + manager->read_pointer(scan, this); + _parts.push_back((PartGroup *)NULL); + } + + // Reinitialize our collector with our name, now that we know it. + if (has_name()) { + _char_pcollector = + PStatCollector(_anim_pcollector, get_name()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Character::complete_pointers +// Access: Public +// Description: Takes in a vector of pointers to TypedWriteable +// objects that correspond to all the requests for +// pointers that this object made to BamReader. +//////////////////////////////////////////////////////////////////// +int Character:: +complete_pointers(vector_typedWriteable &plist, BamReader* manager) +{ + int start = PartBundleNode::complete_pointers(plist, manager); + _computed_vertices = DCAST(ComputedVertices, plist[start]); + start++; + + int num_parts = _parts.size(); + for (int i = 0; i < num_parts; i++) { + _parts[i] = DCAST(PartGroup, plist[start + i]); + } + start += num_parts; + + return start; +} + +//////////////////////////////////////////////////////////////////// +// Function: Character::make_Character +// Access: Protected +// Description: Factory method to generate a Character object +//////////////////////////////////////////////////////////////////// +TypedWriteable* Character:: +make_Character(const FactoryParams ¶ms) +{ + Character *me = new Character; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: Character::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a Character object +//////////////////////////////////////////////////////////////////// +void Character:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_Character); +} + + + + diff --git a/panda/src/char/character.h b/panda/src/char/character.h new file mode 100644 index 0000000000..478b817d70 --- /dev/null +++ b/panda/src/char/character.h @@ -0,0 +1,116 @@ +// Filename: character.h +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CHARACTER_H +#define CHARACTER_H + +#include + +#include "computedVertices.h" + +#include +#include +#include +#include +#include +#include + +class CharacterJointBundle; +class ComputedVertices; + +//////////////////////////////////////////////////////////////////// +// Class : Character +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Character : public PartBundleNode { +protected: + Character(const Character ©); + +public: + Character(const string &name = ""); + virtual ~Character(); + + virtual Node *make_copy() const; + + virtual bool safe_to_transform() const; + + INLINE CharacterJointBundle *get_bundle() const; + + INLINE ComputedVertices *get_computed_vertices() const; + INLINE int get_num_parts() const; + INLINE PartGroup *get_part(int n) const; + + virtual void app_traverse(); + void update(); + +private: + void copy_joints(PartGroup *copy, PartGroup *orig); + + typedef map ArcMap; + virtual Node *r_copy_subgraph(TypeHandle graph_type, + InstanceMap &inst_map) const; + void r_copy_char(Node *dest, const Node *source, TypeHandle graph_type, + const Character *from, ArcMap &arc_map); + PT(Geom) copy_geom(Geom *source, const Character *from); + void copy_arc_pointers(const Character *from, const ArcMap &arc_map); + + // These are the actual dynamic vertex pools for this character's + // ComputedVertices--the vertices that it will recompute each frame + // based on the soft-skinning and morphing requirements. Note that + // we store this concretely, instead of as a pointer, just because + // we don't really need to make it a pointer. + DynamicVertices _cv; + + // And this is the object that animates them. It *is* a pointer, so + // it can be shared between multiple instances of this character. + PT(ComputedVertices) _computed_vertices; + + // This vector is used by the ComputedVertices object to index back + // into our joints and sliders. + typedef vector_PartGroupStar Parts; + Parts _parts; + + // Statistics + PStatCollector _char_pcollector; + static PStatCollector _anim_pcollector; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + virtual int complete_pointers(vector_typedWriteable &plist, + BamReader *manager); + + static TypedWriteable *make_Character(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PartBundleNode::init_type(); + register_type(_type_handle, "Character", + PartBundleNode::get_class_type()); + } + +private: + static TypeHandle _type_handle; + + friend class CharacterMaker; + friend class ComputedVerticesMaker; + friend class ComputedVertices; +}; + +#include "character.I" + +#endif + diff --git a/panda/src/char/characterJoint.cxx b/panda/src/char/characterJoint.cxx new file mode 100644 index 0000000000..8b88fa6f72 --- /dev/null +++ b/panda/src/char/characterJoint.cxx @@ -0,0 +1,351 @@ +// Filename: characterJoint.cxx +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "characterJoint.h" +#include "config_char.h" + +#include +#include +#include +#include +#include +#include + +TypeHandle CharacterJoint::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::Default Constructor +// Access: Protected +// Description: For internal use only. +//////////////////////////////////////////////////////////////////// +CharacterJoint:: +CharacterJoint() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::Copy Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +CharacterJoint:: +CharacterJoint(const CharacterJoint ©) : + MovingPartMatrix(copy), + _net_transform(copy._net_transform), + _initial_net_transform_inverse(copy._initial_net_transform_inverse) +{ + // We don't copy the sets of transform arcs. +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CharacterJoint:: +CharacterJoint(PartGroup *parent, const string &name, + const LMatrix4f &initial_value) + : MovingPartMatrix(parent, name, initial_value) +{ + // Now that we've constructed and we're in the tree, let's call + // update_internals() to get our _net_transform set properly. + update_internals(parent, true, false); + + // And then compute its inverse. This is needed for + // ComputedVertices, during animation. + _initial_net_transform_inverse = invert(_net_transform); +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::make_copy +// Access: Public, Virtual +// Description: Allocates and returns a new copy of the node. +// Children are not copied, but see copy_subgraph(). +//////////////////////////////////////////////////////////////////// +PartGroup *CharacterJoint:: +make_copy() const { + return new CharacterJoint(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::update_internals +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CharacterJoint:: +update_internals(PartGroup *parent, bool self_changed, bool) { + assert(parent != NULL); + + bool net_changed = false; + if (parent->is_of_type(CharacterJoint::get_class_type())) { + CharacterJoint *parent_joint = DCAST(CharacterJoint, parent); + + _net_transform = _value * parent_joint->_net_transform; + net_changed = true; + + } else if (self_changed) { + _net_transform = _value; + net_changed = true; + } + + if (net_changed) { + PT(TransformTransition) t = new TransformTransition(_net_transform); + + ArcList::const_iterator ai; + for (ai = _net_transform_arcs.begin(); + ai != _net_transform_arcs.end(); + ++ai) { + NodeRelation *arc = *ai; + arc->set_transition(t); + } + } + + if (self_changed) { + PT(TransformTransition) t = new TransformTransition(_value); + + ArcList::const_iterator ai; + for (ai = _local_transform_arcs.begin(); + ai != _local_transform_arcs.end(); + ++ai) { + NodeRelation *arc = *ai; + arc->set_transition(t); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::add_net_transform +// Access: Public +// Description: Adds the indicated arc to the list of arcs that will +// be updated each frame with the joint's net transform +// from the root. Returns true if the arc is +// successfully added, false if it had already been +// added. +//////////////////////////////////////////////////////////////////// +bool CharacterJoint:: +add_net_transform(NodeRelation *arc) { + return _net_transform_arcs.insert(arc).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::remove_net_transform +// Access: Public +// Description: Removes the indicated arc from the list of arcs that +// will be updated each frame with the joint's net +// transform from the root. Returns true if the arc is +// successfully removed, false if it was not on the +// list. +//////////////////////////////////////////////////////////////////// +bool CharacterJoint:: +remove_net_transform(NodeRelation *arc) { + return (_net_transform_arcs.erase(arc) > 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::has_net_transform +// Access: Public +// Description: Returns true if the arc is on the list of arcs that +// will be updated each frame with the joint's net +// transform from the root, false otherwise. +//////////////////////////////////////////////////////////////////// +bool CharacterJoint:: +has_net_transform(NodeRelation *arc) const { + return (_net_transform_arcs.count(arc) > 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::clear_net_transforms +// Access: Public +// Description: Removes all arcs from the list of arcs that will be +// updated each frame with the joint's net transform +// from the root. +//////////////////////////////////////////////////////////////////// +void CharacterJoint:: +clear_net_transforms() { + _net_transform_arcs.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::add_local_transform +// Access: Public +// Description: Adds the indicated arc to the list of arcs that will +// be updated each frame with the joint's local +// transform from its parent. Returns true if the arc +// is successfully added, false if it had already been +// added. +//////////////////////////////////////////////////////////////////// +bool CharacterJoint:: +add_local_transform(NodeRelation *arc) { + return _local_transform_arcs.insert(arc).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::remove_local_transform +// Access: Public +// Description: Removes the indicated arc from the list of arcs that +// will be updated each frame with the joint's local +// transform from its parent. Returns true if the arc +// is successfully removed, false if it was not on the +// list. +//////////////////////////////////////////////////////////////////// +bool CharacterJoint:: +remove_local_transform(NodeRelation *arc) { + return (_local_transform_arcs.erase(arc) > 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::has_local_transform +// Access: Public +// Description: Returns true if the arc is on the list of arcs that +// will be updated each frame with the joint's local +// transform from its parent, false otherwise. +//////////////////////////////////////////////////////////////////// +bool CharacterJoint:: +has_local_transform(NodeRelation *arc) const { + return (_local_transform_arcs.count(arc) > 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::clear_local_transforms +// Access: Public +// Description: Removes all arcs from the list of arcs that will be +// updated each frame with the joint's local transform +// from its parent. +//////////////////////////////////////////////////////////////////// +void CharacterJoint:: +clear_local_transforms() { + _local_transform_arcs.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void CharacterJoint:: +write_datagram(BamWriter *manager, Datagram &me) +{ + ArcList::iterator ai; + + MovingPartMatrix::write_datagram(manager, me); + me.add_uint16(_net_transform_arcs.size()); + + for(ai = _net_transform_arcs.begin(); ai != _net_transform_arcs.end(); ai++) + { + manager->write_pointer(me, (*ai)); + } + + me.add_uint16(_local_transform_arcs.size()); + for(ai = _local_transform_arcs.begin(); ai != _local_transform_arcs.end(); ai++) + { + manager->write_pointer(me, (*ai)); + } + + _initial_net_transform_inverse.write_datagram(me); +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void CharacterJoint:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + int i; + MovingPartMatrix::fillin(scan, manager); + _num_net_arcs = scan.get_uint16(); + for(i = 0; i < _num_net_arcs; i++) + { + manager->read_pointer(scan, this); + } + + _num_local_arcs = scan.get_uint16(); + for(i = 0; i < _num_local_arcs; i++) + { + manager->read_pointer(scan, this); + } + + _initial_net_transform_inverse.read_datagram(scan); +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::complete_pointers +// Access: Public +// Description: Takes in a vector of pointes to TypedWriteable +// objects that correspond to all the requests for +// pointers that this object made to BamReader. +//////////////////////////////////////////////////////////////////// +int CharacterJoint:: +complete_pointers(vector_typedWriteable &plist, BamReader* manager) +{ + int i; + int start = MovingPartMatrix::complete_pointers(plist, manager); + int mid = start+_num_net_arcs; + int end = start+_num_net_arcs+_num_local_arcs; + + for(i = start; i < mid; i++) + { + if (plist[i] == TypedWriteable::Null) + { + char_cat->warning() << get_name() + << " Ignoring null Net NodeRelation" << endl; + } + else + { + add_net_transform(DCAST(NodeRelation, plist[i])); + } + } + + for(i = mid; i < end; i++) + { + if (plist[i] == TypedWriteable::Null) + { + char_cat->warning() << get_name() + << " Ignoring null Local NodeRelation" << endl; + } + else + { + add_local_transform(DCAST(NodeRelation, plist[i])); + } + } + + return end; +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::make_CharacterJoint +// Access: Protected +// Description: Factory method to generate a CharacterJoint object +//////////////////////////////////////////////////////////////////// +TypedWriteable* CharacterJoint:: +make_CharacterJoint(const FactoryParams ¶ms) +{ + CharacterJoint *me = new CharacterJoint; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJoint::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a CharacterJoint object +//////////////////////////////////////////////////////////////////// +void CharacterJoint:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_CharacterJoint); +} + + diff --git a/panda/src/char/characterJoint.h b/panda/src/char/characterJoint.h new file mode 100644 index 0000000000..b00921c88c --- /dev/null +++ b/panda/src/char/characterJoint.h @@ -0,0 +1,97 @@ +// Filename: characterJoint.h +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CHARACTERJOINT_H +#define CHARACTERJOINT_H + +#include + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : CharacterJoint +// Description : This represents one joint of the character's +// animation, containing an animating transform matrix. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CharacterJoint : public MovingPartMatrix { +protected: + CharacterJoint(); + CharacterJoint(const CharacterJoint ©); + +public: + CharacterJoint(PartGroup *parent, const string &name, + const LMatrix4f &initial_value); + + virtual PartGroup *make_copy() const; + + virtual void update_internals(PartGroup *parent, bool self_changed, + bool parent_changed); + + bool add_net_transform(NodeRelation *arc); + bool remove_net_transform(NodeRelation *arc); + bool has_net_transform(NodeRelation *arc) const; + void clear_net_transforms(); + + bool add_local_transform(NodeRelation *arc); + bool remove_local_transform(NodeRelation *arc); + bool has_local_transform(NodeRelation *arc) const; + void clear_local_transforms(); + +private: + typedef set ArcList; + ArcList _net_transform_arcs; + ArcList _local_transform_arcs; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + virtual int complete_pointers(vector_typedWriteable &plist, + BamReader *manager); + + static TypedWriteable *make_CharacterJoint(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +private: + int _num_net_arcs, _num_local_arcs; + +public: + // The _geom_node member just holds a temporary pointer to a node + // for the CharacterMaker's convenenience while creating the + // character. It does not store any meaningful value after + // creation is complete. + PT_NamedNode _geom_node; + + // These are filled in as the joint animates. + LMatrix4f _net_transform; + LMatrix4f _initial_net_transform_inverse; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MovingPartMatrix::init_type(); + register_type(_type_handle, "CharacterJoint", + MovingPartMatrix::get_class_type()); + } + +private: + static TypeHandle _type_handle; + + friend class Character; +}; + +#endif + + diff --git a/panda/src/char/characterJointBundle.I b/panda/src/char/characterJointBundle.I new file mode 100644 index 0000000000..2610c6b0e8 --- /dev/null +++ b/panda/src/char/characterJointBundle.I @@ -0,0 +1,29 @@ +// Filename: characterJointBundle.I +// Created by: drose (02Mar99) +// +//////////////////////////////////////////////////////////////////// + +#include "character.h" + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJointBundle::Copy Constructor +// Access: Public +// Description: Normally, you'd use make_copy() or copy_subgraph() to +// make a copy of this. +//////////////////////////////////////////////////////////////////// +INLINE CharacterJointBundle:: +CharacterJointBundle(const CharacterJointBundle ©) : + PartBundle(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJointBundle::get_node +// Access: Public +// Description: Returns the Character node associated with this +// PartBundle. +//////////////////////////////////////////////////////////////////// +INLINE Character *CharacterJointBundle:: +get_node() const { + return DCAST(Character, PartBundle::get_node()); +} diff --git a/panda/src/char/characterJointBundle.cxx b/panda/src/char/characterJointBundle.cxx new file mode 100644 index 0000000000..26843de1bc --- /dev/null +++ b/panda/src/char/characterJointBundle.cxx @@ -0,0 +1,65 @@ +// Filename: characterJointBundle.cxx +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "characterJointBundle.h" +#include "character.h" +#include +#include +#include +#include + +TypeHandle CharacterJointBundle::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJointBundle::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CharacterJointBundle:: +CharacterJointBundle(const string &name) : PartBundle(name) { +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJointBundle::make_copy +// Access: Public, Virtual +// Description: Allocates and returns a new copy of the node. +// Children are not copied, but see copy_subgraph(). +//////////////////////////////////////////////////////////////////// +PartGroup *CharacterJointBundle:: +make_copy() const { + return new CharacterJointBundle(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJointBundle::make_CharacterJointBundle +// Access: Protected +// Description: Factory method to generate a CharacterJointBundle object +//////////////////////////////////////////////////////////////////// +TypedWriteable* CharacterJointBundle:: +make_CharacterJointBundle(const FactoryParams ¶ms) +{ + CharacterJointBundle *me = new CharacterJointBundle; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + manager->register_finalize(me); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterJointBundle::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a CharacterJointBundle object +//////////////////////////////////////////////////////////////////// +void CharacterJointBundle:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_CharacterJointBundle); +} + diff --git a/panda/src/char/characterJointBundle.h b/panda/src/char/characterJointBundle.h new file mode 100644 index 0000000000..dd471ae307 --- /dev/null +++ b/panda/src/char/characterJointBundle.h @@ -0,0 +1,59 @@ +// Filename: characterJointBundle.h +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CHARACTERJOINTBUNDLE_H +#define CHARACTERJOINTBUNDLE_H + +#include + +#include +#include +#include + +class Character; + +//////////////////////////////////////////////////////////////////// +// Class : CharacterJointBundle +// Description : The collection of all the joints and sliders in the +// character. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CharacterJointBundle : public PartBundle { +protected: + INLINE CharacterJointBundle(const CharacterJointBundle ©); + +public: + CharacterJointBundle(const string &name = ""); + + INLINE Character *get_node() const; + + virtual PartGroup *make_copy() const; + + static void register_with_read_factory(void); + + static TypedWriteable *make_CharacterJointBundle(const FactoryParams ¶ms); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PartBundle::init_type(); + register_type(_type_handle, "CharacterJointBundle", + PartBundle::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "characterJointBundle.I" + +#endif + + diff --git a/panda/src/char/characterSlider.cxx b/panda/src/char/characterSlider.cxx new file mode 100644 index 0000000000..8f690e629a --- /dev/null +++ b/panda/src/char/characterSlider.cxx @@ -0,0 +1,87 @@ +// Filename: characterSlider.cxx +// Created by: drose (03Mar99) +// +//////////////////////////////////////////////////////////////////// + +#include "characterSlider.h" +#include +#include +#include +#include + +TypeHandle CharacterSlider::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CharacterSlider::Default Constructor +// Access: Protected +// Description: For internal use only. +//////////////////////////////////////////////////////////////////// +CharacterSlider:: +CharacterSlider() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterSlider::Copy Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +CharacterSlider:: +CharacterSlider(const CharacterSlider ©) : + MovingPartScalar(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterSlider::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CharacterSlider:: +CharacterSlider(PartGroup *parent, const string &name) + : MovingPartScalar(parent, name) { +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterSlider::make_copy +// Access: Public, Virtual +// Description: Allocates and returns a new copy of the node. +// Children are not copied, but see copy_subgraph(). +//////////////////////////////////////////////////////////////////// +PartGroup *CharacterSlider:: +make_copy() const { + return new CharacterSlider(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterSlider::make_CharacterSlider +// Access: Protected +// Description: Factory method to generate a CharacterSlider object +//////////////////////////////////////////////////////////////////// +TypedWriteable* CharacterSlider:: +make_CharacterSlider(const FactoryParams ¶ms) +{ + CharacterSlider *me = new CharacterSlider; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterSlider::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a CharacterSlider object +//////////////////////////////////////////////////////////////////// +void CharacterSlider:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_CharacterSlider); +} + + + + diff --git a/panda/src/char/characterSlider.h b/panda/src/char/characterSlider.h new file mode 100644 index 0000000000..f6eca5ecc5 --- /dev/null +++ b/panda/src/char/characterSlider.h @@ -0,0 +1,54 @@ +// Filename: characterSlider.h +// Created by: drose (03Mar99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CHARACTERSLIDER_H +#define CHARACTERSLIDER_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : CharacterSlider +// Description : This is a morph slider within the character. It's +// simply a single floating-point value that animates +// generally between 0 and 1, that controls the effects +// of one or more morphs within the character. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CharacterSlider : public MovingPartScalar { +protected: + CharacterSlider(); + CharacterSlider(const CharacterSlider ©); + +public: + CharacterSlider(PartGroup *parent, const string &name); + + virtual PartGroup *make_copy() const; + + static void register_with_read_factory(void); + + static TypedWriteable *make_CharacterSlider(const FactoryParams ¶ms); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MovingPartScalar::init_type(); + register_type(_type_handle, "CharacterSlider", + MovingPartScalar::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#endif + + diff --git a/panda/src/char/computedVertices.I b/panda/src/char/computedVertices.I new file mode 100644 index 0000000000..52eb995d7e --- /dev/null +++ b/panda/src/char/computedVertices.I @@ -0,0 +1,39 @@ +// Filename: computedVertices.I +// Created by: drose (01Mar99) +// +//////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVertices::VertexTransform::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ComputedVertices::VertexTransform:: +VertexTransform() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVertices::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ComputedVertices:: +ComputedVertices() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVertices::VertexTransform::Ordering operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ComputedVertices::VertexTransform:: +operator < (const ComputedVertices::VertexTransform &other) const { + if (_joint_index != other._joint_index) { + return _joint_index < other._joint_index; + } + return _effect < other._effect; +} + + diff --git a/panda/src/char/computedVertices.cxx b/panda/src/char/computedVertices.cxx new file mode 100644 index 0000000000..50709ab37c --- /dev/null +++ b/panda/src/char/computedVertices.cxx @@ -0,0 +1,430 @@ +// Filename: computedVertices.cxx +// Created by: drose (01Mar99) +// +//////////////////////////////////////////////////////////////////// + +#include "computedVertices.h" +#include "characterJoint.h" +#include "character.h" +#include "config_char.h" + +#include +#include +#include +#include +#include +#include + +TypeHandle ComputedVertices::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVertices::VertexTransform::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ComputedVertices::VertexTransform:: +VertexTransform(const VertexTransform ©) : + _joint_index(copy._joint_index), + _effect(copy._effect), + _vindex(copy._vindex), + _nindex(copy._nindex) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: compute_morphs +// Description: This is a utility function called by update(), below. +// It applies each of the supplied morphs to the +// vertices in the table, according to the values of the +// relevant slider. +//////////////////////////////////////////////////////////////////// +template +static void +compute_morphs(ValueType *table, const vector &morph_list, + Character *character) { + vector::const_iterator mli; + for (mli = morph_list.begin(); mli != morph_list.end(); ++mli) { + const MorphType &morph = (*mli); + const CharacterSlider *slider; + DCAST_INTO_V(slider, character->get_part(morph._slider_index)); + + float slider_value = slider->_value; + + if (slider_value != 0.0) { + typedef TYPENAME MorphType::Morphs Morphs; + typedef TYPENAME MorphType::MorphValue MorphValue; + TYPENAME Morphs::const_iterator mi; + for (mi = morph._morphs.begin(); mi != morph._morphs.end(); ++mi) { + typedef typename MorphValue::VecType VecType; + ushort index = (*mi)._index; + const VecType &v = (*mi)._vector; + + table[index] += v * slider_value; + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: VertexTransform::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void ComputedVertices::VertexTransform:: +write_datagram(Datagram &dest) +{ + int i; + dest.add_int16(_joint_index); + dest.add_float64(_effect); + dest.add_uint16(_vindex.size()); + for(i = 0; i < _vindex.size(); i++) + { + dest.add_uint16(_vindex[i]); + } + dest.add_uint16(_nindex.size()); + for(i = 0; i < _nindex.size(); i++) + { + dest.add_uint16(_nindex[i]); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: VertexTransform::read_datagram +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +void ComputedVertices::VertexTransform:: +read_datagram(DatagramIterator &source) +{ + int i; + _joint_index = source.get_int16(); + _effect = source.get_float64(); + int vsize = source.get_uint16(); + for(i = 0; i < vsize; i++) + { + _vindex.push_back(source.get_uint16()); + } + int nsize = source.get_uint16(); + for(i = 0; i < nsize; i++) + { + _nindex.push_back(source.get_uint16()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVertices::update +// Access: Public +// Description: Recomputes all of the _coords, _norms, etc. values +// based on the values in _orig_coords, _orig_norms, +// etc., and the current positions of all of the joints. +//////////////////////////////////////////////////////////////////// +void ComputedVertices:: +update(Character *character) { + nassertv(character != (Character *)NULL); + nassertv(character->_cv._coords.size() == _orig_coords.size()); + nassertv(character->_cv._norms.size() == _orig_norms.size()); + nassertv(character->_cv._texcoords.size() == _orig_texcoords.size()); + nassertv(character->_cv._colors.size() == _orig_colors.size()); + + const Vertexf *orig_coords = _orig_coords; + const Normalf *orig_norms = _orig_norms; + + memset(character->_cv._coords, 0, sizeof(Vertexf) * character->_cv._coords.size()); + memset(character->_cv._norms, 0, sizeof(Normalf) * character->_cv._norms.size()); + + if (!_vertex_morphs.empty()) { + // We have some vertex morphs. Compute them first. + int table_size = sizeof(Vertexf) * _orig_coords.size(); + Vertexf *morphed_coords = (Vertexf *)alloca(table_size); + memcpy(morphed_coords, _orig_coords, table_size); + + compute_morphs(morphed_coords, _vertex_morphs, character); + orig_coords = morphed_coords; + } + + if (!_normal_morphs.empty()) { + // We also have some normal morphs. Compute them too. + int table_size = sizeof(Normalf) * _orig_norms.size(); + Normalf *morphed_norms = (Normalf *)alloca(table_size); + memcpy(morphed_norms, _orig_norms, table_size); + + compute_morphs(morphed_norms, _normal_morphs, character); + orig_norms = morphed_norms; + } + + if (!_texcoord_morphs.empty()) { + // We have some uv morphs. These don't particularly need to be + // done before the joints are computed, but do them now anyway. + int table_size = sizeof(TexCoordf) * _orig_texcoords.size(); + TexCoordf *morphed_texcoords = (TexCoordf *)alloca(table_size); + memcpy(character->_cv._texcoords, _orig_texcoords, table_size); + + compute_morphs(character->_cv._texcoords.p(), _texcoord_morphs, character); + } + + if (!_color_morphs.empty()) { + // We have some color morphs. Do these now too. + int table_size = sizeof(Colorf) * _orig_colors.size(); + Colorf *morphed_colors = (Colorf *)alloca(table_size); + memcpy(character->_cv._colors, _orig_colors, table_size); + + compute_morphs(character->_cv._colors.p(), _color_morphs, character); + } + + // Now that we've computed all the morphs, it's safe to transform + // vertices into their proper coordinate space, according to the + // current positions of all the joints. + + LMatrix4f mat = LMatrix4f::ident_mat(); + int last_joint_index = -1; + + VertexTransforms::const_iterator vti; + for (vti = _transforms.begin(); vti != _transforms.end(); ++vti) { + const VertexTransform &vt = (*vti); + + // We cache the matrix from the last joint, because we are likely + // to encounter the same joint several times in a row. + if (vt._joint_index != last_joint_index) { + last_joint_index = vt._joint_index; + + // We won't encounter -1 after the first few joints. + nassertv(vt._joint_index >= 0); + CharacterJoint *joint; + DCAST_INTO_V(joint, character->get_part(vt._joint_index)); + + mat = + joint->_initial_net_transform_inverse * + joint->_net_transform; + } + + Vertices::const_iterator vi; + for (vi = vt._vindex.begin(); vi != vt._vindex.end(); ++vi) { + int i = (*vi); + character->_cv._coords[i] += (orig_coords[i] * mat) * vt._effect; + } + for (vi = vt._nindex.begin(); vi != vt._nindex.end(); ++vi) { + int i = (*vi); + character->_cv._norms[i] += (orig_norms[i] * mat) * vt._effect; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVertices::make_orig +// Access: Public +// Description: Copies all the values loaded in the _coords, _norms, +// etc. arrays into the corresponding _orig_coords, +// etc. arrays. +//////////////////////////////////////////////////////////////////// +void ComputedVertices:: +make_orig(Character *character) { + nassertv(character != (Character *)NULL); + + if (character->_cv._coords.empty()) { + _orig_coords.clear(); + } else { + _orig_coords = PTA_Vertexf(0); + _orig_coords.v() = character->_cv._coords.v(); + } + + if (character->_cv._norms.empty()) { + _orig_norms.clear(); + } else { + _orig_norms = PTA_Normalf(0); + _orig_norms.v() = character->_cv._norms.v(); + } + + if (character->_cv._colors.empty()) { + _orig_colors.clear(); + } else { + _orig_colors = PTA_Colorf(0); + _orig_colors.v() = character->_cv._colors.v(); + } + + if (character->_cv._texcoords.empty()) { + _orig_texcoords.clear(); + } else { + _orig_texcoords = PTA_TexCoordf(0); + _orig_texcoords.v() = character->_cv._texcoords.v(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVertices::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ComputedVertices:: +write(ostream &out, Character *character) const { + VertexTransforms::const_iterator vti; + + out << "ComputedVertices:\n"; + for (vti = _transforms.begin(); vti != _transforms.end(); ++vti) { + const VertexTransform &vt = (*vti); + string name; + if (vt._joint_index >= 0) { + name = character->get_part(vt._joint_index)->get_name(); + } else { + name = "(root)"; + } + + out << " " << name << " * " << vt._effect << " : " + << vt._vindex.size() << " vertices and " + << vt._nindex.size() << " normals\n"; + } + + VertexMorphs::const_iterator vmi; + for (vmi = _vertex_morphs.begin(); vmi != _vertex_morphs.end(); ++vmi) { + out << " vertex " << (*vmi) << "\n"; + } + + NormalMorphs::const_iterator nmi; + for (nmi = _normal_morphs.begin(); nmi != _normal_morphs.end(); ++nmi) { + out << " normal " << (*nmi) << "\n"; + } + + TexCoordMorphs::const_iterator tmi; + for (tmi = _texcoord_morphs.begin(); tmi != _texcoord_morphs.end(); ++tmi) { + out << " uv " << (*tmi) << "\n"; + } + + ColorMorphs::const_iterator cmi; + for (cmi = _color_morphs.begin(); cmi != _color_morphs.end(); ++cmi) { + out << " color " << (*cmi) << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVertices::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void ComputedVertices:: +write_datagram(BamWriter *manager, Datagram &me) +{ + int i; + + me.add_uint16(_transforms.size()); + for(i = 0; i < _transforms.size(); i++){ + _transforms[i].write_datagram(me); + } + + me.add_uint16(_vertex_morphs.size()); + for(i = 0; i < _vertex_morphs.size(); i++){ + _vertex_morphs[i].write_datagram(me); + } + + me.add_uint16(_normal_morphs.size()); + for(i = 0; i < _normal_morphs.size(); i++){ + _normal_morphs[i].write_datagram(me); + } + + me.add_uint16(_texcoord_morphs.size()); + for(i = 0; i < _texcoord_morphs.size(); i++){ + _texcoord_morphs[i].write_datagram(me); + } + + me.add_uint16(_color_morphs.size()); + for(i = 0; i < _color_morphs.size(); i++){ + _color_morphs[i].write_datagram(me); + } + + //Original Coordinates for vertices, colors, normals and textures + WRITE_PTA(manager, me, IPD_Vertexf::write_datagram, _orig_coords) + WRITE_PTA(manager, me, IPD_Normalf::write_datagram, _orig_norms) + WRITE_PTA(manager, me, IPD_Colorf::write_datagram, _orig_colors) + WRITE_PTA(manager, me, IPD_TexCoordf::write_datagram, _orig_texcoords) +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVertices::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void ComputedVertices:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + int i, size; + + size = scan.get_uint16(); + for(i = 0; i < size; i++){ + VertexTransform vt; + vt.read_datagram(scan); + _transforms.push_back(vt); + } + + size = scan.get_uint16(); + for(i = 0; i < size; i++){ + ComputedVerticesMorphVertex vm; + vm.read_datagram(scan); + _vertex_morphs.push_back(vm); + } + + size = scan.get_uint16(); + for(i = 0; i < size; i++){ + ComputedVerticesMorphNormal nm; + nm.read_datagram(scan); + _normal_morphs.push_back(nm); + } + + size = scan.get_uint16(); + for(i = 0; i < size; i++){ + ComputedVerticesMorphTexCoord tm; + tm.read_datagram(scan); + _texcoord_morphs.push_back(tm); + } + + size = scan.get_uint16(); + for(i = 0; i < size; i++){ + ComputedVerticesMorphColor cm; + cm.read_datagram(scan); + _color_morphs.push_back(cm); + } + + //Original Coordinates for vertices, colors, normals and textures + READ_PTA(manager, scan, IPD_Vertexf::read_datagram, _orig_coords) + READ_PTA(manager, scan, IPD_Normalf::read_datagram, _orig_norms) + READ_PTA(manager, scan, IPD_Colorf::read_datagram, _orig_colors) + READ_PTA(manager, scan, IPD_TexCoordf::read_datagram, _orig_texcoords) +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVertices::make_ComputedVertices +// Access: Protected +// Description: Factory method to generate a ComputedVertices object +//////////////////////////////////////////////////////////////////// +TypedWriteable* ComputedVertices:: +make_ComputedVertices(const FactoryParams ¶ms) +{ + ComputedVertices *me = new ComputedVertices; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVertices::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a ComputedVertices object +//////////////////////////////////////////////////////////////////// +void ComputedVertices:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_ComputedVertices); +} + + + + + diff --git a/panda/src/char/computedVertices.h b/panda/src/char/computedVertices.h new file mode 100644 index 0000000000..af94ac5250 --- /dev/null +++ b/panda/src/char/computedVertices.h @@ -0,0 +1,102 @@ +// Filename: computedVertices.h +// Created by: drose (01Mar99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COMPUTEDVERTICES_H +#define COMPUTEDVERTICES_H + +#include + +#include "dynamicVertices.h" +#include "computedVerticesMorph.h" + +#include +#include + +class Character; +class CharacterJoint; + +//////////////////////////////////////////////////////////////////// +// Class : ComputedVertices +// Description : These are the vertices that must be animated as part +// of the normal character animation. This includes +// vertices animated into one or more joints, as well as +// morph vertices. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ComputedVertices : public TypedWriteableReferenceCount { +public: + INLINE ComputedVertices(); + + void update(Character *character); + void make_orig(Character *character); + + void write(ostream &out, Character *character) const; + +private: + typedef vector_ushort Vertices; + + class EXPCL_PANDA VertexTransform { + public: + INLINE VertexTransform(); + VertexTransform(const VertexTransform ©); + + short _joint_index; + float _effect; + Vertices _vindex; + Vertices _nindex; + INLINE bool operator < (const VertexTransform &other) const; + public: + void write_datagram(Datagram &dest); + void read_datagram(DatagramIterator &source); + }; + + typedef vector VertexTransforms; + + VertexTransforms _transforms; + + typedef vector VertexMorphs; + typedef vector NormalMorphs; + typedef vector TexCoordMorphs; + typedef vector ColorMorphs; + VertexMorphs _vertex_morphs; + NormalMorphs _normal_morphs; + TexCoordMorphs _texcoord_morphs; + ColorMorphs _color_morphs; + + PTA_Vertexf _orig_coords; + PTA_Normalf _orig_norms; + PTA_Colorf _orig_colors; + PTA_TexCoordf _orig_texcoords; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + + static TypedWriteable *make_ComputedVertices(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedWriteableReferenceCount::init_type(); + register_type(_type_handle, "ComputedVertices", + TypedWriteableReferenceCount::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class ComputedVerticesMaker; +}; + +#include "computedVertices.I" + +#endif diff --git a/panda/src/char/computedVerticesMorph.I b/panda/src/char/computedVerticesMorph.I new file mode 100644 index 0000000000..ef809fde70 --- /dev/null +++ b/panda/src/char/computedVerticesMorph.I @@ -0,0 +1,137 @@ +// Filename: computedVerticesMorph.I +// Created by: drose (03Mar99) +// +//////////////////////////////////////////////////////////////////// + +#include "characterSlider.h" + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMorphValue2::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ComputedVerticesMorphValue2:: +ComputedVerticesMorphValue2(int index, const VecType &mvector) + : _index(index), _vector(mvector) { +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMorphValue2::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ComputedVerticesMorphValue2:: +ComputedVerticesMorphValue2(void){ +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMorphValue3::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ComputedVerticesMorphValue3:: +ComputedVerticesMorphValue3(int index, const VecType &mvector) + : _index(index), _vector(mvector) { +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMorphValue3::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ComputedVerticesMorphValue3:: +ComputedVerticesMorphValue3(void){ +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMorphValue4::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ComputedVerticesMorphValue4:: +ComputedVerticesMorphValue4(int index, const VecType &mvector) + : _index(index), _vector(mvector) { +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMorphValue4::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ComputedVerticesMorphValue4:: +ComputedVerticesMorphValue4(void){ +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMorph::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +ComputedVerticesMorph:: +ComputedVerticesMorph() { + _slider_index = -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMorph::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +ComputedVerticesMorph:: +ComputedVerticesMorph(const ComputedVerticesMorph ©) : + _slider_index(copy._slider_index), + _morphs(copy._morphs) +{ +} + + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMorph::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void ComputedVerticesMorph:: +output(ostream &out) const { + out << "morph index " << _slider_index << ": " + << _morphs.size() << " points."; +} + + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMorph::write_datagram +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void ComputedVerticesMorph:: +write_datagram(Datagram &dest) +{ + dest.add_int16(_slider_index); + dest.add_uint16(_morphs.size()); + for(int i = 0; i < _morphs.size(); i++) + { + _morphs[i].write_datagram(dest); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMorph::read_datagram +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +template +void ComputedVerticesMorph:: +read_datagram(DatagramIterator& source) +{ + _slider_index = source.get_int16(); + int size = source.get_uint16(); + for(int i = 0; i < size; i++) + { + MorphValue mv; + mv.read_datagram(source); + _morphs.push_back(mv); + } +} diff --git a/panda/src/char/computedVerticesMorph.cxx b/panda/src/char/computedVerticesMorph.cxx new file mode 100644 index 0000000000..fb114da2e1 --- /dev/null +++ b/panda/src/char/computedVerticesMorph.cxx @@ -0,0 +1,93 @@ +// Filename: computedVerticesMorph.cxx +// Created by: jason (23Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "computedVerticesMorph.h" + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMorphValue2::write_datagram +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ComputedVerticesMorphValue2:: +write_datagram(Datagram &dest) +{ + dest.add_uint16(_index); + _vector.write_datagram(dest); +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMorphValue2::read_datagram +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ComputedVerticesMorphValue2:: +read_datagram(DatagramIterator &source) +{ + _index = source.get_uint16(); + _vector.read_datagram(source); +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMorphValue3::write_datagram +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ComputedVerticesMorphValue3:: +write_datagram(Datagram &dest) +{ + dest.add_uint16(_index); + _vector.write_datagram(dest); +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMorphValue3::read_datagram +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ComputedVerticesMorphValue3:: +read_datagram(DatagramIterator &source) +{ + _index = source.get_uint16(); + _vector.read_datagram(source); +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMorphValue4::write_datagram +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ComputedVerticesMorphValue4:: +write_datagram(Datagram &dest) +{ + dest.add_uint16(_index); + _vector.write_datagram(dest); +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMorphValue4::read_datagram +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ComputedVerticesMorphValue4:: +read_datagram(DatagramIterator &source) +{ + _index = source.get_uint16(); + _vector.read_datagram(source); +} + + + + + + + + + + diff --git a/panda/src/char/computedVerticesMorph.h b/panda/src/char/computedVerticesMorph.h new file mode 100644 index 0000000000..e814fff973 --- /dev/null +++ b/panda/src/char/computedVerticesMorph.h @@ -0,0 +1,127 @@ +// Filename: computedVerticesMorph.h +// Created by: drose (03Mar99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COMPUTEDVERTICESMORPH_H +#define COMPUTEDVERTICESMORPH_H + +#include +#include + +#include + +class CharacterSlider; +class BamReader; +class BamWriter; +class Datagram; +class DatagramIterator; +class TypedWriteable; + +//////////////////////////////////////////////////////////////////// +// Class : ComputedVerticesMorphValue2 +// Description : This is a single vertex morph offset value. This is +// the amount by which the given vertex will be offset +// when the morph is full on. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ComputedVerticesMorphValue2 { +public: + typedef LVector2f VecType; + + INLINE ComputedVerticesMorphValue2(int index, const VecType &mvector); + INLINE ComputedVerticesMorphValue2(void); + + ushort _index; + VecType _vector; + +public: + void write_datagram(Datagram &dest); + void read_datagram(DatagramIterator &source); +}; + +//////////////////////////////////////////////////////////////////// +// Class : ComputedVerticesMorphValue3 +// Description : This is a single vertex morph offset value. This is +// the amount by which the given vertex will be offset +// when the morph is full on. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ComputedVerticesMorphValue3 { +public: + typedef LVector3f VecType; + + INLINE ComputedVerticesMorphValue3(int index, const VecType &mvector); + INLINE ComputedVerticesMorphValue3(void); + + ushort _index; + VecType _vector; + +public: + void write_datagram(Datagram &dest); + void read_datagram(DatagramIterator &source); +}; + +//////////////////////////////////////////////////////////////////// +// Class : ComputedVerticesMorphValue4 +// Description : This is a single vertex morph offset value. This is +// the amount by which the given vertex will be offset +// when the morph is full on. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ComputedVerticesMorphValue4 { +public: + typedef LVector4f VecType; + + INLINE ComputedVerticesMorphValue4(int index, const VecType &mvector); + INLINE ComputedVerticesMorphValue4(void); + + ushort _index; + VecType _vector; + +public: + void write_datagram(Datagram &dest); + void read_datagram(DatagramIterator &source); +}; + +//////////////////////////////////////////////////////////////////// +// Class : ComputedVerticesMorph +// Description : A list of MorphValues, this represents the complete +// effect of a given morph target on a particular class +// of vertex values. +//////////////////////////////////////////////////////////////////// +template +class EXPCL_PANDA ComputedVerticesMorph { +public: + INLINE ComputedVerticesMorph(); + INLINE ComputedVerticesMorph(const ComputedVerticesMorph ©); + + typedef MorphValueType MorphValue; + typedef vector Morphs; + + void output(ostream &out) const; + + short _slider_index; + Morphs _morphs; + +public: + void write_datagram(Datagram &dest); + void read_datagram(DatagramIterator &source); +}; + +template +inline ostream &operator << (ostream &out, const ComputedVerticesMorph &morph) { + morph.output(out); + return out; +} + +typedef ComputedVerticesMorph ComputedVerticesMorphVertex; +typedef ComputedVerticesMorph ComputedVerticesMorphNormal; +typedef ComputedVerticesMorph ComputedVerticesMorphTexCoord; +typedef ComputedVerticesMorph ComputedVerticesMorphColor; + +#include "computedVerticesMorph.I" + +#endif + + + + + diff --git a/panda/src/char/config_char.cxx b/panda/src/char/config_char.cxx new file mode 100644 index 0000000000..3e4850b758 --- /dev/null +++ b/panda/src/char/config_char.cxx @@ -0,0 +1,42 @@ +// Filename: config_char.cxx +// Created by: drose (28Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_char.h" +#include "character.h" +#include "characterJoint.h" +#include "characterJointBundle.h" +#include "characterSlider.h" +#include "computedVertices.h" +#include "dynamicVertices.h" + +#include +#include + +Configure(config_char); +NotifyCategoryDef(char, ""); + +ConfigureFn(config_char) { + Character::init_type(); + CharacterJoint::init_type(); + CharacterJointBundle::init_type(); + CharacterSlider::init_type(); + ComputedVertices::init_type(); + DynamicVertices::init_type(); + + // This isn't defined in this package, but it *is* essential that it + // be initialized. We have to do it explicitly here since template + // statics don't necessarily resolve very well across dynamic + // libraries. + LMatrix4f::init_type(); + + //Registration of writeable object's creation + //functions with BamReader's factory + Character::register_with_read_factory(); + CharacterJoint::register_with_read_factory(); + CharacterJointBundle::register_with_read_factory(); + CharacterSlider::register_with_read_factory(); + ComputedVertices::register_with_read_factory(); +} + diff --git a/panda/src/char/config_char.h b/panda/src/char/config_char.h new file mode 100644 index 0000000000..b347943fd4 --- /dev/null +++ b/panda/src/char/config_char.h @@ -0,0 +1,17 @@ +// Filename: config_char.h +// Created by: drose (28Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_CHAR_H +#define CONFIG_CHAR_H + +#include +#include + +// CPPParser can't handle token-pasting to a keyword. +#ifndef CPPPARSER +NotifyCategoryDecl(char, EXPCL_PANDA, EXPTP_PANDA); +#endif + +#endif diff --git a/panda/src/char/dynamicVertices.cxx b/panda/src/char/dynamicVertices.cxx new file mode 100644 index 0000000000..bef7903426 --- /dev/null +++ b/panda/src/char/dynamicVertices.cxx @@ -0,0 +1,101 @@ +// Filename: dynamicVertices.cxx +// Created by: drose (01Mar99) +// +//////////////////////////////////////////////////////////////////// + +#include "dynamicVertices.h" +#include "config_char.h" +#include +#include +#include +#include +#include + +TypeHandle DynamicVertices::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: DynamicVertices::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DynamicVertices:: +DynamicVertices() { +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicVertices::Copy Constructor +// Access: Public +// Description: Makes a copy of the DynamicVertices object by +// reference: the new copy shares the same pointers as +// the original. +//////////////////////////////////////////////////////////////////// +DynamicVertices:: +DynamicVertices(const DynamicVertices ©) : + _coords(copy._coords), + _norms(copy._norms), + _colors(copy._colors), + _texcoords(copy._texcoords) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicVertices::named deep_copy Constructor +// Access: Public +// Description: Makes a complete copy of the indicated +// DynamicVertices object, including copying all of its +// arrays. +//////////////////////////////////////////////////////////////////// +DynamicVertices DynamicVertices:: +deep_copy(const DynamicVertices ©) { + DynamicVertices dv; + if (!copy._coords.empty()) { + dv._coords = PTA_Vertexf(0); + dv._coords.v() = copy._coords.v(); + } + if (!copy._norms.empty()) { + dv._norms = PTA_Normalf(0); + dv._norms.v() = copy._norms.v(); + } + if (!copy._colors.empty()) { + dv._colors = PTA_Colorf(0); + dv._colors.v() = copy._colors.v(); + } + if (!copy._texcoords.empty()) { + dv._texcoords = PTA_TexCoordf(0); + dv._texcoords.v() = copy._texcoords.v(); + } + return dv; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicVertices::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void DynamicVertices:: +write_datagram(BamWriter *manager, Datagram &me) +{ + WRITE_PTA(manager, me, IPD_Vertexf::write_datagram, _coords) + WRITE_PTA(manager, me, IPD_Normalf::write_datagram, _norms) + WRITE_PTA(manager, me, IPD_Colorf::write_datagram, _colors) + WRITE_PTA(manager, me, IPD_TexCoordf::write_datagram, _texcoords) +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicVertices::fillin +// Access: Public +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void DynamicVertices:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + READ_PTA(manager, scan, IPD_Vertexf::read_datagram, _coords) + READ_PTA(manager, scan, IPD_Normalf::read_datagram, _norms) + READ_PTA(manager, scan, IPD_Colorf::read_datagram, _colors) + READ_PTA(manager, scan, IPD_TexCoordf::read_datagram, _texcoords) +} diff --git a/panda/src/char/dynamicVertices.h b/panda/src/char/dynamicVertices.h new file mode 100644 index 0000000000..5f434d01fd --- /dev/null +++ b/panda/src/char/dynamicVertices.h @@ -0,0 +1,60 @@ +// Filename: dynamicVertices.h +// Created by: drose (01Mar99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DYNAMICVERTICES_H +#define DYNAMICVERTICES_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +class BamReader; + +//////////////////////////////////////////////////////////////////// +// Class : DynamicVertices +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DynamicVertices : public TypedWriteable { +public: + DynamicVertices(); + DynamicVertices(const DynamicVertices ©); + static DynamicVertices deep_copy(const DynamicVertices ©); + + PTA_Vertexf _coords; + PTA_Normalf _norms; + PTA_Colorf _colors; + PTA_TexCoordf _texcoords; + +public: + virtual void write_datagram(BamWriter* manager, Datagram &me); + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedWriteable::init_type(); + register_type(_type_handle, "DynamicVertices", + TypedWriteable::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#endif + diff --git a/panda/src/chat/Sources.pp b/panda/src/chat/Sources.pp new file mode 100644 index 0000000000..3ac5bdd606 --- /dev/null +++ b/panda/src/chat/Sources.pp @@ -0,0 +1,18 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET chat + #define LOCAL_LIBS \ + putil dgraph display text event graph + + #define SOURCES \ + chatHelpers.cxx chatHelpers.h chatInput.I chatInput.cxx chatInput.h \ + config_chat.cxx config_chat.h + + #define INSTALL_HEADERS \ + chatHelpers.h chatInput.I chatInput.h + + #define IGATESCAN all + +#end lib_target + diff --git a/panda/src/chat/chatHelpers.cxx b/panda/src/chat/chatHelpers.cxx new file mode 100644 index 0000000000..c9914a5ab8 --- /dev/null +++ b/panda/src/chat/chatHelpers.cxx @@ -0,0 +1,45 @@ +// Filename: chatHelpers.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "chatHelpers.h" + +//////////////////////////////////////////////////////////////////// +// Function: add_line_wraps +// Description: Take an input string and format it so that lines +// will wrap before they reach line_length (unless +// there is no whitespace). +//////////////////////////////////////////////////////////////////// +string add_line_wraps(const string& str, int line_length) { + string new_str; + string old_str = str; + + if (str.length() <= line_length) + return old_str; + + int length_so_far = 0; + string::size_type ws; + + do { + ws = old_str.find(' '); + string sub_str; + if (ws != string::npos) { + sub_str = old_str.substr(0, ws+1); + old_str = old_str.substr(ws+1, string::npos); + } else + sub_str = old_str; + int sub_str_len = sub_str.length(); + if (length_so_far + sub_str_len > line_length) { + new_str += '\n'; + length_so_far = 0; + } + new_str += sub_str; + length_so_far += sub_str_len; + } while (ws != string::npos); + + return new_str; +} diff --git a/panda/src/chat/chatHelpers.h b/panda/src/chat/chatHelpers.h new file mode 100644 index 0000000000..77c6e27f87 --- /dev/null +++ b/panda/src/chat/chatHelpers.h @@ -0,0 +1,16 @@ +// Filename: chatHelpers.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef CHATHELPERS_H +#define CHATHELPERS_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +string add_line_wraps(const string& str, int line_length); + +#endif diff --git a/panda/src/chat/chatInput.I b/panda/src/chat/chatInput.I new file mode 100644 index 0000000000..e6f50603c4 --- /dev/null +++ b/panda/src/chat/chatInput.I @@ -0,0 +1,64 @@ +// Filename: chatInput.I +// Created by: drose (04Jul00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: ChatInput::set_max_chars +// Access: Public +// Description: Sets a limit on the number of characters the user is +// allowed to type. When this limit is exceeded, no +// more characters will be accepted, and the event +// "chat_overflow" is thrown. +//////////////////////////////////////////////////////////////////// +INLINE void ChatInput:: +set_max_chars(int max_chars) { + _max_chars = max_chars; + _has_max_chars = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ChatInput::clear_max_chars +// Access: Public +// Description: Removes the limit on the maximum number of +// characters. +//////////////////////////////////////////////////////////////////// +INLINE void ChatInput:: +clear_max_chars() { + _has_max_chars = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: ChatInput::has_max_chars +// Access: Public +// Description: Returns true if the maximum number of characters has +// been set by a call to set_max_chars(). +//////////////////////////////////////////////////////////////////// +INLINE bool ChatInput:: +has_max_chars() const { + return _has_max_chars; +} + +//////////////////////////////////////////////////////////////////// +// Function: ChatInput::get_max_chars +// Access: Public +// Description: If has_max_chars() returns true, this will return the +// maximum number of characters that was set. +//////////////////////////////////////////////////////////////////// +INLINE int ChatInput:: +get_max_chars() const { + nassertr(has_max_chars(), 0); + return _max_chars; +} + +//////////////////////////////////////////////////////////////////// +// Function: ChatInput::get_string +// Access: Public +// Description: Returns the current string the user has entered so +// far. +//////////////////////////////////////////////////////////////////// +INLINE const string &ChatInput:: +get_string() const { + return _str; +} diff --git a/panda/src/chat/chatInput.cxx b/panda/src/chat/chatInput.cxx new file mode 100644 index 0000000000..ee14c7572d --- /dev/null +++ b/panda/src/chat/chatInput.cxx @@ -0,0 +1,116 @@ +// Filename: chatInput.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "chatInput.h" + +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle ChatInput::_type_handle; + +TypeHandle ChatInput::_button_events_type; + +//////////////////////////////////////////////////////////////////// +// Function: ChatInput::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ChatInput::ChatInput(TextNode* text_node, + const string& name) : DataNode(name) { + assert(text_node != NULL); + _text_node = text_node; + _max_chars = 0; + _has_max_chars = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: ChatInput::reset +// Access: Public +// Description: Empties the string and prepares to accept new input; +// does not reset the max_chars setting. +//////////////////////////////////////////////////////////////////// +void ChatInput:: +reset() { + _str = ""; + _text_node->set_text(_str); +} + +//////////////////////////////////////////////////////////////////// +// Function: ChatInput::transmit_data +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ChatInput::transmit_data(NodeAttributes &data) { + bool changed = false; + + // Look for keyboard events. + const ButtonEventDataAttribute *b; + if (get_attribute_into(b, data, _button_events_type)) { + ButtonEventDataAttribute::const_iterator bi; + for (bi = b->begin(); bi != b->end(); ++bi) { + const ButtonEvent &be = (*bi); + + if (be._down) { + if (be._button == KeyboardButton::enter()) { + throw_event("chat_exit"); + + } else if (be._button == KeyboardButton::backspace()) { + _str = _str.substr(0, _str.length()-1); + changed = true; + + } else if (be._button.has_ascii_equivalent()) { + char ch = be._button.get_ascii_equivalent(); + + if (isprint(ch)) { + if (has_max_chars() && _str.size() >= get_max_chars()) { + throw_event("chat_overflow"); + } else { + _str += ch; + changed = true; + } + } + } + } + } + } + + if (changed) { + _text_node->set_text(_str); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: init_type +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void ChatInput::init_type(void) { + DataNode::init_type(); + register_type(_type_handle, "ChatInput", + DataNode::get_class_type()); + + ButtonEventDataTransition::init_type(); + register_data_transition(_button_events_type, "ButtonEvents", + ButtonEventDataTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: append +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void ChatInput::append(const string &str) { + _str += str; + _text_node->set_text(_str); +} diff --git a/panda/src/chat/chatInput.h b/panda/src/chat/chatInput.h new file mode 100644 index 0000000000..ca1f625140 --- /dev/null +++ b/panda/src/chat/chatInput.h @@ -0,0 +1,76 @@ +// Filename: chatInput.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef CHATINPUT_H +#define CHATINPUT_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : ChatInput +// Description : Reads keyboard input in as that of a chat. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ChatInput : public DataNode { + +public: + ChatInput(TextNode* text_node, const string& name = ""); + void reset(void); + + INLINE void set_max_chars(int max_chars); + INLINE void clear_max_chars(); + INLINE bool has_max_chars() const; + INLINE int get_max_chars() const; + + INLINE const string &get_string() const; + +//////////////////////////////////////////////////////////////////// +// From parent class DataNode +//////////////////////////////////////////////////////////////////// +public: + virtual void + transmit_data(NodeAttributes &data); + + void append(const string &str); + + NodeAttributes _attrib; + + // inputs + static TypeHandle _button_events_type; + +protected: + PT(TextNode) _text_node; + string _str; + int _max_chars; + bool _has_max_chars; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +#include "chatInput.I" + +#endif diff --git a/panda/src/chat/config_chat.cxx b/panda/src/chat/config_chat.cxx new file mode 100644 index 0000000000..e35b6e7b6c --- /dev/null +++ b/panda/src/chat/config_chat.cxx @@ -0,0 +1,16 @@ +// Filename: config_chat.cxx +// Created by: drose (04May00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_chat.h" +#include "chatInput.h" + +#include + +Configure(config_chat); +NotifyCategoryDef(chat, ""); + +ConfigureFn(config_chat) { + ChatInput::init_type(); +} diff --git a/panda/src/chat/config_chat.h b/panda/src/chat/config_chat.h new file mode 100644 index 0000000000..4e7263f621 --- /dev/null +++ b/panda/src/chat/config_chat.h @@ -0,0 +1,14 @@ +// Filename: config_chat.h +// Created by: drose (04May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_CHAT_H +#define CONFIG_CHAT_H + +#include +#include + +NotifyCategoryDecl(chat, EXPCL_PANDA, EXPTP_PANDA); + +#endif diff --git a/panda/src/collide/Sources.pp b/panda/src/collide/Sources.pp new file mode 100644 index 0000000000..340f7c3556 --- /dev/null +++ b/panda/src/collide/Sources.pp @@ -0,0 +1,52 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET collide + #define LOCAL_LIBS \ + light tform sgraphutil gobj graph putil + + #define SOURCES \ + collisionEntry.I collisionEntry.cxx collisionEntry.h \ + collisionHandler.cxx collisionHandler.h collisionHandlerEvent.I \ + collisionHandlerEvent.cxx collisionHandlerEvent.h \ + collisionHandlerFloor.I collisionHandlerFloor.cxx \ + collisionHandlerFloor.h collisionHandlerPhysical.I \ + collisionHandlerPhysical.cxx collisionHandlerPhysical.h \ + collisionHandlerPusher.I collisionHandlerPusher.cxx \ + collisionHandlerPusher.h collisionHandlerQueue.cxx \ + collisionHandlerQueue.h collisionLevelState.I collisionLevelState.N \ + collisionLevelState.cxx collisionLevelState.h collisionNode.I \ + collisionNode.cxx collisionNode.h collisionPlane.I \ + collisionPlane.cxx collisionPlane.h collisionPolygon.I \ + collisionPolygon.cxx collisionPolygon.h collisionRay.I \ + collisionRay.cxx collisionRay.h collisionSolid.I collisionSolid.cxx \ + collisionSolid.h collisionSphere.I collisionSphere.cxx \ + collisionSphere.h collisionTraverser.I collisionTraverser.cxx \ + collisionTraverser.h config_collide.cxx config_collide.h + + #define INSTALL_HEADERS \ + collideMask.h collisionEntry.I collisionEntry.h collisionHandler.h \ + collisionHandlerEvent.I collisionHandlerEvent.h \ + collisionHandlerFloor.I collisionHandlerFloor.h \ + collisionHandlerPhysical.I collisionHandlerPhysical.h \ + collisionHandlerPusher.I collisionHandlerPusher.h \ + collisionHandlerQueue.h collisionLevelState.I collisionLevelState.h \ + collisionNode.I collisionNode.h collisionPlane.I collisionPlane.h \ + collisionPolygon.I collisionPolygon.h collisionRay.I collisionRay.h \ + collisionSolid.I collisionSolid.h collisionSphere.I \ + collisionSphere.h collisionTraverser.I collisionTraverser.h + + #define IGATESCAN all + +#end lib_target + +#begin test_bin_target + #define TARGET test_collide + #define LOCAL_LIBS \ + collide + + #define SOURCES \ + test_collide.cxx + +#end test_bin_target + diff --git a/panda/src/collide/collideMask.h b/panda/src/collide/collideMask.h new file mode 100644 index 0000000000..eb546482dd --- /dev/null +++ b/panda/src/collide/collideMask.h @@ -0,0 +1,20 @@ +// Filename: collideMask.h +// Created by: drose (03Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLIDEMASK_H +#define COLLIDEMASK_H + +#include + +#include + +// This is the data type of the collision mask: the set of bits that +// every CollisionNode has, and that any two nodes must have some in +// common in order to be tested for a mutual intersection. + +typedef BitMask32 CollideMask; + +#endif + diff --git a/panda/src/collide/collisionEntry.I b/panda/src/collide/collisionEntry.I new file mode 100644 index 0000000000..ff0fddf38d --- /dev/null +++ b/panda/src/collide/collisionEntry.I @@ -0,0 +1,222 @@ +// Filename: collisionEntry.I +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionEntry:: +CollisionEntry() { + _flags = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::get_from +// Access: Public +// Description: Returns the CollisionSolid pointer for the particular +// solid that triggered this collision. +//////////////////////////////////////////////////////////////////// +INLINE const CollisionSolid *CollisionEntry:: +get_from() const { + return _from; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::has_into +// Access: Public +// Description: Returns true if the "into" solid is, in fact, a +// CollisionSolid, and its pointer is known (in which +// case get_into() may be called to retrieve it). If +// this returns false, the collision was detected into a +// GeomNode, and there is no CollisionSolid pointer to +// be retrieved. +//////////////////////////////////////////////////////////////////// +INLINE bool CollisionEntry:: +has_into() const { + return (_into != (CollisionSolid *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::get_into +// Access: Public +// Description: Returns the CollisionSolid pointer for the particular +// solid was collided into. This pointer might be NULL +// if the collision was into a piece of visible +// geometry, instead of a normal CollisionSolid +// collision; see has_into(). +//////////////////////////////////////////////////////////////////// +INLINE const CollisionSolid *CollisionEntry:: +get_into() const { + return _into; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::get_from_node +// Access: Public +// Description: Returns the node that contains the CollisionSolid +// that triggered this collision. This will be a node +// that has been added to a CollisionTraverser via +// add_collider(). +//////////////////////////////////////////////////////////////////// +INLINE CollisionNode *CollisionEntry:: +get_from_node() const { + return _from_node; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::get_from_space +// Access: Public +// Description: Returns the global coordinate space of the +// CollisionNode returned by get_from_node(), as of the +// time of the collision. This will be equivalent to a +// wrt() from the node to render. +//////////////////////////////////////////////////////////////////// +INLINE const LMatrix4f &CollisionEntry:: +get_from_space() const { + return _from_space; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::get_into_node +// Access: Public +// Description: Returns the node that contains the CollisionSolid +// that was collided into. This returns a NamedNode +// pointer instead of something more specific, because +// it might be either a CollisionNode or a GeomNode. +//////////////////////////////////////////////////////////////////// +INLINE NamedNode *CollisionEntry:: +get_into_node() const { + return _into_node; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::get_into_space +// Access: Public +// Description: Returns the global coordinate space of the +// CollisionNode or GeomNode returned by +// get_into_node(), as of the time of the collision. +//////////////////////////////////////////////////////////////////// +INLINE const LMatrix4f &CollisionEntry:: +get_into_space() const { + return _into_space; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::get_wrt_space +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const LMatrix4f &CollisionEntry:: +get_wrt_space() const { + return _wrt_space; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::get_inv_wrt_space +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const LMatrix4f &CollisionEntry:: +get_inv_wrt_space() const { + return _inv_wrt_space; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::set_into_intersection_point +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionEntry:: +set_into_intersection_point(const LPoint3f &point) { + _into_intersection_point = point; + _flags |= F_has_into_intersection_point; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::has_into_intersection_point +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool CollisionEntry:: +has_into_intersection_point() const { + return (_flags & F_has_into_intersection_point) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::get_into_intersection_point +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const LPoint3f &CollisionEntry:: +get_into_intersection_point() const { + nassertr(has_into_intersection_point(), _into_intersection_point); + return _into_intersection_point; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::set_into_surface_normal +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionEntry:: +set_into_surface_normal(const LVector3f &normal) { + _into_surface_normal = normal; + _flags |= F_has_into_surface_normal; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::has_into_surface_normal +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool CollisionEntry:: +has_into_surface_normal() const { + return (_flags & F_has_into_surface_normal) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::get_into_surface_normal +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const LVector3f &CollisionEntry:: +get_into_surface_normal() const { + nassertr(has_into_surface_normal(), _into_surface_normal); + return _into_surface_normal; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::set_into_depth +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionEntry:: +set_into_depth(float depth) { + _into_depth = depth; + _flags |= F_has_into_depth; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::has_into_depth +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool CollisionEntry:: +has_into_depth() const { + return (_flags & F_has_into_depth) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::get_into_depth +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float CollisionEntry:: +get_into_depth() const { + nassertr(has_into_depth(), 0.0); + return _into_depth; +} diff --git a/panda/src/collide/collisionEntry.cxx b/panda/src/collide/collisionEntry.cxx new file mode 100644 index 0000000000..52be66ae2b --- /dev/null +++ b/panda/src/collide/collisionEntry.cxx @@ -0,0 +1,51 @@ +// Filename: collisionEntry.cxx +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionEntry.h" + +TypeHandle CollisionEntry::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CollisionEntry:: +CollisionEntry(const CollisionEntry ©) : + _from(copy._from), + _into(copy._into), + _from_node(copy._from_node), + _into_node(copy._into_node), + _from_space(copy._from_space), + _into_space(copy._into_space), + _wrt_space(copy._wrt_space), + _inv_wrt_space(copy._inv_wrt_space), + _flags(copy._flags), + _into_intersection_point(copy._into_intersection_point), + _into_surface_normal(copy._into_surface_normal), + _into_depth(copy._into_depth) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionEntry::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionEntry:: +operator = (const CollisionEntry ©) { + _from = copy._from; + _into = copy._into; + _from_node = copy._from_node; + _into_node = copy._into_node; + _from_space = copy._from_space; + _into_space = copy._into_space; + _wrt_space = copy._wrt_space; + _inv_wrt_space = copy._inv_wrt_space; + _flags = copy._flags; + _into_intersection_point = copy._into_intersection_point; + _into_surface_normal = copy._into_surface_normal; + _into_depth = copy._into_depth; +} diff --git a/panda/src/collide/collisionEntry.h b/panda/src/collide/collisionEntry.h new file mode 100644 index 0000000000..44e0db7d2c --- /dev/null +++ b/panda/src/collide/collisionEntry.h @@ -0,0 +1,111 @@ +// Filename: collisionEntry.h +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLISIONENTRY_H +#define COLLISIONENTRY_H + +#include + +#include "collisionSolid.h" +#include "collisionNode.h" + +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////// +// Class : CollisionEntry +// Description : Defines a single collision event. One of these is +// created for each collision detected by a +// CollisionTraverser, to be dealt with by the +// CollisionHandler. +// +// A CollisionEntry provides slots for a number of data +// values (such as intersection point and normal) that +// might or might not be known for each collision. It +// is up to the handler to determine what information is +// known and to do the right thing with it. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CollisionEntry : public TypedReferenceCount { +public: + INLINE CollisionEntry(); + CollisionEntry(const CollisionEntry ©); + void operator = (const CollisionEntry ©); + + INLINE const CollisionSolid *get_from() const; + INLINE bool has_into() const; + INLINE const CollisionSolid *get_into() const; + + INLINE CollisionNode *get_from_node() const; + INLINE NamedNode *get_into_node() const; + + INLINE const LMatrix4f &get_from_space() const; + INLINE const LMatrix4f &get_into_space() const; + INLINE const LMatrix4f &get_wrt_space() const; + INLINE const LMatrix4f &get_inv_wrt_space() const; + + INLINE void set_into_intersection_point(const LPoint3f &point); + INLINE bool has_into_intersection_point() const; + INLINE const LPoint3f &get_into_intersection_point() const; + + INLINE void set_into_surface_normal(const LVector3f &normal); + INLINE bool has_into_surface_normal() const; + INLINE const LVector3f &get_into_surface_normal() const; + + INLINE void set_into_depth(float depth); + INLINE bool has_into_depth() const; + INLINE float get_into_depth() const; + +private: + CPT(CollisionSolid) _from; + CPT(CollisionSolid) _into; + + PT(CollisionNode) _from_node; + PT_NamedNode _into_node; + LMatrix4f _from_space; + LMatrix4f _into_space; + LMatrix4f _wrt_space; + LMatrix4f _inv_wrt_space; + + enum Flags { + F_has_into_intersection_point = 0x0001, + F_has_into_surface_normal = 0x0002, + F_has_into_depth = 0x0004, + }; + + int _flags; + + LPoint3f _into_intersection_point; + LVector3f _into_surface_normal; + float _into_depth; + + friend class CollisionTraverser; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "CollisionEntry", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "collisionEntry.I" + +#endif + + + diff --git a/panda/src/collide/collisionHandler.cxx b/panda/src/collide/collisionHandler.cxx new file mode 100644 index 0000000000..a0fea02eab --- /dev/null +++ b/panda/src/collide/collisionHandler.cxx @@ -0,0 +1,42 @@ +// Filename: collisionHandler.cxx +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionHandler.h" + +TypeHandle CollisionHandler::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandler::begin_group +// Access: Public, Virtual +// Description: Will be called by the CollisionTraverser before a new +// traversal is begun. It instructs the handler to +// reset itself in preparation for a number of +// CollisionEntries to be sent. +//////////////////////////////////////////////////////////////////// +void CollisionHandler:: +begin_group() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandler::add_entry +// Access: Public, Virtual +// Description: Called between a begin_group() .. end_group() +// sequence for each collision that is detected. +//////////////////////////////////////////////////////////////////// +void CollisionHandler:: +add_entry(CollisionEntry *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandler::end_group +// Access: Public, Virtual +// Description: Called by the CollisionTraverser at the completion of +// all collision detections for this traversal. It +// should do whatever finalization is required for the +// handler. +//////////////////////////////////////////////////////////////////// +void CollisionHandler:: +end_group() { +} diff --git a/panda/src/collide/collisionHandler.h b/panda/src/collide/collisionHandler.h new file mode 100644 index 0000000000..ed19710621 --- /dev/null +++ b/panda/src/collide/collisionHandler.h @@ -0,0 +1,53 @@ +// Filename: collisionHandler.h +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLISIONHANDLER_H +#define COLLISIONHANDLER_H + +#include + +#include + +class CollisionEntry; + +/////////////////////////////////////////////////////////////////// +// Class : CollisionHandler +// Description : The abstract interface to a number of classes that +// decide what to do what a collision is detected. One +// of these must be assigned to the CollisionTraverser +// that is processing collisions in order to specify how +// to dispatch detected collisions. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CollisionHandler : public TypedReferenceCount { +public: + virtual void begin_group(); + virtual void add_entry(CollisionEntry *entry); + virtual void end_group(); + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "CollisionHandler", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend class CollisionTraverser; +}; + +#endif + + + diff --git a/panda/src/collide/collisionHandlerEvent.I b/panda/src/collide/collisionHandlerEvent.I new file mode 100644 index 0000000000..3d5636a21e --- /dev/null +++ b/panda/src/collide/collisionHandlerEvent.I @@ -0,0 +1,93 @@ +// Filename: collisionHandlerEvent.I +// Created by: drose (27Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerEvent::SortEntries::operator () +// Access: Public +// Description: Orders the CollisionEntries in the set so that there +// is one entry for each node/node intersection +// detected. +//////////////////////////////////////////////////////////////////// +INLINE bool CollisionHandlerEvent::SortEntries:: +operator () (const PT(CollisionEntry) &a, + const PT(CollisionEntry) &b) const { + if (a->get_from_node() != b->get_from_node()) { + return a->get_from_node() < b->get_from_node(); + } + if (a->get_into_node() != b->get_into_node()) { + return a->get_into_node() < b->get_into_node(); + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerEvent::set_in_pattern +// Access: Public +// Description: Sets the pattern string that indicates how the event +// names are generated for each collision detected. +// This is a string that may contain any of the +// following: +// +// %fn - the name of the "from" object's node +// %in - the name of the "into" object's node +// %ft - 't' if "from" is tangible, 'i' if intangible +// %ft - 't' if "into" is tangible, 'i' if intangible +// +// The event name will be based on the in_pattern +// string specified here, with all occurrences of the +// above strings replaced with the corresponding values. +//////////////////////////////////////////////////////////////////// +INLINE void CollisionHandlerEvent:: +set_in_pattern(const string &in_pattern) { + _in_pattern = in_pattern; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerEvent::get_in_pattern +// Access: Public +// Description: Returns the pattern string that indicates how the +// event names are generated for each collision +// detected. See set_in_pattern(). +//////////////////////////////////////////////////////////////////// +INLINE string CollisionHandlerEvent:: +get_in_pattern() const { + return _in_pattern; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerEvent::set_out_pattern +// Access: Public +// Description: Sets the pattern string that indicates how the event +// names are generated when a collision between two +// particular nodes is *no longer* detected. +// +// In general, the in_pattern event is thrown on the +// first detection of a collision between two particular +// nodes. Thereafter, as long as a collision between +// those two nodes continues to be detected each frame, +// no further events (associated with this particular +// pair of nodes) are thrown. The first frame in which +// the collision is no longer detected, the out_pattern +// event is thrown. +//////////////////////////////////////////////////////////////////// +INLINE void CollisionHandlerEvent:: +set_out_pattern(const string &out_pattern) { + _out_pattern = out_pattern; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerEvent::get_out_pattern +// Access: Public +// Description: Returns the pattern string that indicates how the +// event names are generated when a collision between +// two particular nodes is *no longer* detected. See +// set_out_pattern() and set_in_pattern(). +//////////////////////////////////////////////////////////////////// +INLINE string CollisionHandlerEvent:: +get_out_pattern() const { + return _out_pattern; +} + diff --git a/panda/src/collide/collisionHandlerEvent.cxx b/panda/src/collide/collisionHandlerEvent.cxx new file mode 100644 index 0000000000..84a6b41165 --- /dev/null +++ b/panda/src/collide/collisionHandlerEvent.cxx @@ -0,0 +1,174 @@ +// Filename: collisionHandlerEvent.cxx +// Created by: drose (27Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionHandlerEvent.h" +#include "config_collide.h" + +#include +#include + +TypeHandle CollisionHandlerEvent::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerEvent::Constructor +// Access: Public +// Description: The default CollisionHandlerEvent will throw no +// events. Its pattern strings must first be set via a +// call to set_in_pattern() and/or set_out_pattern(). +//////////////////////////////////////////////////////////////////// +CollisionHandlerEvent:: +CollisionHandlerEvent() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerEvent::begin_group +// Access: Public, Virtual +// Description: Will be called by the CollisionTraverser before a new +// traversal is begun. It instructs the handler to +// reset itself in preparation for a number of +// CollisionEntries to be sent. +//////////////////////////////////////////////////////////////////// +void CollisionHandlerEvent:: +begin_group() { + if (collide_cat.is_spam()) { + collide_cat.spam() + << "begin_group.\n"; + } + _last_colliding.swap(_current_colliding); + _current_colliding.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerEvent::add_entry +// Access: Public, Virtual +// Description: Called between a begin_group() .. end_group() +// sequence for each collision that is detected. +//////////////////////////////////////////////////////////////////// +void CollisionHandlerEvent:: +add_entry(CollisionEntry *entry) { + nassertv(entry != (CollisionEntry *)NULL); + + // Record this particular entry for later. This will keep track of + // all the unique pairs of node/node intersections. + bool inserted = _current_colliding.insert(entry).second; + + if (collide_cat.is_spam()) { + collide_cat.spam() + << "Detected collision from " << (void *)entry->get_from_node() + << " to " << (void *)entry->get_into_node() + << ", inserted = " << inserted << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerEvent::end_group +// Access: Public, Virtual +// Description: Called by the CollisionTraverser at the completion of +// all collision detections for this traversal. It +// should do whatever finalization is required for the +// handler. +//////////////////////////////////////////////////////////////////// +void CollisionHandlerEvent:: +end_group() { + // Now compare the list of entries we collected this frame with + // those we kept from the last time. Each new entry represents a + // new 'in' event; each missing entry represents a new 'out' event. + + if (collide_cat.is_spam()) { + collide_cat.spam() + << "end_group.\n" + << "current_colliding has " << _current_colliding.size() + << " entries, last_colliding has " << _last_colliding.size() + << "\n"; + } + + Colliding::iterator ca, cb; + + ca = _current_colliding.begin(); + cb = _last_colliding.begin(); + + SortEntries order; + while (ca != _current_colliding.end() && cb != _last_colliding.end()) { + if (order(*ca, *cb)) { + // Here's an element in a but not in b. That's a newly entered + // intersection. + throw_event_pattern(_in_pattern, *ca); + ++ca; + + } else if (order(*cb, *ca)) { + // Here's an element in b but not in a. That's a newly exited + // intersection. + throw_event_pattern(_out_pattern, *cb); + ++cb; + + } else { + // This element is in both b and a. It hasn't changed. + ++ca; + ++cb; + } + } + + while (ca != _current_colliding.end()) { + // Here's an element in a but not in b. That's a newly entered + // intersection. + throw_event_pattern(_in_pattern, *ca); + ++ca; + } + + while (cb != _last_colliding.end()) { + // Here's an element in b but not in a. That's a newly exited + // intersection. + throw_event_pattern(_out_pattern, *cb); + ++cb; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerEvent::throw_event_pattern +// Access: Private +// Description: Throws an event matching the indicated pattern. +//////////////////////////////////////////////////////////////////// +void CollisionHandlerEvent:: +throw_event_pattern(const string &pattern, CollisionEntry *entry) { + if (pattern.empty()) { + return; + } + + string event; + for (size_t p = 0; p < pattern.size(); ++p) { + if (pattern[p] == '%') { + string cmd = pattern.substr(p + 1, 2); + p += 2; + if (cmd == "fn") { + event += entry->get_from_node()->get_name(); + + } else if (cmd == "in") { + event += entry->get_into_node()->get_name(); + + } else if (cmd == "ft") { + event += + (!entry->get_from()->is_tangible() ? 'i' : 't'); + + } else if (cmd == "it") { + event += + (entry->has_into() && !entry->get_into()->is_tangible() ? 'i' : 't'); + + } else if (cmd == "ig") { + event += + (entry->has_into() ? 'c' : 'g'); + + } else { + collide_cat.error() + << "Invalid symbol in event_pattern: %" << cmd << "\n"; + } + } else { + event += pattern[p]; + } + } + + if (!event.empty()) { + throw_event(event, EventParameter(entry)); + } +} diff --git a/panda/src/collide/collisionHandlerEvent.h b/panda/src/collide/collisionHandlerEvent.h new file mode 100644 index 0000000000..8721cfdb4c --- /dev/null +++ b/panda/src/collide/collisionHandlerEvent.h @@ -0,0 +1,81 @@ +// Filename: collisionHandlerEvent.h +// Created by: drose (27Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLISIONHANDLEREVENT_H +#define COLLISIONHANDLEREVENT_H + +#include + +#include "collisionHandler.h" +#include "collisionNode.h" +#include "collisionEntry.h" + +#include + +/////////////////////////////////////////////////////////////////// +// Class : CollisionHandlerEvent +// Description : A specialized kind of CollisionHandler that throws an +// event for each collision detected. The event thrown +// may be based on the name of the moving object or the +// struck object, or both. The first parameter of the +// event will be a pointer to the CollisionEntry that +// triggered it. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CollisionHandlerEvent : public CollisionHandler { +public: + CollisionHandlerEvent(); + + virtual void begin_group(); + virtual void add_entry(CollisionEntry *entry); + virtual void end_group(); + + INLINE void set_in_pattern(const string &pattern); + INLINE string get_in_pattern() const; + INLINE void set_out_pattern(const string &pattern); + INLINE string get_out_pattern() const; + +private: + void throw_event_pattern(const string &pattern, CollisionEntry *entry); + + string _in_pattern; + string _out_pattern; + + int _index; + + class SortEntries { + public: + INLINE bool + operator () (const PT(CollisionEntry) &a, + const PT(CollisionEntry) &b) const; + }; + + typedef set Colliding; + Colliding _current_colliding; + Colliding _last_colliding; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CollisionHandler::init_type(); + register_type(_type_handle, "CollisionHandlerEvent", + CollisionHandler::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "collisionHandlerEvent.I" + +#endif + + + diff --git a/panda/src/collide/collisionHandlerFloor.I b/panda/src/collide/collisionHandlerFloor.I new file mode 100644 index 0000000000..210c85bed6 --- /dev/null +++ b/panda/src/collide/collisionHandlerFloor.I @@ -0,0 +1,28 @@ +// Filename: collisionHandlerFloor.I +// Created by: drose (04Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerFloor::set_offset +// Access: Public +// Description: Sets the linear offset to add to (or subtract from) +// the highest detected collision point to determine the +// actual height at which to set the collider. +//////////////////////////////////////////////////////////////////// +INLINE void CollisionHandlerFloor:: +set_offset(float offset) { + _offset = offset; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerFloor::get_offset +// Access: Public +// Description: Returns the linear offset to add to (or subtract from) +// the highest detected collision point to determine the +// actual height at which to set the collider. +//////////////////////////////////////////////////////////////////// +INLINE float CollisionHandlerFloor:: +get_offset() const { + return _offset; +} diff --git a/panda/src/collide/collisionHandlerFloor.cxx b/panda/src/collide/collisionHandlerFloor.cxx new file mode 100644 index 0000000000..f08a8cf55e --- /dev/null +++ b/panda/src/collide/collisionHandlerFloor.cxx @@ -0,0 +1,99 @@ +// Filename: collisionHandlerFloor.cxx +// Created by: drose (04Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionHandlerFloor.h" +#include "config_collide.h" + +#include +#include + +TypeHandle CollisionHandlerFloor::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerFloor::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CollisionHandlerFloor:: +CollisionHandlerFloor() { + _offset = 0.0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerFloor::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CollisionHandlerFloor:: +~CollisionHandlerFloor() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerFloor::handle_entries +// Access: Protected, Virtual +// Description: Called by the parent class after all collisions have +// been detected, this manages the various collisions +// and moves around the nodes as necessary. +//////////////////////////////////////////////////////////////////// +void CollisionHandlerFloor:: +handle_entries() { + FromEntries::const_iterator fi; + for (fi = _from_entries.begin(); fi != _from_entries.end(); ++fi) { + CollisionNode *from_node = (*fi).first; + nassertv(from_node != (CollisionNode *)NULL); + const Entries &entries = (*fi).second; + + Colliders::const_iterator ci; + ci = _colliders.find(from_node); + if (ci == _colliders.end()) { + // Hmm, someone added a CollisionNode to a traverser and gave + // it this CollisionHandler pointer--but they didn't tell us + // about the node. + collide_cat.error() + << "CollisionHandlerFloor doesn't know about " + << *from_node << "\n"; + + } else { + const ColliderDef &def = (*ci).second; + + // Get the maximum height for all collisions with this node. + bool got_max = false; + float max_height = 0.0; + + Entries::const_iterator ei; + for (ei = entries.begin(); ei != entries.end(); ++ei) { + CollisionEntry *entry = (*ei); + nassertv(entry != (CollisionEntry *)NULL); + nassertv(from_node == entry->get_from_node()); + + if (entry->has_into_intersection_point()) { + LPoint3f point = + entry->get_into_intersection_point() * + entry->get_into_space(); + if (collide_cat.is_debug()) { + collide_cat.debug() + << "Intersection point detected at " << point << "\n"; + } + + float height = point[2]; + if (!got_max || height > max_height) { + got_max = true; + max_height = height; + } + } + } + + // Now set our height accordingly. + if (collide_cat.is_debug()) { + collide_cat.debug() + << "Resetting height to " << max_height << " + " << _offset << "\n"; + } + LMatrix4f mat; + def.get_mat(mat); + mat(3, 2) = max_height + _offset; + def.set_mat(mat); + } + } +} diff --git a/panda/src/collide/collisionHandlerFloor.h b/panda/src/collide/collisionHandlerFloor.h new file mode 100644 index 0000000000..04850f783c --- /dev/null +++ b/panda/src/collide/collisionHandlerFloor.h @@ -0,0 +1,61 @@ +// Filename: collisionHandlerFloor.h +// Created by: drose (04Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLISIONHANDLERFLOOR_H +#define COLLISIONHANDLERFLOOR_H + +#include + +#include "collisionHandlerPhysical.h" +#include "collisionNode.h" + +/////////////////////////////////////////////////////////////////// +// Class : CollisionHandlerFloor +// Description : A specialized kind of CollisionHandler that sets the +// Z height of the collider to a fixed linear offset +// from the highest detected collision point each frame. +// It's intended to implement walking around on a floor +// of varying height by casting a ray down from the +// avatar's head. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CollisionHandlerFloor : public CollisionHandlerPhysical { +public: + CollisionHandlerFloor(); + virtual ~CollisionHandlerFloor(); + + INLINE void set_offset(float offset); + INLINE float get_offset() const; + +protected: + virtual void handle_entries(); + +private: + float _offset; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CollisionHandlerPhysical::init_type(); + register_type(_type_handle, "CollisionHandlerFloor", + CollisionHandlerPhysical::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "collisionHandlerFloor.I" + +#endif + + + diff --git a/panda/src/collide/collisionHandlerPhysical.I b/panda/src/collide/collisionHandlerPhysical.I new file mode 100644 index 0000000000..cbbdd80ad4 --- /dev/null +++ b/panda/src/collide/collisionHandlerPhysical.I @@ -0,0 +1,26 @@ +// Filename: collisionHandlerPhysical.I +// Created by: drose (03Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPhysical::ColliderDef::set_drive_interface +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionHandlerPhysical::ColliderDef:: +set_drive_interface(DriveInterface *drive_interface) { + _drive_interface = drive_interface; + _arc.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPhysical::ColliderDef::set_arc +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionHandlerPhysical::ColliderDef:: +set_arc(NodeRelation *arc) { + _arc = arc; + _drive_interface.clear(); +} diff --git a/panda/src/collide/collisionHandlerPhysical.cxx b/panda/src/collide/collisionHandlerPhysical.cxx new file mode 100644 index 0000000000..6739461458 --- /dev/null +++ b/panda/src/collide/collisionHandlerPhysical.cxx @@ -0,0 +1,189 @@ +// Filename: collisionHandlerPhysical.cxx +// Created by: drose (03Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionHandlerPhysical.h" +#include "config_collide.h" + +#include +#include + +TypeHandle CollisionHandlerPhysical::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPhysical::ColliderDef::get_mat +// Access: Public +// Description: Fills mat with the matrix representing the current +// position and orientation of this collider. +//////////////////////////////////////////////////////////////////// +void CollisionHandlerPhysical::ColliderDef:: +get_mat(LMatrix4f &mat) const { + if (_arc != (NodeRelation *)NULL) { + TransformTransition *tt; + if (get_transition_into(tt, _arc)) { + mat = tt->get_matrix(); + } else { + mat = LMatrix4f::ident_mat(); + } + + } else if (_drive_interface != (DriveInterface *)NULL) { + mat = _drive_interface->get_mat(); + + } else { + collide_cat.error() + << "Invalid CollisionHandlerPhysical::ColliderDef\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPhysical::ColliderDef::set_mat +// Access: Public +// Description: Moves this collider to the position and orientation +// indicated by the given transform. +//////////////////////////////////////////////////////////////////// +void CollisionHandlerPhysical::ColliderDef:: +set_mat(const LMatrix4f &mat) const { + if (_arc != (NodeRelation *)NULL) { + _arc->set_transition(new TransformTransition(mat)); + + } else if (_drive_interface != (DriveInterface *)NULL) { + _drive_interface->set_mat(mat); + + } else { + collide_cat.error() + << "Invalid CollisionHandlerPhysical::ColliderDef\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPhysical::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CollisionHandlerPhysical:: +CollisionHandlerPhysical() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPhysical::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CollisionHandlerPhysical:: +~CollisionHandlerPhysical() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPhysical::begin_group +// Access: Public, Virtual +// Description: Will be called by the CollisionTraverser before a new +// traversal is begun. It instructs the handler to +// reset itself in preparation for a number of +// CollisionEntries to be sent. +//////////////////////////////////////////////////////////////////// +void CollisionHandlerPhysical:: +begin_group() { + CollisionHandlerEvent::begin_group(); + _from_entries.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPhysical::add_entry +// Access: Public, Virtual +// Description: Called between a begin_group() .. end_group() +// sequence for each collision that is detected. +//////////////////////////////////////////////////////////////////// +void CollisionHandlerPhysical:: +add_entry(CollisionEntry *entry) { + nassertv(entry != (CollisionEntry *)NULL); + CollisionHandlerEvent::add_entry(entry); + + if (entry->get_from()->is_tangible() && + (!entry->has_into() || entry->get_into()->is_tangible())) { + _from_entries[entry->get_from_node()].push_back(entry); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPhysical::end_group +// Access: Public, Virtual +// Description: Called by the CollisionTraverser at the completion of +// all collision detections for this traversal. It +// should do whatever finalization is required for the +// handler. +//////////////////////////////////////////////////////////////////// +void CollisionHandlerPhysical:: +end_group() { + CollisionHandlerEvent::end_group(); + + handle_entries(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPhysical::add_collider +// Access: Public +// Description: Adds a new collider to the list with a DriveInterface +// pointer that needs to be told about the collider's +// new position, or updates the existing collider with a +// new DriveInterface pointer. +//////////////////////////////////////////////////////////////////// +void CollisionHandlerPhysical:: +add_collider(CollisionNode *node, DriveInterface *drive_interface) { + _colliders[node].set_drive_interface(drive_interface); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPhysical::add_collider +// Access: Public +// Description: Adds a new collider to the list with a NodeRelation +// pointer that will be updated with the collider's +// new position, or updates the existing collider with a +// new NodeRelation pointer. +//////////////////////////////////////////////////////////////////// +void CollisionHandlerPhysical:: +add_collider(CollisionNode *node, NodeRelation *arc) { + _colliders[node].set_arc(arc); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPhysical::remove_collider +// Access: Public +// Description: Removes the collider from the list of colliders that +// this handler knows about. +//////////////////////////////////////////////////////////////////// +bool CollisionHandlerPhysical:: +remove_collider(CollisionNode *node) { + Colliders::iterator ci = _colliders.find(node); + if (ci == _colliders.end()) { + return false; + } + _colliders.erase(ci); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPhysical::has_collider +// Access: Public +// Description: Returns true if the handler knows about the indicated +// collider, false otherwise. +//////////////////////////////////////////////////////////////////// +bool CollisionHandlerPhysical:: +has_collider(CollisionNode *node) const { + Colliders::const_iterator ci = _colliders.find(node); + return (ci != _colliders.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPhysical::clear_colliders +// Access: Public +// Description: Completely empties the list of colliders this handler +// knows about. +//////////////////////////////////////////////////////////////////// +void CollisionHandlerPhysical:: +clear_colliders() { + _colliders.clear(); +} + + diff --git a/panda/src/collide/collisionHandlerPhysical.h b/panda/src/collide/collisionHandlerPhysical.h new file mode 100644 index 0000000000..aab92e6669 --- /dev/null +++ b/panda/src/collide/collisionHandlerPhysical.h @@ -0,0 +1,86 @@ +// Filename: collisionHandlerPhysical.h +// Created by: drose (03Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLISIONHANDLERPHYSICAL_H +#define COLLISIONHANDLERPHYSICAL_H + +#include + +#include "collisionHandlerEvent.h" +#include "collisionNode.h" + +#include +#include + +/////////////////////////////////////////////////////////////////// +// Class : CollisionHandlerPhysical +// Description : The abstract base class for a number of +// CollisionHandlers that have some physical effect on +// their moving bodies: they need to update the nodes' +// positions based on the effects of the collision. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CollisionHandlerPhysical : public CollisionHandlerEvent { +public: + CollisionHandlerPhysical(); + virtual ~CollisionHandlerPhysical(); + + virtual void begin_group(); + virtual void add_entry(CollisionEntry *entry); + virtual void end_group(); + + void add_collider(CollisionNode *node, DriveInterface *drive_interface); + void add_collider(CollisionNode *node, NodeRelation *arc); + bool remove_collider(CollisionNode *node); + bool has_collider(CollisionNode *node) const; + void clear_colliders(); + +protected: + virtual void handle_entries()=0; + +protected: + typedef vector Entries; + typedef map FromEntries; + FromEntries _from_entries; + + class ColliderDef { + public: + INLINE void set_drive_interface(DriveInterface *drive_interface); + INLINE void set_arc(NodeRelation *arc); + + void get_mat(LMatrix4f &mat) const; + void set_mat(const LMatrix4f &mat) const; + + PT(DriveInterface) _drive_interface; + PT(NodeRelation) _arc; + }; + + typedef map Colliders; + Colliders _colliders; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CollisionHandlerEvent::init_type(); + register_type(_type_handle, "CollisionHandlerPhysical", + CollisionHandlerEvent::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "collisionHandlerPhysical.I" + +#endif + + + diff --git a/panda/src/collide/collisionHandlerPusher.I b/panda/src/collide/collisionHandlerPusher.I new file mode 100644 index 0000000000..71f20fe059 --- /dev/null +++ b/panda/src/collide/collisionHandlerPusher.I @@ -0,0 +1,24 @@ +// Filename: collisionHandlerPusher.I +// Created by: drose (26Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPusher::set_horizontal +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionHandlerPusher:: +set_horizontal(bool flag) { + _horizontal = flag; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPusher::get_horizontal +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool CollisionHandlerPusher:: +get_horizontal() const { + return _horizontal; +} diff --git a/panda/src/collide/collisionHandlerPusher.cxx b/panda/src/collide/collisionHandlerPusher.cxx new file mode 100644 index 0000000000..a1b28691fe --- /dev/null +++ b/panda/src/collide/collisionHandlerPusher.cxx @@ -0,0 +1,95 @@ +// Filename: collisionHandlerPusher.cxx +// Created by: drose (25Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionHandlerPusher.h" +#include "config_collide.h" + +TypeHandle CollisionHandlerPusher::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPusher::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CollisionHandlerPusher:: +CollisionHandlerPusher() { + _horizontal = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPusher::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CollisionHandlerPusher:: +~CollisionHandlerPusher() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerPusher::handle_entries +// Access: Protected, Virtual +// Description: Called by the parent class after all collisions have +// been detected, this manages the various collisions +// and moves around the nodes as necessary. +//////////////////////////////////////////////////////////////////// +void CollisionHandlerPusher:: +handle_entries() { + FromEntries::const_iterator fi; + for (fi = _from_entries.begin(); fi != _from_entries.end(); ++fi) { + CollisionNode *from_node = (*fi).first; + nassertv(from_node != (CollisionNode *)NULL); + const Entries &entries = (*fi).second; + + Colliders::const_iterator ci; + ci = _colliders.find(from_node); + if (ci == _colliders.end()) { + // Hmm, someone added a CollisionNode to a traverser and gave + // it this CollisionHandler pointer--but they didn't tell us + // about the node. + collide_cat.error() + << "CollisionHandlerPusher doesn't know about " + << *from_node << "\n"; + + } else { + const ColliderDef &def = (*ci).second; + + Entries::const_iterator ei; + for (ei = entries.begin(); ei != entries.end(); ++ei) { + CollisionEntry *entry = (*ei); + nassertv(entry != (CollisionEntry *)NULL); + nassertv(from_node == entry->get_from_node()); + + if (!entry->has_into_surface_normal() || + !entry->has_into_depth()) { + if (collide_cat.is_debug()) { + collide_cat.debug() + << "Cannot shove on " << *from_node << " for collision into " + << *entry->get_into_node() << "; no normal/depth information.\n"; + } + + } else { + // Shove it just enough to clear the volume. + LVector3f shove = + entry->get_into_surface_normal() * + entry->get_into_depth(); + + if (_horizontal) { + shove[2] = 0.0; + } + + if (collide_cat.is_debug()) { + collide_cat.debug() + << "Shoving on " << *from_node << " in the amount of: " + << shove << "\n"; + } + + LMatrix4f mat; + def.get_mat(mat); + def.set_mat(LMatrix4f::translate_mat(shove) * mat); + } + } + } + } +} diff --git a/panda/src/collide/collisionHandlerPusher.h b/panda/src/collide/collisionHandlerPusher.h new file mode 100644 index 0000000000..fce0b3a232 --- /dev/null +++ b/panda/src/collide/collisionHandlerPusher.h @@ -0,0 +1,59 @@ +// Filename: collisionHandlerPusher.h +// Created by: drose (25Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLISIONHANDLERPUSHER_H +#define COLLISIONHANDLERPUSHER_H + +#include + +#include "collisionHandlerPhysical.h" +#include "collisionNode.h" + +/////////////////////////////////////////////////////////////////// +// Class : CollisionHandlerPusher +// Description : A specialized kind of CollisionHandler that simply +// pushes back on things that attempt to move into solid +// walls. This is the simplest kind of "real-world" +// collisions you can have. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CollisionHandlerPusher : public CollisionHandlerPhysical { +public: + CollisionHandlerPusher(); + virtual ~CollisionHandlerPusher(); + + INLINE void set_horizontal(bool flag); + INLINE bool get_horizontal() const; + +protected: + virtual void handle_entries(); + +private: + bool _horizontal; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CollisionHandlerPhysical::init_type(); + register_type(_type_handle, "CollisionHandlerPusher", + CollisionHandlerPhysical::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "collisionHandlerPusher.I" + +#endif + + + diff --git a/panda/src/collide/collisionHandlerQueue.cxx b/panda/src/collide/collisionHandlerQueue.cxx new file mode 100644 index 0000000000..bc012e6e5d --- /dev/null +++ b/panda/src/collide/collisionHandlerQueue.cxx @@ -0,0 +1,65 @@ +// Filename: collisionHandlerQueue.cxx +// Created by: drose (27Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionHandlerQueue.h" +#include "config_collide.h" + +TypeHandle CollisionHandlerQueue::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerQueue::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CollisionHandlerQueue:: +CollisionHandlerQueue() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerQueue::begin_group +// Access: Public, Virtual +// Description: Will be called by the CollisionTraverser before a new +// traversal is begun. It instructs the handler to +// reset itself in preparation for a number of +// CollisionEntries to be sent. +//////////////////////////////////////////////////////////////////// +void CollisionHandlerQueue:: +begin_group() { + _entries.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerQueue::add_entry +// Access: Public, Virtual +// Description: Called between a begin_group() .. end_group() +// sequence for each collision that is detected. +//////////////////////////////////////////////////////////////////// +void CollisionHandlerQueue:: +add_entry(CollisionEntry *entry) { + nassertv(entry != (CollisionEntry *)NULL); + _entries.push_back(entry); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerQueue::get_num_entries +// Access: Public +// Description: Returns the number of CollisionEntries detected last +// pass. +//////////////////////////////////////////////////////////////////// +int CollisionHandlerQueue:: +get_num_entries() const { + return _entries.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionHandlerQueue::get_entry +// Access: Public +// Description: Returns the nth CollisionEntry detected last pass. +//////////////////////////////////////////////////////////////////// +CollisionEntry *CollisionHandlerQueue:: +get_entry(int n) const { + nassertr(n >= 0 && n < (int)_entries.size(), NULL); + return _entries[n]; +} diff --git a/panda/src/collide/collisionHandlerQueue.h b/panda/src/collide/collisionHandlerQueue.h new file mode 100644 index 0000000000..98be3119b9 --- /dev/null +++ b/panda/src/collide/collisionHandlerQueue.h @@ -0,0 +1,58 @@ +// Filename: collisionHandlerQueue.h +// Created by: drose (27Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLISIONHANDLERQUEUE_H +#define COLLISIONHANDLERQUEUE_H + +#include + +#include "collisionHandler.h" +#include "collisionEntry.h" + +/////////////////////////////////////////////////////////////////// +// Class : CollisionHandlerQueue +// Description : A special kind of CollisionHandler that does nothing +// except remember the CollisionEntries detected the +// last pass. This set of CollisionEntries may then be +// queried by the calling function. It's primarily +// useful when a simple intersection test is being made, +// e.g. for picking from the window. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CollisionHandlerQueue : public CollisionHandler { +public: + CollisionHandlerQueue(); + + virtual void begin_group(); + virtual void add_entry(CollisionEntry *entry); + + int get_num_entries() const; + CollisionEntry *get_entry(int n) const; + +private: + typedef vector Entries; + Entries _entries; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CollisionHandler::init_type(); + register_type(_type_handle, "CollisionHandlerQueue", + CollisionHandler::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif + + + diff --git a/panda/src/collide/collisionLevelState.I b/panda/src/collide/collisionLevelState.I new file mode 100644 index 0000000000..257c68e907 --- /dev/null +++ b/panda/src/collide/collisionLevelState.I @@ -0,0 +1,162 @@ +// Filename: collisionLevelState.I +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::get_num_colliders +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int CollisionLevelState:: +get_num_colliders() const { + return _colliders.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::has_collider +// Access: Public +// Description: Returns true if the nth collider in the LevelState is +// still part of the level. +//////////////////////////////////////////////////////////////////// +INLINE bool CollisionLevelState:: +has_collider(int n) const { + nassertr(n >= 0 && n < _colliders.size(), false); + return (_current & get_mask(n)) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::has_collider_with_geom +// Access: Public +// Description: Returns true if the nth collider in the LevelState is +// still part of the level, and it has the +// "collide_geom" flag set. +//////////////////////////////////////////////////////////////////// +INLINE bool CollisionLevelState:: +has_collider_with_geom(int n) const { + nassertr(n >= 0 && n < _colliders.size(), false); + return (_current & _colliders_with_geom & get_mask(n)) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::has_any_collider +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool CollisionLevelState:: +has_any_collider() const { + return _current != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::has_any_collide_geom +// Access: Public +// Description: Returns true if any Collider in the level state has +// the "collide_geom" flag set, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool CollisionLevelState:: +has_any_collide_geom() const { + return (_current & _colliders_with_geom) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::reached_collision_node +// Access: Public +// Description: Called by the traverser when we reach a CollisionNode +// in the traversal. At this point, we zero out our set +// of colliders with the "collide_geom" flag set, +// because no colliders will test against geometry +// parented beneath a CollisionNode. +//////////////////////////////////////////////////////////////////// +INLINE void CollisionLevelState:: +reached_collision_node() { + _colliders_with_geom = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::get_collider +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionSolid *CollisionLevelState:: +get_collider(int n) const { + nassertr(n >= 0 && n < _colliders.size(), NULL); + nassertr(has_collider(n), NULL); + + return _colliders[n]._collider; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::get_node +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionNode *CollisionLevelState:: +get_node(int n) const { + nassertr(n >= 0 && n < _colliders.size(), NULL); + nassertr(has_collider(n), NULL); + + return _colliders[n]._node; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::get_space +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const LMatrix4f &CollisionLevelState:: +get_space(int n) const { + nassertr(n >= 0 && n < _colliders.size(), LMatrix4f::ident_mat()); + nassertr(has_collider(n), LMatrix4f::ident_mat()); + + return _colliders[n]._space; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::get_inv_space +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const LMatrix4f &CollisionLevelState:: +get_inv_space(int n) const { + nassertr(n >= 0 && n < _colliders.size(), LMatrix4f::ident_mat()); + nassertr(has_collider(n), LMatrix4f::ident_mat()); + + return _colliders[n]._inv_space; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::get_local_bound +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const GeometricBoundingVolume *CollisionLevelState:: +get_local_bound(int n) const { + nassertr(n >= 0 && n < _colliders.size(), NULL); + nassertr(has_collider(n), NULL); + nassertr(n >= 0 && n < _local_bounds.size(), NULL); + return _local_bounds[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::omit_collider +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionLevelState:: +omit_collider(int n) { + nassertv(n >= 0 && n < _colliders.size()); + nassertv(has_collider(n)); + + _current &= ~get_mask(n); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::get_mask +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionLevelState::ColliderMask CollisionLevelState:: +get_mask(int n) const { + return ((ColliderMask)1) << n; +} diff --git a/panda/src/collide/collisionLevelState.N b/panda/src/collide/collisionLevelState.N new file mode 100644 index 0000000000..c4480338e8 --- /dev/null +++ b/panda/src/collide/collisionLevelState.N @@ -0,0 +1 @@ +ignoreinvolved ColliderDef diff --git a/panda/src/collide/collisionLevelState.cxx b/panda/src/collide/collisionLevelState.cxx new file mode 100644 index 0000000000..59992ec2d4 --- /dev/null +++ b/panda/src/collide/collisionLevelState.cxx @@ -0,0 +1,88 @@ +// Filename: collisionLevelState.cxx +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionLevelState.h" +#include "collisionSolid.h" +#include "collisionNode.h" + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::clear +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionLevelState:: +clear() { + _colliders.clear(); + _local_bounds.clear(); + _current = 0; + _colliders_with_geom = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::reserve +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionLevelState:: +reserve(int max_colliders) { + _colliders.reserve(max_colliders); + _local_bounds.reserve(max_colliders); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::prepare_collider +// Access: Public +// Description: Adds the indicated Collider to the set of Colliders +// in the current level state. +//////////////////////////////////////////////////////////////////// +void CollisionLevelState:: +prepare_collider(const ColliderDef &def) { + int index = (int)_colliders.size(); + _colliders.push_back(def); + + CollisionSolid *collider = def._collider; + const BoundingVolume &bv = collider->get_bound(); + if (!bv.is_of_type(GeometricBoundingVolume::get_class_type())) { + _local_bounds.push_back((GeometricBoundingVolume *)NULL); + } else { + GeometricBoundingVolume *gbv; + DCAST_INTO_V(gbv, bv.make_copy()); + gbv->xform(def._space); + _local_bounds.push_back(gbv); + } + + _current |= get_mask(index); + + if (def._node->get_collide_geom()) { + _colliders_with_geom |= get_mask(index); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionLevelState::xform +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionLevelState:: +xform(const LMatrix4f &mat) { + BoundingVolumes new_bounds; + + int num_colliders = get_num_colliders(); + new_bounds.reserve(num_colliders); + for (int c = 0; c < num_colliders; c++) { + if (!has_collider(c) || + get_local_bound(c) == (GeometricBoundingVolume *)NULL) { + new_bounds.push_back((GeometricBoundingVolume *)NULL); + } else { + const GeometricBoundingVolume *old_bound = get_local_bound(c); + GeometricBoundingVolume *new_bound; + DCAST_INTO_V(new_bound, old_bound->make_copy()); + new_bound->xform(mat); + new_bounds.push_back(new_bound); + } + } + + _local_bounds = new_bounds; +} diff --git a/panda/src/collide/collisionLevelState.h b/panda/src/collide/collisionLevelState.h new file mode 100644 index 0000000000..2004d8880f --- /dev/null +++ b/panda/src/collide/collisionLevelState.h @@ -0,0 +1,75 @@ +// Filename: collisionLevelState.h +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLISIONLEVELSTATE_H +#define COLLISIONLEVELSTATE_H + +#include + +#include +#include +#include + +#include + +class CollisionSolid; +class CollisionNode; + +//////////////////////////////////////////////////////////////////// +// Class : CollisionLevelState +// Description : This is the state information the +// CollisionTraverser retains for each level during +// traversal. +//////////////////////////////////////////////////////////////////// +class CollisionLevelState { +public: + class ColliderDef { + public: + CollisionSolid *_collider; + CollisionNode *_node; + LMatrix4f _space; + LMatrix4f _inv_space; + }; + + void clear(); + void reserve(int max_colliders); + void prepare_collider(const ColliderDef &def); + void xform(const LMatrix4f &mat); + + INLINE int get_num_colliders() const; + INLINE bool has_collider(int n) const; + INLINE bool has_collider_with_geom(int n) const; + INLINE bool has_any_collider() const; + INLINE bool has_any_collide_geom() const; + + INLINE void reached_collision_node(); + + INLINE CollisionSolid *get_collider(int n) const; + INLINE CollisionNode *get_node(int n) const; + INLINE const LMatrix4f &get_space(int n) const; + INLINE const LMatrix4f &get_inv_space(int n) const; + INLINE const GeometricBoundingVolume *get_local_bound(int n) const; + + INLINE void omit_collider(int n); + +private: + typedef int ColliderMask; + + INLINE ColliderMask get_mask(int n) const; + + typedef PTA(ColliderDef) Colliders; + Colliders _colliders; + ColliderMask _current; + ColliderMask _colliders_with_geom; + + typedef PTA(CPT(GeometricBoundingVolume)) BoundingVolumes; + BoundingVolumes _local_bounds; +}; + +#include "collisionLevelState.I" + +#endif + + diff --git a/panda/src/collide/collisionNode.I b/panda/src/collide/collisionNode.I new file mode 100644 index 0000000000..84bda3dc83 --- /dev/null +++ b/panda/src/collide/collisionNode.I @@ -0,0 +1,150 @@ +// Filename: collisionNode.I +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionSolid.h" + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::set_collide_mask +// Access: Public +// Description: Simultaneously sets both the "from" and "into" +// CollideMask values to the same thing. +//////////////////////////////////////////////////////////////////// +INLINE void CollisionNode:: +set_collide_mask(CollideMask mask) { + _from_collide_mask = mask; + _into_collide_mask = mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::set_from_collide_mask +// Access: Public +// Description: Sets the "from" CollideMask. In order for a +// collision to be detected from this object into +// another object, the intersection of this object's +// "from" mask and the other object's "into" mask must +// be nonzero. +//////////////////////////////////////////////////////////////////// +INLINE void CollisionNode:: +set_from_collide_mask(CollideMask mask) { + _from_collide_mask = mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::set_into_collide_mask +// Access: Public +// Description: Sets the "into" CollideMask. In order for a +// collision to be detected from another object into +// this object, the intersection of the other object's +// "from" mask and this object's "into" mask must be +// nonzero. +//////////////////////////////////////////////////////////////////// +INLINE void CollisionNode:: +set_into_collide_mask(CollideMask mask) { + _into_collide_mask = mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::get_from_collide_mask +// Access: Public +// Description: Returns the current "from" CollideMask. In order for +// a collision to be detected from this object into +// another object, the intersection of this object's +// "from" mask and the other object's "into" mask must +// be nonzero. +//////////////////////////////////////////////////////////////////// +INLINE CollideMask CollisionNode:: +get_from_collide_mask() const { + return _from_collide_mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::get_into_collide_mask +// Access: Public +// Description: Returns the current "into" CollideMask. In order for +// a collision to be detected from another object into +// this object, the intersection of the other object's +// "from" mask and this object's "into" mask must be +// nonzero. +//////////////////////////////////////////////////////////////////// +INLINE CollideMask CollisionNode:: +get_into_collide_mask() const { + return _into_collide_mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::set_collide_geom +// Access: Public +// Description: Sets the state of the "collide geom" flag for this +// CollisionNode. Normally, this is false; when this is +// set true, the CollisionSolids in this node will test +// for collisions with actual renderable geometry, in +// addition to whatever CollisionSolids may be indicated +// by the from_collide_mask. +// +// Setting this to true causes this to test *all* +// GeomNodes for collisions. It is an all-or-none +// thing; there is no way to collide with only some +// GeomNodes, as GeomNodes have no into_collide_mask. +//////////////////////////////////////////////////////////////////// +INLINE void CollisionNode:: +set_collide_geom(bool flag) { + _collide_geom = flag; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::get_collide_geom +// Access: Public +// Description: Returns the current state of the collide_geom flag. +// See set_collide_geom(). +//////////////////////////////////////////////////////////////////// +INLINE bool CollisionNode:: +get_collide_geom() const { + return _collide_geom; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::get_num_solids +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int CollisionNode:: +get_num_solids() const { + return _solids.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::get_solid +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionSolid *CollisionNode:: +get_solid(int n) const { + nassertr(n >= 0 && n < get_num_solids(), NULL); + return _solids[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::remove_solid +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionNode:: +remove_solid(int n) { + nassertv(n >= 0 && n < get_num_solids()); + _solids.erase(_solids.begin() + n); + mark_bound_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::add_solid +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int CollisionNode:: +add_solid(CollisionSolid *solid) { + _solids.push_back(solid); + mark_bound_stale(); + return _solids.size() - 1; +} diff --git a/panda/src/collide/collisionNode.cxx b/panda/src/collide/collisionNode.cxx new file mode 100644 index 0000000000..b5c1b3521b --- /dev/null +++ b/panda/src/collide/collisionNode.cxx @@ -0,0 +1,286 @@ +// Filename: collisionNode.cxx +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionNode.h" +#include "config_collide.h" + +#include +#include +#include +#include + +TypeHandle CollisionNode::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CollisionNode:: +CollisionNode(const string &name) : + NamedNode(name), + _from_collide_mask(CollideMask::all_on()), + _into_collide_mask(CollideMask::all_on()), + _collide_geom(false) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CollisionNode:: +CollisionNode(const CollisionNode ©) : + NamedNode(copy), + _from_collide_mask(copy._from_collide_mask), + _into_collide_mask(copy._into_collide_mask), + _collide_geom(copy._collide_geom), + _solids(copy._solids) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionNode:: +operator = (const CollisionNode ©) { + NamedNode::operator = (copy); + _from_collide_mask = copy._from_collide_mask; + _into_collide_mask = copy._into_collide_mask; + _collide_geom = copy._collide_geom; + _solids = copy._solids; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CollisionNode:: +~CollisionNode() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Node that is a shallow copy +// of this one. It will be a different Node pointer, +// but its internal data may or may not be shared with +// that of the original Node. +//////////////////////////////////////////////////////////////////// +Node *CollisionNode:: +make_copy() const { + return new CollisionNode(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::xform +// Access: Public, Virtual +// Description: Transforms the contents of this node by the indicated +// matrix, if it means anything to do so. For most +// kinds of nodes, this does nothing. +//////////////////////////////////////////////////////////////////// +void CollisionNode:: +xform(const LMatrix4f &mat) { + nassertv(!mat.is_nan()); + + if (mat.almost_equal(LMatrix4f::ident_mat())) { + return; + } + + Solids::iterator si; + for (si = _solids.begin(); si != _solids.end(); ++si) { + CollisionSolid *solid = (*si); + + // We may have to copy each of our solids as we transform them if + // someone else is sharing their pointers. + if (solid->get_count() > 1) { + solid = solid->make_copy(); + (*si) = solid; + } + + solid->xform(mat); + } + mark_bound_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::draw_traverse +// Access: Public, Virtual +// Description: This is called by the Draw traversal by virtue of the +// node's being present in the scene graph. Its job is +// to make sure the visualization of the collideable +// geometry is up-to-date. +//////////////////////////////////////////////////////////////////// +void CollisionNode:: +draw_traverse() { + Solids::iterator si; + for (si = _solids.begin(); si != _solids.end(); ++si) { + (*si)->update_viz(this); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::output +// Access: Public, Virtual +// Description: Writes a brief description of the node to the +// indicated output stream. This is invoked by the << +// operator. It may be overridden in derived classes to +// include some information relevant to the class. +//////////////////////////////////////////////////////////////////// +void CollisionNode:: +output(ostream &out) const { + NamedNode::output(out); + out << " (" << _solids.size() << " solids)"; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::recompute_bound +// Access: Protected, Virtual +// Description: Recomputes the dynamic bounding volume for this node. +//////////////////////////////////////////////////////////////////// +void CollisionNode:: +recompute_bound() { + // First, get ourselves a fresh, empty bounding volume. + BoundedObject::recompute_bound(); + assert(_bound != (BoundingVolume *)NULL); + + // Now actually compute the bounding volume by putting it around all + // of our solids' bounding volumes. + vector child_volumes; + + Solids::const_iterator gi; + for (gi = _solids.begin(); gi != _solids.end(); ++gi) { + child_volumes.push_back(&(*gi)->get_bound()); + } + + bool success = + _bound->around(child_volumes.begin(), child_volumes.end()); + +#ifndef NDEBUG + if (!success) { + collide_cat.error() + << "Unable to generate bounding volume for " << *this << ":\n" + << "Cannot put " << _bound->get_type() << " around:\n"; + for (int i = 0; i < (int)child_volumes.size(); i++) { + collide_cat.error(false) + << " " << *child_volumes[i] << "\n"; + } + } +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void CollisionNode:: +write_datagram(BamWriter *manager, Datagram &me) +{ + int i; + NamedNode::write_datagram(manager, me); + me.add_uint16(_solids.size()); + + for(i = 0; i < _solids.size(); i++) + { + manager->write_pointer(me, _solids[i]); + } + + me.add_uint32(_from_collide_mask.get_word()); + me.add_uint32(_into_collide_mask.get_word()); + me.add_uint8(_collide_geom); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void CollisionNode:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + int i; + NamedNode::fillin(scan, manager); + int num_solids = scan.get_uint16(); + _solids.clear(); + _solids.reserve(num_solids); + + for(i = 0; i < num_solids; i++) { + manager->read_pointer(scan, this); + _solids.push_back((CollisionSolid *)NULL); + } + + _from_collide_mask.set_word(scan.get_uint32()); + _into_collide_mask.set_word(scan.get_uint32()); + + if (manager->get_file_minor_ver() >= 2) { + _collide_geom = (scan.get_uint8() != 0); + } else { + _collide_geom = false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::complete_pointers +// Access: Public +// Description: Takes in a vector of pointes to TypedWriteable +// objects that correspond to all the requests for +// pointers that this object made to BamReader. +//////////////////////////////////////////////////////////////////// +int CollisionNode:: +complete_pointers(vector_typedWriteable &plist, BamReader* manager) { + int num_solids = _solids.size(); + int start = NamedNode::complete_pointers(plist, manager); + + for (int i = 0; i < num_solids; i++) { + _solids[i] = DCAST(CollisionSolid, plist[start + i]); + } + + return start + num_solids; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::make_CollisionNode +// Access: Protected +// Description: Factory method to generate a CollisionNode object +//////////////////////////////////////////////////////////////////// +TypedWriteable* CollisionNode:: +make_CollisionNode(const FactoryParams ¶ms) +{ + CollisionNode *me = new CollisionNode; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionNode::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a CollisionNode object +//////////////////////////////////////////////////////////////////// +void CollisionNode:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_CollisionNode); +} + + + + diff --git a/panda/src/collide/collisionNode.h b/panda/src/collide/collisionNode.h new file mode 100644 index 0000000000..64eba77e01 --- /dev/null +++ b/panda/src/collide/collisionNode.h @@ -0,0 +1,93 @@ +// Filename: collisionNode.h +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLISIONNODE_H +#define COLLISIONNODE_H + +#include + +#include "collisionSolid.h" +#include "collideMask.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : CollisionNode +// Description : A node in the scene graph that can hold any number of +// CollisionSolids. This may either represent a bit of +// static geometry in the scene that things will collide +// with, or an animated object twirling around in the +// world and running into things. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CollisionNode : public NamedNode { +public: + CollisionNode(const string &name = ""); + CollisionNode(const CollisionNode ©); + void operator = (const CollisionNode ©); + virtual ~CollisionNode(); + + virtual Node *make_copy() const; + virtual void xform(const LMatrix4f &mat); + + INLINE void set_collide_mask(CollideMask mask); + INLINE void set_from_collide_mask(CollideMask mask); + INLINE void set_into_collide_mask(CollideMask mask); + INLINE CollideMask get_from_collide_mask() const; + INLINE CollideMask get_into_collide_mask() const; + + INLINE void set_collide_geom(bool flag); + INLINE bool get_collide_geom() const; + + INLINE int get_num_solids() const; + INLINE CollisionSolid *get_solid(int n) const; + INLINE void remove_solid(int n); + INLINE int add_solid(CollisionSolid *solid); + + virtual void draw_traverse(); + virtual void output(ostream &out) const; + +protected: + virtual void recompute_bound(); + +private: + CollideMask _from_collide_mask; + CollideMask _into_collide_mask; + bool _collide_geom; + + typedef vector Solids; + Solids _solids; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + virtual int complete_pointers(vector_typedWriteable &plist, + BamReader *manager); + + static TypedWriteable *make_CollisionNode(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + NamedNode::init_type(); + register_type(_type_handle, "CollisionNode", + NamedNode::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "collisionNode.I" + +#endif diff --git a/panda/src/collide/collisionPlane.I b/panda/src/collide/collisionPlane.I new file mode 100644 index 0000000000..9d0d04f67a --- /dev/null +++ b/panda/src/collide/collisionPlane.I @@ -0,0 +1,80 @@ +// Filename: collisionPlane.I +// Created by: drose (25Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::Default Constructor +// Access: Public +// Description: This is only for the convenience of CollisionPolygon. +// Normally, you should not attempt to create an +// uninitialized CollisionPlane. +//////////////////////////////////////////////////////////////////// +INLINE CollisionPlane:: +CollisionPlane() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionPlane:: +CollisionPlane(const Planef &plane) : + _plane(plane) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionPlane:: +CollisionPlane(const CollisionPlane ©) : + CollisionSolid(copy), + _plane(copy._plane) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::get_normal +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LVector3f CollisionPlane:: +get_normal() const { + return _plane.get_normal(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::dist_to_plane +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float CollisionPlane:: +dist_to_plane(const LPoint3f &point) const { + return _plane.dist_to_plane(point); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::set_plane +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionPlane:: +set_plane(const Planef &plane) { + _plane = plane; + mark_bound_stale(); + mark_viz_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::get_plane +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const Planef &CollisionPlane:: +get_plane() const { + return _plane; +} diff --git a/panda/src/collide/collisionPlane.cxx b/panda/src/collide/collisionPlane.cxx new file mode 100644 index 0000000000..e224e7d352 --- /dev/null +++ b/panda/src/collide/collisionPlane.cxx @@ -0,0 +1,291 @@ +// Filename: collisionPlane.cxx +// Created by: drose (25Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionPlane.h" +#include "collisionHandler.h" +#include "collisionEntry.h" +#include "collisionSphere.h" +#include "collisionRay.h" +#include "config_collide.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TypeHandle CollisionPlane::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CollisionSolid *CollisionPlane:: +make_copy() { + return new CollisionPlane(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::test_intersection +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int CollisionPlane:: +test_intersection(CollisionHandler *, const CollisionEntry &, + const CollisionSolid *) const { + // Planes cannot currently be intersected from, only into. Do not + // add a CollisionPlane to a CollisionTraverser. + nassertr(false, 0); + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::xform +// Access: Public, Virtual +// Description: Transforms the solid by the indicated matrix. +//////////////////////////////////////////////////////////////////// +void CollisionPlane:: +xform(const LMatrix4f &mat) { + _plane = _plane * mat; + clear_viz_arcs(); + mark_bound_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionPlane:: +output(ostream &out) const { + out << "cplane, (" << _plane << ")"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::recompute_bound +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionPlane:: +recompute_bound() { + // Planes have an infinite bounding volume. + BoundedObject::recompute_bound(); + // Less than ideal: we throw away whatever we just allocated in + // BoundedObject. + _bound = new OmniBoundingVolume; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::test_intersection_from_sphere +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int CollisionPlane:: +test_intersection_from_sphere(CollisionHandler *record, + const CollisionEntry &entry) const { + const CollisionSphere *sphere; + DCAST_INTO_R(sphere, entry.get_from(), 0); + + LPoint3f from_center = sphere->get_center() * entry.get_wrt_space(); + LVector3f from_radius_v = + LVector3f(sphere->get_radius(), 0.0, 0.0) * entry.get_wrt_space(); + float from_radius = length(from_radius_v); + + float dist = dist_to_plane(from_center); + if (dist > from_radius) { + // No intersection. + return 0; + } + + if (collide_cat.is_debug()) { + collide_cat.debug() + << "intersection detected from " << *entry.get_from_node() << " into " + << *entry.get_into_node() << "\n"; + } + PT(CollisionEntry) new_entry = new CollisionEntry(entry); + + LVector3f into_normal = get_normal() * entry.get_inv_wrt_space(); + float into_depth = from_radius - dist; + + new_entry->set_into_surface_normal(into_normal); + new_entry->set_into_depth(into_depth); + + record->add_entry(new_entry); + return 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::test_intersection_from_ray +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int CollisionPlane:: +test_intersection_from_ray(CollisionHandler *record, + const CollisionEntry &entry) const { + const CollisionRay *ray; + DCAST_INTO_R(ray, entry.get_from(), 0); + + LPoint3f from_origin = ray->get_origin() * entry.get_wrt_space(); + LVector3f from_direction = ray->get_direction() * entry.get_wrt_space(); + + float t; + if (!_plane.intersects_line(t, from_origin, from_direction)) { + // No intersection. + return 0; + } + + if (t < 0.0) { + // The intersection point is before the start of the ray. + return 0; + } + + if (collide_cat.is_debug()) { + collide_cat.debug() + << "intersection detected from " << *entry.get_from_node() << " into " + << *entry.get_into_node() << "\n"; + } + PT(CollisionEntry) new_entry = new CollisionEntry(entry); + + LPoint3f into_intersection_point = from_origin + t * from_direction; + new_entry->set_into_intersection_point(into_intersection_point); + + record->add_entry(new_entry); + return 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::recompute_viz +// Access: Public, Virtual +// Description: Rebuilds the geometry that will be used to render a +// visible representation of the collision solid. +//////////////////////////////////////////////////////////////////// +void CollisionPlane:: +recompute_viz(Node *parent) { + if (collide_cat.is_debug()) { + collide_cat.debug() + << "Recomputing viz for " << *this << " on " << *parent << "\n"; + } + + // Since we can't represent an infinite plane, we'll have to be + // satisfied with drawing a big polygon. Choose four points on the + // plane to be the corners of the polygon. + + // We must choose four points fairly reasonably spread apart on + // the plane. We'll start with a center point and one corner + // point, and then use cross products to find the remaining three + // corners of a square. + + // The center point will be on the axis with the largest + // coefficent. The first corner will be diagonal in the other two + // dimensions. + + LPoint3f cp; + LVector3f p1, p2, p3, p4; + + LVector3f normal = get_normal(); + float D = _plane._d; + + if (fabs(normal[0]) > fabs(normal[1]) && + fabs(normal[0]) > fabs(normal[2])) { + // X has the largest coefficient. + cp.set(-D / normal[0], 0.0, 0.0); + p1 = LPoint3f(-(normal[1] + normal[2] + D)/normal[0], 1.0, 1.0) - cp; + + } else if (fabs(normal[1]) > fabs(normal[2])) { + // Y has the largest coefficient. + cp.set(0.0, -D / normal[1], 0.0); + p1 = LPoint3f(1.0, -(normal[0] + normal[2] + D)/normal[1], 1.0) - cp; + + } else { + // Z has the largest coefficient. + cp.set(0.0, 0.0, -D / normal[2]); + p1 = LPoint3f(1.0, 1.0, -(normal[0] + normal[1] + D)/normal[2]) - cp; + } + + p1 = normalize(p1); + p2 = cross(normal, p1); + p3 = cross(normal, p2); + p4 = cross(normal, p3); + + static const double plane_scale = 10.0; + + PTA_Vertexf verts; + verts.push_back(cp + p1 * plane_scale); + verts.push_back(cp + p2 * plane_scale); + verts.push_back(cp + p3 * plane_scale); + verts.push_back(cp + p4 * plane_scale); + + GeomQuad *quad = new GeomQuad; + quad->set_coords(verts, G_PER_VERTEX); + quad->set_num_prims(1); + + GeomNode *viz = new GeomNode("viz-plane"); + viz->add_geom(quad); + add_solid_viz(parent, viz); + add_wireframe_viz(parent, viz); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void CollisionPlane:: +write_datagram(BamWriter *manager, Datagram &me) +{ + CollisionSolid::write_datagram(manager, me); + _plane.write_datagram(me); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void CollisionPlane:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + CollisionSolid::fillin(scan, manager); + _plane.read_datagram(scan); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::make_CollisionPlane +// Access: Protected +// Description: Factory method to generate a CollisionPlane object +//////////////////////////////////////////////////////////////////// +TypedWriteable* CollisionPlane:: +make_CollisionPlane(const FactoryParams ¶ms) +{ + CollisionPlane *me = new CollisionPlane; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPlane::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a CollisionPlane object +//////////////////////////////////////////////////////////////////// +void CollisionPlane:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_CollisionPlane); +} diff --git a/panda/src/collide/collisionPlane.h b/panda/src/collide/collisionPlane.h new file mode 100644 index 0000000000..54b2b58ab6 --- /dev/null +++ b/panda/src/collide/collisionPlane.h @@ -0,0 +1,92 @@ +// Filename: collisionPlane.h +// Created by: drose (25Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLISIONPLANE_H +#define COLLISIONPLANE_H + +#include + +#include "collisionSolid.h" + +#include +#include + +/////////////////////////////////////////////////////////////////// +// Class : CollisionPlane +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CollisionPlane : public CollisionSolid { +protected: + INLINE CollisionPlane(); + +public: + INLINE CollisionPlane(const Planef &plane); + INLINE CollisionPlane(const CollisionPlane ©); + + virtual CollisionSolid *make_copy(); + + virtual int + test_intersection(CollisionHandler *record, + const CollisionEntry &entry, + const CollisionSolid *into) const; + + virtual void xform(const LMatrix4f &mat); + + virtual void output(ostream &out) const; + + INLINE LVector3f get_normal() const; + INLINE float dist_to_plane(const LPoint3f &point) const; + + INLINE void set_plane(const Planef &plane); + INLINE const Planef &get_plane() const; + +protected: + virtual void recompute_bound(); + +protected: + virtual int + test_intersection_from_sphere(CollisionHandler *record, + const CollisionEntry &entry) const; + virtual int + test_intersection_from_ray(CollisionHandler *record, + const CollisionEntry &entry) const; + + virtual void recompute_viz(Node *parent); + +private: + Planef _plane; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + + static TypedWriteable *make_CollisionPlane(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CollisionSolid::init_type(); + register_type(_type_handle, "CollisionPlane", + CollisionSolid::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "collisionPlane.I" + +#endif + + diff --git a/panda/src/collide/collisionPolygon.I b/panda/src/collide/collisionPolygon.I new file mode 100644 index 0000000000..c6bdc82966 --- /dev/null +++ b/panda/src/collide/collisionPolygon.I @@ -0,0 +1,55 @@ +// Filename: collisionPolygon.I +// Created by: drose (25Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionPolygon:: +CollisionPolygon(const LPoint3f &a, const LPoint3f &b, + const LPoint3f &c) { + LPoint3f array[3]; + array[0] = a; + array[1] = b; + array[2] = c; + setup_points(array, array + 3); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionPolygon:: +CollisionPolygon(const LPoint3f &a, const LPoint3f &b, + const LPoint3f &c, const LPoint3f &d) { + LPoint3f array[4]; + array[0] = a; + array[1] = b; + array[2] = c; + array[3] = d; + setup_points(array, array + 4); +} + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionPolygon:: +CollisionPolygon(const LPoint3f *begin, const LPoint3f *end) { + setup_points(begin, end); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionPolygon:: +CollisionPolygon(void) { +} diff --git a/panda/src/collide/collisionPolygon.cxx b/panda/src/collide/collisionPolygon.cxx new file mode 100644 index 0000000000..8d54ee9c51 --- /dev/null +++ b/panda/src/collide/collisionPolygon.cxx @@ -0,0 +1,650 @@ +// Filename: collisionPolygon.cxx +// Created by: drose (25Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionPolygon.h" +#include "collisionHandler.h" +#include "collisionEntry.h" +#include "collisionSphere.h" +#include "collisionRay.h" +#include "config_collide.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TypeHandle CollisionPolygon::_type_handle; + + + +//////////////////////////////////////////////////////////////////// +// Function: is_right +// Description: Returns true if the 2-d v1 is to the right of v2. +//////////////////////////////////////////////////////////////////// +INLINE bool +is_right(const LVector2f &v1, const LVector2f &v2) { + return (-v1[0] * v2[1] + v1[1] * v2[0]) > 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CollisionPolygon:: +CollisionPolygon(const CollisionPolygon ©) : + CollisionPlane(copy), + _points(copy._points), + _median(copy._median), + _axis(copy._axis), + _reversed(copy._reversed) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CollisionSolid *CollisionPolygon:: +make_copy() { + return new CollisionPolygon(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::test_intersection +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int CollisionPolygon:: +test_intersection(CollisionHandler *, const CollisionEntry &, + const CollisionSolid *into) const { + // Polygons cannot currently be intersected from, only into. Do not + // add a CollisionPolygon to a CollisionTraverser. + nassertr(false, 0); + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::xform +// Access: Public, Virtual +// Description: Transforms the solid by the indicated matrix. +//////////////////////////////////////////////////////////////////// +void CollisionPolygon:: +xform(const LMatrix4f &mat) { + // We need to convert all the vertices to 3-d for this operation, + // and then convert them back. Hopefully we won't lose too much + // precision during all of this. + +#ifndef NDEBUG + if (collide_cat.is_debug()) { + collide_cat.debug() + << "CollisionPolygon transformed by:\n"; + mat.write(collide_cat.debug(false), 2); + if (_points.empty()) { + collide_cat.debug(false) + << " (no points)\n"; + } + } +#endif + + if (!_points.empty()) { + vector verts; + Points::const_iterator pi; + for (pi = _points.begin(); pi != _points.end(); ++pi) { + verts.push_back(to_3d(*pi) * mat); + } + if (_reversed) { + reverse(verts.begin(), verts.end()); + } + setup_points(verts.begin(), verts.end()); + } + + clear_viz_arcs(); + mark_bound_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionPolygon:: +output(ostream &out) const { + out << "cpolygon"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::recompute_bound +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionPolygon:: +recompute_bound() { + // First, get ourselves a fresh, empty bounding volume. + BoundedObject::recompute_bound(); + assert(_bound != (BoundingVolume*)0L); + + GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, _bound); + + // Now actually compute the bounding volume by putting it around all + // of our vertices. + vector vertices; + Points::const_iterator pi; + for (pi = _points.begin(); pi != _points.end(); ++pi) { + vertices.push_back(to_3d(*pi)); + } + + gbv->around(vertices.begin(), vertices.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::test_intersection_from_sphere +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int CollisionPolygon:: +test_intersection_from_sphere(CollisionHandler *record, + const CollisionEntry &entry) const { + if (_points.size() < 3) { + return 0; + } + + const CollisionSphere *sphere; + DCAST_INTO_R(sphere, entry.get_from(), 0); + + LPoint3f from_center = sphere->get_center() * entry.get_wrt_space(); + LVector3f from_radius_v = + LVector3f(sphere->get_radius(), 0.0, 0.0) * entry.get_wrt_space(); + float from_radius = length(from_radius_v); + + float dist = dist_to_plane(from_center); + if (dist > from_radius || dist < -from_radius) { + // No intersection. + return 0; + } + + // Ok, we intersected the plane, but did we intersect the polygon? + + // The nearest point within the plane to our center is the + // intersection of the line (center, center+normal) with the plane. + LPoint3f plane_point; + bool really_intersects = + get_plane().intersects_line(plane_point, + from_center, from_center + get_normal()); + nassertr(really_intersects, 0); + + LPoint2f p = to_2d(plane_point); + + // Now we have found a point on the polygon's plane that corresponds + // to the point tangent to our collision sphere where it first + // touches the plane. We want to decide whether the sphere itself + // will intersect the polygon. We can approximate this by testing + // whether a circle of the given radius centered around this tangent + // point, in the plane of the polygon, would intersect. + + // But even this approximate test is too difficult. To approximate + // the approximation, we'll test two points: (1) the center itself. + // If this is inside the polygon, then certainly the circle + // intersects the polygon, and the sphere collides. (2) a point on + // the outside of the circle, nearest to the center of the polygon. + // If _this_ point is inside the polygon, then again the circle, and + // hence the sphere, intersects. If neither point is inside the + // polygon, chances are reasonably good the sphere doesn't intersect + // the polygon after all. + + if (is_inside(p)) { + // The circle's center is inside the polygon; we have a collision! + + } else { + + if (from_radius > 0.0) { + // Now find the point on the rim of the circle nearest the + // polygon's center. + + // First, get a vector from the center of the circle to the center + // of the polygon. + LVector2f rim = _median - p; + float rim_length = length(rim); + + if (rim_length <= from_radius) { + // Here's a surprise: the center of the polygon is within the + // circle! Since the center is guaranteed to be interior to the + // polygon (the polygon is convex), it follows that the circle + // intersects the polygon. + + } else { + // Now scale this vector to length radius, and get the new point. + rim = (rim * from_radius / rim_length) + p; + + // Is the new point within the polygon? + if (is_inside(rim)) { + // It sure is! The circle intersects! + + } else { + // No intersection. + return 0; + } + } + } + } + + if (collide_cat.is_debug()) { + collide_cat.debug() + << "intersection detected from " << *entry.get_from_node() << " into " + << *entry.get_into_node() << "\n"; + } + PT(CollisionEntry) new_entry = new CollisionEntry(entry); + + LVector3f into_normal = get_normal() * entry.get_inv_wrt_space(); + float into_depth = from_radius - dist; + + new_entry->set_into_surface_normal(into_normal); + new_entry->set_into_depth(into_depth); + + record->add_entry(new_entry); + return 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::test_intersection_from_ray +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int CollisionPolygon:: +test_intersection_from_ray(CollisionHandler *record, + const CollisionEntry &entry) const { + if (_points.size() < 3) { + return 0; + } + + const CollisionRay *ray; + DCAST_INTO_R(ray, entry.get_from(), 0); + + LPoint3f from_origin = ray->get_origin() * entry.get_wrt_space(); + LVector3f from_direction = ray->get_direction() * entry.get_wrt_space(); + + float t; + if (!get_plane().intersects_line(t, from_origin, from_direction)) { + // No intersection. + return 0; + } + + if (t < 0.0) { + // The intersection point is before the start of the ray. + return 0; + } + + LPoint3f plane_point = from_origin + t * from_direction; + if (!is_inside(to_2d(plane_point))) { + // Outside the polygon's perimeter. + return 0; + } + + if (collide_cat.is_debug()) { + collide_cat.debug() + << "intersection detected from " << *entry.get_from_node() << " into " + << *entry.get_into_node() << "\n"; + } + PT(CollisionEntry) new_entry = new CollisionEntry(entry); + + new_entry->set_into_intersection_point(plane_point); + + record->add_entry(new_entry); + return 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::recompute_viz +// Access: Public, Virtual +// Description: Rebuilds the geometry that will be used to render a +// visible representation of the collision solid. +//////////////////////////////////////////////////////////////////// +void CollisionPolygon:: +recompute_viz(Node *parent) { + if (collide_cat.is_debug()) { + collide_cat.debug() + << "Recomputing viz for " << *this << " on " << *parent << "\n"; + } + + if (_points.size() < 3) { + if (collide_cat.is_debug()) { + collide_cat.debug() + << "(Degenerate poly, ignoring.)\n"; + } + return; + } + + PTA_Vertexf verts; + Points::const_iterator pi; + for (pi = _points.begin(); pi != _points.end(); ++pi) { + verts.push_back(to_3d(*pi)); + } + if (_reversed) { + reverse(verts.begin(), verts.end()); + } + + PTA_int lengths; + lengths.push_back(_points.size()); + + GeomPolygon *polygon = new GeomPolygon; + polygon->set_coords(verts, G_PER_VERTEX); + polygon->set_num_prims(1); + polygon->set_lengths(lengths); + + GeomNode *viz = new GeomNode("viz-polygon"); + viz->add_geom(polygon); + add_solid_viz(parent, viz); + add_wireframe_viz(parent, viz); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::is_inside +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +bool CollisionPolygon:: +is_inside(const LPoint2f &p) const { + // We insist that the polygon be convex. This makes things a bit simpler. + + // In the case of a convex polygon, defined with points in counterclockwise + // order, a point is interior to the polygon iff the point is not right of + // each of the edges. + + for (int i = 0; i < _points.size() - 1; i++) { + if (is_right(p - _points[i], _points[i+1] - _points[i])) { + return false; + } + } + if (is_right(p - _points[_points.size() - 1], + _points[0] - _points[_points.size() - 1])) { + return false; + } + + return true; + +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::setup_points +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionPolygon:: +setup_points(const LPoint3f *begin, const LPoint3f *end) { + int num_points = end - begin; + nassertv(num_points >= 3); + + _points.clear(); + + // Tell the base CollisionPlane class what its plane will be. We + // can determine this from the first three 3-d points. + Planef plane(begin[0], begin[1], begin[2]); + set_plane(plane); + + LVector3f normal = get_normal(); + +#ifndef NDEBUG + // Make sure all the source points are good. + { + float normal_length = normal.length(); + bool all_ok = IS_THRESHOLD_EQUAL(normal_length, 1.0, 0.001); + + const LPoint3f *pi; + for (pi = begin; pi != end && all_ok; ++pi) { + if ((*pi).is_nan()) { + all_ok = false; + } else { + // Make sure no points are repeated. + const LPoint3f *pj; + for (pj = begin; pj != pi && all_ok; ++pj) { + if ((*pj).almost_equal(*pi)) { + all_ok = false; + } + } + } + } + + if (!all_ok) { + collide_cat.error() << "Invalid points passed to CollisionPolygon:\n"; + for (pi = begin; pi != end; ++pi) { + collide_cat.error(false) << " " << (*pi) << "\n"; + } + collide_cat.error(false) + << " normal " << normal << " with length " << normal_length << "\n"; + + nassertv(false); + } + } + + if (collide_cat.is_debug()) { + collide_cat.debug() + << "CollisionPolygon defined with " << num_points << " vertices:\n"; + const LPoint3f *pi; + for (pi = begin; pi != end; ++pi) { + collide_cat.debug(false) << " " << (*pi) << "\n"; + } + } +#endif + + // First determine the largest of |normal[0]|, |normal[1]|, and + // |normal[2]|. This will tell us which axis-aligned plane the + // polygon is most nearly aligned with, and therefore which plane we + // should project onto for determining interiorness of the + // intersection point. + + if (fabs(normal[0]) >= fabs(normal[1])) { + if (fabs(normal[0]) >= fabs(normal[2])) { + _axis = AT_x; + } else { + _axis = AT_z; + } + } else { + if (fabs(normal[1]) >= fabs(normal[2])) { + _axis = AT_y; + } else { + _axis = AT_z; + } + } + + // Now project all of the points onto the 2-d plane. + + const LPoint3f *pi; + switch (_axis) { + case AT_x: + for (pi = begin; pi != end; ++pi) { + const LPoint3f &point = (*pi); + _points.push_back(LPoint2f(point[1], point[2])); + } + break; + + case AT_y: + for (pi = begin; pi != end; ++pi) { + const LPoint3f &point = (*pi); + _points.push_back(LPoint2f(point[0], point[2])); + } + break; + + case AT_z: + for (pi = begin; pi != end; ++pi) { + const LPoint3f &point = (*pi); + _points.push_back(LPoint2f(point[0], point[1])); + } + break; + } + + nassertv(_points.size() >= 3); + + // One other complication: In projecting the polygon onto the plane, + // we might have lost its counterclockwise-vertex orientation. If + // this is the case, we must reverse the order of the vertices. + _reversed = is_right(_points[2] - _points[0], _points[1] - _points[0]); + if (_reversed) { + reverse(_points.begin(), _points.end()); + } + + // Finally, average up all the points to get the median. This is + // the geometric center of the polygon, and (since the polygon must + // be convex) is also a point within the polygon. + + _median = _points[0]; + for (int n = 1; n < _points.size(); n++) { + _median += _points[n]; + } + _median /= _points.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::to_2d +// Access: Private +// Description: Assuming the indicated point in 3-d space lies within +// the polygon's plane, returns the corresponding point +// in the polygon's 2-d definition space. +//////////////////////////////////////////////////////////////////// +LPoint2f CollisionPolygon:: +to_2d(const LPoint3f &point3d) const { + nassertr(!point3d.is_nan(), LPoint2f(0.0, 0.0)); + + // Project the point of intersection with the plane onto the + // axis-aligned plane we projected the polygon onto, and see if the + // point is interior to the polygon. + switch (_axis) { + case AT_x: + return LPoint2f(point3d[1], point3d[2]); + + case AT_y: + return LPoint2f(point3d[0], point3d[2]); + + case AT_z: + return LPoint2f(point3d[0], point3d[1]); + } + + nassertr(false, LPoint2f(0.0, 0.0)); + return LPoint2f(0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::to_3d +// Access: Private +// Description: Extrude the indicated point in the polygon's 2-d +// definition space back into 3-d coordinates. +//////////////////////////////////////////////////////////////////// +LPoint3f CollisionPolygon:: +to_3d(const LPoint2f &point2d) const { + nassertr(!point2d.is_nan(), LPoint3f(0.0, 0.0, 0.0)); + + LVector3f normal = get_normal(); + float D = get_plane()._d; + + nassertr(!normal.is_nan(), LPoint3f(0.0, 0.0, 0.0)); + nassertr(!cnan(D), LPoint3f(0.0, 0.0, 0.0)); + + switch (_axis) { + case AT_x: + return LPoint3f(-(normal[1]*point2d[0] + normal[2]*point2d[1] + D)/normal[0], + point2d[0], point2d[1]); + + case AT_y: + return LPoint3f(point2d[0], + -(normal[0]*point2d[0] + normal[2]*point2d[1] + D)/normal[1], + point2d[1]); + + case AT_z: + return LPoint3f(point2d[0], point2d[1], + -(normal[0]*point2d[0] + normal[1]*point2d[1] + D)/normal[2]); + } + + nassertr(false, LPoint3f(0.0, 0.0, 0.0)); + return LPoint3f(0.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void CollisionPolygon:: +write_datagram(BamWriter *manager, Datagram &me) +{ + int i; + + CollisionPlane::write_datagram(manager, me); + me.add_uint16(_points.size()); + for(i = 0; i < _points.size(); i++) + { + _points[i].write_datagram(me); + } + _median.write_datagram(me); + me.add_uint8(_axis); + me.add_uint8(_reversed); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void CollisionPolygon:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + int i; + LPoint2f temp; + CollisionPlane::fillin(scan, manager); + int size = scan.get_uint16(); + for(i = 0; i < size; i++) + { + temp.read_datagram(scan); + _points.push_back(temp); + } + _median.read_datagram(scan); + _axis = (enum AxisType)scan.get_uint8(); + + // It seems that Windows wants this expression to prevent a + // 'performance warning'. Whatever. + _reversed = (scan.get_uint8() != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::make_CollisionPolygon +// Access: Protected +// Description: Factory method to generate a CollisionPolygon object +//////////////////////////////////////////////////////////////////// +TypedWriteable* CollisionPolygon:: +make_CollisionPolygon(const FactoryParams ¶ms) +{ + CollisionPolygon *me = new CollisionPolygon; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionPolygon::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a CollisionPolygon object +//////////////////////////////////////////////////////////////////// +void CollisionPolygon:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_CollisionPolygon); +} + + diff --git a/panda/src/collide/collisionPolygon.h b/panda/src/collide/collisionPolygon.h new file mode 100644 index 0000000000..2aed5a7cca --- /dev/null +++ b/panda/src/collide/collisionPolygon.h @@ -0,0 +1,102 @@ +// Filename: collisionPolygon.h +// Created by: drose (25Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLISIONPOLYGON_H +#define COLLISIONPOLYGON_H + +#include + +#include "collisionPlane.h" + +#include + +/////////////////////////////////////////////////////////////////// +// Class : CollisionPolygon +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CollisionPolygon : public CollisionPlane { +public: + INLINE CollisionPolygon(const LPoint3f &a, const LPoint3f &b, + const LPoint3f &c); + INLINE CollisionPolygon(const LPoint3f &a, const LPoint3f &b, + const LPoint3f &c, const LPoint3f &d); + INLINE CollisionPolygon(const LPoint3f *begin, const LPoint3f *end); + CollisionPolygon(const CollisionPolygon ©); + + virtual CollisionSolid *make_copy(); + + virtual int + test_intersection(CollisionHandler *record, + const CollisionEntry &entry, + const CollisionSolid *into) const; + + virtual void xform(const LMatrix4f &mat); + + virtual void output(ostream &out) const; + +protected: + INLINE CollisionPolygon(void); + virtual void recompute_bound(); + +protected: + virtual int + test_intersection_from_sphere(CollisionHandler *record, + const CollisionEntry &entry) const; + virtual int + test_intersection_from_ray(CollisionHandler *record, + const CollisionEntry &entry) const; + + virtual void recompute_viz(Node *parent); + +private: + bool is_inside(const LPoint2f &p) const; + + void setup_points(const LPoint3f *begin, const LPoint3f *end); + LPoint2f to_2d(const LPoint3f &point3d) const; + LPoint3f to_3d(const LPoint2f &point2d) const; + +private: + typedef vector_LPoint2f Points; + Points _points; + LPoint2f _median; + + enum AxisType { + AT_x, AT_y, AT_z + }; + AxisType _axis; + bool _reversed; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + + static TypedWriteable *make_CollisionPolygon(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CollisionPlane::init_type(); + register_type(_type_handle, "CollisionPolygon", + CollisionPlane::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "collisionPolygon.I" + +#endif + + diff --git a/panda/src/collide/collisionRay.I b/panda/src/collide/collisionRay.I new file mode 100644 index 0000000000..d8d2b024b9 --- /dev/null +++ b/panda/src/collide/collisionRay.I @@ -0,0 +1,133 @@ +// Filename: collisionRay.I +// Created by: drose (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::Default Constructor +// Access: Public +// Description: Creates an invalid ray. This isn't terribly useful; +// it's expected that the user will subsequently adjust +// the ray via set_origin()/set_direction() or +// set_projection(). +//////////////////////////////////////////////////////////////////// +INLINE CollisionRay:: +CollisionRay() : + _origin(LPoint3f(0.0, 0.0, 0.0)), + _direction(LVector3f(0.0, 0.0, 0.0)) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionRay:: +CollisionRay(const LPoint3f &origin, const LVector3f &direction) : + _origin(origin), _direction(direction) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionRay:: +CollisionRay(float ox, float oy, float oz, + float dx, float dy, float dz) : + _origin(ox, oy, oz), _direction(dx, dy, dz) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionRay:: +CollisionRay(const CollisionRay ©) : + CollisionSolid(copy), + _origin(copy._origin), + _direction(copy._direction) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::set_origin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionRay:: +set_origin(const LPoint3f &origin) { + _origin = origin; + mark_bound_stale(); + mark_viz_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::set_origin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionRay:: +set_origin(float x, float y, float z) { + set_origin(LPoint3f(x, y, z)); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::get_origin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const LPoint3f &CollisionRay:: +get_origin() const { + return _origin; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::set_direction +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionRay:: +set_direction(const LVector3f &direction) { + _direction = direction; + mark_bound_stale(); + mark_viz_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::set_direction +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionRay:: +set_direction(float x, float y, float z) { + set_direction(LVector3f(x, y, z)); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::get_direction +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const LVector3f &CollisionRay:: +get_direction() const { + return _direction; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::set_projection +// Access: Public +// Description: Accepts a ProjectionNode and a 2-d point in the range +// [-1,1]. Sets the CollisionRay so that it begins at +// the ProjectionNode's near plane and extends to +// infinity, making it suitable for picking objects from +// the screen given a camera and a mouse location. +//////////////////////////////////////////////////////////////////// +INLINE bool CollisionRay:: +set_projection(const ProjectionNode *camera, float px, float py) { + return set_projection(camera, LPoint2f(px, py)); +} diff --git a/panda/src/collide/collisionRay.cxx b/panda/src/collide/collisionRay.cxx new file mode 100644 index 0000000000..9697bf02ed --- /dev/null +++ b/panda/src/collide/collisionRay.cxx @@ -0,0 +1,139 @@ +// Filename: collisionRay.cxx +// Created by: drose (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionRay.h" +#include "collisionHandler.h" +#include "collisionEntry.h" +#include "config_collide.h" + +#include +#include +#include +#include +#include +#include + +TypeHandle CollisionRay::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CollisionSolid *CollisionRay:: +make_copy() { + return new CollisionRay(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::test_intersection +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int CollisionRay:: +test_intersection(CollisionHandler *record, const CollisionEntry &entry, + const CollisionSolid *into) const { + return into->test_intersection_from_ray(record, entry); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::xform +// Access: Public, Virtual +// Description: Transforms the solid by the indicated matrix. +//////////////////////////////////////////////////////////////////// +void CollisionRay:: +xform(const LMatrix4f &mat) { + _origin = _origin * mat; + _direction = _direction * mat; + clear_viz_arcs(); + mark_bound_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionRay:: +output(ostream &out) const { + out << "ray, o (" << _origin << "), d (" << _direction << ")"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::set_projection +// Access: Public +// Description: Accepts a ProjectionNode and a 2-d point in the range +// [-1,1]. Sets the CollisionRay so that it begins at +// the ProjectionNode's near plane and extends to +// infinity, making it suitable for picking objects from +// the screen given a camera and a mouse location. +// +// Returns true if the point was acceptable, false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool CollisionRay:: +set_projection(const ProjectionNode *camera, const LPoint2f &point) { + const Projection *proj = camera->get_projection(); + + bool success = true; + if (!proj->extrude(point, _origin, _direction)) { + _origin.set(0.0, 0.0, 0.0); + _direction.set(0.0, 0.0, 0.0); + success = false; + } + mark_bound_stale(); + mark_viz_stale(); + + return success; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::recompute_bound +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionRay:: +recompute_bound() { + BoundedObject::recompute_bound(); + // Less than ideal: we throw away whatever we just allocated in + // BoundedObject. + _bound = new BoundingLine(_origin, _origin + _direction); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionRay::recompute_viz +// Access: Public, Virtual +// Description: Rebuilds the geometry that will be used to render a +// visible representation of the collision solid. +//////////////////////////////////////////////////////////////////// +void CollisionRay:: +recompute_viz(Node *parent) { + if (collide_cat.is_debug()) { + collide_cat.debug() + << "Recomputing viz for " << *this << " on " << *parent << "\n"; + } + + GeomLinestrip *ray = new GeomLinestrip; + PTA_Vertexf verts; + PTA_Colorf colors; + for (int i = 0; i < 100; i++) { + verts.push_back(_origin + (double)i * _direction); + colors.push_back(Colorf(1.0, 1.0, 1.0, 1.0) + + ((double)i / 100.0) * Colorf(0.0, 0.0, 0.0, -1.0)); + } + ray->set_coords(verts, G_PER_VERTEX); + ray->set_colors(colors, G_PER_VERTEX); + + PTA_int lengths; + lengths.push_back(99); + ray->set_lengths(lengths); + + ray->set_num_prims(1); + + GeomNode *viz = new GeomNode("viz-ray"); + viz->add_geom(ray); + add_other_viz(parent, viz); +} diff --git a/panda/src/collide/collisionRay.h b/panda/src/collide/collisionRay.h new file mode 100644 index 0000000000..8739251d35 --- /dev/null +++ b/panda/src/collide/collisionRay.h @@ -0,0 +1,85 @@ +// Filename: collisionRay.h +// Created by: drose (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLISIONRAY_H +#define COLLISIONRAY_H + +#include + +#include "collisionSolid.h" + +class ProjectionNode; + +/////////////////////////////////////////////////////////////////// +// Class : CollisionRay +// Description : An infinite ray, with a specific origin and +// direction. It begins at its origin and continues in +// one direction to infinity, and it has no radius. +// Useful for picking from a window, or for gravity +// effects. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CollisionRay : public CollisionSolid { +public: + INLINE CollisionRay(); + INLINE CollisionRay(const LPoint3f &origin, const LVector3f &direction); + INLINE CollisionRay(float ox, float oy, float oz, + float dx, float dy, float dz); + INLINE CollisionRay(const CollisionRay ©); + + virtual CollisionSolid *make_copy(); + + virtual int + test_intersection(CollisionHandler *record, + const CollisionEntry &entry, + const CollisionSolid *into) const; + + virtual void xform(const LMatrix4f &mat); + + virtual void output(ostream &out) const; + + INLINE void set_origin(const LPoint3f &origin); + INLINE void set_origin(float x, float y, float z); + INLINE const LPoint3f &get_origin() const; + + INLINE void set_direction(const LVector3f &direction); + INLINE void set_direction(float x, float y, float z); + INLINE const LVector3f &get_direction() const; + + bool set_projection(const ProjectionNode *camera, const LPoint2f &point); + INLINE bool set_projection(const ProjectionNode *camera, float px, float py); + +protected: + virtual void recompute_bound(); + +protected: + virtual void recompute_viz(Node *parent); + +private: + LPoint3f _origin; + LVector3f _direction; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CollisionSolid::init_type(); + register_type(_type_handle, "CollisionRay", + CollisionSolid::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "collisionRay.I" + +#endif + + diff --git a/panda/src/collide/collisionSolid.I b/panda/src/collide/collisionSolid.I new file mode 100644 index 0000000000..87e54af831 --- /dev/null +++ b/panda/src/collide/collisionSolid.I @@ -0,0 +1,50 @@ +// Filename: collisionSolid.I +// Created by: drose (27Jun00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::set_tangible +// Access: Public +// Description: Sets the current state of the 'tangible' flag. Set +// this true to make the solid tangible, so that a +// CollisionHandlerPusher will not allow another object +// to intersect it, or false to make it intangible, so +// that a CollisionHandlerPusher will ignore it except +// to throw an event. +//////////////////////////////////////////////////////////////////// +INLINE void CollisionSolid:: +set_tangible(bool tangible) { + if (tangible != _tangible) { + _tangible = tangible; + mark_viz_stale(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::is_tangible +// Access: Public +// Description: Returns whether the solid is considered 'tangible' or +// not. An intangible solid has no effect in a +// CollisionHandlerPusher (except to throw an event); +// it's useful for defining 'trigger' planes and +// spheres, that cause an effect when passed through. +//////////////////////////////////////////////////////////////////// +INLINE bool CollisionSolid:: +is_tangible() const { + return _tangible; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::mark_viz_stale +// Access: Protected +// Description: Called internally when the visualization may have +// been compromised by some change to internal state and +// will need to be recomputed the next time it is +// rendered. +//////////////////////////////////////////////////////////////////// +INLINE void CollisionSolid:: +mark_viz_stale() { + _viz_stale = true; +} diff --git a/panda/src/collide/collisionSolid.cxx b/panda/src/collide/collisionSolid.cxx new file mode 100644 index 0000000000..76a18e34c5 --- /dev/null +++ b/panda/src/collide/collisionSolid.cxx @@ -0,0 +1,233 @@ +// Filename: collisionSolid.cxx +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionSolid.h" +#include "config_collide.h" +#include "collisionEntry.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TypeHandle CollisionSolid::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CollisionSolid:: +CollisionSolid() { + _viz_stale = true; + _tangible = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CollisionSolid:: +CollisionSolid(const CollisionSolid ©) : + _tangible(copy._tangible) +{ + // Actually, there's not a whole lot here we want to copy. + _viz_stale = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CollisionSolid:: +~CollisionSolid() { + clear_viz_arcs(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::update_viz +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionSolid:: +update_viz(Node *parent) { + if (_viz_stale) { + clear_viz_arcs(); + recompute_viz(parent); + _viz_stale = false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionSolid:: +output(ostream &out) const { + out << get_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionSolid:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << (*this) << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::test_intersection_from_sphere +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int CollisionSolid:: +test_intersection_from_sphere(CollisionHandler *, + const CollisionEntry &) const { + collide_cat.warning() + << get_type() << "::test_intersection_from_sphere() called!\n"; + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::test_intersection_from_ray +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int CollisionSolid:: +test_intersection_from_ray(CollisionHandler *, + const CollisionEntry &) const { + collide_cat.warning() + << get_type() << "::test_intersection_from_ray() called!\n"; + return 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::clear_viz_arcs +// Access: Protected +// Description: Removes all of the solids' visualization geometry and +// marks the viz as stale so it will be recomputed next +// time the solid is rendered. +//////////////////////////////////////////////////////////////////// +void CollisionSolid:: +clear_viz_arcs() { + VizArcs::iterator vi; + for (vi = _solid_viz_arcs.begin(); vi != _solid_viz_arcs.end(); ++vi) { + remove_arc(*vi); + } + for (vi = _wireframe_viz_arcs.begin(); vi != _wireframe_viz_arcs.end(); ++vi) { + remove_arc(*vi); + } + for (vi = _other_viz_arcs.begin(); vi != _other_viz_arcs.end(); ++vi) { + remove_arc(*vi); + } + _solid_viz_arcs.clear(); + _wireframe_viz_arcs.clear(); + _other_viz_arcs.clear(); + _viz_stale = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::add_solid_viz +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionSolid:: +add_solid_viz(Node *parent, GeomNode *viz) { + RenderRelation *arc = new RenderRelation(parent, viz); + arc->set_transition(new CullFaceTransition(CullFaceProperty::M_cull_clockwise)); + arc->set_transition(new RenderModeTransition(RenderModeProperty::M_filled)); + arc->set_transition(new LightTransition(LightTransition::all_off())); + arc->set_transition(new TextureTransition(TextureTransition::off())); + arc->set_transition(new TransparencyTransition(TransparencyProperty::M_alpha)); + + if (is_tangible()) { + arc->set_transition(new ColorTransition(1.0, 1.0, 1.0, 0.5)); + } else { + arc->set_transition(new ColorTransition(1.0, 0.3, 0.5, 0.5)); + } + + _solid_viz_arcs.push_back(arc); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::add_wireframe_viz +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionSolid:: +add_wireframe_viz(Node *parent, GeomNode *viz) { + RenderRelation *arc = new RenderRelation(parent, viz); + arc->set_transition(new CullFaceTransition(CullFaceProperty::M_cull_none)); + arc->set_transition(new RenderModeTransition(RenderModeProperty::M_wireframe)); + arc->set_transition(new LightTransition(LightTransition::all_off())); + arc->set_transition(new TextureTransition(TextureTransition::off())); + arc->set_transition(new TransparencyTransition(TransparencyProperty::M_none)); + if (is_tangible()) { + arc->set_transition(new ColorTransition(0.0, 0.0, 1.0, 1.0)); + } else { + arc->set_transition(new ColorTransition(1.0, 1.0, 0.0, 1.0)); + } + + _wireframe_viz_arcs.push_back(arc); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void CollisionSolid:: +write_datagram(BamWriter *, Datagram &me) +{ + me.add_uint8(_tangible); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void CollisionSolid:: +fillin(DatagramIterator& scan, BamReader*) +{ + _tangible = (scan.get_uint8() != 0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolid::add_other_viz +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionSolid:: +add_other_viz(Node *parent, GeomNode *viz) { + RenderRelation *arc = new RenderRelation(parent, viz); + arc->set_transition(new CullFaceTransition(CullFaceProperty::M_cull_clockwise)); + arc->set_transition(new RenderModeTransition(RenderModeProperty::M_filled)); + arc->set_transition(new LightTransition(LightTransition::all_off())); + arc->set_transition(new TextureTransition(TextureTransition::off())); + arc->set_transition(new TransparencyTransition(TransparencyProperty::M_alpha)); + + _other_viz_arcs.push_back(arc); +} diff --git a/panda/src/collide/collisionSolid.h b/panda/src/collide/collisionSolid.h new file mode 100644 index 0000000000..268b95c752 --- /dev/null +++ b/panda/src/collide/collisionSolid.h @@ -0,0 +1,121 @@ +// Filename: collisionSolid.h +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLISIONSOLID_H +#define COLLISIONSOLID_H + +#include + +#include +#include +#include +#include +#include + +class CollisionHandler; +class CollisionEntry; +class CollisionSphere; +class Node; +class GeomNode; +class CollisionNode; + +/////////////////////////////////////////////////////////////////// +// Class : CollisionSolid +// Description : The abstract base class for all things that can +// collide with other things in the world, and all the +// things they can collide with (except geometry). +// +// This class and its derivatives really work very +// similarly to the way BoundingVolume and all of its +// derivatives work. There's a different subclass for +// each basic shape of solid, and double-dispatch +// function calls handle the subset of the N*N +// intersection tests that we care about. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CollisionSolid : + public TypedWriteableReferenceCount, public BoundedObject { +public: + CollisionSolid(); + CollisionSolid(const CollisionSolid ©); + virtual ~CollisionSolid(); + + virtual CollisionSolid *make_copy()=0; + + INLINE void set_tangible(bool tangible); + INLINE bool is_tangible() const; + + virtual int + test_intersection(CollisionHandler *record, + const CollisionEntry &entry, + const CollisionSolid *into) const=0; + + virtual void xform(const LMatrix4f &mat)=0; + + void update_viz(Node *parent); + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int + test_intersection_from_sphere(CollisionHandler *record, + const CollisionEntry &entry) const; + virtual int + test_intersection_from_ray(CollisionHandler *record, + const CollisionEntry &entry) const; + + INLINE void mark_viz_stale(); + void clear_viz_arcs(); + void add_solid_viz(Node *parent, GeomNode *viz); + void add_wireframe_viz(Node *parent, GeomNode *viz); + void add_other_viz(Node *parent, GeomNode *viz); + + virtual void recompute_viz(Node *parent)=0; + + typedef vector VizArcs; + VizArcs _solid_viz_arcs; + VizArcs _wireframe_viz_arcs; + VizArcs _other_viz_arcs; + bool _viz_stale; + bool _tangible; + +public: + virtual void write_datagram(BamWriter* manager, Datagram &me); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedWriteableReferenceCount::init_type(); + BoundedObject::init_type(); + register_type(_type_handle, "CollisionSolid", + TypedWriteableReferenceCount::get_class_type(), + BoundedObject::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + +friend class CollisionSphere; +friend class CollisionRay; +}; + +INLINE ostream &operator << (ostream &out, const CollisionSolid &cs) { + cs.output(out); + return out; +} + +#include "collisionSolid.I" + +#endif + diff --git a/panda/src/collide/collisionSphere.I b/panda/src/collide/collisionSphere.I new file mode 100644 index 0000000000..c0f6614851 --- /dev/null +++ b/panda/src/collide/collisionSphere.I @@ -0,0 +1,104 @@ +// Filename: collisionSphere.I +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionSphere:: +CollisionSphere(const LPoint3f ¢er, float radius) : + _center(center), _radius(radius) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionSphere:: +CollisionSphere(float cx, float cy, float cz, float radius) : + _center(cx, cy, cz), _radius(radius) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionSphere:: +CollisionSphere() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollisionSphere:: +CollisionSphere(const CollisionSphere ©) : + CollisionSolid(copy), + _center(copy._center), + _radius(copy._radius) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::set_center +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionSphere:: +set_center(const LPoint3f ¢er) { + _center = center; + mark_bound_stale(); + mark_viz_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::set_center +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionSphere:: +set_center(float x, float y, float z) { + set_center(LPoint3f(x, y, z)); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::get_center +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const LPoint3f &CollisionSphere:: +get_center() const { + return _center; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::set_radius +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CollisionSphere:: +set_radius(float radius) { + _radius = radius; + mark_bound_stale(); + mark_viz_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::get_radius +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float CollisionSphere:: +get_radius() const { + return _radius; +} + diff --git a/panda/src/collide/collisionSphere.cxx b/panda/src/collide/collisionSphere.cxx new file mode 100644 index 0000000000..0ea592995f --- /dev/null +++ b/panda/src/collide/collisionSphere.cxx @@ -0,0 +1,336 @@ +// Filename: collisionSphere.cxx +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionSphere.h" +#include "collisionRay.h" +#include "collisionHandler.h" +#include "collisionEntry.h" +#include "config_collide.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +TypeHandle CollisionSphere::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CollisionSolid *CollisionSphere:: +make_copy() { + return new CollisionSphere(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::test_intersection +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int CollisionSphere:: +test_intersection(CollisionHandler *record, const CollisionEntry &entry, + const CollisionSolid *into) const { + return into->test_intersection_from_sphere(record, entry); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::xform +// Access: Public, Virtual +// Description: Transforms the solid by the indicated matrix. +//////////////////////////////////////////////////////////////////// +void CollisionSphere:: +xform(const LMatrix4f &mat) { + _center = _center * mat; + + // This is a little cheesy and fails miserably in the presence of a + // non-proportionate scale. + LVector3f radius_v = LVector3f(_radius, 0.0, 0.0) * mat; + _radius = length(radius_v); + + clear_viz_arcs(); + mark_bound_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionSphere:: +output(ostream &out) const { + out << "csphere, c (" << _center << "), r " << _radius; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::recompute_bound +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionSphere:: +recompute_bound() { + BoundedObject::recompute_bound(); + nassertv(_bound != (BoundingVolume*)0L); + nassertv(!_center.is_nan() && !cnan(_radius)); + BoundingSphere sphere(_center, _radius); + _bound->extend_by(&sphere); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::test_intersection_from_sphere +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int CollisionSphere:: +test_intersection_from_sphere(CollisionHandler *record, + const CollisionEntry &entry) const { + const CollisionSphere *sphere; + DCAST_INTO_R(sphere, entry.get_from(), 0); + + LPoint3f from_center = sphere->get_center() * entry.get_wrt_space(); + LVector3f from_radius_v = + LVector3f(sphere->get_radius(), 0.0, 0.0) * entry.get_wrt_space(); + float from_radius = length(from_radius_v); + + LPoint3f into_center = _center; + float into_radius = _radius; + + LVector3f vec = from_center - into_center; + float dist2 = dot(vec, vec); + if (dist2 > (into_radius + from_radius) * (into_radius + from_radius)) { + // No intersection. + return 0; + } + + if (collide_cat.is_debug()) { + collide_cat.debug() + << "intersection detected from " << *entry.get_from_node() << " into " + << *entry.get_into_node() << "\n"; + } + PT(CollisionEntry) new_entry = new CollisionEntry(entry); + + float dist = sqrtf(dist2); + LVector3f into_normal = normalize(vec); + LPoint3f into_intersection_point = into_normal * (dist - from_radius); + float into_depth = into_radius + from_radius - dist; + + new_entry->set_into_surface_normal(into_normal); + new_entry->set_into_intersection_point(into_intersection_point); + new_entry->set_into_depth(into_depth); + + record->add_entry(new_entry); + return 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::test_intersection_from_ray +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int CollisionSphere:: +test_intersection_from_ray(CollisionHandler *record, + const CollisionEntry &entry) const { + const CollisionRay *ray; + DCAST_INTO_R(ray, entry.get_from(), 0); + + LPoint3f from_origin = ray->get_origin() * entry.get_wrt_space(); + LVector3f from_direction = ray->get_direction() * entry.get_wrt_space(); + + double t1, t2; + if (!intersects_line(t1, t2, from_origin, from_direction)) { + // No intersection. + return 0; + } + + if (t2 < 0.0) { + // Both intersection points are before the start of the ray. + return 0; + } + + if (collide_cat.is_debug()) { + collide_cat.debug() + << "intersection detected from " << *entry.get_from_node() << " into " + << *entry.get_into_node() << "\n"; + } + PT(CollisionEntry) new_entry = new CollisionEntry(entry); + + LPoint3f into_intersection_point; + if (t1 < 0.0) { + into_intersection_point = from_origin + t2 * from_direction; + } else { + into_intersection_point = from_origin + t1 * from_direction; + } + new_entry->set_into_intersection_point(into_intersection_point); + + record->add_entry(new_entry); + return 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::recompute_viz +// Access: Public, Virtual +// Description: Rebuilds the geometry that will be used to render a +// visible representation of the collision solid. +//////////////////////////////////////////////////////////////////// +void CollisionSphere:: +recompute_viz(Node *parent) { + if (collide_cat.is_debug()) { + collide_cat.debug() + << "Recomputing viz for " << *this << " on " << *parent << "\n"; + } + + GeomSphere *sphere = new GeomSphere; + PTA_Vertexf verts; + verts.push_back(_center); + verts.push_back(_center + LVector3f(_radius, 0.0, 0.0)); + sphere->set_coords(verts, G_PER_VERTEX); + sphere->set_num_prims(1); + + GeomNode *viz = new GeomNode("viz-sphere"); + viz->add_geom(sphere); + add_solid_viz(parent, viz); + // add_wireframe_viz(parent, viz); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::intersects_line +// Access: Protected +// Description: Determine the point(s) of intersect of a parametric +// line with the sphere. The line is infinite in both +// directions, and passes through "from" and from+delta. +// If the line does not intersect the sphere, the +// function returns false, and t1 and t2 are undefined. +// If it does intersect the sphere, it returns true, and +// t1 and t2 are set to the points along the equation +// from+t*delta that correspond to the two points of +// intersection. +//////////////////////////////////////////////////////////////////// +bool CollisionSphere:: +intersects_line(double &t1, double &t2, + const LPoint3f &from, const LVector3f &delta) const { + // Solve the equation for the intersection of a line with a sphere + // using the quadratic formula. + + // A line segment from f to f+d is defined as all P such that + // P = f + td for 0 <= t <= 1. + + // A sphere with radius r about point c is defined as all P such + // that r^2 = (P - c)^2. + + // Subsituting P in the above we have: + + // r^2 = (f + td - c)^2 = + // (f^2 + ftd - fc + ftd + t^2d^2 - tdc - fc - tdc + c^2) = + // t^2(d^2) + t(fd + fd - dc - dc) + (f^2 - fc - fc + c^2) = + // t^2(d^2) + t(2d(f - c)) + (f - c)^2 + + // Thus, the equation is quadratic in t, and we have + // at^2 + bt + c = 0 + + // Where a = d^2 + // b = 2d(f - c) + // c = (f - c)^2 - r^2 + + // Solving for t using the quadratic equation gives us the point of + // intersection along the line segment. Actually, there are two + // solutions (since it is quadratic): one for the front of the + // sphere, and one for the back. In the case there the line is + // tangent to the sphere, there is only one solution (and the + // radical is zero). + + double A = dot(delta, delta); + + nassertr(A != 0.0, false); + + LVector3f fc = from - _center; + double B = 2.0 * dot(delta, fc); + double fc_d2 = dot(fc, fc); + double C = fc_d2 - _radius * _radius; + + double radical = B*B - 4.0*A*C; + + if (IS_NEARLY_ZERO(radical)) { + // Tangent. + t1 = t2 = -B / (2.0*A); + return true; + } + + if (radical < 0.0) { + // No real roots: no intersection with the line. + return false; + } + + double sqrt_radical = sqrtf(radical); + t1 = ( -B - sqrt_radical ) / (2.0*A); + t2 = ( -B + sqrt_radical ) / (2.0*A); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void CollisionSphere:: +write_datagram(BamWriter *manager, Datagram &me) +{ + CollisionSolid::write_datagram(manager, me); + _center.write_datagram(me); + me.add_float64(_radius); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void CollisionSphere:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + CollisionSolid::fillin(scan, manager); + _center.read_datagram(scan); + _radius = scan.get_float64(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::make_CollisionSphere +// Access: Protected +// Description: Factory method to generate a CollisionSphere object +//////////////////////////////////////////////////////////////////// +TypedWriteable* CollisionSphere:: +make_CollisionSphere(const FactoryParams ¶ms) +{ + CollisionSphere *me = new CollisionSphere; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSphere::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a CollisionSphere object +//////////////////////////////////////////////////////////////////// +void CollisionSphere:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_CollisionSphere); +} + diff --git a/panda/src/collide/collisionSphere.h b/panda/src/collide/collisionSphere.h new file mode 100644 index 0000000000..c24f529cad --- /dev/null +++ b/panda/src/collide/collisionSphere.h @@ -0,0 +1,93 @@ +// Filename: collisionSphere.h +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLISIONSPHERE_H +#define COLLISIONSPHERE_H + +#include + +#include "collisionSolid.h" + +/////////////////////////////////////////////////////////////////// +// Class : CollisionSphere +// Description : A spherical collision volume or object. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CollisionSphere : public CollisionSolid { +public: + INLINE CollisionSphere(const LPoint3f ¢er, float radius); + INLINE CollisionSphere(float cx, float cy, float cz, float radius); + INLINE CollisionSphere(const CollisionSphere ©); + + virtual CollisionSolid *make_copy(); + + virtual int + test_intersection(CollisionHandler *record, + const CollisionEntry &entry, + const CollisionSolid *into) const; + + virtual void xform(const LMatrix4f &mat); + + virtual void output(ostream &out) const; + + INLINE void set_center(const LPoint3f ¢er); + INLINE void set_center(float x, float y, float z); + INLINE const LPoint3f &get_center() const; + + INLINE void set_radius(float radius); + INLINE float get_radius() const; + +protected: + INLINE CollisionSphere(void); + virtual void recompute_bound(); + +protected: + virtual int + test_intersection_from_sphere(CollisionHandler *record, + const CollisionEntry &entry) const; + virtual int + test_intersection_from_ray(CollisionHandler *record, + const CollisionEntry &entry) const; + + virtual void recompute_viz(Node *parent); + + bool intersects_line(double &t1, double &t2, + const LPoint3f &from, const LVector3f &delta) const; + +private: + LPoint3f _center; + float _radius; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + + static TypedWriteable *make_CollisionSphere(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CollisionSolid::init_type(); + register_type(_type_handle, "CollisionSphere", + CollisionSolid::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "collisionSphere.I" + +#endif + + diff --git a/panda/src/collide/collisionTraverser.I b/panda/src/collide/collisionTraverser.I new file mode 100644 index 0000000000..6116bc0d73 --- /dev/null +++ b/panda/src/collide/collisionTraverser.I @@ -0,0 +1,4 @@ +// Filename: collisionTraverser.I +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// diff --git a/panda/src/collide/collisionTraverser.cxx b/panda/src/collide/collisionTraverser.cxx new file mode 100644 index 0000000000..2447d018da --- /dev/null +++ b/panda/src/collide/collisionTraverser.cxx @@ -0,0 +1,553 @@ +// Filename: collisionTraverser.cxx +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionTraverser.h" +#include "collisionNode.h" +#include "collisionEntry.h" +#include "collisionPolygon.h" +#include "config_collide.h" + +#include +#include +#include +#include +#include +#include +#include + + +PStatCollector CollisionTraverser::_collisions_pcollector = + PStatCollector("Collisions", RGBColorf(1,0.5,0), 40); + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CollisionTraverser:: +CollisionTraverser(TypeHandle graph_type) : + _graph_type(graph_type) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CollisionTraverser:: +~CollisionTraverser() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::add_collider +// Access: Public +// Description: Adds a new CollisionNode, representing an object that +// will be tested for collisions into other objects, +// along with the handler that will serve each detected +// collision. Each CollisionNode may be served by only +// one handler at a time, but a given handler may serve +// many CollisionNodes. +// +// The handler that serves a particular node may be +// changed from time to time by calling add_collider() +// again on the same node. +//////////////////////////////////////////////////////////////////// +void CollisionTraverser:: +add_collider(CollisionNode *node, CollisionHandler *handler) { + nassertv(node != (CollisionNode *)NULL); + nassertv(handler != (CollisionHandler *)NULL); + + Colliders::iterator ci = _colliders.find(node); + if (ci != _colliders.end()) { + // We already knew about this collider. + if ((*ci).second != handler) { + // Change the handler. + (*ci).second = handler; + + // Now update our own reference counts within our handler set. + CollisionHandler *old_handler = (*ci).second; + Handlers::iterator hi = _handlers.find(old_handler); + nassertv(hi != _handlers.end()); + (*hi).second--; + nassertv((*hi).second >= 0); + if ((*hi).second == 0) { + _handlers.erase(hi); + } + + hi = _handlers.find(handler); + if (hi == _handlers.end()) { + _handlers.insert(Handlers::value_type(handler, 1)); + } else { + (*hi).second++; + } + } + + } else { + // We hadn't already known about this collider. + _colliders.insert(Colliders::value_type(node, handler)); + + Handlers::iterator hi = _handlers.find(handler); + if (hi == _handlers.end()) { + _handlers.insert(Handlers::value_type(handler, 1)); + } else { + (*hi).second++; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::remove_collider +// Access: Public +// Description: Removes the collider (and its associated handler) +// from the set of CollisionNodes that will be tested +// each frame for collisions into other objects. +// Returns true if the definition was found and removed, +// false if it wasn't present to begin with. +//////////////////////////////////////////////////////////////////// +bool CollisionTraverser:: +remove_collider(CollisionNode *node) { + Colliders::iterator ci = _colliders.find(node); + if (ci == _colliders.end()) { + // We didn't know about this node. + return false; + } + + CollisionHandler *handler = (*ci).second; + + // Update the set of handlers. + Handlers::iterator hi = _handlers.find(handler); + nassertr(hi != _handlers.end(), false); + (*hi).second--; + nassertr((*hi).second >= 0, false); + if ((*hi).second == 0) { + _handlers.erase(hi); + } + + _colliders.erase(ci); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::has_collider +// Access: Public +// Description: Returns true if the indicated node is current in the +// set of nodes that will be tested each frame for +// collisions into other objects. +//////////////////////////////////////////////////////////////////// +bool CollisionTraverser:: +has_collider(CollisionNode *node) const { + Colliders::const_iterator ci = _colliders.find(node); + return (ci != _colliders.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::get_handler +// Access: Public +// Description: Returns the handler that is currently assigned to +// serve the indicated collision node, or NULL if the +// node is not on the traverser's set of active nodes. +//////////////////////////////////////////////////////////////////// +CollisionHandler *CollisionTraverser:: +get_handler(CollisionNode *node) const { + Colliders::const_iterator ci = _colliders.find(node); + if (ci != _colliders.end()) { + return (*ci).second; + }; + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::clear_colliders +// Access: Public +// Description: Completely empties the set of collision nodes and +// their associated handlers. +//////////////////////////////////////////////////////////////////// +void CollisionTraverser:: +clear_colliders() { + _colliders.clear(); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::traverse +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionTraverser:: +traverse(Node *root) { + PStatTimer timer(_collisions_pcollector); + + CollisionLevelState level_state; + prepare_colliders(level_state); + + Handlers::iterator hi; + for (hi = _handlers.begin(); hi != _handlers.end(); ++hi) { + (*hi).first->begin_group(); + } + + df_traverse(root, *this, NullAttributeWrapper(), level_state, + _graph_type); + + for (hi = _handlers.begin(); hi != _handlers.end(); ++hi) { + (*hi).first->end_group(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionTraverser:: +output(ostream &out) const { + out << "CollisionTraverser, " << _colliders.size() + << " colliders and " << _handlers.size() << " handlers.\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionTraverser:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) + << "CollisionTraverser, " << _colliders.size() + << " colliders and " << _handlers.size() << " handlers:\n"; + Colliders::const_iterator ci; + for (ci = _colliders.begin(); ci != _colliders.end(); ++ci) { + CollisionNode *cnode = (*ci).first; + CollisionHandler *handler = (*ci).second; + nassertv(cnode != (CollisionNode *)NULL); + nassertv(handler != (CollisionHandler *)NULL); + + indent(out, indent_level + 2) + << *cnode << " handled by " << handler->get_type() << "\n"; + int num_solids = cnode->get_num_solids(); + for (int i = 0; i < num_solids; i++) { + cnode->get_solid(i)->write(out, indent_level + 4); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::prepare_colliders +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionTraverser:: +prepare_colliders(CollisionLevelState &level_state) { + level_state.clear(); + level_state.reserve(_colliders.size()); + + Colliders::iterator ci = _colliders.begin(); + while (ci != _colliders.end()) { + CollisionNode *cnode = (*ci).first; + ++ci; + + CollisionLevelState::ColliderDef def; + def._node = cnode; + get_rel_mat(cnode, NULL, def._space, _graph_type); + def._inv_space.invert_from(def._space); + +#ifndef NDEBUG + if (def._space.is_nan()) { + collide_cat.error() + << "Collider " << *cnode + << " has become NaN. Dropping from traverser.\n"; + // This is safe to do while traversing the list of colliders, + // because we have already incremented ci, above. + remove_collider(cnode); + } else +#endif + { + int num_solids = cnode->get_num_solids(); + for (int s = 0; s < num_solids; s++) { + CollisionSolid *collider = cnode->get_solid(s); + def._collider = collider; + level_state.prepare_collider(def); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::reached_node +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CollisionTraverser:: +reached_node(Node *node, NullAttributeWrapper &, + CollisionLevelState &level_state) { + if (node->is_of_type(CollisionNode::get_class_type())) { + level_state.reached_collision_node(); + + CollisionNode *cnode; + DCAST_INTO_R(cnode, node, false); + const BoundingVolume &node_bv = cnode->get_bound(); + const GeometricBoundingVolume *node_gbv = NULL; + if (node_bv.is_of_type(GeometricBoundingVolume::get_class_type())) { + DCAST_INTO_R(node_gbv, &node_bv, false); + } + + CollisionEntry entry; + entry._into_node = cnode; + get_rel_mat(node, NULL, entry._into_space, _graph_type); + + int num_colliders = level_state.get_num_colliders(); + for (int c = 0; c < num_colliders; c++) { + if (level_state.has_collider(c)) { + entry._from_node = level_state.get_node(c); + + if ((entry._from_node->get_from_collide_mask() & + cnode->get_into_collide_mask()) != 0) { + entry._from = level_state.get_collider(c); + entry._from_space = level_state.get_space(c); + + LMatrix4f into_space_inv; + get_rel_mat(NULL, node, into_space_inv, _graph_type); + entry._wrt_space = entry._from_space * into_space_inv; + entry._inv_wrt_space = + entry._into_space * level_state.get_inv_space(c); + + const GeometricBoundingVolume *col_gbv = + level_state.get_local_bound(c); + + compare_collider_to_node(entry, col_gbv, node_gbv); + } + } + } + + } else if (node->is_of_type(GeomNode::get_class_type()) && + level_state.has_any_collide_geom()) { + GeomNode *gnode; + DCAST_INTO_R(gnode, node, false); + const BoundingVolume &node_bv = gnode->get_bound(); + const GeometricBoundingVolume *node_gbv = NULL; + if (node_bv.is_of_type(GeometricBoundingVolume::get_class_type())) { + DCAST_INTO_R(node_gbv, &node_bv, false); + } + + CollisionEntry entry; + entry._into_node = gnode; + get_rel_mat(node, NULL, entry._into_space, _graph_type); + + int num_colliders = level_state.get_num_colliders(); + for (int c = 0; c < num_colliders; c++) { + if (level_state.has_collider_with_geom(c)) { + entry._from_node = level_state.get_node(c); + + entry._from = level_state.get_collider(c); + entry._from_space = level_state.get_space(c); + + LMatrix4f into_space_inv; + get_rel_mat(NULL, node, into_space_inv, _graph_type); + entry._wrt_space = entry._from_space * into_space_inv; + entry._inv_wrt_space = + entry._into_space * level_state.get_inv_space(c); + + const GeometricBoundingVolume *col_gbv = + level_state.get_local_bound(c); + + compare_collider_to_geom_node(entry, col_gbv, node_gbv); + } + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::forward_arc +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CollisionTraverser:: +forward_arc(NodeRelation *arc, NullTransitionWrapper &, + NullAttributeWrapper &, NullAttributeWrapper &, + CollisionLevelState &level_state) { + // Check the bounding volume on the arc against each of our + // colliders. + const BoundingVolume &arc_bv = arc->get_bound(); + if (arc_bv.is_of_type(GeometricBoundingVolume::get_class_type())) { + const GeometricBoundingVolume *arc_gbv; + DCAST_INTO_R(arc_gbv, &arc_bv, false); + + int num_colliders = level_state.get_num_colliders(); + for (int c = 0; c < num_colliders; c++) { + if (level_state.has_collider(c)) { + const GeometricBoundingVolume *col_gbv = + level_state.get_local_bound(c); + if (col_gbv != (GeometricBoundingVolume *)NULL) { + if (arc_gbv->contains(col_gbv) == 0) { + // This collider certainly does not intersect with any + // geometry at this arc or below. + level_state.omit_collider(c); + } + } + } + } + } + + if (!level_state.has_any_collider()) { + // No colliders intersect with the geometry at this arc or below, + // so don't bother traversing them. + return false; + } + + TransformTransition *tt; + if (get_transition_into(tt, arc)) { + // There's a transform on the arc; apply it to our colliders' + // bounding volumes. + LMatrix4f inv_mat; + inv_mat.invert_from(tt->get_matrix()); + level_state.xform(inv_mat); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::compare_collider_to_node +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionTraverser:: +compare_collider_to_node(CollisionEntry &entry, + const GeometricBoundingVolume *from_node_gbv, + const GeometricBoundingVolume *into_node_gbv) { + bool within_node_bounds = true; + if (from_node_gbv != (GeometricBoundingVolume *)NULL && + into_node_gbv != (GeometricBoundingVolume *)NULL) { + within_node_bounds = (into_node_gbv->contains(from_node_gbv) != 0); + } + + if (within_node_bounds) { + CollisionNode *cnode; + DCAST_INTO_V(cnode, entry._into_node); + int num_solids = cnode->get_num_solids(); + for (int s = 0; s < num_solids; s++) { + entry._into = cnode->get_solid(s); + if (entry._from != entry._into) { + const BoundingVolume &solid_bv = entry._into->get_bound(); + const GeometricBoundingVolume *solid_gbv = NULL; + if (num_solids > 1 && + solid_bv.is_of_type(GeometricBoundingVolume::get_class_type())) { + // Only bother to test against each solid's bounding + // volume if we have more than one solid in the node, as a + // slight optimization. (If the node contains just one + // solid, then the node's bounding volume, which we just + // tested, is the same as the solid's bounding volume.) + DCAST_INTO_V(solid_gbv, &solid_bv); + } + + compare_collider_to_solid(entry, from_node_gbv, solid_gbv); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::compare_collider_to_geom_node +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionTraverser:: +compare_collider_to_geom_node(CollisionEntry &entry, + const GeometricBoundingVolume *from_node_gbv, + const GeometricBoundingVolume *into_node_gbv) { + bool within_node_bounds = true; + if (from_node_gbv != (GeometricBoundingVolume *)NULL && + into_node_gbv != (GeometricBoundingVolume *)NULL) { + within_node_bounds = (into_node_gbv->contains(from_node_gbv) != 0); + } + + if (within_node_bounds) { + GeomNode *gnode; + DCAST_INTO_V(gnode, entry._into_node); + int num_geoms = gnode->get_num_geoms(); + for (int s = 0; s < num_geoms; s++) { + entry._into = (CollisionSolid *)NULL; + Geom *geom = DCAST(Geom, gnode->get_geom(s)); + if (geom != (Geom *)NULL) { + const BoundingVolume &geom_bv = geom->get_bound(); + const GeometricBoundingVolume *geom_gbv = NULL; + if (num_geoms > 1 && + geom_bv.is_of_type(GeometricBoundingVolume::get_class_type())) { + // Only bother to test against each geom's bounding + // volume if we have more than one geom in the node, as a + // slight optimization. (If the node contains just one + // geom, then the node's bounding volume, which we just + // tested, is the same as the geom's bounding volume.) + DCAST_INTO_V(geom_gbv, &geom_bv); + } + + compare_collider_to_geom(entry, geom, from_node_gbv, geom_gbv); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::compare_collider_to_solid +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionTraverser:: +compare_collider_to_solid(CollisionEntry &entry, + const GeometricBoundingVolume *from_node_gbv, + const GeometricBoundingVolume *solid_gbv) { + bool within_solid_bounds = true; + if (from_node_gbv != (GeometricBoundingVolume *)NULL && + solid_gbv != (GeometricBoundingVolume *)NULL) { + within_solid_bounds = (solid_gbv->contains(from_node_gbv) != 0); + } + if (within_solid_bounds) { + Colliders::const_iterator ci; + ci = _colliders.find(entry.get_from_node()); + nassertv(ci != _colliders.end()); + entry.get_from()->test_intersection((*ci).second, entry, entry.get_into()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionTraverser::compare_collider_to_geom +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CollisionTraverser:: +compare_collider_to_geom(CollisionEntry &entry, Geom *geom, + const GeometricBoundingVolume *from_node_gbv, + const GeometricBoundingVolume *geom_gbv) { + bool within_geom_bounds = true; + if (from_node_gbv != (GeometricBoundingVolume *)NULL && + geom_gbv != (GeometricBoundingVolume *)NULL) { + within_geom_bounds = (geom_gbv->contains(from_node_gbv) != 0); + } + if (within_geom_bounds) { + Colliders::const_iterator ci; + ci = _colliders.find(entry.get_from_node()); + nassertv(ci != _colliders.end()); + + PTA_Vertexf coords; + GeomBindType bind; + PTA_ushort vindex; + geom->get_coords(coords, bind, vindex); + PTA_ushort tris = geom->get_tris(); + + for (int i = 0; i < (int)tris.size(); i += 3) { + // Generate a temporary CollisionPolygon on the fly for each + // triangle in the Geom. + CollisionPolygon poly(coords[tris[i]], coords[tris[i + 1]], + coords[tris[i + 2]]); + if (entry.get_from()->test_intersection((*ci).second, entry, &poly) != 0) { + return; + } + } + } +} diff --git a/panda/src/collide/collisionTraverser.h b/panda/src/collide/collisionTraverser.h new file mode 100644 index 0000000000..24d08f4acc --- /dev/null +++ b/panda/src/collide/collisionTraverser.h @@ -0,0 +1,98 @@ +// Filename: collisionTraverser.h +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLLISIONTRAVERSER_H +#define COLLISIONTRAVERSER_H + +#include + +#include "collisionHandler.h" +#include "collisionLevelState.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +class CollisionNode; +class Geom; + +//////////////////////////////////////////////////////////////////// +// Class : CollisionTraverser +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CollisionTraverser : + public TraverserVisitor { +public: + CollisionTraverser(TypeHandle graph_type = RenderRelation::get_class_type()); + ~CollisionTraverser(); + + void add_collider(CollisionNode *node, CollisionHandler *handler); + bool remove_collider(CollisionNode *node); + bool has_collider(CollisionNode *node) const; + CollisionHandler *get_handler(CollisionNode *node) const; + void clear_colliders(); + + void traverse(Node *root); + + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + +private: + void prepare_colliders(CollisionLevelState &state); + +public: + // These methods, from parent class TraverserVisitor, define the + // behavior of the CollisionTraverser as it traverses the graph. + // Normally you would never call these directly. + bool reached_node(Node *node, NullAttributeWrapper &render_state, + CollisionLevelState &level_state); + + bool forward_arc(NodeRelation *arc, NullTransitionWrapper &trans, + NullAttributeWrapper &pre, NullAttributeWrapper &post, + CollisionLevelState &level_state); + +private: + void compare_collider_to_node(CollisionEntry &entry, + const GeometricBoundingVolume *from_node_gbv, + const GeometricBoundingVolume *into_node_gbv); + void compare_collider_to_geom_node(CollisionEntry &entry, + const GeometricBoundingVolume *from_node_gbv, + const GeometricBoundingVolume *into_node_gbv); + void compare_collider_to_solid(CollisionEntry &entry, + const GeometricBoundingVolume *from_node_gbv, + const GeometricBoundingVolume *solid_gbv); + void compare_collider_to_geom(CollisionEntry &entry, Geom *geom, + const GeometricBoundingVolume *from_node_gbv, + const GeometricBoundingVolume *solid_gbv); + +private: + PT(CollisionHandler) _default_handler; + TypeHandle _graph_type; + + typedef map Colliders; + Colliders _colliders; + + typedef map Handlers; + Handlers _handlers; + + // Statistics + static PStatCollector _collisions_pcollector; +}; + +INLINE ostream &operator << (ostream &out, const CollisionTraverser &trav) { + trav.output(out); + return out; +} + +#include "collisionTraverser.I" + +#endif + diff --git a/panda/src/collide/config_collide.cxx b/panda/src/collide/config_collide.cxx new file mode 100644 index 0000000000..9b8e463dc4 --- /dev/null +++ b/panda/src/collide/config_collide.cxx @@ -0,0 +1,48 @@ +// Filename: config_collide.cxx +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_collide.h" +#include "collisionNode.h" +#include "collisionSolid.h" +#include "collisionSphere.h" +#include "collisionPlane.h" +#include "collisionPolygon.h" +#include "collisionRay.h" +#include "collisionEntry.h" +#include "collisionHandler.h" +#include "collisionHandlerEvent.h" +#include "collisionHandlerPhysical.h" +#include "collisionHandlerPusher.h" +#include "collisionHandlerFloor.h" +#include "collisionHandlerQueue.h" + +#include + +Configure(config_collide); +NotifyCategoryDef(collide, ""); + +ConfigureFn(config_collide) { + CollisionNode::init_type(); + CollisionSolid::init_type(); + CollisionSphere::init_type(); + CollisionPlane::init_type(); + CollisionPolygon::init_type(); + + //Registration of writeable object's creation + //functions with BamReader's factory + CollisionNode::register_with_read_factory(); + CollisionPlane::register_with_read_factory(); + CollisionPolygon::register_with_read_factory(); + CollisionSphere::register_with_read_factory(); + CollisionRay::init_type(); + CollisionEntry::init_type(); + CollisionHandler::init_type(); + CollisionHandlerEvent::init_type(); + CollisionHandlerPhysical::init_type(); + CollisionHandlerPusher::init_type(); + CollisionHandlerFloor::init_type(); + CollisionHandlerQueue::init_type(); +} + diff --git a/panda/src/collide/config_collide.h b/panda/src/collide/config_collide.h new file mode 100644 index 0000000000..41a58c53b7 --- /dev/null +++ b/panda/src/collide/config_collide.h @@ -0,0 +1,14 @@ +// Filename: config_collide.h +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_COLLIDE_H +#define CONFIG_COLLIDE_H + +#include +#include + +NotifyCategoryDecl(collide, EXPCL_PANDA, EXPTP_PANDA); + +#endif diff --git a/panda/src/collide/test_collide.cxx b/panda/src/collide/test_collide.cxx new file mode 100644 index 0000000000..7b29d7f59b --- /dev/null +++ b/panda/src/collide/test_collide.cxx @@ -0,0 +1,62 @@ +// Filename: test_collide.cxx +// Created by: drose (24Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "collisionTraverser.h" +#include "collisionNode.h" +#include "collisionSphere.h" +#include "collisionPlane.h" +#include "collisionHandlerPusher.h" + +#include +#include +#include +#include +#include +#include +#include + +int +main(int argc, char *argv[]) { + PT_NamedNode r = new NamedNode("r"); + + PT_NamedNode a = new NamedNode("a"); + PT_NamedNode b = new NamedNode("b"); + + PT(CollisionNode) aa = new CollisionNode("aa"); + PT(CollisionNode) ab = new CollisionNode("ab"); + PT(CollisionNode) ba = new CollisionNode("ba"); + + RenderRelation *r_a = new RenderRelation(r, a); + RenderRelation *r_b = new RenderRelation(r, b); + + RenderRelation *a_aa = new RenderRelation(a, aa); + RenderRelation *a_ab = new RenderRelation(a, ab); + RenderRelation *b_ba = new RenderRelation(b, ba); + + + CollisionSphere *aa1 = new CollisionSphere(LPoint3f(0, 0, 0), 1); + aa->add_solid(aa1); + a_aa->set_transition(new TransformTransition(LMatrix4f::translate_mat(0, -5, 0))); + + CollisionSphere *ab1 = new CollisionSphere(LPoint3f(0, 2, 0), 1.5); + ab->add_solid(ab1); + + Planef plane(LVector3f(0, 1, 0), LPoint3f(0, 0, 0)); + CollisionPlane *ba1 = new CollisionPlane(plane); + ba->add_solid(ba1); + + CollisionTraverser ct; + PT(CollisionHandlerPusher) chp = new CollisionHandlerPusher; + ct.add_collider(aa, chp); + + ct.traverse(r); + + nout << "\nFrame 2:\n\n"; + + ct.traverse(r); + + return (0); +} + diff --git a/panda/src/configfiles/Configrc b/panda/src/configfiles/Configrc new file mode 100644 index 0000000000..976d7ad45f --- /dev/null +++ b/panda/src/configfiles/Configrc @@ -0,0 +1,5 @@ +# Let's set up a default window size of 800x600. The user can +# override this with a local ~/Configrc. +win-width 800 +win-height 600 + diff --git a/panda/src/configfiles/Sources.pp b/panda/src/configfiles/Sources.pp new file mode 100644 index 0000000000..7e39c1a54d --- /dev/null +++ b/panda/src/configfiles/Sources.pp @@ -0,0 +1,4 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#define INSTALL_DATA \ + panda.emacs panda.emacs.Xdefaults panda.init Configrc diff --git a/panda/src/configfiles/panda.emacs b/panda/src/configfiles/panda.emacs new file mode 100644 index 0000000000..877f7aaf6e --- /dev/null +++ b/panda/src/configfiles/panda.emacs @@ -0,0 +1,276 @@ +;; +;; C++ customization +;; + +; .h files should be in c++ mode. +(setq auto-mode-alist + (append + '(("\\.hh$" . c++-mode) + ("\\.h$" . c++-mode) + ("\\.I$" . c++-mode) + ("\\.y$" . c++-mode) ; yacc source files + ("\\.l$" . c++-mode) ; lex source files + ("\\.stk$" . scheme-mode) + ("\\.ss$" . scheme-mode) + ("\\.sal$" . scheme-mode) + ("\\.emacs$". lisp-mode) + ) auto-mode-alist)) + +;; +;; Scheme customization +;; +(defun scheme-send-buffer () + "Send the buffer, to inferior shell running scheme." + (interactive) + (scheme-send-region (point-min) (point-max))) + +(setq cmuscheme-load-hook + '((lambda () + (define-key inferior-scheme-mode-map "\n" 'newline-and-indent) + ))) + +(add-hook 'scheme-mode-hook + (function + (lambda () + (define-key scheme-mode-map "\r" 'newline-and-indent) + (define-key scheme-mode-map "\C-cd" 'scheme-send-definition) + (define-key scheme-mode-map "\C-cr" 'scheme-send-region) + (define-key scheme-mode-map "\C-cb" 'scheme-send-buffer) + (define-key scheme-mode-map "\C-c\C-i" 'scheme-indent-definition) + + ;; Scheme style + (put 'set! 'scheme-indent-function 1) + + ;; Chez Scheme specific + (put 'extend-syntax 'scheme-indent-function 1) + (put 'syntax-rules 'scheme-indent-function 1) + (put 'with 'scheme-indent-function 1) + (put 'when 'scheme-indent-function 1) + (put 'unless 'scheme-indent-function 1) + (put 'syntax-case 'scheme-indent-function 2) + (put 'with-syntax 'scheme-indent-function 1) + (put 'with 'scheme-indent-function 1) + (put 'foreign-procedure 'scheme-indent-function 1) + (put 'letrec-syntax 'scheme-indent-function 1) + + ;; Sal Specific + (put 'task-while 'scheme-indent-function 1) + (put 'task-until 'scheme-indent-function 1) + (put 'while 'scheme-indent-function 1) + (put 'until 'scheme-indent-function 1) + (put 'upon-death-of 'scheme-indent-function 1) + (put 'compile-time-if 'scheme-indent-function 1) + ))) + +(defun scheme-indent-definition () + "Fix indentation of the current definition." + (interactive) + (save-excursion + (beginning-of-defun 1) + (scheme-indent-sexp))) + +;; SAL/CHEZ-SCHEME HILIGHTING +(cond (window-system + (setq hilit-mode-enable-list '(not text-mode) + hilit-inhibit-hooks nil + hilit-inhibit-rebinding nil) + + (require 'hilit19) + + ;; Don't define explicit syntax colors here in deepevil.emacs, + ;; because that forces everyone to use your color scheme. + ;; Instead, just define a reasonable mapping for new syntax + ;; forms to old syntax forms. + + ;; If you don't like the color scheme and want to set explicit + ;; colors, do it in your own .emacs file. + + ;; If you simply can't read the highlited text, then try + ;; setting hilit-background-mode in your .emacs file to either + ;; 'light or 'dark, according to the background color you have + ;; set. + + (hilit-translate + ;; SAL + sal-task 'type + sal-event 'define + sal-define 'decl + sal-rtfm1 'include + sal-rtfm2 'include + sal-operator 'keyword + sal-comment1 'comment + ) + (hilit-set-mode-patterns + '(scheme-mode) + '( + ;; dwd + ("\\([Ss][Ff][Ww]\\|[Ww][Aa][Tt][Ss][Oo][Nn]\\|[Ss][Cc][Oo][Tt][Tt]\\)" nil sal-comment1) + + ;; rtfm stuff + ; ";;;*Description", ";;;*Public", and ";;;*Private" are rtfm1 + ("\\(;;;\\*Description\\|;;;\\*Public\\|;;;\\*Private\\)" nil sal-rtfm1) + ; ";;*", ";;.", and ";;;." are rtfm2 + ("\\(;;\\*\\|;;\\.\\|;;;\\.\\)" nil sal-rtfm2) + + ;; comments + (";.*" nil comment) + ("(/\\*" "\\*/)" comment) + ("(/\\*" nil sal-comment1) + ("\\*/)" nil sal-comment1) + + ;; loads, provides, requires + ("^\\s *(\\(provide\\|require\\|load-once\\|\\(try-\\|auto\\)?load\\) " nil include) + + ;; strings + (hilit-string-find ?\\ string) + + ;; define, define-macro, define-method + ("(def\\(ine\\|macro\\)[^ \t]*[ \t]*[^ \t]*[ \t\n]" nil sal-define) + + ;; mutators, anything ending in ! + ("(\\(\\(\\w\\|[:-]\\)*!\\)[ \t\n]" 1 define) + + ;; keywords + ("(\\(let\\w*\\|let\\*\\|case\\|let-generic\\|do\\|cond\\|else\\|sequence\\|begin\\|force\\|delay\\|if\\|lambda\\|fluid-let\\|when\\|unless\\|map\\|for-each\\|call\\(-with-current-continuation\\|/cc\\)\\)[ \t\n]" 1 keyword) + ("\\[\\(else\\)[ \t\n]" 1 keyword) + + ;; operators + ("(\\(c[ad]*r\\|list\\|list\\?\\|eq.*\\?\\|eval\\|apply\\|or\\|and\\|not\\|map\\|append\\|cons\\|null\\?\\|zero\\?\\|length\\|reverse\\|list-tail\\|list-ref\\|mem.*\\?\\|ass\\*?\\)[ \t\n]" 1 sal-operator) + + ;; events + ("\\(event-\\(number\\|name\\)\\|add\\(-persistent\\|-value\\)\\(-hook\\|-thunk\\)\\|add-hook\\|rm-hook\\|rm-all-hooks-named\\|reset-hooks\\|put-event\\|print-hooks\\|do-\\(events\\|hooks\\|later\\|timed-thunks\\)\\|timer-event\\)[ \t\n]" 1 sal-event) + + ;; task stuff + ("(\\(rm-task\\|spawn-task\\|reset-tasks\\|go\\|if-ever\\|upon-\\(death\\|death-of\\)\\|suspend-task\\|task-\\(while\\|pause\\|go\\|release\\|until\\)\\)[ \t\n]" 1 sal-task) + +)))) + +;; +;; Auto NEWHEADER command +;; + +(defun new-mk-elem () + (interactive) + (ct-mk-elem) + (remove-hook 'local-write-file-hooks 'new-mk-elem) + ) + +(defun newheader () + "Execute the newheader command with the current buffer name" + (interactive) + (add-hook 'local-write-file-hooks 'new-mk-elem) + (save-excursion (goto-char (point-min)) + (message (concat "Making a header for " + (file-name-nondirectory (buffer-name)) + "...")) + (shell-command-on-region + (point-min) (point-min) + (concat "newheader " (file-name-nondirectory (buffer-name))) + 1) + ) + ) + +(setq no-newheader nil) + +(defun auto-newheader () + "Automatically generate header for specfic file types" + + (let ((inslist '("\\.c++$" "\\.c$" "\\.I$" "\\.h$" "\\.hh$" "\\.ss$" "\\.sal$" "\\.stk$")) + (name (file-name-sans-versions buffer-file-name)) + (insert-file nil)) + + (while (and (not insert-file) inslist) + (if (string-match (car inslist) name) + (setq insert-file (car inslist)) + ) + (setq inslist (cdr inslist))) + (if insert-file + (if no-newheader + (message "Newheader is disabled") + (newheader))) + ) + ) + +; Create a new header if a file is not found. +(setq find-file-not-found-hooks + (cons 'auto-newheader + find-file-not-found-hooks)) + +;; Find an extra open paren... +;; We need more paren matching utilities! +;; Another way to find an extra open paren is to use backward-up-list from +;; the end of the buffer. + +(defun find-unbalanced-paren () + "Find an unbalanced parenthesis in the current buffer." + (interactive) + (let ((opoint (point))) + (goto-char (point-min)) + (while (beginning-of-defun -1) + ;; This errs if there is an extra open paren (missing close paren). + (forward-sexp 1) + ;; This errs if there is an extra close paren (missing open paren). + (if (looking-at "[ \t\n\r]*)") + (progn + (goto-char (- (match-end 0) 1)) + (error "Extra close parenthesis."))) + (goto-char (match-beginning 0))) + ;; Now try heuristics + (goto-char (point-min)) + ;; Beginning-of-defun isn't supposed to err, but it does if called at + ;; point-max, in Emacs 19.22. + (while (and (not (eobp)) (beginning-of-defun -1)) + ;; This errs if there is an extra open paren (missing close paren). + (forward-sexp 1) + ;; This errs if there is an extra close paren (missing open paren). + (if (not (looking-at "[ \t\n\r]+")) + (error "No whitespace at end of definition.")) + (goto-char (match-end 0)) + ;; If looking at a comment immediately following the def, skip it + (if (looking-at ";") + (forward-line 1)) + ;; Skip over whitespace + (if (looking-at "[ \t\n\r]+") + (goto-char (match-end 0))) + ;; We aren't in column 0 after skipping post-def comments and whitespace. + (if (not (= (current-column) 0)) + (error "Possible missing open parenthesis or extra close parenthesis.")) + (goto-char (match-beginning 0))) + (goto-char opoint) + (message "Couldn't find unbalanced parenthesis."))) + +(setq chez-last-line "") + +(defun chez () + "Run Chez Scheme (with arguments) as an inferior Scheme process." + (interactive) + (setq chez-last-line + (read-string "Run scheme (like this): scheme " chez-last-line)) + (run-scheme (concat "/fit/tool/bin/scheme " chez-last-line))) + +(setq sal-last-line "") + +(defun rtfm () + "Load the rtfm dump into a buffer." + (interactive) + (find-file "/usr/local/etc/rtfm/info")) + +(defun sal () + "Run sal (with arguments) as an inferior Scheme process." + (interactive) + (setq sal-last-line + (read-string "Run sal (like this): sal " sal-last-line)) + (run-scheme (concat "sal " sal-last-line))) + +;; PAREN MATCHING +(load "flashparen") +(flash-matching-mode 1) + +(defun goto-matching-paren () + "Goto the matching parenthesis of sexp after point (visually at point)." + (interactive) + (if (= (char-syntax (char-after (point))) ?\() + (goto-char (1- (scan-sexps (point) 1))) + (if (= (char-syntax (char-after (point))) ?\)) + (goto-char (scan-sexps (1+ (point)) -1))))) + diff --git a/panda/src/configfiles/panda.emacs.Xdefaults b/panda/src/configfiles/panda.emacs.Xdefaults new file mode 100644 index 0000000000..2f090a203a --- /dev/null +++ b/panda/src/configfiles/panda.emacs.Xdefaults @@ -0,0 +1,12 @@ +turn*organization: WDI +turn.editor: emacs + +Emacs*foreground: #00c0ff +Emacs*background: #002040 +Emacs*pointerColor: yellow +Emacs*cursorColor: yellow +Emacs*highlight.attributeForeground: white +Emacs*highlight.attributeBackground: black +Emacs*font: 8x13 +Emacs.geometry: 80x77+616+6 +Emacs*bitmapIcon: on diff --git a/panda/src/configfiles/panda.init b/panda/src/configfiles/panda.init new file mode 100644 index 0000000000..c25b572673 --- /dev/null +++ b/panda/src/configfiles/panda.init @@ -0,0 +1,24 @@ +SETABS PANDA_VER 0.8 +MODREL ETC_PATH etc +DOCSH if ( -e /usr/local/mozilla ) setenv USE_NSPR t +DOCSH if ( -e /for/program/mozilla ) setenv USE_NSPR t +DOCSH if (( -e /usr/include/GL ) || ( -e /usr/local/include/GL )) setenv HAVE_GL "yes" +DOCSH if ( ! $?CONFIG_CONFIG ) then +DOCSH setenv CONFIG_CONFIG :configpath=CFG_PATH:configpath=ETC_PATH +DOCSH setenv CFG_PATH ~ +DOCSH setenv CFG_PATH .:${CFG_PATH}:/usr/local/etc +DOCSH endif +DOCSH if ( -e /usr/local/libmikmod ) setenv USE_MIKMOD t +DOCSH if ( -e /for/program/libmikmod ) setenv USE_MIKMOD t +DOSH if [ -e /usr/local/mozilla ]; then USE_NSPR="t"; export USE_NSPR; fi +DOSH if [ -e /for/program/mozilla ]; then USE_NSPR="t"; export USE_NSPR; fi +DOSH if [ -e /usr/include/GL -o -e /usr/local/include/GL ]; then HAVE_GL="yes"; export HAVE_GL; fi +DOSH if [ -z "$CONFIG_CONFIG" ]; then +DOSH CONFIG_CONFIG=:configpath=CFG_PATH:configpath=ETC_PATH +DOSH CFG_PATH=.:$HOME:/usr/local/etc +DOSH export CONFIG_CONFIG CFG_PATH +DOSH fi +SEP PYTHONPATH : +MODREL PYTHONPATH lib +DOSH if [ -e /usr/local/libmikmod ]; then USE_MIKMOD="t"; export USE_MIKMOD; fi +DOSH if [ -e /for/program/libmikmod ]; then USE_MIKMOD="t"; export USE_MIKMOD; fi diff --git a/panda/src/cull/Sources.pp b/panda/src/cull/Sources.pp new file mode 100644 index 0000000000..9eb624525b --- /dev/null +++ b/panda/src/cull/Sources.pp @@ -0,0 +1,48 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET cull + #define LOCAL_LIBS \ + gobj sgraphutil graph putil sgraph mathutil sgattrib display \ + pstatclient + + #define SOURCES \ + config_cull.cxx config_cull.h cullState.I cullState.cxx cullState.h \ + cullStateLookup.I cullStateLookup.cxx cullStateLookup.h \ + cullStateSubtree.I cullStateSubtree.cxx cullStateSubtree.h \ + cullTraverser.I cullTraverser.cxx cullTraverser.h \ + directRenderTransition.I directRenderTransition.cxx \ + directRenderTransition.h geomBin.I geomBin.cxx geomBin.h \ + geomBinAttribute.I geomBinAttribute.N geomBinAttribute.cxx \ + geomBinAttribute.h geomBinBackToFront.I geomBinBackToFront.cxx \ + geomBinBackToFront.h geomBinFixed.I geomBinFixed.cxx geomBinFixed.h \ + geomBinGroup.I geomBinGroup.cxx geomBinGroup.h geomBinNormal.cxx \ + geomBinNormal.h geomBinTransition.I geomBinTransition.cxx \ + geomBinTransition.h geomBinUnsorted.I geomBinUnsorted.cxx \ + geomBinUnsorted.h + + #define INSTALL_HEADERS \ + config_cull.h cullLevelState.h cullState.I cullState.h \ + cullStateLookup.I cullStateLookup.h cullStateSubtree.I \ + cullStateSubtree.h cullTraverser.I cullTraverser.h \ + directRenderTransition.I directRenderTransition.h geomBin.I \ + geomBin.h geomBinAttribute.I geomBinAttribute.h \ + geomBinBackToFront.I geomBinBackToFront.h geomBinFixed.I \ + geomBinFixed.h geomBinGroup.I geomBinGroup.h geomBinNormal.h \ + geomBinTransition.I geomBinTransition.h geomBinUnsorted.I \ + geomBinUnsorted.h + + #define IGATESCAN all + +#end lib_target + +#begin test_bin_target + #define TARGET test_cull + #define LOCAL_LIBS \ + cull sgattrib + + #define SOURCES \ + test_cull.cxx + +#end test_bin_target + diff --git a/panda/src/cull/config_cull.cxx b/panda/src/cull/config_cull.cxx new file mode 100644 index 0000000000..6f60a701aa --- /dev/null +++ b/panda/src/cull/config_cull.cxx @@ -0,0 +1,35 @@ +// Filename: config_cull.cxx +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_cull.h" +#include "cullTraverser.h" +#include "geomBin.h" +#include "geomBinUnsorted.h" +#include "geomBinBackToFront.h" +#include "geomBinGroup.h" +#include "geomBinNormal.h" +#include "geomBinTransition.h" +#include "geomBinAttribute.h" +#include "geomBinFixed.h" +#include "directRenderTransition.h" + +#include + +Configure(config_cull); +NotifyCategoryDef(cull, ""); + +ConfigureFn(config_cull) { + CullTraverser::init_type(); + GeomBin::init_type(); + GeomBinUnsorted::init_type(); + GeomBinBackToFront::init_type(); + GeomBinGroup::init_type(); + GeomBinNormal::init_type(); + GeomBinTransition::init_type(); + GeomBinAttribute::init_type(); + GeomBinFixed::init_type(); + DirectRenderTransition::init_type(); +} + diff --git a/panda/src/cull/config_cull.h b/panda/src/cull/config_cull.h new file mode 100644 index 0000000000..78091d2e6e --- /dev/null +++ b/panda/src/cull/config_cull.h @@ -0,0 +1,14 @@ +// Filename: config_cull.h +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_CULL_H +#define CONFIG_CULL_H + +#include +#include + +NotifyCategoryDecl(cull, EXPCL_PANDA, EXPTP_PANDA); + +#endif diff --git a/panda/src/cull/cullLevelState.h b/panda/src/cull/cullLevelState.h new file mode 100644 index 0000000000..2b85da916f --- /dev/null +++ b/panda/src/cull/cullLevelState.h @@ -0,0 +1,25 @@ +// Filename: cullLevelState.h +// Created by: drose (17Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CULLLEVELSTATE_H +#define CULLLEVELSTATE_H + +#include + +#include "cullStateLookup.h" + +//////////////////////////////////////////////////////////////////// +// Class : CullLevelState +// Description : This is the state information the +// CullTraverser retains for each level during +// traversal. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CullLevelState { +public: + CullStateLookup *_lookup; +}; + +#endif + diff --git a/panda/src/cull/cullState.I b/panda/src/cull/cullState.I new file mode 100644 index 0000000000..b6cb479a67 --- /dev/null +++ b/panda/src/cull/cullState.I @@ -0,0 +1,248 @@ +// Filename: cullState.I +// Created by: drose (14Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: CullState::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullState:: +CullState(const AllTransitionsWrapper &trans) : + _trans(trans) +{ + _attrib = (NodeAttributes *)NULL; + _bin = (GeomBin *)NULL; + _empty_frames_count = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullState:: +~CullState() { + // This shouldn't be destructing while it's still assigned to a bin. + // If it is, something went screwy with the reference counts or with + // the bin logic somewhere. + nassertv(_bin == (GeomBin *)NULL); + + if (_attrib != (NodeAttributes *)NULL) { + delete _attrib; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int CullState:: +compare_to(const CullState &other) const { + return _trans.compare_to(other._trans); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::mark_verified +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CullState:: +mark_verified(Node *node, UpdateSeq now) { + _verified[node] = now; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::clear_current_nodes +// Access: Public +// Description: Called by the CullTraverser at the beginning of the +// frame to reset the list of GeomNodes currently +// visible, in preparation for building up a new list +// (via repeated calls to record_current_node()). +//////////////////////////////////////////////////////////////////// +INLINE void CullState:: +clear_current_nodes() { + if (_current_geom_nodes.empty() && _current_direct_nodes.empty()) { + _empty_frames_count++; + } + _current_geom_nodes.clear(); + _current_direct_nodes.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::record_current_geom_node +// Access: Public +// Description: Called by the CullTraverser to indicate a particular +// GeomNode, associated with this state, that is known to +// be visible this frame. +//////////////////////////////////////////////////////////////////// +INLINE void CullState:: +record_current_geom_node(const PT(GeomNode) &node) { + _current_geom_nodes.push_back(node); + _empty_frames_count = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::record_current_direct_node +// Access: Public +// Description: Called by the CullTraverser to indicate a particular +// Node, to be directly rendered, that is known to be +// visible this frame. +//////////////////////////////////////////////////////////////////// +INLINE void CullState:: +record_current_direct_node(const PT_Node &node) { + _current_direct_nodes.push_back(node); + _empty_frames_count = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::is_empty +// Access: Public +// Description: Returns true if the CullState has no current nodes to +// render (i.e. count_current_nodes() == 0), false +// otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool CullState:: +is_empty() const { + return _current_geom_nodes.empty() && _current_direct_nodes.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::count_current_nodes +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int CullState:: +count_current_nodes() const { + return _current_geom_nodes.size() + _current_direct_nodes.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::get_empty_frames_count +// Access: Public +// Description: Returns the number of consecutive frames the state +// has been empty, i.e. has represented no visible +// nodes. +//////////////////////////////////////////////////////////////////// +INLINE int CullState:: +get_empty_frames_count() const { + return _empty_frames_count; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::apply_to +// Access: Public +// Description: Indicates the initial state this frame; this is +// applied to the net transitions indicated by the +// CullState and stored within the CullState object. +//////////////////////////////////////////////////////////////////// +INLINE void CullState:: +apply_to(const AllAttributesWrapper &initial_state) { + if (_attrib != (NodeAttributes *)NULL) { + delete _attrib; + } + _attrib = initial_state.apply(_trans); + nassertv(_attrib != NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::get_attributes +// Access: Public +// Description: Returns the current state indicated by the CullState +// object, based on the CullState's net transitions set +// and the initial state set by apply_to(). +//////////////////////////////////////////////////////////////////// +INLINE const NodeAttributes &CullState:: +get_attributes() const { +#ifndef NDEBUG + static NodeAttributes empty_attributes; +#endif + + nassertr(_attrib != NULL, empty_attributes); + return *_attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::has_bin +// Access: Public +// Description: Returns true if the CullState has been assigned to a +// particular GeomBin. +//////////////////////////////////////////////////////////////////// +INLINE bool CullState:: +has_bin() const { + return _bin != (GeomBin *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::get_bin +// Access: Public +// Description: Returns the particular GeomBin the CullState has been +// assigned to, or NULL if it has not been assigned to a +// bin. +//////////////////////////////////////////////////////////////////// +INLINE GeomBin *CullState:: +get_bin() const { + return _bin; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::geom_size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullState::geom_size_type CullState:: +geom_size() const { + return _current_geom_nodes.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::geom_begin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullState::geom_iterator CullState:: +geom_begin() const { + return _current_geom_nodes.begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::geom_end +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullState::geom_iterator CullState:: +geom_end() const { + return _current_geom_nodes.end(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::direct_size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullState::direct_size_type CullState:: +direct_size() const { + return _current_direct_nodes.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::direct_begin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullState::direct_iterator CullState:: +direct_begin() const { + return _current_direct_nodes.begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::direct_end +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullState::direct_iterator CullState:: +direct_end() const { + return _current_direct_nodes.end(); +} diff --git a/panda/src/cull/cullState.cxx b/panda/src/cull/cullState.cxx new file mode 100644 index 0000000000..f986344b1e --- /dev/null +++ b/panda/src/cull/cullState.cxx @@ -0,0 +1,91 @@ +// Filename: cullState.cxx +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "cullState.h" + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: CullState::check_currency +// Access: Public +// Description: Returns true if the CullState is fresh enough to still +// apply to the indicated node and its associated cached +// transition, false if it should be recomputed. If +// false, the node entry is removed. +//////////////////////////////////////////////////////////////////// +bool CullState:: +check_currency(Node *node, const AllTransitionsWrapper &, + UpdateSeq now) { + // First, check the verified time stamp. + Verified::iterator vi; + vi = _verified.find(node); + nassertr(vi != _verified.end(), false); + + UpdateSeq verified_stamp = (*vi).second; + if (verified_stamp == now) { + return true; + } + + // Now we should verify the set of transitions individually. Skip + // that for now. + + // So we now know this node is no longer appropriate for this state. + // Remove any record we ever had of the node. + _verified.erase(vi); + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CullState:: +output(ostream &out) const { + out << count_current_nodes() << " nodes: { " << _trans << " }"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullState::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CullState:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) + << _current_geom_nodes.size() << " geom nodes"; + + if (!_current_geom_nodes.empty()) { + CurrentGeomNodes::const_iterator ci; + ci = _current_geom_nodes.begin(); + out << " (" << *(*ci); + ++ci; + while (ci != _current_geom_nodes.end()) { + out << ", " << *(*ci); + ++ci; + } + out << ")"; + } + + out << ", " << _current_direct_nodes.size() << " direct nodes"; + + if (!_current_direct_nodes.empty()) { + CurrentDirectNodes::const_iterator ci; + ci = _current_direct_nodes.begin(); + out << " (" << *(*ci); + ++ci; + while (ci != _current_direct_nodes.end()) { + out << ", " << *(*ci); + ++ci; + } + out << ")"; + } + out << ":\n"; + + _trans.write(out, indent_level + 2); +} diff --git a/panda/src/cull/cullState.h b/panda/src/cull/cullState.h new file mode 100644 index 0000000000..33ee73769d --- /dev/null +++ b/panda/src/cull/cullState.h @@ -0,0 +1,112 @@ +// Filename: cullState.h +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CULLSTATE_H +#define CULLSTATE_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class GraphicsStateGuardian; +class GeomBin; + +//////////////////////////////////////////////////////////////////// +// Class : CullState +// Description : This is the basic grouping unit of the CullTraverser. +// A single CullState object represents all of the +// GeomNodes throughout the scene graph that share an +// identical state. The CullTraverser collects these as +// it traverses, and tries to cache them between +// subsequent frames. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CullState : public ReferenceCount { +public: + typedef vector CurrentGeomNodes; + typedef vector_PT_Node CurrentDirectNodes; + +public: + INLINE CullState(const AllTransitionsWrapper &trans); + INLINE ~CullState(); + + INLINE int compare_to(const CullState &other) const; + + bool check_currency(Node *node, + const AllTransitionsWrapper &trans, + UpdateSeq now); + + INLINE void mark_verified(Node *node, UpdateSeq now); + + INLINE void clear_current_nodes(); + INLINE void record_current_geom_node(const PT(GeomNode) &node); + INLINE void record_current_direct_node(const PT_Node &node); + INLINE bool is_empty() const; + INLINE int count_current_nodes() const; + INLINE int get_empty_frames_count() const; + + INLINE void apply_to(const AllAttributesWrapper &initial_state); + INLINE const NodeAttributes &get_attributes() const; + + INLINE bool has_bin() const; + INLINE GeomBin *get_bin() const; + + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + +public: + // Functions and typedefs to support treating the CullState as a + // read-only STL container of current nodes. Beware! This + // interface is not safe to use outside of PANDA.DLL. + typedef CurrentGeomNodes::const_iterator geom_iterator; + typedef CurrentGeomNodes::const_iterator geom_const_iterator; + typedef CurrentGeomNodes::value_type geom_value_type; + typedef CurrentGeomNodes::size_type geom_size_type; + + INLINE geom_size_type geom_size() const; + INLINE geom_iterator geom_begin() const; + INLINE geom_iterator geom_end() const; + + typedef CurrentDirectNodes::const_iterator direct_iterator; + typedef CurrentDirectNodes::const_iterator direct_const_iterator; + typedef CurrentDirectNodes::value_type direct_value_type; + typedef CurrentDirectNodes::size_type direct_size_type; + + INLINE direct_size_type direct_size() const; + INLINE direct_iterator direct_begin() const; + INLINE direct_iterator direct_end() const; + +private: + AllTransitionsWrapper _trans; + NodeAttributes *_attrib; + + typedef map Verified; + Verified _verified; + + CurrentGeomNodes _current_geom_nodes; + CurrentDirectNodes _current_direct_nodes; + int _empty_frames_count; + + GeomBin *_bin; + friend class GeomBin; +}; + +INLINE ostream &operator << (ostream &out, const CullState &bs) { + bs.output(out); + return out; +} + +#include "cullState.I" + +#endif diff --git a/panda/src/cull/cullStateLookup.I b/panda/src/cull/cullStateLookup.I new file mode 100644 index 0000000000..28a69ff80a --- /dev/null +++ b/panda/src/cull/cullStateLookup.I @@ -0,0 +1,37 @@ +// Filename: cullStateLookup.I +// Created by: drose (13Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "cullState.h" + +//////////////////////////////////////////////////////////////////// +// Function: CullStateLookup::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullStateLookup:: +CullStateLookup() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CullStateLookup::is_completely_empty +// Access: Public +// Description: Returns true if the lookup is completely empty; that +// is, it represents no Nodes, and no subtrees. +//////////////////////////////////////////////////////////////////// +INLINE bool CullStateLookup:: +is_completely_empty() const { + return _cull_states.empty() && _subtrees.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullStateLookup::record_node +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CullStateLookup:: +record_node(Node *node, CullState *cs, UpdateSeq now) { + _cull_states[node] = cs; + cs->mark_verified(node, now); +} diff --git a/panda/src/cull/cullStateLookup.cxx b/panda/src/cull/cullStateLookup.cxx new file mode 100644 index 0000000000..0fa0352383 --- /dev/null +++ b/panda/src/cull/cullStateLookup.cxx @@ -0,0 +1,215 @@ +// Filename: cullStateLookup.cxx +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "cullStateLookup.h" +#include "cullState.h" +#include "cullStateSubtree.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: CullStateLookup::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CullStateLookup:: +~CullStateLookup() { + clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullStateLookup::clear +// Access: Public +// Description: Removes all of the data cached in the lookup. +//////////////////////////////////////////////////////////////////// +void CullStateLookup:: +clear() { + _cull_states.clear(); + + // We have to explicitly delete each of the subtrees created within + // this class. + Subtrees::iterator si; + for (si = _subtrees.begin(); si != _subtrees.end(); ++si) { + delete (*si).second; + } + _subtrees.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullStateLookup::find_node +// Access: Public +// Description: Looks up the indicated Node in the table, and +// determines if the associated CullState (if any) is +// fresh. If it is acceptable, returns its pointer; if +// there is no associated CullState or if the CullState +// is stale, removes the CullState and returns NULL. +//////////////////////////////////////////////////////////////////// +CullState *CullStateLookup:: +find_node(Node *node, + const AllTransitionsWrapper &trans, + UpdateSeq now) { + CullStates::iterator csi; + csi = _cull_states.find(node); + if (csi == _cull_states.end()) { + // No entry for the node. + return NULL; + } + + CullState *cs = (*csi).second; + if (cs->check_currency(node, trans, now)) { + // The entry is current enough to use. + return cs; + } + + // The entry is stale; remove it and return NULL. + _cull_states.erase(csi); + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullStateLookup::get_subtree +// Access: Public +// Description: A subtree is declared for each arc that begins a new +// shared instance of a node within the scene graph; +// i.e. each arc that leads to a node with multiple +// parents. Each of the descendents of this arc is +// added only to the thus-defined subtree, and not +// directly to the parent CullStateLookup; this allows us +// to define a different cached value for each instance +// of a particular node. +// +// This function looks up the indicated NodeRelation in +// the table, and determines if the associated +// CullStateSubtree (if any) is fresh. If it is +// acceptable, returns its pointer; if there is no +// associated CullStateSubtree or if the +// CullStateSubtree is stale, creates and returns a new +// CullStateSubtree. In any case, a usable +// CullStateSubtree is always returned. +//////////////////////////////////////////////////////////////////// +CullStateSubtree *CullStateLookup:: +get_subtree(const PT(NodeRelation) &arc, + const AllTransitionsWrapper &trans, + Node *top_subtree, + UpdateSeq now) { + Subtrees::iterator si; + si = _subtrees.find(arc); + if (si == _subtrees.end()) { + // No entry for the arc. + CullStateSubtree *subtree = + new CullStateSubtree(this, trans, top_subtree, now); + _subtrees.insert(Subtrees::value_type(arc, subtree)); + return subtree; + } + + CullStateSubtree *subtree = (*si).second; + if (subtree->check_currency(trans, top_subtree, now)) { + // The entry is current enough to use. + return subtree; + } + + // The entry is stale; update it. + subtree->update(trans, top_subtree, now); + return subtree; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullStateLookup::clean_out_top_nodes +// Access: Public +// Description: Walks through the list of nodes and arcs, and removes +// any pointers to CullStates that don't seem to be +// active any more. +//////////////////////////////////////////////////////////////////// +void CullStateLookup:: +clean_out_old_nodes() { + CullStates::iterator csi, csnext; + csi = _cull_states.begin(); + csnext = csi; + while (csi != _cull_states.end()) { + ++csnext; + + CullState *cs = (*csi).second; + if (!cs->has_bin() && cs->is_empty() && + cs->get_empty_frames_count() > 100) { + _cull_states.erase(csi); + } + csi = csnext; + } + + Subtrees::iterator si, snext; + si = _subtrees.begin(); + snext = si; + while (si != _subtrees.end()) { + ++snext; + + CullStateSubtree *subtree = (*si).second; + subtree->clean_out_old_nodes(); + if (subtree->is_completely_empty()) { + _subtrees.erase(si); + } + si = snext; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CullStateLookup::get_top_subtree +// Access: Public, Virtual +// Description: Returns the node that represents the top of the +// subtree for this particular CullStateSubtree. In the +// case of the root CullStateLookup, this always returns +// NULL, indicating the root of the graph. +//////////////////////////////////////////////////////////////////// +Node *CullStateLookup:: +get_top_subtree() const { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullStateLookup::compose_trans +// Access: Public, Virtual +// Description: Composes the transitions given in "from" with the net +// transitions that appear above the top node of this +// subtree, and returns the result in "to". This is a +// pass-thru for CullStateLookup; it has effect only for +// CullStateSubtree. +//////////////////////////////////////////////////////////////////// +void CullStateLookup:: +compose_trans(const AllTransitionsWrapper &from, + AllTransitionsWrapper &to) const { + to = from; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullStateLookup::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CullStateLookup:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << "CullStateLookup("; + bool is_first = true; + + CullStates::const_iterator csi; + for (csi = _cull_states.begin(); csi != _cull_states.end(); ++csi) { + if (!is_first) { + out << ", "; + } + is_first = false; + out << *(*csi).first; + } + out << ") {\n"; + + Subtrees::const_iterator si; + for (si = _subtrees.begin(); si != _subtrees.end(); ++si) { + const NodeRelation *arc = (*si).first; + const CullStateSubtree *subtree = (*si).second; + + indent(out, indent_level + 2) << *arc << " {\n"; + subtree->write(out, indent_level + 4); + indent(out, indent_level + 2) << "}\n"; + } + + indent(out, indent_level) << "}\n"; +} diff --git a/panda/src/cull/cullStateLookup.h b/panda/src/cull/cullStateLookup.h new file mode 100644 index 0000000000..0172c792fd --- /dev/null +++ b/panda/src/cull/cullStateLookup.h @@ -0,0 +1,72 @@ +// Filename: cullStateLookup.h +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CULLSTATELOOKUP_H +#define CULLSTATELOOKUP_H + +#include + +#include +#include +#include + +#include + +class CullState; +class CullStateSubtree; + +//////////////////////////////////////////////////////////////////// +// Class : CullStateLookup +// Description : This object stores a set of CullState pointers, one +// for each unique state encountered in the scene graph. +// It can combine two identical states encountered at +// different parts of the scene graph into the same +// CullState pointer. +// +// It also supports instancing by maintaining a tree of +// instanced nodes: for each node in the scene graph +// with multiple parents, a new CullStateLookup object +// (actually, a CullStateSubtree object) is kept which +// tracks the state changes below that node. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CullStateLookup { +public: + CullStateLookup(); + virtual ~CullStateLookup(); + + void clear(); + INLINE bool is_completely_empty() const; + + CullState *find_node(Node *node, + const AllTransitionsWrapper &trans, + UpdateSeq now); + CullStateSubtree *get_subtree(const PT(NodeRelation) &arc, + const AllTransitionsWrapper &trans, + Node *top_subtree, + UpdateSeq now); + + INLINE void record_node(Node *node, CullState *cs, + UpdateSeq now); + + void clean_out_old_nodes(); + + virtual Node *get_top_subtree() const; + + virtual void compose_trans(const AllTransitionsWrapper &from, + AllTransitionsWrapper &to) const; + + virtual void write(ostream &out, int indent_level = 0) const; + +private: + typedef map CullStates; + CullStates _cull_states; + + typedef map Subtrees; + Subtrees _subtrees; +}; + +#include "cullStateLookup.I" + +#endif diff --git a/panda/src/cull/cullStateSubtree.I b/panda/src/cull/cullStateSubtree.I new file mode 100644 index 0000000000..3ceb209667 --- /dev/null +++ b/panda/src/cull/cullStateSubtree.I @@ -0,0 +1,44 @@ +// Filename: cullStateSubtree.I +// Created by: drose (13Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: CullStateSubtree::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullStateSubtree:: +CullStateSubtree(CullStateLookup *parent, + const AllTransitionsWrapper &trans, + Node *top_subtree, + UpdateSeq now) : + _parent(parent), + _trans(trans), + _top_subtree(top_subtree), + _verified(now) +{ + _parent->compose_trans(_trans, _trans_from_root); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullStateSubtree::update +// Access: Public +// Description: Indicates a possibly new set of transitions for the +// top node of this subtree. Updates all internal state +// to reflect the new set, or does nothing if the set is +// unchanged. +//////////////////////////////////////////////////////////////////// +INLINE void CullStateSubtree:: +update(const AllTransitionsWrapper &trans, Node *top_subtree, UpdateSeq now) { + // We shouldn't *need* to call clear(). This will unnecessarily + // blow out the cache for all of our children. We do it for now + // just to cut down on things that can go wrong, but this should + // come out of here before long. + clear(); + + _trans = trans; + _parent->compose_trans(_trans, _trans_from_root); + _top_subtree = top_subtree; + _verified = now; +} diff --git a/panda/src/cull/cullStateSubtree.cxx b/panda/src/cull/cullStateSubtree.cxx new file mode 100644 index 0000000000..3b84abbae6 --- /dev/null +++ b/panda/src/cull/cullStateSubtree.cxx @@ -0,0 +1,69 @@ +// Filename: cullStateSubtree.cxx +// Created by: drose (09Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "cullStateSubtree.h" + + +//////////////////////////////////////////////////////////////////// +// Function: CullStateSubtree::Constructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CullStateSubtree:: +~CullStateSubtree() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CullStateSubtree::check_currency +// Access: Public +// Description: Returns true if the CullStateSubtree is fresh enough to +// still apply to the indicated cached transition, false +// if it should be recomputed. +//////////////////////////////////////////////////////////////////// +bool CullStateSubtree:: +check_currency(const AllTransitionsWrapper &, Node *top_subtree, + UpdateSeq now) { + // Make sure we've still got the same top_subtree node. + if (_top_subtree != top_subtree) { + return false; + } + + // First, check the verified time stamp. + if (_verified == now) { + return true; + } + + // Now we should verify the set of transitions individually. Skip + // that for now. + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullStateLookup::get_top_subtree +// Access: Public, Virtual +// Description: Returns the node that represents the top of the +// subtree for this particular CullStateSubtree. +//////////////////////////////////////////////////////////////////// +Node *CullStateSubtree:: +get_top_subtree() const { + return _top_subtree; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullStateSubtree::compose_trans +// Access: Public, Virtual +// Description: Composes the transitions given in "from" with the net +// transitions that appear above the top node of this +// subtree, and returns the result in "to". This is a +// pass-thru for CullStateLookup; it has effect only for +// CullStateSubtree. +//////////////////////////////////////////////////////////////////// +void CullStateSubtree:: +compose_trans(const AllTransitionsWrapper &from, + AllTransitionsWrapper &to) const { + to = _trans_from_root; + to.compose_in_place(from); +} diff --git a/panda/src/cull/cullStateSubtree.h b/panda/src/cull/cullStateSubtree.h new file mode 100644 index 0000000000..b58f4f465e --- /dev/null +++ b/panda/src/cull/cullStateSubtree.h @@ -0,0 +1,55 @@ +// Filename: cullStateSubtree.h +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CULLSTATESUBTREE_H +#define CULLSTATESUBTREE_H + +#include + +#include "cullStateLookup.h" + +#include +#include +#include + +class CullStateLookup; + +//////////////////////////////////////////////////////////////////// +// Class : CullStateSubtree +// Description : A specialization of CullStateLookup for tracking the +// state at and below an instanced node in the scene +// graph. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CullStateSubtree : public CullStateLookup { +public: + INLINE CullStateSubtree(CullStateLookup *parent, + const AllTransitionsWrapper &trans, + Node *top_subtree, + UpdateSeq now); + virtual ~CullStateSubtree(); + + bool check_currency(const AllTransitionsWrapper &trans, + Node *top_subtree, + UpdateSeq now); + INLINE void update(const AllTransitionsWrapper &trans, + Node *top_subtree, + UpdateSeq now); + + virtual Node *get_top_subtree() const; + + virtual void compose_trans(const AllTransitionsWrapper &from, + AllTransitionsWrapper &to) const; + +private: + CullStateLookup *_parent; + AllTransitionsWrapper _trans; + AllTransitionsWrapper _trans_from_root; + Node *_top_subtree; + UpdateSeq _verified; +}; + +#include "cullStateSubtree.I" + +#endif diff --git a/panda/src/cull/cullTraverser.I b/panda/src/cull/cullTraverser.I new file mode 100644 index 0000000000..58d5695649 --- /dev/null +++ b/panda/src/cull/cullTraverser.I @@ -0,0 +1,121 @@ +// Filename: cullTraverser.I +// Created by: drose (13Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "cullStateSubtree.h" +#include "config_cull.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: CullTraverser::set_default_bin +// Access: Public +// Description: Specifies the default GeomBin that any geometry will +// be assigned to if the scene graph doesn't specify +// otherwise. There must always be some default GeomBin +// in effect. +//////////////////////////////////////////////////////////////////// +INLINE void CullTraverser:: +set_default_bin(GeomBin *bin) { + nassertv(bin != (GeomBin *)NULL); + bin->attach_to(this, bin->get_sort()); + _default_bin = bin; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullTraverser::get_default_bin +// Access: Public +// Description: Returns the default GeomBin that any geometry will +// be assigned to if the scene graph doesn't specify +// otherwise. +//////////////////////////////////////////////////////////////////// +INLINE GeomBin *CullTraverser:: +get_default_bin() const { + return _default_bin; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullTraverser::draw_geom +// Access: Public +// Description: Sets up the GSG to draw the indicated GeomNode in the +// indicated state. If the state so demands it and the +// GeomNode has children, draws the rest of the +// hierarchy below it immediately. +//////////////////////////////////////////////////////////////////// +INLINE void CullTraverser:: +draw_geom(GeomNode *geom_node, const AllAttributesWrapper &initial_state) { + if (cull_cat.is_spam()) { + cull_cat.spam() + << "Drawing " << *geom_node << " with state: " << initial_state << "\n"; + } + nassertv(geom_node != (GeomNode *)NULL); + _gsg->set_state(initial_state.get_attributes(), true); + _gsg->prepare_display_region(); + geom_node->draw(_gsg); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullTraverser::draw_direct +// Access: Public +// Description: Draws the indicated subtree beginning at the +// indicated node, and including all nested nodes below. +//////////////////////////////////////////////////////////////////// +INLINE void CullTraverser:: +draw_direct(Node *node, const AllAttributesWrapper &initial_state) { + if (cull_cat.is_spam()) { + cull_cat.spam() + << "Drawing " << *node << " in direct mode.\n"; + } + nassertv(node != (Node *)NULL); + DirectRenderTraverser drt(_gsg, _graph_type); + drt.traverse(node, initial_state, AllTransitionsWrapper()); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullTraverser::add_instance +// Access: Private +// Description: This function should be called for each arc that +// leads to a multiply-instanced node in the graph; that +// is, each arc that points to a node with multiple +// parents. It will return a suitable CullStateLookup +// pointer that should be passed to subsequent +// add_geom_node() and add_instance() calls for nodes +// that descend from this arc. +//////////////////////////////////////////////////////////////////// +INLINE CullStateLookup *CullTraverser:: +add_instance(NodeRelation *arc, const AllTransitionsWrapper &trans, + Node *top_subtree, const CullLevelState &level_state) { + return level_state._lookup->get_subtree(arc, trans, top_subtree, _now); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullTraverser::find_bin_state +// Access: Private +// Description: Looks for a CullState in the set that corresponds to +// the indicates set of transitions; if one is found, +// returns its pointer; otherwise, creates a new one and +// returns it. +//////////////////////////////////////////////////////////////////// +INLINE CullState *CullTraverser:: +find_bin_state(const AllTransitionsWrapper &trans) { + PT(CullState) cs = new CullState(trans); + + // The insert operation will either succeed or fail, depending on + // whether a matching CullState was already in the set. It doesn't + // matter; in either case, the return value represents the + // corresponding CullState that is (now) stored in the set. + pair result = _states.insert(cs); + + /* + if (result.second) { + // The insert succeeded, so the CullState was not there + // previously. + cerr << "Created CullState for:\n"; + trans.write(cerr, 2); + } + */ + + return *result.first; +} diff --git a/panda/src/cull/cullTraverser.cxx b/panda/src/cull/cullTraverser.cxx new file mode 100644 index 0000000000..04df5a4faf --- /dev/null +++ b/panda/src/cull/cullTraverser.cxx @@ -0,0 +1,431 @@ +// Filename: cullTraverser.cxx +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "cullTraverser.h" +#include "geomBinTransition.h" +#include "geomBinAttribute.h" +#include "cullStateSubtree.h" +#include "geomBinNormal.h" +#include "directRenderTransition.h" +#include "config_cull.h" + +#include +#include +#include +#include +#include +#include +#include +#include // for implicit_app_traversal +#include + +TypeHandle CullTraverser::_type_handle; + +PStatCollector CullTraverser::_cull_pcollector = + PStatCollector("Cull", RGBColorf(0,1,0), 10); +PStatCollector CullTraverser::_draw_pcollector = + PStatCollector("Draw", RGBColorf(1,0,0), 20); + + + +//////////////////////////////////////////////////////////////////// +// Function: CullTraverser::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CullTraverser:: +CullTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type) : + RenderTraverser(gsg, graph_type) +{ + _default_bin = new GeomBinNormal("default", this); + _nested_count = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullTraverser::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CullTraverser:: +~CullTraverser() { + // We should detach each of our associated bins when we destruct. + // We can't just run a for loop, because this is a self-modifying + // operation. + while (!_bins.empty()) { + GeomBin *bin = (*_bins.begin()); + nassertv(bin != (GeomBin *)NULL); + nassertv(bin->get_traverser() == this); + bin->detach(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CullTraverser::traverse +// Access: Public, Virtual +// Description: This performs a normal, complete cull-and-render +// traversal using this CullTraverser object. State is +// saved within the CullTraverser object so that the +// next frame will be processed much more quickly than +// the first frame. +//////////////////////////////////////////////////////////////////// +void CullTraverser:: +traverse(Node *root, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans) { + // Statistics + PStatTimer timer(_cull_pcollector); + + if (cull_cat.is_debug()) { + cull_cat.debug() + << "Beginning level " << _nested_count << " cull traversal at " + << *root << "\n"; + } + + nassertv(!_bins.empty()); + + _now = last_graph_update[_graph_type]; + + bool is_initial = (_nested_count == 0); + _nested_count++; + + if (is_initial) { + _initial_state.apply_from(initial_state, net_trans); + + States::iterator si; + for (si = _states.begin(); si != _states.end(); ++si) { + (*si)->clear_current_nodes(); + } + } + + CullLevelState level_state; + level_state._lookup = &_lookup; + fc_traverse(root, *this, NullAttributeWrapper(), level_state, + _gsg, _graph_type); + + if (is_initial) { + draw(); + clean_out_old_states(); + } + + _nested_count--; + + if (cull_cat.is_debug()) { + cull_cat.debug() + << "Ending level " << _nested_count << " cull traversal at " + << *root << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CullTraverser::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CullTraverser:: +output(ostream &out) const { + int node_count = 0; + int used_states = 0; + + States::const_iterator si; + for (si = _states.begin(); si != _states.end(); ++si) { + const CullState *cs = (*si); + int c = cs->count_current_nodes(); + if (c != 0) { + node_count += c; + used_states++; + } + } + + out << node_count << " nodes with " << used_states << " states; " + << _states.size() - used_states << " unused states."; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullTraverser::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CullTraverser:: +write(ostream &out, int indent_level) const { + /* + States::const_iterator si; + for (si = _states.begin(); si != _states.end(); ++si) { + const CullState *cs = (*si); + cs->write(out, indent_level); + out << "\n"; + } + */ + + Bins::const_iterator bi; + for (bi = _bins.begin(); bi != _bins.end(); ++bi) { + (*bi)->write(out, indent_level); + } + _lookup.write(out, indent_level); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullTraverser::draw +// Access: Private +// Description: Puts all of the renderable geometry into the +// appropriate bins, and renders the bins. +//////////////////////////////////////////////////////////////////// +void CullTraverser:: +draw() { + if (cull_cat.is_debug()) { + // Count up the nonempty states for debug output. + int num_states = 0; + States::iterator si; + for (si = _states.begin(); si != _states.end(); ++si) { + CullState *cs = (*si); + if (!cs->is_empty()) { + num_states++; + } + } + + cull_cat.debug() + << "Initiating draw with " << num_states + << " nonempty states of " << _states.size() << " total.\n"; + } + + Bins::iterator bi; + for (bi = _bins.begin(); bi != _bins.end(); ++bi) { + (*bi)->clear_current_states(); + } + + States::iterator si; + for (si = _states.begin(); si != _states.end(); ++si) { + CullState *cs = (*si); + if (!cs->is_empty()) { + cs->apply_to(_initial_state); + + // Check the requested bin for the Geoms in this state. + GeomBin *requested_bin = _default_bin; + int draw_order = 0; + + const GeomBinAttribute *bin_attrib; + if (get_attribute_into(bin_attrib, cs->get_attributes(), + GeomBinTransition::get_class_type())) { + requested_bin = bin_attrib->get_bin(); + draw_order = bin_attrib->get_draw_order(); + } + + requested_bin->record_current_state(_gsg, cs, draw_order, this); + } + } + + if (_gsg != (GraphicsStateGuardian *)NULL) { + if (cull_cat.is_debug()) { + cull_cat.debug() + << "Drawing " << _bins.size() << " bins.\n"; + } + for (bi = _bins.begin(); bi != _bins.end(); ++bi) { + (*bi)->draw(this); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CullTraverser::clean_out_old_states +// Access: Private +// Description: Walks through the list of CullStates after a frame +// has been completely rendered and drawn, and removes +// any from the list that don't seem to be in use any +// more. +//////////////////////////////////////////////////////////////////// +void CullTraverser:: +clean_out_old_states() { + _lookup.clean_out_old_nodes(); + + States::iterator si, snext; + si = _states.begin(); + snext = si; + while (si != _states.end()) { + ++snext; + + CullState *cs = (*si); + if (cs->get_count() == 1 && cs->is_empty() && !cs->has_bin()) { + // This CullState doesn't seem to be used anywhere else. + _states.erase(si); + } + si = snext; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CullTraverser::add_geom_node +// Access: Private +// Description: Records the indicated GeomNode as one that is visible +// this frame with the indicated state, setting up an +// appropriate CullState node and adding it to the +// appropriate bin. +//////////////////////////////////////////////////////////////////// +void CullTraverser:: +add_geom_node(const PT(GeomNode) &node, const AllTransitionsWrapper &trans, + const CullLevelState &level_state) { + nassertv(node != (GeomNode *)NULL); + + AllTransitionsWrapper complete_trans; + level_state._lookup->compose_trans(trans, complete_trans); + + // Actually, there should be no need to remove this transition + // explicitly, as we should never encounter it--we'll never traverse + // below a DirectRenderTransition in the CullTraverser. We try to + // remove it here just to be paranoid. + complete_trans.clear_transition(DirectRenderTransition::get_class_type()); + + CullState *cs = level_state._lookup->find_node(node, complete_trans, _now); + if (cs == (CullState *)NULL) { + // The node didn't have a previously-associated CullState that we + // could use, so determine a new one for it. + cs = find_bin_state(complete_trans); + nassertv(cs != (CullState *)NULL); + + level_state._lookup->record_node(node, cs, _now); + } + + cs->record_current_geom_node(node); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullTraverser::add_direct_node +// Access: Private +// Description: Records the indicated Node as one that is immediately +// under a DirectRenderTransition, and thus it and its +// subtree should be rendered directly, in a depth-first +// traversal. This is usuall done when the render order +// is very important within a small subtree of nodes. +//////////////////////////////////////////////////////////////////// +void CullTraverser:: +add_direct_node(const PT_Node &node, const AllTransitionsWrapper &trans, + const CullLevelState &level_state) { + nassertv(node != (Node *)NULL); + + AllTransitionsWrapper complete_trans; + level_state._lookup->compose_trans(trans, complete_trans); + + // Remove this, just so we won't clutter up the state unnecessarily + // and interfere with state-sorting. + complete_trans.clear_transition(DirectRenderTransition::get_class_type()); + + CullState *cs = level_state._lookup->find_node(node, complete_trans, _now); + if (cs == (CullState *)NULL) { + // The node didn't have a previously-associated CullState that we + // could use, so determine a new one for it. + cs = find_bin_state(complete_trans); + nassertv(cs != (CullState *)NULL); + + level_state._lookup->record_node(node, cs, _now); + } + + cs->record_current_direct_node(node); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullTraverser::forward_arc +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CullTraverser:: +forward_arc(NodeRelation *arc, NullTransitionWrapper &, + NullAttributeWrapper &, NullAttributeWrapper &, + CullLevelState &level_state) { + nassertr(level_state._lookup != (CullStateLookup *)NULL, false); + + if (arc->has_transition(PruneTransition::get_class_type())) { + return false; + } + + Node *node = arc->get_child(); + + if (implicit_app_traversal) { + node->app_traverse(); + } + node->draw_traverse(); + + AllTransitionsWrapper trans; + + bool is_instanced = (node->get_num_parents(_graph_type) > 1); + bool is_geom = node->is_of_type(GeomNode::get_class_type()); + bool node_has_sub_render = node->has_sub_render(); + bool arc_has_sub_render = arc->has_sub_render_trans(); + bool has_direct_render = + arc->has_transition(DirectRenderTransition::get_class_type()) || + arc->has_transition(DecalTransition::get_class_type()); + + if (cull_cat.is_spam()) { + cull_cat.spam() + << "Reached " << *node << ":\n" + << "is_instanced = " << is_instanced + << " is_geom = " << is_geom + << " node_has_sub_render = " << node_has_sub_render + << " arc_has_sub_render = " << arc_has_sub_render + << " has_direct_render = " << has_direct_render + << "\n"; + } + + if (is_instanced || is_geom || node_has_sub_render || + arc_has_sub_render || has_direct_render) { + // In any of these cases, we'll need to determine the net + // transition to this node. + wrt_subtree(arc, level_state._lookup->get_top_subtree(), + trans, _graph_type); + } + + if (arc_has_sub_render || node_has_sub_render) { + if (_gsg != (GraphicsStateGuardian *)NULL) { + AllTransitionsWrapper complete_trans; + level_state._lookup->compose_trans(trans, complete_trans); + AllAttributesWrapper attrib; + attrib.apply_from(_initial_state, complete_trans); + + AllTransitionsWrapper new_trans; + + if (!arc->sub_render_trans(attrib, new_trans, _gsg)) { + return false; + } + if (!node->sub_render(attrib, new_trans, _gsg)) { + return false; + } + + trans.compose_in_place(new_trans); + } + } + + if (has_direct_render) { + add_direct_node(node, trans, level_state); + // Since we're adding the node to be rendered directly, we won't + // traverse any further beyond it--the rest of the subgraph + // beginning at this node will be traversed when the node is + // rendered. + return false; + } + + if (is_instanced || arc_has_sub_render) { + // This node is multiply instanced; thus, it begins a subtree. + level_state._lookup = add_instance(arc, trans, node, level_state); + if (cull_cat.is_spam()) { + cull_cat.spam() + << "Added " << *node << " as instance.\n"; + } + + // It might also be an instanced GeomNode. + if (is_geom) { + if (cull_cat.is_spam()) { + cull_cat.spam() + << "Added " << *node << "\n"; + } + add_geom_node(DCAST(GeomNode, node), AllTransitionsWrapper(), level_state); + } + + } else if (is_geom) { + if (cull_cat.is_spam()) { + cull_cat.spam() + << "Added " << *node << "\n"; + } + add_geom_node(DCAST(GeomNode, node), trans, level_state); + } + + return true; +} diff --git a/panda/src/cull/cullTraverser.h b/panda/src/cull/cullTraverser.h new file mode 100644 index 0000000000..2baa9077f3 --- /dev/null +++ b/panda/src/cull/cullTraverser.h @@ -0,0 +1,127 @@ +// Filename: cullTraverser.h +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CULLTRAVERSER_H +#define CULLTRAVERSER_H + +#include + +#include "cullLevelState.h" +#include "geomBin.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +class GraphicsStateGuardian; +class CullStateLookup; +class CullState; +class AllTransitionsWrapper; + +//////////////////////////////////////////////////////////////////// +// Class : CullTraverser +// Description : A special kind of RenderTraverser that makes a +// complete "cull" pass over the scene graph, grouping +// the geometry by state and adding it to individual +// GeomBins, and then renders the GeomBins in sequence. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CullTraverser : + public RenderTraverser, + public TraverserVisitor { +public: + CullTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type); + virtual ~CullTraverser(); + + INLINE void set_default_bin(GeomBin *bin); + INLINE GeomBin *get_default_bin() const; + + virtual void traverse(Node *root, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans); + + INLINE void draw_geom(GeomNode *geom_node, + const AllAttributesWrapper &initial_state); + INLINE void draw_direct(Node *node, + const AllAttributesWrapper &initial_state); + + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + +private: + void draw(); + void clean_out_old_states(); + + void add_geom_node(const PT(GeomNode) &node, + const AllTransitionsWrapper &trans, + const CullLevelState &level_state); + void add_direct_node(const PT_Node &node, + const AllTransitionsWrapper &trans, + const CullLevelState &level_state); + + INLINE CullStateLookup *add_instance(NodeRelation *arc, + const AllTransitionsWrapper &trans, + Node *top_subtree, + const CullLevelState &level_state); + + INLINE CullState *find_bin_state(const AllTransitionsWrapper &trans); + + +public: + // These methods, from parent class TraverserVisitor, define the + // behavior of the RenderTraverser as it traverses the graph. + // Normally you would never call these directly. + bool forward_arc(NodeRelation *arc, NullTransitionWrapper &trans, + NullAttributeWrapper &pre, NullAttributeWrapper &post, + CullLevelState &level_state); + +private: + UpdateSeq _now; + AllAttributesWrapper _initial_state; + + typedef set Bins; + Bins _bins; + PT(GeomBin) _default_bin; + + typedef set > States; + States _states; + + CullStateLookup _lookup; + int _nested_count; + +public: + // Statistics + static PStatCollector _cull_pcollector; + static PStatCollector _draw_pcollector; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + RenderTraverser::init_type(); + register_type(_type_handle, "CullTraverser", + RenderTraverser::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + +friend class GeomBin; +}; + +#include "cullTraverser.I" + +#endif + diff --git a/panda/src/cull/directRenderTransition.I b/panda/src/cull/directRenderTransition.I new file mode 100644 index 0000000000..3bb56a8183 --- /dev/null +++ b/panda/src/cull/directRenderTransition.I @@ -0,0 +1,13 @@ +// Filename: directRenderTransition.I +// Created by: drose (17Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: DirectRenderTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DirectRenderTransition:: +DirectRenderTransition() { +} diff --git a/panda/src/cull/directRenderTransition.cxx b/panda/src/cull/directRenderTransition.cxx new file mode 100644 index 0000000000..e4cd833c3c --- /dev/null +++ b/panda/src/cull/directRenderTransition.cxx @@ -0,0 +1,34 @@ +// Filename: directRenderTransition.cxx +// Created by: drose (17Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "directRenderTransition.h" + +#include + +TypeHandle DirectRenderTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: DirectRenderTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated DirectRenderTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *DirectRenderTransition:: +make_copy() const { + return new DirectRenderTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: DirectRenderTransition::has_sub_render +// Access: Public, Virtual +// Description: DirectRenderTransition doesn't actually have a +// sub_render() function, but it might as well, because +// it's treated as a special case. We set this function +// to return true so GraphReducer will behave correctly. +//////////////////////////////////////////////////////////////////// +bool DirectRenderTransition:: +has_sub_render() const { + return true; +} diff --git a/panda/src/cull/directRenderTransition.h b/panda/src/cull/directRenderTransition.h new file mode 100644 index 0000000000..b7c2f8c91b --- /dev/null +++ b/panda/src/cull/directRenderTransition.h @@ -0,0 +1,61 @@ +// Filename: directRenderTransition.h +// Created by: drose (17Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DIRECTRENDERTRANSITION_H +#define DIRECTRENDERTRANSITION_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : DirectRenderTransition +// Description : When this transition is attached to an arc, it +// indicates that the node below this arc, and all +// subsequent nodes below, should be treated as a single +// binnable unit. All the nodes at this point and below +// will be placed into the same bin, according to the +// state as of the top node, and when rendered, they +// will be rendered using a depth-first traversal rooted +// at this top node. +// +// This can be a way to establish a local draw-order +// within a few related nodes. It does not, however, +// affect view-frustum culling. +// +// This is the only way to achieve certain special +// effects that depend on scene-graph relationships, for +// instance decalling, while using the CullTraverser. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DirectRenderTransition : public ImmediateTransition { +public: + INLINE DirectRenderTransition(); + + virtual NodeTransition *make_copy() const; + + virtual bool has_sub_render() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ImmediateTransition::init_type(); + register_type(_type_handle, "DirectRenderTransition", + ImmediateTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class DirectRenderAttribute; +}; + +#include "directRenderTransition.I" + +#endif diff --git a/panda/src/cull/geomBin.I b/panda/src/cull/geomBin.I new file mode 100644 index 0000000000..364ae97c55 --- /dev/null +++ b/panda/src/cull/geomBin.I @@ -0,0 +1,90 @@ +// Filename: geomBin.I +// Created by: drose (13Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: GeomBin::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GeomBin:: +GeomBin(const string &name, CullTraverser *traverser, int sort) : + Namable(name) +{ + _traverser = (CullTraverser *)NULL; + if (traverser != (CullTraverser *)NULL) { + attach_to(traverser, sort); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBin::get_traverser +// Access: Public +// Description: Returns a pointer to the CullTraverser this bin is +// currently attached to, or NULL if it is not attached. +//////////////////////////////////////////////////////////////////// +INLINE CullTraverser *GeomBin:: +get_traverser() const { + return _traverser; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBin::get_sort +// Access: Public +// Description: Returns the sort index associated with this +// particular bin. The CullTraverser will render bins +// in order according to their sort index. +//////////////////////////////////////////////////////////////////// +INLINE int GeomBin:: +get_sort() const { + return _sort; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBin::set_sort +// Access: Public +// Description: Changes the sort index associated with this +// particular bin. The CullTraverser will render bins +// in order according to their sort index. +//////////////////////////////////////////////////////////////////// +INLINE void GeomBin:: +set_sort(int sort) { + if (_traverser != (CullTraverser *)NULL) { + attach_to(_traverser, sort); + } else { + _sort = sort; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBin::claim_cull_state +// Access: Protected +// Description: Marks the indicated CullState as being assigned to +// this bin, presumably in preparation to storing its +// pointer within the bin. +//////////////////////////////////////////////////////////////////// +INLINE void GeomBin:: +claim_cull_state(CullState *cs) { + nassertv(cs != (CullState *)NULL); + + if (cs->_bin != (GeomBin *)NULL && cs->_bin != this) { + cs->_bin->remove_state(cs); + } + + cs->_bin = this; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBin::claim_cull_state +// Access: Protected +// Description: Marks the indicated CullState as being assigned to +// no particular bin, presumably in preparation to +// removing it from the bin. +//////////////////////////////////////////////////////////////////// +INLINE void GeomBin:: +disclaim_cull_state(CullState *cs) { + nassertv(cs != (CullState *)NULL); + nassertv(cs->_bin == this); + cs->_bin = (GeomBin *)NULL; +} diff --git a/panda/src/cull/geomBin.cxx b/panda/src/cull/geomBin.cxx new file mode 100644 index 0000000000..44407e00f1 --- /dev/null +++ b/panda/src/cull/geomBin.cxx @@ -0,0 +1,108 @@ +// Filename: geomBin.cxx +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "geomBin.h" +#include "cullTraverser.h" +#include "config_cull.h" + +#include +#include +#include + +TypeHandle GeomBin::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GeomBin::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +GeomBin:: +~GeomBin() { + // We shouldn't be attached to anything when we destruct. If we + // are, something went screwy in the reference counting. + nassertv(_traverser == (CullTraverser *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBin::attach_to +// Access: Public +// Description: Detaches the bin from whichever CullTraverser it is +// currently attached to, and attaches it to the +// indicated CullTraverser instead, at the indicated +// sort level. The CullTraverser will render all of its +// attached GeomBins, in order according to sort level. +//////////////////////////////////////////////////////////////////// +void GeomBin:: +attach_to(CullTraverser *traverser, int sort) { + nassertv(traverser != (CullTraverser *)NULL); + PT(GeomBin) keep = detach(); + _sort = sort; + _traverser = traverser; + _traverser->_bins.insert(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBin::detach +// Access: Public +// Description: Detaches the bin from whichever CullTraverser it is +// currently attached to, if any. The bin will no +// longer be rendered. The return value is a +// PT(GeomBin) that refers to the GeomBin itself; the +// caller should save this pointer in a local PT +// variable to avoid possibly destructing the GeomBin +// (since detaching it may remove the last outstanding +// reference count). +//////////////////////////////////////////////////////////////////// +PT(GeomBin) GeomBin:: +detach() { + PT(GeomBin) keep = this; + if (_traverser != (CullTraverser *)NULL) { + _traverser->_bins.erase(this); + } + _traverser = (CullTraverser *)NULL; + return keep; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBin::remove_state +// Access: Public, Virtual +// Description: Disassociates the indicated state from the bin, if it +// was previously associated; presumably because a +// change in the scene's initial attributes as resulted +// in the indicated CullState switching to a new bin. +// +// Since the initial attributes will remain constant +// throughout a given frame, this function will only be +// called for GeomBins that save CullStates between +// frames; simple GeomBins that empty the entire list of +// CullStates upon a call to clear_current_states() +// should never see this function. +//////////////////////////////////////////////////////////////////// +void GeomBin:: +remove_state(CullState *) { + cull_cat.error() + << "remove_state() unexpectedly called for " << get_type() << "\n"; + nassertv(false); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBin::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GeomBin:: +output(ostream &out) const { + out << get_type() << " " << get_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBin::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GeomBin:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << *this << "\n"; +} diff --git a/panda/src/cull/geomBin.h b/panda/src/cull/geomBin.h new file mode 100644 index 0000000000..497cc1d0c2 --- /dev/null +++ b/panda/src/cull/geomBin.h @@ -0,0 +1,94 @@ +// Filename: geomBin.h +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMBIN_H +#define GEOMBIN_H + +#include + +#include "cullState.h" +#include "cullStateLookup.h" + +#include +#include +#include +#include +#include +#include + +#include + +class GeomNode; +class CullTraverser; + +//////////////////////////////////////////////////////////////////// +// Class : GeomBin +// Description : This is an abstract class that defines the interface +// for a number of different kinds of bins that may be +// assigned to a CullTraverser. The traverser will +// assign GeomNodes to the bins according to the various +// GeomBinTransitions encountered in the scene graph; +// the individual GeomBins are then responsible for +// sorting and rendering the geometry. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomBin : public TypedReferenceCount, public Namable { +public: + INLINE GeomBin(const string &name, CullTraverser *traverser = NULL, + int sort = 0); + virtual ~GeomBin(); + + void attach_to(CullTraverser *traverser, int sort = 0); + PT(GeomBin) detach(); + + INLINE CullTraverser *get_traverser() const; + INLINE int get_sort() const; + INLINE void set_sort(int sort); + + virtual void clear_current_states()=0; + virtual void record_current_state(GraphicsStateGuardian *gsg, + CullState *cs, int draw_order, + CullTraverser *trav)=0; + virtual void remove_state(CullState *cs); + + virtual void draw(CullTraverser *trav)=0; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + INLINE void claim_cull_state(CullState *cs); + INLINE void disclaim_cull_state(CullState *cs); + + CullTraverser *_traverser; + int _sort; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + Namable::init_type(); + register_type(_type_handle, "GeomBin", + TypedReferenceCount::get_class_type(), + Namable::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +INLINE ostream &operator << (ostream &out, const GeomBin &bin) { + bin.output(out); + return out; +} + +#include "geomBin.I" + +#endif diff --git a/panda/src/cull/geomBinAttribute.I b/panda/src/cull/geomBinAttribute.I new file mode 100644 index 0000000000..bb0f63130f --- /dev/null +++ b/panda/src/cull/geomBinAttribute.I @@ -0,0 +1,66 @@ +// Filename: geomBinAttribute.I +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GeomBinAttribute:: +GeomBinAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GeomBinAttribute:: +GeomBinAttribute(GeomBin *bin, int draw_order) { + set_on(bin, draw_order); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinAttribute::set_on +// Access: Public +// Description: Changes the GeomBinAttribute to render the particular +// geomBin. +//////////////////////////////////////////////////////////////////// +INLINE void GeomBinAttribute:: +set_on(GeomBin *bin, int draw_order) { + nassertv(bin != (GeomBin *)NULL); + _value = bin; + _draw_order = draw_order; + OnOffAttribute::set_on(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinAttribute::get_bin +// Access: Public +// Description: Returns the bin that the GeomBinAttribute +// represents. It is only valid to call this if is_on() +// has returned true. +//////////////////////////////////////////////////////////////////// +INLINE PT(GeomBin) GeomBinAttribute:: +get_bin() const { + nassertr(is_on(), NULL); + return _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinAttribute::get_draw_order +// Access: Public +// Description: Returns the draw order associated with the +// GeomBinAttribute. It is only valid to call this if +// is_on() has returned true. This draw order has +// meaning only if the associated bin (see get_bin()) is +// of type GeomBinFixed. +//////////////////////////////////////////////////////////////////// +INLINE int GeomBinAttribute:: +get_draw_order() const { + nassertr(is_on(), 0); + return _draw_order; +} + diff --git a/panda/src/cull/geomBinAttribute.N b/panda/src/cull/geomBinAttribute.N new file mode 100644 index 0000000000..4efaa99498 --- /dev/null +++ b/panda/src/cull/geomBinAttribute.N @@ -0,0 +1 @@ +ignoremember get_handle diff --git a/panda/src/cull/geomBinAttribute.cxx b/panda/src/cull/geomBinAttribute.cxx new file mode 100644 index 0000000000..1da7944bde --- /dev/null +++ b/panda/src/cull/geomBinAttribute.cxx @@ -0,0 +1,98 @@ +// Filename: geomBinAttribute.cxx +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "geomBinAttribute.h" +#include "geomBinTransition.h" + +#include +#include + +TypeHandle GeomBinAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle GeomBinAttribute:: +get_handle() const { + return GeomBinTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated GeomBinAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *GeomBinAttribute:: +make_copy() const { + return new GeomBinAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated GeomBinAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *GeomBinAttribute:: +make_initial() const { + return new GeomBinAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinAttribute::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the indicated transition +// pointer, which is guaranteed to be of type +// GeomBinTransition. +//////////////////////////////////////////////////////////////////// +void GeomBinAttribute:: +set_value_from(const OnOffTransition *other) { + const GeomBinTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; + _draw_order = ot->_draw_order; + nassertv(_value != (GeomBin *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinAttribute::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int GeomBinAttribute:: +compare_values(const OnOffAttribute *other) const { + const GeomBinAttribute *ot; + DCAST_INTO_R(ot, other, false); + if (_value != ot->_value) { + return (int)(_value - ot->_value); + } + return _draw_order - ot->_draw_order; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void GeomBinAttribute:: +output_value(ostream &out) const { + nassertv(_value != (GeomBin *)NULL); + out << *_value << ":" << _draw_order; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void GeomBinAttribute:: +write_value(ostream &out, int indent_level) const { + nassertv(_value != (GeomBin *)NULL); + indent(out, indent_level) << *_value << ":" << _draw_order << "\n"; +} diff --git a/panda/src/cull/geomBinAttribute.h b/panda/src/cull/geomBinAttribute.h new file mode 100644 index 0000000000..53314dd21c --- /dev/null +++ b/panda/src/cull/geomBinAttribute.h @@ -0,0 +1,61 @@ +// Filename: geomBinAttribute.h +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMBINATTRIBUTE_H +#define GEOMBINATTRIBUTE_H + +#include + +#include "geomBin.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : GeomBinAttribute +// Description : See GeomBinTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomBinAttribute : public OnOffAttribute { +public: + INLINE GeomBinAttribute(); + INLINE GeomBinAttribute(GeomBin *bin, int draw_order = 0); + + INLINE void set_on(GeomBin *bin, int draw_order = 0); + INLINE PT(GeomBin) get_bin() const; + INLINE int get_draw_order() const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + +protected: + virtual void set_value_from(const OnOffTransition *other); + virtual int compare_values(const OnOffAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + PT(GeomBin) _value; + int _draw_order; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnOffAttribute::init_type(); + register_type(_type_handle, "GeomBinAttribute", + OnOffAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "geomBinAttribute.I" + +#endif diff --git a/panda/src/cull/geomBinBackToFront.I b/panda/src/cull/geomBinBackToFront.I new file mode 100644 index 0000000000..e558425efd --- /dev/null +++ b/panda/src/cull/geomBinBackToFront.I @@ -0,0 +1,84 @@ +// Filename: geomBinBackToFront.I +// Created by: drose (13Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "cullTraverser.h" + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinBackToFront::NodeEntry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GeomBinBackToFront::NodeEntry:: +NodeEntry(float distance, const PT(CullState) &state, + Node *node, bool is_direct) : + _distance(distance), + _state(state), + _node(node), + _is_direct(is_direct) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinBackToFront::NodeEntry::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GeomBinBackToFront::NodeEntry:: +NodeEntry(const GeomBinBackToFront::NodeEntry ©) : + _distance(copy._distance), + _state(copy._state), + _node(copy._node), + _is_direct(copy._is_direct) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinBackToFront::NodeEntry::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GeomBinBackToFront::NodeEntry:: +operator = (const NodeEntry ©) { + _distance = copy._distance; + _state = copy._state; + _node = copy._node; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinBackToFront::NodeEntry::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool GeomBinBackToFront::NodeEntry:: +operator < (const NodeEntry &other) const { + return _distance > other._distance; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinBackToFront::NodeEntry::draw +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GeomBinBackToFront::NodeEntry:: +draw(CullTraverser *trav) const { + nassertv(_node != (GeomNode *)NULL); + if (_is_direct) { + trav->draw_direct(_node, AllAttributesWrapper(_state->get_attributes())); + } else { + trav->draw_geom(DCAST(GeomNode, _node), + AllAttributesWrapper(_state->get_attributes())); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinBackToFront::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GeomBinBackToFront:: +GeomBinBackToFront(const string &name, CullTraverser *traverser, int sort) : + GeomBin(name, traverser, sort) +{ +} diff --git a/panda/src/cull/geomBinBackToFront.cxx b/panda/src/cull/geomBinBackToFront.cxx new file mode 100644 index 0000000000..7272d33e74 --- /dev/null +++ b/panda/src/cull/geomBinBackToFront.cxx @@ -0,0 +1,144 @@ +// Filename: geomBinBackToFront.cxx +// Created by: drose (13Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "geomBinBackToFront.h" +#include "cullTraverser.h" + +#include +#include +#include +#include +#include +#include + +TypeHandle GeomBinBackToFront::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinBackToFront::clear_current_states +// Access: Public, Virtual +// Description: Called each frame by the CullTraverser to reset the +// list of CullStates that were added last frame, in +// preparation for defining a new set of CullStates +// visible this frame. +//////////////////////////////////////////////////////////////////// +void GeomBinBackToFront:: +clear_current_states() { + _node_entries.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinBackToFront::record_current_state +// Access: Public, Virtual +// Description: Called each frame by the CullTraverser to indicated +// that the given CullState (and all of its current +// GeomNodes) is visible this frame. +//////////////////////////////////////////////////////////////////// +void GeomBinBackToFront:: +record_current_state(GraphicsStateGuardian *gsg, CullState *cs, int, + CullTraverser *trav) { + // Get the transform matrix from the state. + LMatrix4f mat; + + TransformAttribute *trans_attrib = NULL; + get_attribute_into(trans_attrib, cs->get_attributes(), + TransformTransition::get_class_type()); + + CullState::geom_const_iterator gi; + for (gi = cs->geom_begin(); gi != cs->geom_end(); ++gi) { + GeomNode *node = (*gi); + nassertv(node != (GeomNode *)NULL); + const BoundingVolume &volume = node->get_bound(); + + if (!volume.is_empty() && + volume.is_of_type(GeometricBoundingVolume::get_class_type())) { + const GeometricBoundingVolume *gbv; + DCAST_INTO_V(gbv, &volume); + + LPoint3f center = gbv->get_approx_center(); + if (trans_attrib != (TransformAttribute *)NULL) { + center = center * trans_attrib->get_matrix(); + } + + float distance = gsg->compute_distance_to(center); + _node_entries.insert(NodeEntry(distance, cs, node, false)); + } + } + + CullState::direct_const_iterator di; + for (di = cs->direct_begin(); di != cs->direct_end(); ++di) { + Node *node = (*di); + nassertv(node != (Node *)NULL); + + const BoundingVolume &volume = node->get_bound(); + float distance = 0.0; + bool got_distance = false; + + if (!volume.is_empty() && + volume.is_of_type(GeometricBoundingVolume::get_class_type())) { + const GeometricBoundingVolume *gbv; + DCAST_INTO_V(gbv, &volume); + + LPoint3f center = gbv->get_approx_center(); + if (trans_attrib != (TransformAttribute *)NULL) { + center = center * trans_attrib->get_matrix(); + } + + distance = gsg->compute_distance_to(center); + got_distance = true; + } + + if (!got_distance) { + // Choose the average center of all of our children. + LPoint3f avg(0.0, 0.0, 0.0); + int num_points = 0; + + TypeHandle graph_type = trav->get_graph_type(); + int num_children = node->get_num_children(graph_type); + for (int i = 0; i < num_children; i++) { + NodeRelation *arc = node->get_child(graph_type, i); + + const BoundingVolume &volume = arc->get_bound(); + if (!volume.is_empty() && + volume.is_of_type(GeometricBoundingVolume::get_class_type())) { + const GeometricBoundingVolume *gbv; + DCAST_INTO_V(gbv, &volume); + + LPoint3f center = gbv->get_approx_center(); + avg += center; + num_points++; + } + } + + if (num_points > 0) { + avg /= (float)num_points; + if (trans_attrib != (TransformAttribute *)NULL) { + avg = avg * trans_attrib->get_matrix(); + } + distance = gsg->compute_distance_to(avg); + got_distance = true; + } + } + + _node_entries.insert(NodeEntry(distance, cs, node, true)); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinBackToFront::draw +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GeomBinBackToFront:: +draw(CullTraverser *trav) { + PStatTimer timer(CullTraverser::_draw_pcollector); + if (cull_cat.is_spam()) { + cull_cat.spam() + << "GeomBinBackToFront drawing " << _node_entries.size() << " entries.\n"; + } + NodeEntries::iterator nei; + for (nei = _node_entries.begin(); nei != _node_entries.end(); ++nei) { + (*nei).draw(trav); + } +} diff --git a/panda/src/cull/geomBinBackToFront.h b/panda/src/cull/geomBinBackToFront.h new file mode 100644 index 0000000000..23a10cde04 --- /dev/null +++ b/panda/src/cull/geomBinBackToFront.h @@ -0,0 +1,81 @@ +// Filename: geomBinBackToFront.h +// Created by: drose (13Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMBINBACKTOFRONT_H +#define GEOMBINBACKTOFRONT_H + +#include + +#include "geomBin.h" + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : GeomBinBackToFront +// Description : This kind of GeomBin renders its GeomNodes in order +// according to distance from the camera plane, +// beginning with the farthest away. It's particularly +// appropriate for rendering transparent geometry, but +// it may also be useful for rendering simple scenes +// without a Z-buffer. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomBinBackToFront : public GeomBin { +public: + INLINE GeomBinBackToFront(const string &name, + CullTraverser *traverser = NULL, + int sort = 0); + + virtual void clear_current_states(); + virtual void record_current_state(GraphicsStateGuardian *gsg, + CullState *cs, int draw_order, + CullTraverser *trav); + + virtual void draw(CullTraverser *trav); + +private: + class NodeEntry { + public: + INLINE NodeEntry(float distance, const PT(CullState) &state, + Node *node, bool is_direct); + INLINE NodeEntry(const NodeEntry ©); + INLINE void operator = (const NodeEntry ©); + + INLINE bool operator < (const NodeEntry &other) const; + + INLINE void draw(CullTraverser *trav) const; + + private: + float _distance; + PT(CullState) _state; + Node *_node; + bool _is_direct; + }; + + typedef multiset NodeEntries; + NodeEntries _node_entries; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GeomBin::init_type(); + register_type(_type_handle, "GeomBinBackToFront", + GeomBin::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "geomBinBackToFront.I" + +#endif diff --git a/panda/src/cull/geomBinFixed.I b/panda/src/cull/geomBinFixed.I new file mode 100644 index 0000000000..9aa047f7c5 --- /dev/null +++ b/panda/src/cull/geomBinFixed.I @@ -0,0 +1,85 @@ +// Filename: geomBinFixed.I +// Created by: drose (14Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "cullTraverser.h" + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinFixed::NodeEntry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GeomBinFixed::NodeEntry:: +NodeEntry(int draw_order, const PT(CullState) &state, + Node *node, bool is_direct) : + _draw_order(draw_order), + _state(state), + _node(node), + _is_direct(is_direct) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinFixed::NodeEntry::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GeomBinFixed::NodeEntry:: +NodeEntry(const GeomBinFixed::NodeEntry ©) : + _draw_order(copy._draw_order), + _state(copy._state), + _node(copy._node), + _is_direct(copy._is_direct) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinFixed::NodeEntry::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GeomBinFixed::NodeEntry:: +operator = (const NodeEntry ©) { + _draw_order = copy._draw_order; + _state = copy._state; + _node = copy._node; + _is_direct = copy._is_direct; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinFixed::NodeEntry::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool GeomBinFixed::NodeEntry:: +operator < (const NodeEntry &other) const { + return _draw_order < other._draw_order; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinFixed::NodeEntry::draw +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GeomBinFixed::NodeEntry:: +draw(CullTraverser *trav) const { + nassertv(_node != (GeomNode *)NULL); + if (_is_direct) { + trav->draw_direct(_node, AllAttributesWrapper(_state->get_attributes())); + } else { + trav->draw_geom(DCAST(GeomNode, _node), + AllAttributesWrapper(_state->get_attributes())); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinFixed::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GeomBinFixed:: +GeomBinFixed(const string &name, CullTraverser *traverser, int sort) : + GeomBin(name, traverser, sort) +{ +} diff --git a/panda/src/cull/geomBinFixed.cxx b/panda/src/cull/geomBinFixed.cxx new file mode 100644 index 0000000000..3ae47a6d09 --- /dev/null +++ b/panda/src/cull/geomBinFixed.cxx @@ -0,0 +1,76 @@ +// Filename: geomBinFixed.cxx +// Created by: drose (14Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "geomBinFixed.h" +#include "cullTraverser.h" + +#include +#include +#include +#include +#include +#include +#include + +TypeHandle GeomBinFixed::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinFixed::clear_current_states +// Access: Public, Virtual +// Description: Called each frame by the CullTraverser to reset the +// list of CullStates that were added last frame, in +// preparation for defining a new set of CullStates +// visible this frame. +//////////////////////////////////////////////////////////////////// +void GeomBinFixed:: +clear_current_states() { + _node_entries.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinFixed::record_current_state +// Access: Public, Virtual +// Description: Called each frame by the CullTraverser to indicated +// that the given CullState (and all of its current +// GeomNodes) is visible this frame. +//////////////////////////////////////////////////////////////////// +void GeomBinFixed:: +record_current_state(GraphicsStateGuardian *, CullState *cs, + int draw_order, CullTraverser *) { + // Get the transform matrix from the state. + LMatrix4f mat; + + TransformAttribute *trans_attrib = NULL; + get_attribute_into(trans_attrib, cs->get_attributes(), + TransformTransition::get_class_type()); + + CullState::geom_const_iterator gi; + for (gi = cs->geom_begin(); gi != cs->geom_end(); ++gi) { + GeomNode *node = (*gi); + nassertv(node != (GeomNode *)NULL); + _node_entries.insert(NodeEntry(draw_order, cs, node, false)); + } + + CullState::direct_const_iterator di; + for (di = cs->direct_begin(); di != cs->direct_end(); ++di) { + Node *node = (*di); + nassertv(node != (Node *)NULL); + _node_entries.insert(NodeEntry(draw_order, cs, node, true)); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinFixed::draw +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GeomBinFixed:: +draw(CullTraverser *trav) { + PStatTimer timer(CullTraverser::_draw_pcollector); + NodeEntries::iterator nei; + for (nei = _node_entries.begin(); nei != _node_entries.end(); ++nei) { + (*nei).draw(trav); + } +} diff --git a/panda/src/cull/geomBinFixed.h b/panda/src/cull/geomBinFixed.h new file mode 100644 index 0000000000..db4da1ed7c --- /dev/null +++ b/panda/src/cull/geomBinFixed.h @@ -0,0 +1,78 @@ +// Filename: geomBinFixed.h +// Created by: drose (14Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMBINFIXED_H +#define GEOMBINFIXED_H + +#include + +#include "geomBin.h" + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : GeomBinFixed +// Description : This kind of GeomBin renders its GeomNodes in a +// user-specified order according to the draw_order +// specified at each GeomBinTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomBinFixed : public GeomBin { +public: + INLINE GeomBinFixed(const string &name, + CullTraverser *traverser = NULL, + int sort = 0); + + virtual void clear_current_states(); + virtual void record_current_state(GraphicsStateGuardian *gsg, + CullState *cs, int draw_order, + CullTraverser *trav); + + virtual void draw(CullTraverser *trav); + +private: + class NodeEntry { + public: + INLINE NodeEntry(int draw_order, const PT(CullState) &state, + Node *node, bool is_direct); + INLINE NodeEntry(const NodeEntry ©); + INLINE void operator = (const NodeEntry ©); + + INLINE bool operator < (const NodeEntry &other) const; + + INLINE void draw(CullTraverser *trav) const; + + private: + int _draw_order; + PT(CullState) _state; + Node *_node; + bool _is_direct; + }; + + typedef multiset NodeEntries; + NodeEntries _node_entries; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GeomBin::init_type(); + register_type(_type_handle, "GeomBinFixed", + GeomBin::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "geomBinFixed.I" + +#endif diff --git a/panda/src/cull/geomBinGroup.I b/panda/src/cull/geomBinGroup.I new file mode 100644 index 0000000000..8e70b00e0f --- /dev/null +++ b/panda/src/cull/geomBinGroup.I @@ -0,0 +1,64 @@ +// Filename: geomBinGroup.I +// Created by: drose (13Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinGroup::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GeomBinGroup:: +GeomBinGroup(const string &name, CullTraverser *traverser, int sort) : + GeomBin(name, traverser, sort) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinGroup::add_sub_bin +// Access: Public +// Description: Appends the indicated bin to the end of the sub_bin +// list, and returns the new index. +//////////////////////////////////////////////////////////////////// +INLINE int GeomBinGroup:: +add_sub_bin(GeomBin *sub_bin) { + _sub_bins.push_back(sub_bin); + return _sub_bins.size() - 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinGroup::get_num_bins +// Access: Public +// Description: Returns the number of available sub bins. +//////////////////////////////////////////////////////////////////// +INLINE int GeomBinGroup:: +get_num_bins() const { + return _sub_bins.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinGroup::get_bin +// Access: Public +// Description: Returns the nth sub bin. +//////////////////////////////////////////////////////////////////// +INLINE GeomBin *GeomBinGroup:: +get_bin(int n) { + nassertr(n >= 0 && n < (int)_sub_bins.size(), NULL); + return _sub_bins[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinGroup::remove_bin +// Access: Public +// Description: Removes the nth bin. All subsequent index numbers +// shift down by one. The return value is the +// just-removed bin, which may be deleted if it is not +// immediately saved. +//////////////////////////////////////////////////////////////////// +INLINE PT(GeomBin) GeomBinGroup:: +remove_bin(int n) { + nassertr(n >= 0 && n < (int)_sub_bins.size(), NULL); + PT(GeomBin) keep = get_bin(n); + _sub_bins.erase(_sub_bins.begin() + n); + return keep; +} diff --git a/panda/src/cull/geomBinGroup.cxx b/panda/src/cull/geomBinGroup.cxx new file mode 100644 index 0000000000..1e36588239 --- /dev/null +++ b/panda/src/cull/geomBinGroup.cxx @@ -0,0 +1,86 @@ +// Filename: geomBinGroup.cxx +// Created by: drose (13Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "geomBinGroup.h" +#include "cullTraverser.h" + +#include +#include +#include + +TypeHandle GeomBinGroup::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinGroup::clear_current_states +// Access: Public, Virtual +// Description: Called each frame by the CullTraverser to reset the +// list of CullStates that were added last frame, in +// preparation for defining a new set of CullStates +// visible this frame. +//////////////////////////////////////////////////////////////////// +void GeomBinGroup:: +clear_current_states() { + SubBins::iterator sbi; + for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) { + (*sbi)->clear_current_states(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinUnsorted::record_current_state +// Access: Public, Virtual +// Description: Called each frame by the CullTraverser to indicated +// that the given CullState (and all of its current +// GeomNodes) is visible this frame. +//////////////////////////////////////////////////////////////////// +void GeomBinGroup:: +record_current_state(GraphicsStateGuardian *gsg, CullState *cs, + int draw_order, CullTraverser *trav) { + int index = choose_bin(cs); + if (index >= 0 && index < get_num_bins()) { + get_bin(index)->record_current_state(gsg, cs, draw_order, trav); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinGroup::draw +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GeomBinGroup:: +draw(CullTraverser *trav) { + SubBins::iterator sbi; + for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) { + (*sbi)->draw(trav); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinGroup::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GeomBinGroup:: +output(ostream &out) const { + out << get_type() << " " << get_name() + << ", " << _sub_bins.size() << " bins."; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinGroup::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GeomBinGroup:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << get_type() << " " << get_name() << " {\n"; + + SubBins::const_iterator sbi; + for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) { + (*sbi)->write(out, indent_level + 2); + } + + indent(out, indent_level) << "}\n"; +} diff --git a/panda/src/cull/geomBinGroup.h b/panda/src/cull/geomBinGroup.h new file mode 100644 index 0000000000..5adbdb2bb1 --- /dev/null +++ b/panda/src/cull/geomBinGroup.h @@ -0,0 +1,71 @@ +// Filename: geomBinGroup.h +// Created by: drose (13Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMBINGROUP_H +#define GEOMBINGROUP_H + +#include + +#include "geomBin.h" + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : GeomBinGroup +// Description : A special kind of abstract GeomBin that assigns its +// CullState objects to any of a number of sub-bins. +// This class is abstract because it does not define the +// function choose_bin(), which should identity the bin +// a given CullState should be assigned to. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomBinGroup : public GeomBin { +public: + INLINE GeomBinGroup(const string &name, CullTraverser *traverser = NULL, + int sort = 0); + + INLINE int add_sub_bin(GeomBin *sub_bin); + INLINE int get_num_bins() const; + INLINE GeomBin *get_bin(int n); + INLINE PT(GeomBin) remove_bin(int n); + + virtual int choose_bin(CullState *cs) const=0; + + virtual void clear_current_states(); + virtual void record_current_state(GraphicsStateGuardian *gsg, + CullState *cs, int draw_order, + CullTraverser *trav); + + virtual void draw(CullTraverser *trav); + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +private: + typedef vector SubBins; + SubBins _sub_bins; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GeomBin::init_type(); + register_type(_type_handle, "GeomBinGroup", + GeomBin::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "geomBinGroup.I" + +#endif diff --git a/panda/src/cull/geomBinNormal.cxx b/panda/src/cull/geomBinNormal.cxx new file mode 100644 index 0000000000..6e144ed89b --- /dev/null +++ b/panda/src/cull/geomBinNormal.cxx @@ -0,0 +1,47 @@ +// Filename: geomBinNormal.cxx +// Created by: drose (13Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "geomBinNormal.h" +#include "geomBinUnsorted.h" +#include "geomBinBackToFront.h" + +#include +#include +#include + +TypeHandle GeomBinNormal::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinNormal::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GeomBinNormal:: +GeomBinNormal(const string &name, CullTraverser *traverser, int sort) : + GeomBinGroup(name, traverser, sort) +{ + add_sub_bin(new GeomBinUnsorted("opaque")); + add_sub_bin(new GeomBinBackToFront("transparent")); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinNormal::Constructor +// Access: Public +// Description: Identifies the particular sub-bin the indicated +// CullState should be assigned to. +//////////////////////////////////////////////////////////////////// +int GeomBinNormal:: +choose_bin(CullState *cs) const { + bool is_transparent = false; + + TransparencyAttribute *trans_attrib; + if (get_attribute_into(trans_attrib, cs->get_attributes(), + TransparencyTransition::get_class_type())) { + is_transparent = + (trans_attrib->get_mode() != TransparencyProperty::M_none); + } + + return is_transparent ? 1 : 0; +} diff --git a/panda/src/cull/geomBinNormal.h b/panda/src/cull/geomBinNormal.h new file mode 100644 index 0000000000..f77f9a9318 --- /dev/null +++ b/panda/src/cull/geomBinNormal.h @@ -0,0 +1,49 @@ +// Filename: geomBinNormal.h +// Created by: drose (13Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMBINNORMAL_H +#define GEOMBINNORMAL_H + +#include + +#include "geomBinGroup.h" + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : GeomBinNormal +// Description : This is the most typical kind of GeomBin: it renders +// nontransparent geometry in state-sorted order, +// followed by transparent geometry sorted +// back-to-front. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomBinNormal : public GeomBinGroup { +public: + GeomBinNormal(const string &name, CullTraverser *traverser = NULL, + int sort = 0); + + virtual int choose_bin(CullState *cs) const; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GeomBinGroup::init_type(); + register_type(_type_handle, "GeomBinNormal", + GeomBinGroup::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/cull/geomBinTransition.I b/panda/src/cull/geomBinTransition.I new file mode 100644 index 0000000000..de3f4dd94e --- /dev/null +++ b/panda/src/cull/geomBinTransition.I @@ -0,0 +1,81 @@ +// Filename: geomBinTransition.I +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GeomBinTransition:: +GeomBinTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GeomBinTransition:: +GeomBinTransition(GeomBin *bin, int draw_order) : + OnOffTransition(TD_on), + _value(bin), + _draw_order(draw_order) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinTransition::off +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GeomBinTransition GeomBinTransition:: +off() { + GeomBinTransition result; + result.set_off(); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinTransition::set_on +// Access: Public +// Description: Changes the GeomBinTransition to render the particular +// bin. +//////////////////////////////////////////////////////////////////// +INLINE void GeomBinTransition:: +set_on(GeomBin *bin, int draw_order) { + nassertv(bin != (GeomBin *)NULL); + _value = bin; + _draw_order = draw_order; + OnOffTransition::set_on(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinTransition::get_bin +// Access: Public +// Description: Returns the bin that the GeomBinTransition +// represents. It is only valid to call this if is_on() +// has returned true. +//////////////////////////////////////////////////////////////////// +INLINE PT(GeomBin) GeomBinTransition:: +get_bin() const { + nassertr(is_on(), NULL); + return _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinTransition::get_draw_order +// Access: Public +// Description: Returns the draw order associated with the +// GeomBinTransition. It is only valid to call this if +// is_on() has returned true. This draw order has +// meaning only if the associated bin (see get_bin()) is +// of type GeomBinFixed. +//////////////////////////////////////////////////////////////////// +INLINE int GeomBinTransition:: +get_draw_order() const { + nassertr(is_on(), 0); + return _draw_order; +} + diff --git a/panda/src/cull/geomBinTransition.cxx b/panda/src/cull/geomBinTransition.cxx new file mode 100644 index 0000000000..c741763180 --- /dev/null +++ b/panda/src/cull/geomBinTransition.cxx @@ -0,0 +1,85 @@ +// Filename: geomBinTransition.cxx +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "geomBinTransition.h" +#include "geomBinAttribute.h" + +#include + +TypeHandle GeomBinTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated GeomBinTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *GeomBinTransition:: +make_copy() const { + return new GeomBinTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated GeomBinAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *GeomBinTransition:: +make_attrib() const { + return new GeomBinAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinTransition::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the other transition pointer, +// which is guaranteed to be another GeomBinTransition. +//////////////////////////////////////////////////////////////////// +void GeomBinTransition:: +set_value_from(const OnOffTransition *other) { + const GeomBinTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; + _draw_order = ot->_draw_order; + nassertv(_value != (GeomBin *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinTransition::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int GeomBinTransition:: +compare_values(const OnOffTransition *other) const { + const GeomBinTransition *ot; + DCAST_INTO_R(ot, other, false); + if (_value != ot->_value) { + return (int)(_value - ot->_value); + } + return _draw_order - ot->_draw_order; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void GeomBinTransition:: +output_value(ostream &out) const { + nassertv(_value != (GeomBin *)NULL); + out << *_value << ":" << _draw_order; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void GeomBinTransition:: +write_value(ostream &out, int indent_level) const { + nassertv(_value != (GeomBin *)NULL); + indent(out, indent_level) << *_value << ":" << _draw_order << "\n"; +} diff --git a/panda/src/cull/geomBinTransition.h b/panda/src/cull/geomBinTransition.h new file mode 100644 index 0000000000..999499f67b --- /dev/null +++ b/panda/src/cull/geomBinTransition.h @@ -0,0 +1,61 @@ +// Filename: geomBinTransition.h +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMBINTRANSITION_H +#define GEOMBINTRANSITION_H + +#include + +#include +#include "geomBin.h" + +//////////////////////////////////////////////////////////////////// +// Class : GeomBinTransition +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomBinTransition : public OnOffTransition { +public: + INLINE GeomBinTransition(); + INLINE GeomBinTransition(GeomBin *bin, int draw_order = 0); + INLINE static GeomBinTransition off(); + + INLINE void set_on(GeomBin *bin, int draw_order = 0); + INLINE PT(GeomBin) get_bin() const; + INLINE int get_draw_order() const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + virtual void set_value_from(const OnOffTransition *other); + virtual int compare_values(const OnOffTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + PT(GeomBin) _value; + int _draw_order; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnOffTransition::init_type(); + register_type(_type_handle, "GeomBinTransition", + OnOffTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class GeomBinAttribute; +}; + +#include "geomBinTransition.I" + +#endif diff --git a/panda/src/cull/geomBinUnsorted.I b/panda/src/cull/geomBinUnsorted.I new file mode 100644 index 0000000000..cf1d8a0f1c --- /dev/null +++ b/panda/src/cull/geomBinUnsorted.I @@ -0,0 +1,15 @@ +// Filename: geomBinUnsorted.I +// Created by: drose (13Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinUnsorted::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GeomBinUnsorted:: +GeomBinUnsorted(const string &name, CullTraverser *traverser, int sort) : + GeomBin(name, traverser, sort) +{ +} diff --git a/panda/src/cull/geomBinUnsorted.cxx b/panda/src/cull/geomBinUnsorted.cxx new file mode 100644 index 0000000000..6da44bfac5 --- /dev/null +++ b/panda/src/cull/geomBinUnsorted.cxx @@ -0,0 +1,145 @@ +// Filename: geomBinUnsorted.cxx +// Created by: drose (13Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "geomBinUnsorted.h" +#include "cullTraverser.h" + +#include +#include +#include +#include + +TypeHandle GeomBinUnsorted::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinUnsorted::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +GeomBinUnsorted:: +~GeomBinUnsorted() { + clear_current_states(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinUnsorted::clear_current_states +// Access: Public, Virtual +// Description: Called each frame by the CullTraverser to reset the +// list of CullStates that were added last frame, in +// preparation for defining a new set of CullStates +// visible this frame. +//////////////////////////////////////////////////////////////////// +void GeomBinUnsorted:: +clear_current_states() { + CullStates::iterator csi; + for (csi = _cull_states.begin(); csi != _cull_states.end(); ++csi) { + disclaim_cull_state(*csi); + } + + _cull_states.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinUnsorted::record_current_state +// Access: Public, Virtual +// Description: Called each frame by the CullTraverser to indicated +// that the given CullState (and all of its current +// GeomNodes) is visible this frame. +//////////////////////////////////////////////////////////////////// +void GeomBinUnsorted:: +record_current_state(GraphicsStateGuardian *, CullState *cs, int, + CullTraverser *) { + // This shouldn't be called twice for a particular CullState on this + // bin, since we don't preserve any CullStates between frames. + nassertv(cs->get_bin() != this); + + claim_cull_state(cs); + _cull_states.push_back(cs); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinUnsorted::draw +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GeomBinUnsorted:: +draw(CullTraverser *trav) { + PStatTimer timer(CullTraverser::_draw_pcollector); + GraphicsStateGuardian *gsg = trav->get_gsg(); + TypeHandle graph_type = trav->get_graph_type(); + + if (cull_cat.is_spam()) { + cull_cat.spam() + << "GeomBinUnsorted drawing " << _cull_states.size() << " states.\n"; + } + + CullStates::const_iterator csi; + for (csi = _cull_states.begin(); csi != _cull_states.end(); ++csi) { + const CullState *cs = (*csi); + if (cull_cat.is_spam()) { + cull_cat.spam() + << "GeomBinUnsorted state with " << cs->geom_size() << " geoms and " + << cs->direct_size() << " direct nodes.\n" + << "setting state " << cs->get_attributes() << "\n"; + } + + if (cs->geom_size() != 0) { + gsg->set_state(cs->get_attributes(), true); + gsg->prepare_display_region(); + + CullState::geom_iterator gi; + for (gi = cs->geom_begin(); gi != cs->geom_end(); ++gi) { + GeomNode *geom = (*gi); + nassertv(geom != (GeomNode *)NULL); + if (cull_cat.is_spam()) { + cull_cat.spam() + << "Drawing " << *geom << "\n"; + } + geom->draw(gsg); + } + } + + CullState::direct_iterator di; + for (di = cs->direct_begin(); di != cs->direct_end(); ++di) { + Node *node = (*di); + nassertv(node != (Node *)NULL); + + if (cull_cat.is_spam()) { + cull_cat.spam() + << "Drawing direct: " << *node << "\n"; + } + trav->draw_direct(node, cs->get_attributes()); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinUnsorted::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GeomBinUnsorted:: +output(ostream &out) const { + out << get_type() << " " << get_name() + << ", " << _cull_states.size() << " states."; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomBinUnsorted::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GeomBinUnsorted:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << get_type() << " " << get_name() << " {\n"; + + CullStates::const_iterator csi; + for (csi = _cull_states.begin(); csi != _cull_states.end(); ++csi) { + const CullState *cs = (*csi); + cs->write(out, indent_level + 2); + } + + indent(out, indent_level) << "}\n"; +} diff --git a/panda/src/cull/geomBinUnsorted.h b/panda/src/cull/geomBinUnsorted.h new file mode 100644 index 0000000000..75db098b2a --- /dev/null +++ b/panda/src/cull/geomBinUnsorted.h @@ -0,0 +1,65 @@ +// Filename: geomBinUnsorted.h +// Created by: drose (13Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMBINUNSORTED_H +#define GEOMBINUNSORTED_H + +#include + +#include "geomBin.h" + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : GeomBinUnsorted +// Description : This kind of GeomBin will group the GeomNodes +// together by state (since that's how they come from +// the CullTraverser, anyway), but won't attempt to +// render the various different states in any particular +// order. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomBinUnsorted : public GeomBin { +public: + INLINE GeomBinUnsorted(const string &name, CullTraverser *traverser = NULL, + int sort = 0); + virtual ~GeomBinUnsorted(); + + virtual void clear_current_states(); + virtual void record_current_state(GraphicsStateGuardian *gsg, + CullState *cs, int draw_order, + CullTraverser *trav); + + virtual void draw(CullTraverser *trav); + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +private: + typedef vector CullStates; + CullStates _cull_states; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GeomBin::init_type(); + register_type(_type_handle, "GeomBinUnsorted", + GeomBin::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "geomBinUnsorted.I" + +#endif diff --git a/panda/src/cull/test_cull.cxx b/panda/src/cull/test_cull.cxx new file mode 100644 index 0000000000..978a8be1af --- /dev/null +++ b/panda/src/cull/test_cull.cxx @@ -0,0 +1,141 @@ +// Filename: test_cull.cxx +// Created by: drose (07Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "cullTraverser.h" +#include "directRenderTransition.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class PrintNodes : public TraverserVisitor { +public: + PrintNodes() { + _indent_level = 0; + } + bool reached_node(Node *node, AttributeWrapper &state, NullLevelState &) { + indent(nout, _indent_level) + << *node << ", state is:\n"; + state.write(nout, _indent_level); + return true; + } + bool forward_arc(NodeRelation *arc, TransitionWrapper &trans, + AttributeWrapper &pre, AttributeWrapper &post, + NullLevelState &) { + _indent_level += 2; + return true; + } + void backward_arc(NodeRelation *arc, TransitionWrapper &trans, + AttributeWrapper &pre, AttributeWrapper &post, + const NullLevelState &) { + _indent_level -= 2; + } + int _indent_level; +}; + + +int +main(int argc, char *argv[]) { + PT_NamedNode r = new NamedNode("r"); + + PT_NamedNode a = new NamedNode("a"); + PT_NamedNode b = new NamedNode("b"); + + PT(GeomNode) aa = new GeomNode("aa"); + PT(GeomNode) aaa = new GeomNode("aaa"); + PT(GeomNode) ab = new GeomNode("ab"); + PT(GeomNode) ba = new GeomNode("ba"); + PT(GeomNode) bb = new GeomNode("bb"); + + PT_NamedNode m = new GeomNode("m"); + PT(GeomNode) ma = new GeomNode("ma"); + PT(GeomNode) mb = new GeomNode("mb"); + + NodeRelation *r_a = + new NodeRelation(r, a, 0, NodeRelation::get_class_type()); + NodeRelation *r_b = + new NodeRelation(r, b, 0, NodeRelation::get_class_type()); + + NodeRelation *a_aa = + new NodeRelation(a, aa, 0, NodeRelation::get_class_type()); + NodeRelation *a_ab = + new NodeRelation(a, ab, 0, NodeRelation::get_class_type()); + NodeRelation *b_ba = + new NodeRelation(b, ba, 0, NodeRelation::get_class_type()); + NodeRelation *b_bb = + new NodeRelation(b, bb, 0, NodeRelation::get_class_type()); + + NodeRelation *aa_aaa = + new NodeRelation(aa, aaa, 0, NodeRelation::get_class_type()); + + NodeRelation *a_m = + new NodeRelation(a, m, 0, NodeRelation::get_class_type()); + + NodeRelation *b_m = + new NodeRelation(b, m, 0, NodeRelation::get_class_type()); + + NodeRelation *m_ma = + new NodeRelation(m, ma, 0, NodeRelation::get_class_type()); + NodeRelation *m_mb = + new NodeRelation(m, mb, 0, NodeRelation::get_class_type()); + + + PT(Texture) alpha = new Texture; + alpha->set_name("alpha"); + PT(Texture) beta = new Texture; + beta->set_name("beta"); + + r_a->set_transition(new ColorTransition(1.0, 1.0, 1.0, 1.0)); + r_a->set_transition(new TextureTransition(alpha)); + a_aa->set_transition(new ColorTransition(0.5, 1.0, 0.5, 0.2)); + a_ab->set_transition(new TextureTransition(beta)); + + a_m->set_transition(new BillboardTransition); + b_m->set_transition(new ColorTransition(0.0, 0.0, 1.0, 1.0)); + m_ma->set_transition(new TextureTransition(alpha)); + + // r_a->set_transition(new DirectRenderTransition); + + nout << "\nr to a has:\n"; + r_a->write_transitions(nout, 2); + nout << "\nr to b has:\n"; + r_b->write_transitions(nout, 2); + nout << "\na to aa has:\n"; + a_aa->write_transitions(nout, 2); + nout << "\na to ab has:\n"; + a_ab->write_transitions(nout, 2); + nout << "\nb to ba has:\n"; + b_ba->write_transitions(nout, 2); + nout << "\n"; + + nout << "\n"; + PrintNodes pn; + df_traverse(r, pn, + AllAttributesWrapper(), NullLevelState(), + NodeRelation::get_class_type()); + nout << "\n"; + + CullTraverser ct(NULL, NodeRelation::get_class_type()); + + ct.traverse(r, AllAttributesWrapper(), AllTransitionsWrapper()); + ct.write(nout, 0); + + nout << "\nframe 2:\n"; + ct.traverse(r, AllAttributesWrapper(), AllTransitionsWrapper()); + ct.write(nout, 0); + + return (0); +} + diff --git a/panda/src/device/Sources.pp b/panda/src/device/Sources.pp new file mode 100644 index 0000000000..098bef9bc0 --- /dev/null +++ b/panda/src/device/Sources.pp @@ -0,0 +1,23 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET device + #define LOCAL_LIBS \ + dgraph display gobj sgraph graph gsgbase ipc mathutil linmath putil + + #define SOURCES \ + adinputNode.cxx adinputNode.h analogData.I analogData.cxx \ + analogData.h buttonData.I buttonData.cxx buttonData.h \ + clientBase.cxx clientBase.h config_device.cxx config_device.h \ + dialData.I dialData.cxx dialData.h mouse.cxx mouse.h trackerData.I \ + trackerData.cxx trackerData.h trackerNode.cxx trackerNode.h + + #define INSTALL_HEADERS \ + adinputNode.h analogData.I analogData.h buttonData.I buttonData.h \ + clientBase.h dialData.I dialData.h mouse.h trackerData.I \ + trackerData.h trackerNode.h + + #define IGATESCAN all + +#end lib_target + diff --git a/panda/src/device/adinputNode.cxx b/panda/src/device/adinputNode.cxx new file mode 100644 index 0000000000..a2ebda2f49 --- /dev/null +++ b/panda/src/device/adinputNode.cxx @@ -0,0 +1,124 @@ +// Filename: adinputNode.cxx +// Created by: jason (08Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "adinputNode.h" +#include "config_device.h" + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle ADInputNode::_type_handle; + +TypeHandle ADInputNode::_dtime_type; +TypeHandle ADInputNode::_dial_id_type; +TypeHandle ADInputNode::_change_type; + +TypeHandle ADInputNode::_btime_type; +TypeHandle ADInputNode::_button_id_type; +TypeHandle ADInputNode::_state_type; + +TypeHandle ADInputNode::_atime_type; +TypeHandle ADInputNode::_channels_type; + + +//////////////////////////////////////////////////////////////////// +// Function: ADInputNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ADInputNode:: +ADInputNode(PT(ClientBase) client, const string &adinput) : + DataNode(adinput), _adinput(adinput), _client(client) +{ + _client->add_remote_analog(_adinput); + _client->add_remote_button(_adinput); + _client->add_remote_dial(_adinput); + + _dtime = new DoubleDataAttribute(); + _dial_id = new IntDataAttribute(); + _change = new DoubleDataAttribute(); + + _btime = new DoubleDataAttribute(); + _button_id = new IntDataAttribute(); + _state = new IntDataAttribute(); + + _atime = new DoubleDataAttribute(); + _channels = new DoublePtrDataAttribute(); + + _adinput_attrib.set_attribute(_dtime_type, _dtime); + _adinput_attrib.set_attribute(_dial_id_type, _dial_id); + _adinput_attrib.set_attribute(_change_type, _change); + + _adinput_attrib.set_attribute(_btime_type, _btime); + _adinput_attrib.set_attribute(_button_id_type, _button_id); + _adinput_attrib.set_attribute(_state_type, _state); + + _adinput_attrib.set_attribute(_atime_type, _atime); + _adinput_attrib.set_attribute(_channels_type, _channels); +} + +//////////////////////////////////////////////////////////////////// +// Function: ADInputNode::transmit_data +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void ADInputNode:: +transmit_data(NodeAttributes &data) { + AnalogData new_analog = _client->get_analog_data(_adinput); + ButtonData new_button = _client->get_button_data(_adinput); + DialData new_dial = _client->get_dial_data(_adinput); + + _dtime->set_value(new_dial.dtime); + _dial_id->set_value(new_dial.dial_id); + _change->set_value(new_dial.change); + + _btime->set_value(new_button.btime); + _button_id->set_value(new_button.button_id); + _state->set_value(new_button.state); + + _atime->set_value(new_analog.atime); + _channels->set_value((double*)new_analog.channels); + + data = _adinput_attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: ADInputNode::init_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ADInputNode:: +init_type() { + DataNode::init_type(); + register_type(_type_handle, "ADInputNode", + DataNode::get_class_type()); + + DoubleDataTransition::init_type(); + IntDataTransition::init_type(); + DoublePtrDataTransition::init_type(); + + register_data_transition(_dtime_type, "Dial Time", + DoubleDataTransition::get_class_type()); + register_data_transition(_dial_id_type, "Dial ID", + IntDataTransition::get_class_type()); + register_data_transition(_change_type, "Dial Change", + DoubleDataTransition::get_class_type()); + + register_data_transition(_btime_type, "Button Time", + DoubleDataTransition::get_class_type()); + register_data_transition(_button_id_type, "Button ID", + IntDataTransition::get_class_type()); + register_data_transition(_state_type, "Button State", + IntDataTransition::get_class_type()); + + register_data_transition(_atime_type, "Analog Time", + DoubleDataTransition::get_class_type()); + register_data_transition(_channels_type, "Analog Channels", + DoublePtrDataTransition::get_class_type()); +} + + + + diff --git a/panda/src/device/adinputNode.h b/panda/src/device/adinputNode.h new file mode 100644 index 0000000000..3a3a6d0c37 --- /dev/null +++ b/panda/src/device/adinputNode.h @@ -0,0 +1,77 @@ +// Filename: cerealboxNode.h +// Created by: jason (08Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef _ADINPUT_NODE +#define _ADINPUT_NODE + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "clientBase.h" + +//////////////////////////////////////////////////////////////////// +// Class : ADInputNode +// Description : Reads the analog, buttons and dials from a adinput +// and sends it down the DataGraph +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ADInputNode : public DataNode { +public: + ADInputNode(PT(ClientBase) client, const string &adinput); + + virtual void transmit_data(NodeAttributes &data); + +public: + NodeAttributes _adinput_attrib; + + PT(DoubleDataAttribute) _btime; + PT(IntDataAttribute) _button_id; + PT(IntDataAttribute) _state; + + PT(DoubleDataAttribute) _dtime; + PT(IntDataAttribute) _dial_id; + PT(DoubleDataAttribute) _change; + + PT(DoubleDataAttribute) _atime; + PT(DoublePtrDataAttribute) _channels; + + static TypeHandle _btime_type; + static TypeHandle _button_id_type; + static TypeHandle _state_type; + + static TypeHandle _dtime_type; + static TypeHandle _dial_id_type; + static TypeHandle _change_type; + + static TypeHandle _atime_type; + static TypeHandle _channels_type; + +protected: + PT(ClientBase) _client; + string _adinput; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/device/analogData.I b/panda/src/device/analogData.I new file mode 100644 index 0000000000..b9eec10896 --- /dev/null +++ b/panda/src/device/analogData.I @@ -0,0 +1,62 @@ +// Filename: analogData.I +// Created by: jason (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: AnalogData::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AnalogData:: +AnalogData() : + atime(0) +{ + channels = new vector_double; + stored_channels = new vector_double; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnalogData::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AnalogData:: +~AnalogData() { + delete channels; + delete stored_channels; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnalogData::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AnalogData:: +AnalogData(const AnalogData ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnalogData::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AnalogData &AnalogData:: +operator = (const AnalogData ©) { + atime = copy.atime; + channels = new vector_double(*copy.channels); + stored_channels = new vector_double(*copy.stored_channels); + + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: AnalogData::none +// Access: Public, Static +// Description: Returns an empty AnalogData object +//////////////////////////////////////////////////////////////////// +INLINE const AnalogData &AnalogData:: +none() { + return _none; +} diff --git a/panda/src/device/analogData.cxx b/panda/src/device/analogData.cxx new file mode 100644 index 0000000000..5557801cfc --- /dev/null +++ b/panda/src/device/analogData.cxx @@ -0,0 +1,9 @@ +// Filename: analogData.cxx +// Created by: jason (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "analogData.h" + +// This is initialized to zero by static initialization. +AnalogData AnalogData::_none; diff --git a/panda/src/device/analogData.h b/panda/src/device/analogData.h new file mode 100644 index 0000000000..7fdd2532eb --- /dev/null +++ b/panda/src/device/analogData.h @@ -0,0 +1,37 @@ +// Filename: analogData.h +// Created by: jason (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANALOG_DATA +#define ANALOG_DATA + +#include +#include + +class EXPCL_PANDA AnalogData { +public: + INLINE AnalogData(); + INLINE ~AnalogData(); + INLINE AnalogData(const AnalogData ©); + INLINE AnalogData &operator = (const AnalogData ©); + + INLINE static const AnalogData &none(); + + double atime; + //These two pointers are so that we can return a reference to the + //current channel values, but not incure the overhead of copying all + //those values everywhere. We need two so that we can implement a + //rotating pointer scheme to ensure that there are no collisions + //between writing and reading. + vector_double *channels; +private: + vector_double *stored_channels; + static AnalogData _none; + + friend class ClientBase; +}; + +#include "analogData.I" + +#endif diff --git a/panda/src/device/buttonData.I b/panda/src/device/buttonData.I new file mode 100644 index 0000000000..a4d45ac6a0 --- /dev/null +++ b/panda/src/device/buttonData.I @@ -0,0 +1,49 @@ +// Filename: buttonData.I +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ButtonData::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ButtonData:: +ButtonData() : + btime(0), button_id(0), state(0) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonData::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ButtonData:: +ButtonData(const ButtonData ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonData::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ButtonData &ButtonData:: +operator = (const ButtonData ©) { + btime = copy.btime; + button_id = copy.button_id; + state = copy.state; + + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonData::none +// Access: Public, Static +// Description: Returns an empty ButtonData object +//////////////////////////////////////////////////////////////////// +INLINE const ButtonData &ButtonData:: +none() { + return _none; +} diff --git a/panda/src/device/buttonData.cxx b/panda/src/device/buttonData.cxx new file mode 100644 index 0000000000..d82899a1b0 --- /dev/null +++ b/panda/src/device/buttonData.cxx @@ -0,0 +1,9 @@ +// Filename: buttonData.cxx +// Created by: jason (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "buttonData.h" + +// This is initialized to zero by static initialization. +ButtonData ButtonData::_none; diff --git a/panda/src/device/buttonData.h b/panda/src/device/buttonData.h new file mode 100644 index 0000000000..e54bc76e5d --- /dev/null +++ b/panda/src/device/buttonData.h @@ -0,0 +1,29 @@ +// Filename: buttonData.h +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BUTTON_DATA +#define BUTTON_DATA + +#include +#include + +class EXPCL_PANDA ButtonData { +public: + INLINE ButtonData(); + INLINE ButtonData(const ButtonData ©); + INLINE ButtonData &operator = (const ButtonData ©); + + INLINE static const ButtonData &none(); + + double btime; + int button_id; + int state; +private: + static ButtonData _none; +}; + +#include "buttonData.I" + +#endif diff --git a/panda/src/device/clientBase.cxx b/panda/src/device/clientBase.cxx new file mode 100644 index 0000000000..5d1bafa1ef --- /dev/null +++ b/panda/src/device/clientBase.cxx @@ -0,0 +1,423 @@ +// Filename: clientBase.h +// Created by: jason (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "clientBase.h" +#include "config_device.h" + +TypeHandle ClientBase::_type_handle; + +#include + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ClientBase:: +ClientBase(const string &server) : + _server(server), _sleep_time(1000000/60), + _shutdown(false), _forked(false) +{ +} + + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ClientBase:: +~ClientBase() +{ + if (asynchronous_clients && _forked == true) { + { + //Make sure that you grab all locks before setting shutdown to + //true. This ensures that all polling actions on the thread + //serving the devices have finished before we tell it to + //shutdown + mutex_lock t_lock(_tracker_lock); + mutex_lock a_lock(_analog_lock); + mutex_lock b_lock(_button_lock); + _shutdown = true; + } + + // Join the loader thread - calling process blocks until the loader + // thread returns. + void *ret; + _client_thread->join(&ret); + } +} +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::fork_asynchronous_thread +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ClientBase:: +fork_asynchronous_thread(void) { + if (asynchronous_clients) { + _client_thread = thread::create(&st_callback, this); + _forked = true; + if (device_cat.is_debug()) { + device_cat.debug() + << "fork_asynchronous_thread() - forking client thread" + << endl; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::set_poll_time +// Access: Public +// Description: Sets the time between polls. The poll time is assumed +// to be in seconds +//////////////////////////////////////////////////////////////////// +void ClientBase:: +set_poll_time(float poll_time) { + _sleep_time = (int)(1000000 * poll_time); +} + + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::st_callback +// Access: Private, static +// Description: Call back function for thread (if thread has been +// spawned). A call back function must be static, so +// this merely calls the non-static member callback In +// addition, the function has a void* return type even +// though we don't actually return anything. This is +// necessary because ipc assumes a function that does +// not return anything indicates that the associated +// thread should be created as unjoinable (detached). +//////////////////////////////////////////////////////////////////// +void *ClientBase:: +st_callback(void *arg) { + nassertr(arg != NULL, NULL); + ((ClientBase *)arg)->callback(); + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::callback +// Access: Private +// Description: This is the main body of the sub-thread. It sleeps +// a certain time and then polls all devices currently +// being watched +//////////////////////////////////////////////////////////////////// +void ClientBase:: +callback(void) { + while(true) { + if (_shutdown) { + break; + } + poll_trackers(); + poll_analogs(); + poll_buttons(); + ipc_traits::sleep(0, _sleep_time); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::get_tracker_data +// Access: Public, Virtual +// Description: Returns the current data for the sensor of a particular +// tracker +//////////////////////////////////////////////////////////////////// +const TrackerData& ClientBase:: +get_tracker_data(const string &tracker, int sensor) { + + //Very important that this check and associated call are made before + //the mutex lock, otherwise if we are not forked and we lock before + //doing poll we will get into a deadlock condition when the + //associated push for this function is called + if (!_forked) { + poll_tracker(tracker); + } + + //Make sure to prevent simultaneous write and read of device + mutex_lock lock(_tracker_lock); + + if ((find(_trackers.begin(), _trackers.end(), tracker) != _trackers.end()) || + (find(_sensors[tracker].begin(), _sensors[tracker].end(), sensor) + != _sensors[tracker].end())) { + if (_tracker_datas.find(tracker) != _tracker_datas.end()) { + if (_tracker_datas[tracker].find(sensor) != _tracker_datas[tracker].end()) { + return _tracker_datas[tracker][sensor]; + } + } + } + else { + device_cat.error() << "Request for either unknown sensor " << sensor + << " or unknown tracker " << tracker << " made" << endl; + } + + return TrackerData::none(); + +} + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::get_analog_data +// Access: Public, Virtual +// Description: Returns the current data for the sensor of a particular +// analog +//////////////////////////////////////////////////////////////////// +const AnalogData& ClientBase:: +get_analog_data(const string &analog) { + + //Very important that this check and associated call are made before + //the mutex lock, otherwise if we are not forked and we lock before + //doing poll we will get into a deadlock condition when the + //associated push for this function is called + if (!_forked) { + poll_analog(analog); + } + + //Make sure to prevent simultaneous write and read of device + mutex_lock lock(_analog_lock); + + if (find(_analogs.begin(), _analogs.end(), analog) != _analogs.end()) { + if (_analog_datas.find(analog) != _analog_datas.end()) { + //Make sure to rotate the arrays to ensure access safety + swap(_analog_datas[analog].channels, _analog_datas[analog].stored_channels); + return _analog_datas[analog]; + } + } + else { + device_cat.error() << "Request for unknown analog " << analog << " made" << endl; + } + + return AnalogData::none(); + +} + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::get_button_data +// Access: Public, Virtual +// Description: Returns the current data for the sensor of a particular +// button +//////////////////////////////////////////////////////////////////// +const ButtonData& ClientBase:: +get_button_data(const string &button) { + + //Very important that this check and associated call are made before + //the mutex lock, otherwise if we are not forked and we lock before + //doing poll we will get into a deadlock condition when the + //associated push for this function is called + if (!_forked) { + poll_button(button); + } + + //Make sure to prevent simultaneous write and read of device + mutex_lock lock(_button_lock); + + if (find(_buttons.begin(), _buttons.end(), button) != _buttons.end()) { + if (_button_datas.find(button) != _button_datas.end()) { + return _button_datas[button]; + } + } + else { + device_cat.error() << "Request for unknown button " << button << " made" << endl; + } + + return ButtonData::none(); + +} + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::get_dial_data +// Access: Public, Virtual +// Description: Returns the current data for the sensor of a particular +// dial +//////////////////////////////////////////////////////////////////// +const DialData& ClientBase:: +get_dial_data(const string &dial) { + + //Very important that this check and associated call are made before + //the mutex lock, otherwise if we are not forked and we lock before + //doing poll we will get into a deadlock condition when the + //associated push for this function is called + if (!_forked) { + poll_dial(dial); + } + + //Make sure to prevent simultaneous write and read of device + mutex_lock lock(_dial_lock); + + if (find(_dials.begin(), _dials.end(), dial) != _dials.end()) { + if (_dial_datas.find(dial) != _dial_datas.end()) { + return _dial_datas[dial]; + } + } + else { + device_cat.error() << "Request for unknown dial " << dial << " made" << endl; + } + + return DialData::none(); + +} + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::push_tracker_position +// Access: Protected, Virtual +// Description: Use this function in the children class to fill in +// tracker data +//////////////////////////////////////////////////////////////////// +void ClientBase:: +push_tracker_position(const string &tracker, const int &sensor, const double &ptime, + const LPoint3f &pos, const LVector4f &pquat) { + //Make sure to prevent simultaneous write and read of device + mutex_lock lock(_tracker_lock); + + _tracker_datas[tracker][sensor].ptime = ptime; + _tracker_datas[tracker][sensor].position = pos; + _tracker_datas[tracker][sensor].pquat = pquat; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::push_tracker_position +// Access: Protected, Virtual +// Description: Use this function in the children class to fill in +// tracker data +//////////////////////////////////////////////////////////////////// +void ClientBase:: +push_tracker_velocity(const string &tracker, int sensor, const double &vtime, + const LPoint3f &vel, const LVector4f &vquat, const float &dt) { + //Make sure to prevent simultaneous write and read of device + mutex_lock lock(_tracker_lock); + + _tracker_datas[tracker][sensor].vtime = vtime; + _tracker_datas[tracker][sensor].velocity = vel; + _tracker_datas[tracker][sensor].vquat = vquat; + _tracker_datas[tracker][sensor].vquat_dt = dt; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::push_tracker_position +// Access: Protected, Virtual +// Description: Use this function in the children class to fill in +// tracker data +//////////////////////////////////////////////////////////////////// +void ClientBase:: +push_tracker_acceleration(const string &tracker, const int &sensor, const double &atime, + const LPoint3f &acc, const LVector4f &aquat, const float &dt) { + //Make sure to prevent simultaneous write and read of device + mutex_lock lock(_tracker_lock); + + _tracker_datas[tracker][sensor].atime = atime; + _tracker_datas[tracker][sensor].acceleration = acc; + _tracker_datas[tracker][sensor].aquat = aquat; + _tracker_datas[tracker][sensor].aquat_dt = dt; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::push_analog +// Access: Protected, Virtual +// Description: Use this function in the children class to fill in +// analog data +//////////////////////////////////////////////////////////////////// +void ClientBase:: +push_analog(const string &analog, const float &atime, + const double *channels, int num_channels) { + //Make sure to prevent simultaneous write and read of device + mutex_lock lock(_analog_lock); + + _analog_datas[analog].atime = atime; + + const double *head = channels; + const double *end = head + num_channels; + _analog_datas[analog].stored_channels->clear(); + vector_double::iterator start = _analog_datas[analog].stored_channels->begin(); + _analog_datas[analog].stored_channels->insert(start, head, end); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::push_button +// Access: Protected, Virtual +// Description: Use this function in the children class to fill in +// button data +//////////////////////////////////////////////////////////////////// +void ClientBase:: +push_button(const string &button, const float &btime, const int &button_id, + const int &state) { + //Make sure to prevent simultaneous write and read of device + mutex_lock lock(_button_lock); + + _button_datas[button].btime = btime; + _button_datas[button].button_id = button_id; + _button_datas[button].state = state; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::push_dial +// Access: Protected, Virtual +// Description: Use this function in the children class to fill in +// dial data +//////////////////////////////////////////////////////////////////// +void ClientBase:: +push_dial(const string &dial, const float &dtime, const int &dial_id, + const float &change) { + //Make sure to prevent simultaneous write and read of device + mutex_lock lock(_dial_lock); + + _dial_datas[dial].dtime = dtime; + _dial_datas[dial].dial_id = dial_id; + _dial_datas[dial].change = change; +} + + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::poll_trackers() +// Access: Private +// Description: Polls all current trackers +//////////////////////////////////////////////////////////////////// +void ClientBase:: +poll_trackers() { + Trackers::iterator ti = _trackers.begin(); + for(; ti != _trackers.end(); ti++) { + poll_tracker((*ti)); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::poll_analogs() +// Access: Private +// Description: Polls all current analogs +//////////////////////////////////////////////////////////////////// +void ClientBase:: +poll_analogs() { + Analogs::iterator ti = _analogs.begin(); + for(; ti != _analogs.end(); ti++) { + poll_analog((*ti)); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::poll_buttons() +// Access: Private +// Description: Polls all current buttons +//////////////////////////////////////////////////////////////////// +void ClientBase:: +poll_buttons() { + Buttons::iterator ti = _buttons.begin(); + for(; ti != _buttons.end(); ti++) { + poll_button((*ti)); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ClientBase::poll_dials() +// Access: Private +// Description: Polls all current dials +//////////////////////////////////////////////////////////////////// +void ClientBase:: +poll_dials() { + Dials::iterator ti = _dials.begin(); + for(; ti != _dials.end(); ti++) { + poll_dial((*ti)); + } +} + + + + diff --git a/panda/src/device/clientBase.h b/panda/src/device/clientBase.h new file mode 100644 index 0000000000..7dbe1c3218 --- /dev/null +++ b/panda/src/device/clientBase.h @@ -0,0 +1,141 @@ +// Filename: clientBase.h +// Created by: jason (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef _CLIENT_BASE +#define _CLIENT_BASE + +#include + +#include "trackerData.h" +#include "analogData.h" +#include "buttonData.h" +#include "dialData.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +class EXPCL_PANDA ClientBase : public TypedReferenceCount { +public: + ClientBase(const string &server); + ~ClientBase(); + void fork_asynchronous_thread(void); + void set_poll_time(float poll_time); + + //ADD FUNCTIONS + virtual bool add_remote_tracker(const string &tracker, int sensor) = 0; + virtual bool add_remote_analog(const string &analog) = 0; + virtual bool add_remote_button(const string &button) = 0; + virtual bool add_remote_dial(const string &dial) = 0; + + //GET FUNCTIONS + virtual const TrackerData &get_tracker_data(const string &tracker, int sensor); + virtual const AnalogData &get_analog_data(const string &analog); + virtual const ButtonData &get_button_data(const string &button); + virtual const DialData &get_dial_data(const string &dial); + +protected: + void push_tracker_position(const string &tracker, const int &sensor, const double &ptime, + const LPoint3f &pos, const LVector4f &pquat); + void push_tracker_velocity(const string &tracker, int sensor, const double &vtime, + const LPoint3f &vel, const LVector4f &vquat, const float &dt); + void push_tracker_acceleration(const string &tracker, const int &sensor, const double &atime, + const LPoint3f &acc, const LVector4f &aquat, const float &dt); + + void push_analog(const string &analog, const float &atime, + const double *channels, int num_channels); + void push_button(const string &button, const float &btime, const int &button_id, + const int &state); + void push_dial(const string &dial, const float &dtime, const int &dial_id, + const float &change); + +private: + int _sleep_time; + const string _server; + + //Device locks and conditionals + mutex _tracker_lock; + mutex _analog_lock; + mutex _button_lock; + mutex _dial_lock; + + //Thread variables and functions + thread *_client_thread; + bool _forked; + bool _shutdown; + + static void* st_callback(void *arg); + void callback(void); + +protected: + //Device polling functions + void poll_trackers(); + virtual void poll_tracker(const string &tracker) = 0; + + void poll_analogs(); + virtual void poll_analog(const string &analog) = 0; + virtual int max_analog_channels(void) = 0; + + void poll_buttons(); + virtual void poll_button(const string &button) = 0; + + void poll_dials(); + virtual void poll_dial(const string &dial) = 0; + + +protected: + typedef map< int, TrackerData > SensorDatas; + typedef map< const string, SensorDatas > TrackerDatas; + typedef map< const string, AnalogData > AnalogDatas; + typedef map< const string, ButtonData > ButtonDatas; + typedef map< const string, DialData > DialDatas; + + typedef vector_string Trackers; + typedef vector_int Sensors; + typedef map< string, Sensors > TrackerSensors; + typedef vector_string Analogs; + typedef vector_string Buttons; + typedef vector_string Dials; + + TrackerDatas _tracker_datas; + Trackers _trackers; + TrackerSensors _sensors; + + AnalogDatas _analog_datas; + Analogs _analogs; + + ButtonDatas _button_datas; + Buttons _buttons; + + DialDatas _dial_datas; + Dials _dials; + +public: + static TypeHandle get_class_type( void ) { + return _type_handle; + } + static void init_type( void ) { + TypedReferenceCount::init_type(); + register_type( _type_handle, "ClientBase", + TypedReferenceCount::get_class_type() ); + } + virtual TypeHandle get_type( void ) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() { + init_type(); + return get_class_type(); + } + +private: + static TypeHandle _type_handle; +}; +#endif diff --git a/panda/src/device/clients.txt b/panda/src/device/clients.txt new file mode 100644 index 0000000000..d6005f51c7 --- /dev/null +++ b/panda/src/device/clients.txt @@ -0,0 +1,52 @@ +OVERVIEW + + The ClientBase class (and subsequent children) were created to +solve the problem of interfaces to device API's that don't match up +well with panda (especially the DataGraph), and to allow for threading +of the polling of these devices in a manageable way. + + + To use, simply create an instance of the particular client +that you need. In most cases, this will be VrpnClient, when you +create the client you will specify the name of the server that is +controlling the devices. + + After you have created it, you can tell it to "add" whatever +kind of device that you are interested in. When you do this it will +setup the connection to that device and prepare itself to poll that +device. (If you have asynchronous-clients $t in you config file, and +call fork_asynchronous_thread(), then a thread will be created to +continuously poll the device(s) in the background for you). Whenever +you want, possibly, new data from the device simply call the +appropriate get function. + +INTERFACE TO DATAGRAPH + + The interface to the data graph in panda is fairly simple, and +best explained through an existing example. The DataGraph is +traversed every frame, and each node in it, should defined as a +sub-class of DataNode. Any sub-class of DataNode should have a method +called transmit_data. Each traversal transmit_data will be called on +each node. Those nodes will, if they are devices, package up their +data and send it down the line to the next node, or, if they are +tforms, take the incoming data, and put that data in some usable form +on the scene graph. For Clients to fit in, a sub-class of DataNode +should be defined that takes a pointer to a ClientBase object, and +calls the appropriate add and get functions as necessary for whatever +kind of device is appropriate. + +EXAMPLE + TrackerNode inherits from DataNode and takes a ClientBase, +tracker name and sensor number as paramaters at creation. On +creation, it calls add_remote_tracker() on the Client object. When +transmit_data() is called, it calls get_tracker_data() and sends that +data down the line. + + TrackerTransform inherits from DataNode. When transmit_data() +is called, it grabs the postion and orientation information as sent to +it from a TrackerNode above it, and creates a matrix from that data +and sends that matrix down the line. + + Transform2SG inherits from DataNode. When transmit_data() is +called, it grabs the matrix as sent to it by some DataNode above it +and stuffs that matrix onto a speficied arc in the SceneGraph. diff --git a/panda/src/device/config_device.cxx b/panda/src/device/config_device.cxx new file mode 100644 index 0000000000..f3c06c0c17 --- /dev/null +++ b/panda/src/device/config_device.cxx @@ -0,0 +1,24 @@ +// Filename: config_device.cxx +// Created by: drose (04May00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_device.h" +#include "mouse.h" +#include "clientBase.h" +#include "trackerNode.h" +#include "adinputNode.h" + +#include + +Configure(config_device); +NotifyCategoryDef(device, ""); + +const bool asynchronous_clients = config_device.GetBool("asynchronous-clients", false); + +ConfigureFn(config_device) { + MouseAndKeyboard::init_type(); + TrackerNode::init_type(); + ADInputNode::init_type(); + ClientBase::init_type(); +} diff --git a/panda/src/device/config_device.h b/panda/src/device/config_device.h new file mode 100644 index 0000000000..5dd953b837 --- /dev/null +++ b/panda/src/device/config_device.h @@ -0,0 +1,16 @@ +// Filename: config_device.h +// Created by: drose (04May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_DEVICE_H +#define CONFIG_DEVICE_H + +#include +#include + +NotifyCategoryDecl(device, EXPCL_PANDA, EXPTP_PANDA); + +extern const bool asynchronous_clients; + +#endif diff --git a/panda/src/device/dialData.I b/panda/src/device/dialData.I new file mode 100644 index 0000000000..d843ad8872 --- /dev/null +++ b/panda/src/device/dialData.I @@ -0,0 +1,49 @@ +// Filename: dialData.I +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: DialData::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DialData:: +DialData() : + dtime(0), dial_id(0), change(0) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DialData::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DialData:: +DialData(const DialData ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: DialData::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DialData &DialData:: +operator = (const DialData ©) { + dtime = copy.dtime; + dial_id = copy.dial_id; + change = copy.change; + + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: DialData::none +// Access: Public, Static +// Description: Returns an empty DialData object +//////////////////////////////////////////////////////////////////// +INLINE const DialData &DialData:: +none() { + return _none; +} diff --git a/panda/src/device/dialData.cxx b/panda/src/device/dialData.cxx new file mode 100644 index 0000000000..b2d381b8c7 --- /dev/null +++ b/panda/src/device/dialData.cxx @@ -0,0 +1,9 @@ +// Filename: dialData.cxx +// Created by: jason (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "dialData.h" + +// This is initialized to zero by static initialization. +DialData DialData::_none; diff --git a/panda/src/device/dialData.h b/panda/src/device/dialData.h new file mode 100644 index 0000000000..7096a382a2 --- /dev/null +++ b/panda/src/device/dialData.h @@ -0,0 +1,29 @@ +// Filename: dialData.h +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DIAL_DATA +#define DIAL_DATA + +#include +#include + +class EXPCL_PANDA DialData { +public: + INLINE DialData(); + INLINE DialData(const DialData ©); + INLINE DialData &operator = (const DialData ©); + + INLINE static const DialData &none(); + + double dtime; + int dial_id; + float change; +private: + static DialData _none; +}; + +#include "dialData.I" + +#endif diff --git a/panda/src/device/mouse.cxx b/panda/src/device/mouse.cxx new file mode 100644 index 0000000000..6e4a6f6aa1 --- /dev/null +++ b/panda/src/device/mouse.cxx @@ -0,0 +1,119 @@ +// Filename: mouse.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "mouse.h" + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle MouseAndKeyboard::_type_handle; + +TypeHandle MouseAndKeyboard::_mods_type; +TypeHandle MouseAndKeyboard::_pixel_xyz_type; +TypeHandle MouseAndKeyboard::_xyz_type; +TypeHandle MouseAndKeyboard::_button_events_type; + + +//////////////////////////////////////////////////////////////////// +// Function: MouseAndKeyboard::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +MouseAndKeyboard:: +MouseAndKeyboard(GraphicsWindow *window, int device, const string& name) : + DataNode(name), + _window(window), + _device(device) +{ + _mods = new ModifierButtonDataAttribute(); + _pixel_xyz = new Vec3DataAttribute(LPoint3f(0, 0, 0)); + _xyz = new Vec3DataAttribute(LPoint3f(0, 0, 0)); + _button_events = new ButtonEventDataAttribute(); + + _got_mouse_attrib.set_attribute(_mods_type, _mods); + _got_mouse_attrib.set_attribute(_pixel_xyz_type, _pixel_xyz); + _got_mouse_attrib.set_attribute(_xyz_type, _xyz); + _got_mouse_attrib.set_attribute(_button_events_type, _button_events); + + _no_mouse_attrib.set_attribute(_mods_type, _mods); + _no_mouse_attrib.set_attribute(_button_events_type, _button_events); +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseAndKeyboard::transmit_data +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void MouseAndKeyboard:: +transmit_data(NodeAttributes &data) { + // Fill up the button events. + _button_events->clear(); + while (_window->has_button_event(_device)) { + ButtonEvent be = _window->get_button_event(_device); + _button_events->push_back(be); + } + + // Get modifier buttons + _mods->set_mods(_window->get_modifier_buttons(_device)); + + if (_window->has_pointer(_device)) { + const MouseData &mdata = _window->get_mouse_data(_device); + + if (mdata._in_window) { + // Get motion + _pixel_xyz->set_value(LPoint3f(mdata._xpos, mdata._ypos, 0)); + + int w = _window->get_width(); + int h = _window->get_height(); + + // Scale to range [-1,1] + float xf = (float)(2 * mdata._xpos) / (float)w - 1.0; + float yf = 1.0 - (float)(2 * mdata._ypos) / (float)h; + + _xyz->set_value(LPoint3f(xf, yf, 0)); + data = _got_mouse_attrib; + + } else { + // The mouse isn't within the window right now. + data = _no_mouse_attrib; + } + + } else { + // We don't have a mouse device. + data = _no_mouse_attrib; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseAndKeyboard::init_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void MouseAndKeyboard:: +init_type() { + DataNode::init_type(); + register_type(_type_handle, "MouseAndKeyboard", + DataNode::get_class_type()); + + ModifierButtonDataTransition::init_type(); + register_data_transition(_mods_type, "ModifierButtons", + ModifierButtonDataTransition::get_class_type()); + Vec3DataTransition::init_type(); + register_data_transition(_pixel_xyz_type, "PixelXYZ", + Vec3DataTransition::get_class_type()); + register_data_transition(_xyz_type, "XYZ", + Vec3DataTransition::get_class_type()); + ButtonEventDataTransition::init_type(); + register_data_transition(_button_events_type, "ButtonEvents", + ButtonEventDataTransition::get_class_type()); +} diff --git a/panda/src/device/mouse.h b/panda/src/device/mouse.h new file mode 100644 index 0000000000..3c88520dbc --- /dev/null +++ b/panda/src/device/mouse.h @@ -0,0 +1,88 @@ +// Filename: mouse.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef MOUSE_H +#define MOUSE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +const int MIN_MOVE = 2; + +//////////////////////////////////////////////////////////////////// +// Class : MouseAndKeyboard +// Description : Reads the mouse and/or keyboard data sent from a +// GraphicsWindow, and transmits it down the data graph. +// +// The mouse and keyboard devices are bundled together +// into one device here, because they interrelate so +// much. A mouse might be constrained by the holding +// down of the shift key, for instance, or the clicking +// of the mouse button might be handled in much the same +// way as a keyboard key. +// +// Mouse data is sent down the data graph as an x,y +// position as well as the set of buttons currently +// being held down; keyboard data is sent down as a set +// of keypress events in an EventDataAttribute. To +// throw these events to the system, you must child an +// EventThrower to the MouseAndKeyboard object; +// otherwise, the events will be discarded. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA MouseAndKeyboard : public DataNode { +public: + + MouseAndKeyboard(GraphicsWindow *window, int device, + const string& name = ""); + + virtual void transmit_data(NodeAttributes &data); + +public: + NodeAttributes _got_mouse_attrib; + NodeAttributes _no_mouse_attrib; + PT(ModifierButtonDataAttribute) _mods; + PT(Vec3DataAttribute) _pixel_xyz; + PT(Vec3DataAttribute) _xyz; + PT(ButtonEventDataAttribute) _button_events; + + static TypeHandle _mods_type; + static TypeHandle _pixel_xyz_type; + static TypeHandle _xyz_type; + static TypeHandle _button_events_type; + +protected: + PT(GraphicsWindow) _window; + int _device; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/device/trackerData.I b/panda/src/device/trackerData.I new file mode 100644 index 0000000000..f7d749ab91 --- /dev/null +++ b/panda/src/device/trackerData.I @@ -0,0 +1,59 @@ +// Filename: trackerData.I +// Created by: jason (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TrackerData::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TrackerData:: +TrackerData() : + ptime(0), vtime(0), vquat_dt(0), atime(0), aquat_dt(0) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: TrackerData::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TrackerData:: +TrackerData(const TrackerData ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: TrackerData::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TrackerData &TrackerData:: +operator = (const TrackerData ©) { + ptime = copy.ptime; + position = copy.position; + pquat = copy.pquat; + + vtime = copy.vtime; + velocity = copy.velocity; + vquat = copy.vquat; + vquat_dt = copy.vquat_dt; + + atime = copy.atime; + acceleration = copy.acceleration; + aquat = copy.aquat; + aquat_dt = copy.aquat_dt; + + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: TrackerData::none +// Access: Public, Static +// Description: Returns an empty TrackerData object +//////////////////////////////////////////////////////////////////// +INLINE const TrackerData &TrackerData:: +none() { + return _none; +} diff --git a/panda/src/device/trackerData.cxx b/panda/src/device/trackerData.cxx new file mode 100644 index 0000000000..f6cc9917e9 --- /dev/null +++ b/panda/src/device/trackerData.cxx @@ -0,0 +1,9 @@ +// Filename: trackerData.cxx +// Created by: jason (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "trackerData.h" + +// This is initialized to zero by static initialization. +TrackerData TrackerData::_none; diff --git a/panda/src/device/trackerData.h b/panda/src/device/trackerData.h new file mode 100644 index 0000000000..bfd474138b --- /dev/null +++ b/panda/src/device/trackerData.h @@ -0,0 +1,40 @@ +// Filename: trackerData.h +// Created by: jason (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TRACKER_DATA +#define TRACKER_DATA + +#include +#include + +class EXPCL_PANDA TrackerData { +public: + INLINE TrackerData(); + INLINE TrackerData(const TrackerData ©); + INLINE TrackerData &operator = (const TrackerData ©); + + INLINE static const TrackerData &none(); + + double ptime; + LPoint3f position; + LVector4f pquat; + + double vtime; + LVector3f velocity; + LVector4f vquat; + float vquat_dt; + + double atime; + LVector3f acceleration; + LVector4f aquat; + float aquat_dt; + +private: + static TrackerData _none; +}; + +#include "trackerData.I" + +#endif diff --git a/panda/src/device/trackerNode.cxx b/panda/src/device/trackerNode.cxx new file mode 100644 index 0000000000..771eb07ceb --- /dev/null +++ b/panda/src/device/trackerNode.cxx @@ -0,0 +1,156 @@ +// Filename: trackerNode.cxx +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "trackerNode.h" +#include "config_device.h" + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle TrackerNode::_type_handle; + +TypeHandle TrackerNode::_ptime_type; +TypeHandle TrackerNode::_position_type; +TypeHandle TrackerNode::_pquat_type; + +TypeHandle TrackerNode::_vtime_type; +TypeHandle TrackerNode::_velocity_type; +TypeHandle TrackerNode::_vquat_type; +TypeHandle TrackerNode::_vquat_dt_type; + +TypeHandle TrackerNode::_atime_type; +TypeHandle TrackerNode::_acceleration_type; +TypeHandle TrackerNode::_aquat_type; +TypeHandle TrackerNode::_aquat_dt_type; + +//////////////////////////////////////////////////////////////////// +// Function: TrackerNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +TrackerNode:: +TrackerNode(PT(ClientBase) client, const string &tracker, + int sensor) : + DataNode(tracker), _tracker(tracker), _client(client), _sensor(sensor) +{ + _client->add_remote_tracker(_tracker, _sensor); + + _ptime = new DoubleDataAttribute(); + _position = new Vec3DataAttribute(LPoint3f(0,0,0)); + _pquat = new Vec4DataAttribute(LVector4f(0,0,0,0)); + + _vtime = new DoubleDataAttribute(); + _velocity = new Vec3DataAttribute(LPoint3f(0,0,0)); + _vquat = new Vec4DataAttribute(LVector4f(0,0,0,0)); + _vquat_dt = new DoubleDataAttribute(); + + _atime = new DoubleDataAttribute(); + _acceleration = new Vec3DataAttribute(LPoint3f(0,0,0)); + _aquat = new Vec4DataAttribute(LVector4f(0,0,0,0)); + _aquat_dt = new DoubleDataAttribute(); + + _tracker_attrib.set_attribute(_ptime_type, _ptime); + _tracker_attrib.set_attribute(_position_type, _position); + _tracker_attrib.set_attribute(_pquat_type, _pquat); + + _tracker_attrib.set_attribute(_vtime_type, _vtime); + _tracker_attrib.set_attribute(_velocity_type, _velocity); + _tracker_attrib.set_attribute(_vquat_type, _vquat); + _tracker_attrib.set_attribute(_vquat_dt_type, _vquat_dt); + + _tracker_attrib.set_attribute(_atime_type, _atime); + _tracker_attrib.set_attribute(_acceleration_type, _acceleration); + _tracker_attrib.set_attribute(_aquat_type, _aquat); + _tracker_attrib.set_attribute(_aquat_dt_type, _aquat_dt); +} + +//////////////////////////////////////////////////////////////////// +// Function: TrackerNode::transmit_data +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void TrackerNode:: +transmit_data(NodeAttributes &data) { + TrackerData new_data = _client->get_tracker_data(_tracker, _sensor); + + if (device_cat.is_debug()) { + device_cat.debug() << "TrackerNode:transmit_data" << endl; + } + + _ptime->set_value(new_data.ptime); + _position->set_value(new_data.position); + _pquat->set_value(new_data.pquat); + + _vtime->set_value(new_data.vtime); + _velocity->set_value(new_data.velocity); + _vquat->set_value(new_data.vquat); + _vquat_dt->set_value(new_data.vquat_dt); + + _atime->set_value(new_data.atime); + _acceleration->set_value(new_data.acceleration); + _aquat->set_value(new_data.aquat); + _aquat_dt->set_value(new_data.aquat_dt); + + if (device_cat.is_debug()) { + device_cat.debug() << "TrackerNode:attributes" << endl; + _tracker_attrib.write(device_cat.debug(false), 3); + } + + data = _tracker_attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: TrackerNode::init_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void TrackerNode:: +init_type() { + DataNode::init_type(); + register_type(_type_handle, "TrackerNode", + DataNode::get_class_type()); + + DoubleDataTransition::init_type(); + Vec3DataTransition::init_type(); + Vec4DataTransition::init_type(); + + register_data_transition(_ptime_type, "Position Time", + DoubleDataTransition::get_class_type()); + register_data_transition(_position_type, "Position", + Vec3DataTransition::get_class_type()); + register_data_transition(_pquat_type, "Position Quat", + Vec4DataTransition::get_class_type()); + + register_data_transition(_vtime_type, "Velocity Time", + DoubleDataTransition::get_class_type()); + register_data_transition(_velocity_type, "Velocity", + Vec3DataTransition::get_class_type()); + register_data_transition(_vquat_type, "Velocity Quat", + Vec4DataTransition::get_class_type()); + register_data_transition(_vquat_dt_type, "Velocity Quat dt", + DoubleDataTransition::get_class_type()); + + register_data_transition(_atime_type, "Acceleration Time", + DoubleDataTransition::get_class_type()); + register_data_transition(_acceleration_type, "Acceleration", + Vec3DataTransition::get_class_type()); + register_data_transition(_aquat_type, "Acceleration Quat", + Vec4DataTransition::get_class_type()); + register_data_transition(_aquat_dt_type, "Acceleration Quat dt", + DoubleDataTransition::get_class_type()); +} + + + + + + + + + + + + + diff --git a/panda/src/device/trackerNode.h b/panda/src/device/trackerNode.h new file mode 100644 index 0000000000..7a0a178c53 --- /dev/null +++ b/panda/src/device/trackerNode.h @@ -0,0 +1,87 @@ +// Filename: trackerNode.h +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef _TRACKER_NODE +#define _TRACKER_NODE + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "clientBase.h" + +//////////////////////////////////////////////////////////////////// +// Class : TrackerNode +// Description : Reads the position, velocity and acceleration +// information from one sensor on a tracker and sends it +// down the DataGraph +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TrackerNode : public DataNode { +public: + TrackerNode(PT(ClientBase) client, const string &tracker, + int sensor); + + virtual void transmit_data(NodeAttributes &data); + +public: + NodeAttributes _tracker_attrib; + + PT(DoubleDataAttribute) _ptime; + PT(Vec3DataAttribute) _position; + PT(Vec4DataAttribute) _pquat; + + PT(DoubleDataAttribute) _vtime; + PT(Vec3DataAttribute) _velocity; + PT(Vec4DataAttribute) _vquat; + PT(DoubleDataAttribute) _vquat_dt; + + PT(DoubleDataAttribute) _atime; + PT(Vec3DataAttribute) _acceleration; + PT(Vec4DataAttribute) _aquat; + PT(DoubleDataAttribute) _aquat_dt; + + static TypeHandle _ptime_type; + static TypeHandle _position_type; + static TypeHandle _pquat_type; + + static TypeHandle _vtime_type; + static TypeHandle _velocity_type; + static TypeHandle _vquat_type; + static TypeHandle _vquat_dt_type; + + static TypeHandle _atime_type; + static TypeHandle _acceleration_type; + static TypeHandle _aquat_type; + static TypeHandle _aquat_dt_type; + +protected: + PT(ClientBase) _client; + string _tracker; + int _sensor; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/dgraph/Sources.pp b/panda/src/dgraph/Sources.pp new file mode 100644 index 0000000000..770fa899c3 --- /dev/null +++ b/panda/src/dgraph/Sources.pp @@ -0,0 +1,69 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET dgraph + #define LOCAL_LIBS \ + sgraph graph putil sgattrib mathutil + + #define SOURCES \ + buttonEventDataAttribute.I buttonEventDataAttribute.cxx \ + buttonEventDataAttribute.h buttonEventDataTransition.I \ + buttonEventDataTransition.cxx buttonEventDataTransition.h \ + config_dgraph.cxx config_dgraph.h dataGraphTraversal.cxx \ + dataGraphTraversal.h dataNode.cxx dataNode.h dataRelation.I \ + dataRelation.N dataRelation.cxx dataRelation.h \ + describe_data_verbose.cxx describe_data_verbose.h \ + doubleDataAttribute.I doubleDataAttribute.cxx doubleDataAttribute.h \ + doubleDataTransition.I doubleDataTransition.cxx \ + doubleDataTransition.h doublePtrDataAttribute.I \ + doublePtrDataAttribute.cxx doublePtrDataAttribute.h \ + doublePtrDataTransition.I doublePtrDataTransition.cxx \ + doublePtrDataTransition.h intDataAttribute.I intDataAttribute.cxx \ + intDataAttribute.h intDataTransition.I intDataTransition.cxx \ + intDataTransition.h matrixDataAttribute.I matrixDataAttribute.cxx \ + matrixDataAttribute.h matrixDataTransition.I \ + matrixDataTransition.cxx matrixDataTransition.h \ + modifierButtonDataAttribute.I modifierButtonDataAttribute.cxx \ + modifierButtonDataAttribute.h modifierButtonDataTransition.I \ + modifierButtonDataTransition.cxx modifierButtonDataTransition.h \ + vec3DataAttribute.I vec3DataAttribute.cxx vec3DataAttribute.h \ + vec3DataTransition.I vec3DataTransition.cxx vec3DataTransition.h \ + vec4DataAttribute.I vec4DataAttribute.cxx vec4DataAttribute.h \ + vec4DataTransition.I vec4DataTransition.cxx vec4DataTransition.h + + #define INSTALL_HEADERS \ + buttonEventDataAttribute.I buttonEventDataAttribute.h \ + buttonEventDataTransition.I buttonEventDataTransition.h \ + dataGraphTraversal.h dataNode.h dataRelation.I dataRelation.h \ + describe_data_verbose.h doubleDataAttribute.I doubleDataAttribute.h \ + doubleDataTransition.I doubleDataTransition.h \ + doublePtrDataAttribute.I doublePtrDataAttribute.h \ + doublePtrDataTransition.I doublePtrDataTransition.h \ + intDataAttribute.I intDataAttribute.h intDataTransition.I \ + intDataTransition.h matrixDataAttribute.I matrixDataAttribute.h \ + matrixDataTransition.I matrixDataTransition.h \ + modifierButtonDataAttribute.I modifierButtonDataAttribute.h \ + modifierButtonDataTransition.I modifierButtonDataTransition.h \ + numericDataAttribute.I numericDataAttribute.h \ + numericDataTransition.I numericDataTransition.h \ + pointerDataAttribute.I pointerDataAttribute.h \ + pointerDataTransition.I pointerDataTransition.h vec3DataAttribute.I \ + vec3DataAttribute.h vec3DataTransition.I vec3DataTransition.h \ + vec4DataAttribute.I vec4DataAttribute.h vec4DataTransition.I \ + vec4DataTransition.h vectorDataAttribute.I vectorDataAttribute.h \ + vectorDataTransition.I vectorDataTransition.h + + #define IGATESCAN all + +#end lib_target + +#begin test_bin_target + #define TARGET test_dgraph + #define LOCAL_LIBS \ + dgraph + + #define SOURCES \ + test_dgraph.cxx + +#end test_bin_target + diff --git a/panda/src/dgraph/buttonEventDataAttribute.I b/panda/src/dgraph/buttonEventDataAttribute.I new file mode 100644 index 0000000000..d125c1c8f6 --- /dev/null +++ b/panda/src/dgraph/buttonEventDataAttribute.I @@ -0,0 +1,76 @@ +// Filename: buttonEventDataAttribute.I +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ButtonEventDataAttribute:: +ButtonEventDataAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataAttribute::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ButtonEventDataAttribute:: +ButtonEventDataAttribute(const ButtonEventDataAttribute ©) : + NodeAttribute(copy), + _buttons(copy._buttons) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataAttribute::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ButtonEventDataAttribute:: +operator = (const ButtonEventDataAttribute ©) { + NodeAttribute::operator = (copy); + _buttons = copy._buttons; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataAttribute::begin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ButtonEventDataAttribute::const_iterator ButtonEventDataAttribute:: +begin() const { + return _buttons.begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataAttribute::end +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ButtonEventDataAttribute::const_iterator ButtonEventDataAttribute:: +end() const { + return _buttons.end(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataAttribute::clear +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ButtonEventDataAttribute:: +clear() { + _buttons.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataAttribute::clear +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ButtonEventDataAttribute:: +push_back(const ButtonEvent &event) { + _buttons.push_back(event); +} diff --git a/panda/src/dgraph/buttonEventDataAttribute.cxx b/panda/src/dgraph/buttonEventDataAttribute.cxx new file mode 100644 index 0000000000..e6a54da0a6 --- /dev/null +++ b/panda/src/dgraph/buttonEventDataAttribute.cxx @@ -0,0 +1,94 @@ +// Filename: buttonEventDataAttribute.cxx +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "buttonEventDataAttribute.h" +#include "buttonEventDataTransition.h" + +#include + +#include + +TypeHandle ButtonEventDataAttribute::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataAttribute::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *ButtonEventDataAttribute:: +make_copy() const { + return new ButtonEventDataAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataAttribute::make_initial +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *ButtonEventDataAttribute:: +make_initial() const { + return new ButtonEventDataAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataAttribute::get_handle +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +TypeHandle ButtonEventDataAttribute:: +get_handle() const { + return ButtonEventDataTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataAttribute::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void ButtonEventDataAttribute:: +output(ostream &out) const { + Buttons::const_iterator bi; + for (bi = _buttons.begin(); bi != _buttons.end(); ++bi) { + if (bi != _buttons.begin()) { + out << ", "; + } + out << *bi; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataAttribute::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void ButtonEventDataAttribute:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << (*this) << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataAttribute::internal_compare_to +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int ButtonEventDataAttribute:: +internal_compare_to(const NodeAttribute *other) const { + const ButtonEventDataAttribute *ot; + DCAST_INTO_R(ot, other, false); + +#ifdef WIN32_VC + if (ot->_buttons == _buttons) + return 0; + else if (lexicographical_compare(_buttons.begin(), _buttons.end(), + ot->_buttons.begin(), ot->_buttons.end())) + return -1; + else + return 1; +#else + return lexicographical_compare_3way(_buttons.begin(), _buttons.end(), + ot->_buttons.begin(), ot->_buttons.end()); +#endif +} diff --git a/panda/src/dgraph/buttonEventDataAttribute.h b/panda/src/dgraph/buttonEventDataAttribute.h new file mode 100644 index 0000000000..9b5435bd88 --- /dev/null +++ b/panda/src/dgraph/buttonEventDataAttribute.h @@ -0,0 +1,81 @@ +// Filename: buttonEventDataAttribute.h +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BUTTONEVENTDATAATTRIBUTE_H +#define BUTTONEVENTDATAATTRIBUTE_H + +#include + +#include +#include + +#include + +class ButtonEventDataTransition; + +//////////////////////////////////////////////////////////////////// +// Class : ButtonEventDataAttribute +// Description : This data graph attribute stores a collection of +// button events that have been generated by the user +// since the last time the data graph pulsed, in the +// order they were generated. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ButtonEventDataAttribute : public NodeAttribute { +private: + typedef vector Buttons; + +public: + INLINE ButtonEventDataAttribute(); + INLINE ButtonEventDataAttribute(const ButtonEventDataAttribute ©); + INLINE void operator = (const ButtonEventDataAttribute ©); + +public: + // Functions to access and manipulate the set of buttons. + typedef Buttons::const_iterator const_iterator; + typedef Buttons::const_iterator iterator; + + INLINE const_iterator begin() const; + INLINE const_iterator end() const; + + INLINE void clear(); + INLINE void push_back(const ButtonEvent &event); + +public: + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual TypeHandle get_handle() const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeAttribute *other) const; + +private: + Buttons _buttons; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeAttribute::init_type(); + register_type(_type_handle, "ButtonEventDataAttribute", + NodeAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +friend class ButtonEventDataTransition; +}; + +#include "buttonEventDataAttribute.I" + +#endif diff --git a/panda/src/dgraph/buttonEventDataTransition.I b/panda/src/dgraph/buttonEventDataTransition.I new file mode 100644 index 0000000000..fb31617372 --- /dev/null +++ b/panda/src/dgraph/buttonEventDataTransition.I @@ -0,0 +1,34 @@ +// Filename: buttonEventDataTransition.I +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ButtonEventDataTransition:: +ButtonEventDataTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataTransition::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ButtonEventDataTransition:: +ButtonEventDataTransition(const ButtonEventDataTransition ©) : + NodeTransition(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataTransition::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ButtonEventDataTransition:: +operator = (const ButtonEventDataTransition ©) { + NodeTransition::operator = (copy); +} diff --git a/panda/src/dgraph/buttonEventDataTransition.cxx b/panda/src/dgraph/buttonEventDataTransition.cxx new file mode 100644 index 0000000000..9bc80cd812 --- /dev/null +++ b/panda/src/dgraph/buttonEventDataTransition.cxx @@ -0,0 +1,80 @@ +// Filename: buttonEventDataTransition.cxx +// Created by: drose (09Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "buttonEventDataTransition.h" +#include "buttonEventDataAttribute.h" + +TypeHandle ButtonEventDataTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataTransition::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeTransition *ButtonEventDataTransition:: +make_copy() const { + return new ButtonEventDataTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataTransition::make_attrib +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *ButtonEventDataTransition:: +make_attrib() const { + return new ButtonEventDataAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataTransition::compose +// Access: Public, Virtual +// Description: Returns a new transition that corresponds to the +// composition of this transition with the second +// transition (which must be of an equivalent type). +// This may return the same pointer as either source +// transition. Applying the transition returned from +// this function to an attribute attribute will produce +// the same effect as applying each transition +// separately. +//////////////////////////////////////////////////////////////////// +NodeTransition *ButtonEventDataTransition:: +compose(const NodeTransition *) const { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataTransition::invert +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeTransition *ButtonEventDataTransition:: +invert() const { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataTransition::apply +// Access: Public, Virtual +// Description: Returns a new attribute (or possibly the same +// attribute) that represents the effect of applying this +// indicated transition to the indicated attribute. The +// source attribute may be NULL, indicating the initial +// attribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ButtonEventDataTransition:: +apply(const NodeAttribute *attrib) const { + return (NodeAttribute *)attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventDataTransition::internal_compare_to +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int ButtonEventDataTransition:: +internal_compare_to(const NodeTransition *) const { + return 0; +} diff --git a/panda/src/dgraph/buttonEventDataTransition.h b/panda/src/dgraph/buttonEventDataTransition.h new file mode 100644 index 0000000000..e8543fdc7b --- /dev/null +++ b/panda/src/dgraph/buttonEventDataTransition.h @@ -0,0 +1,58 @@ +// Filename: buttonEventDataTransition.h +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BUTTONEVENTDATATRANSITION_H +#define BUTTONEVENTDATATRANSITION_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : ButtonEventDataTransition +// Description : A ButtonEventDataAttribute is a collection of buttons +// that have been pressed recently, in the order they +// were pressed. The ButtonEventDataTransition does +// nothing, since there's no sensible transformation to +// apply to this collection of buttons. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ButtonEventDataTransition : public NodeTransition { +public: + INLINE ButtonEventDataTransition(); + INLINE ButtonEventDataTransition(const ButtonEventDataTransition ©); + INLINE void operator = (const ButtonEventDataTransition ©); + +public: + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + + virtual NodeTransition *compose(const NodeTransition *other) const; + virtual NodeTransition *invert() const; + virtual NodeAttribute *apply(const NodeAttribute *attrib) const; + +protected: + virtual int internal_compare_to(const NodeTransition *other) const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeTransition::init_type(); + register_type(_type_handle, "ButtonEventDataTransition", + NodeTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "buttonEventDataTransition.I" + +#endif diff --git a/panda/src/dgraph/config_dgraph.cxx b/panda/src/dgraph/config_dgraph.cxx new file mode 100644 index 0000000000..ffb2c99a1a --- /dev/null +++ b/panda/src/dgraph/config_dgraph.cxx @@ -0,0 +1,46 @@ +// Filename: config_dgraph.cxx +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_dgraph.h" +#include "dataNode.h" +#include "dataRelation.h" +#include "intDataTransition.h" +#include "intDataAttribute.h" +#include "doubleDataTransition.h" +#include "doubleDataAttribute.h" +#include "vec3DataTransition.h" +#include "vec3DataAttribute.h" +#include "matrixDataTransition.h" +#include "matrixDataAttribute.h" +#include "buttonEventDataTransition.h" +#include "buttonEventDataAttribute.h" +#include "modifierButtonDataTransition.h" +#include "modifierButtonDataAttribute.h" +#include "dataGraphTraversal.h" + +#include + +Configure(config_dgraph); +NotifyCategoryDef(dgraph, ""); + +ConfigureFn(config_dgraph) { + DataNode::init_type(); + DataRelation::init_type(); + IntDataTransition::init_type(); + IntDataAttribute::init_type(); + DoubleDataTransition::init_type(); + DoubleDataAttribute::init_type(); + Vec3DataTransition::init_type(); + Vec3DataAttribute::init_type(); + MatrixDataTransition::init_type(); + MatrixDataAttribute::init_type(); + ButtonEventDataTransition::init_type(); + ButtonEventDataAttribute::init_type(); + ModifierButtonDataTransition::init_type(); + ModifierButtonDataAttribute::init_type(); + DataGraphTraversal::init_spam_flag(); + + DataRelation::register_with_factory(); +} diff --git a/panda/src/dgraph/config_dgraph.h b/panda/src/dgraph/config_dgraph.h new file mode 100644 index 0000000000..3839f4177e --- /dev/null +++ b/panda/src/dgraph/config_dgraph.h @@ -0,0 +1,15 @@ +// Filename: config_dgraph.h +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_DGRAPH_H +#define CONFIG_DGRAPH_H + +#include + +#include + +NotifyCategoryDecl(dgraph, EXPCL_PANDA, EXPTP_PANDA); + +#endif diff --git a/panda/src/dgraph/dataGraphTraversal.cxx b/panda/src/dgraph/dataGraphTraversal.cxx new file mode 100644 index 0000000000..053b8510ab --- /dev/null +++ b/panda/src/dgraph/dataGraphTraversal.cxx @@ -0,0 +1,99 @@ +// Filename: dataGraphTraversal.cxx +// Created by: drose (26Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "dataGraphTraversal.h" +#include "dataNode.h" +#include "dataRelation.h" +#include "intDataTransition.h" +#include "intDataAttribute.h" +#include "config_dgraph.h" +#include "describe_data_verbose.h" + +#include +#include +#include +#include + + +// Here's the static member of DataGraphTraversal. +TypeHandle DataGraphTraversal::_spam_flag_type; + + + +//////////////////////////////////////////////////////////////////// +// Function: DataGraphTraversal::init_spam_flag +// Access: Public, Static +// Description: This function is called during static +// initialization and registers a convenient type handle +// for the spam flag. +//////////////////////////////////////////////////////////////////// +void DataGraphTraversal:: +init_spam_flag() { + // We shouldn't try to call this function more than once. This + // function is only to be called during static initialization. + nassertv(_spam_flag_type == TypeHandle::none()); + IntDataTransition::init_type(); + register_type(_spam_flag_type, "SpamFlagTransition", + IntDataTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: DataGraphVisitor::reached_node +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool DataGraphVisitor:: +reached_node(Node *node, AllAttributesWrapper &state, NullLevelState &) { + // Is the spam flag set? + bool has_spam_flag = + state.has_attribute(DataGraphTraversal::_spam_flag_type); + + if (has_spam_flag && dgraph_cat.is_info()) { + dgraph_cat.info() << "Sending into " << *node << " {\n"; + describe_data_verbose(dgraph_cat.info(false), state.get_attributes(), 2); + dgraph_cat.info(false) << "}\n"; + } + + if (node->is_of_type(DataNode::get_class_type())) { + DataNode *dnode = DCAST(DataNode, node); + + // Now let the data node process the data itself. + dnode->transmit_data(state.get_attributes()); + + if (dnode->get_spam_mode() && !has_spam_flag) { + // If the node we pass through has the spam flag set, then we'll + // set it on our current state, and insist that it gets through + // to all our children. + state.set_attribute(DataGraphTraversal::_spam_flag_type, + new IntDataAttribute); + has_spam_flag = true; + } + } + + if (has_spam_flag && !node->_children.empty() && + dgraph_cat.is_info()) { + dgraph_cat.info() << "Receiving from " << *node << " {\n"; + describe_data_verbose(dgraph_cat.info(false), state.get_attributes(), 2); + dgraph_cat.info(false) << "}\n"; + } + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: traverse_data_graph +// Description: Initiates a traversal of the data graph given the +// indicated root node, with no initial data. Data +// generated by the nodes encountered will be propagated +// downwards. +//////////////////////////////////////////////////////////////////// +void traverse_data_graph(Node *root) { + DataGraphVisitor dgv; + df_traverse(root, dgv, AllAttributesWrapper(), + NullLevelState(), RenderRelation::get_class_type()); + + df_traverse(root, dgv, AllAttributesWrapper(), + NullLevelState(), DataRelation::get_class_type()); +} diff --git a/panda/src/dgraph/dataGraphTraversal.h b/panda/src/dgraph/dataGraphTraversal.h new file mode 100644 index 0000000000..15615c8cb7 --- /dev/null +++ b/panda/src/dgraph/dataGraphTraversal.h @@ -0,0 +1,70 @@ +// Filename: dataGraphTraversal.h +// Created by: drose (26Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DATAGRAPHTRAVERSAL_H +#define DATAGRAPHTRAVERSAL_H + +//////////////////////////////////////////////////////////////////// +// +// DataGraphTraversal +// +// This header file exists mainly to define the function +// traverse_data_graph(), which initiates a traversal of the data +// graph. +// +// This is the main interface to the data graph. Traversing the data +// graph transmits data down the graph--reading data from devices and +// sending it to the DataNodes that want it. +//////////////////////////////////////////////////////////////////// + +#include + +#include +#include +#include +#include + + +//////////////////////////////////////////////////////////////////// +// Class : DataGraphTraversal +// Description : This is mainly a holder for the static +// _spam_flag_type TypeHandle, which is a registered +// NumericDataAttribute that keeps track of the state of +// the spam flag during data graph traversal. The class +// otherwise doesn't do anything useful, but see the +// traverse_data_graph() function, below. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DataGraphTraversal { +public: + static void init_spam_flag(); + + static TypeHandle _spam_flag_type; +}; + + +//////////////////////////////////////////////////////////////////// +// Class : DataGraphVisitor +// Description : The TraverserVisitor to handle data graph traversals. +// This manages the spam flag, and may eventually handle +// merging of data from different parents into a single +// node. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DataGraphVisitor : + public TraverserVisitor { +public: + bool reached_node(Node *node, AllAttributesWrapper &state, NullLevelState &); +}; + + +//////////////////////////////////////////////////////////////////// +// Function: traverse_data_graph +// Description: Initiates a traversal of the data graph given the +// indicated root node, with no initial data. Data +// generated by the nodes encountered will be propagated +// downwards. +//////////////////////////////////////////////////////////////////// +void EXPCL_PANDA traverse_data_graph(Node *root); + +#endif diff --git a/panda/src/dgraph/dataNode.cxx b/panda/src/dgraph/dataNode.cxx new file mode 100644 index 0000000000..2394c0a91c --- /dev/null +++ b/panda/src/dgraph/dataNode.cxx @@ -0,0 +1,73 @@ +// Filename: dataNode.cxx +// Created by: drose (26Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "dataNode.h" + +TypeHandle DataNode::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: DataNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DataNode:: +DataNode(const string &name) : NamedNode(name) { + _spam_mode = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: DataNode::set_spam_mode +// Access: Public +// Description: Sets the "spam" flag, which if set indicates that all +// data produced from this DataNode, along with all data +// derived from it downstream, should be reported to the +// user. Handy for figuring out where things are going +// wrong. +//////////////////////////////////////////////////////////////////// +void DataNode:: +set_spam_mode(bool flag) { + _spam_mode = flag; +} + +//////////////////////////////////////////////////////////////////// +// Function: DataNode::get_spam_mode +// Access: Public +// Description: Gets the current state of the "spam" flag. See +// set_spam_mode. +//////////////////////////////////////////////////////////////////// +bool DataNode:: +get_spam_mode() const { + return _spam_mode; +} + + + +//////////////////////////////////////////////////////////////////// +// Function: register_data_transition +// Description: Defines a new data transition of the indicated name +// and inheriting from the indicated transition type. +// This will represent a channel of data, for instance a +// 3-component position from a tracker. +// +// All data transitions that have a common name and base +// class will share the same TypeHandle. This makes it +// possible to unify data producers and consumers based +// on the TypeHandle of the data they share. +//////////////////////////////////////////////////////////////////// +void +register_data_transition(TypeHandle &type_handle, const string &name, + TypeHandle derived_from) { + // Make sure the user gave us a transition type as the base. + nassertv(derived_from.is_derived_from(NodeTransition::get_class_type())); + + string actual_name = name + "_" + derived_from.get_name(); + type_handle = TypeRegistry::ptr()->find_type(actual_name); + + if (type_handle == TypeHandle::none()) { + register_type(type_handle, actual_name, derived_from); + } +} + + diff --git a/panda/src/dgraph/dataNode.h b/panda/src/dgraph/dataNode.h new file mode 100644 index 0000000000..a06cc54f5d --- /dev/null +++ b/panda/src/dgraph/dataNode.h @@ -0,0 +1,117 @@ +// Filename: dataNode.h +// Created by: drose (25Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DATANODE_H +#define DATANODE_H + +//////////////////////////////////////////////////////////////////// +// +// The Data Graph. +// +// The data graph is intended to hook up devices and their inputs +// and/or outputs in a clean interface. It uses the same graph +// relationship that is used to construct the scene graph, with the +// same nodes and arcs. +// +// In a data graph, each node may potentially produce and/or consume +// data, and the arcs transmit data downward, from the root of the +// graph to its leaves. Thus, an input device such as a mouse might +// be added to the graph near the root, and a tformer-style object +// that interprets the mouse data as a trackball motion and outputs a +// matrix might be the immediate child of the mouse, followed by an +// object that accepts a matrix and sets it on some particular arc in +// the scene graph. +// +// In a normal scene graph, the arcs transmit state, and each node +// inherits a collection of NodeAttribute values that are defined by +// the total set of arcs above it. In a data graph, the arcs transmit +// data instead of state, and each piece of data is stored in a +// NodeAttribute value. Thus, each data node still inherits a +// collection of NodeAttribute values, but now those values contain +// data instead of state information. In addition, a data node may +// retransmit a different set of NodeAttribute values further down the +// chain. This is implemented via the transmit_data() function, below. +// +// Each data node may define its own set of input values and output +// values, each with its own unique attribute type. This could be +// done by subclassing a different kind of NodeAttribute for each +// different input or output value, but this quickly gets unwieldy. +// Instead, you may find it convenient to use the function +// register_data_transition(), below, which creates a new TypeHandle +// associated with some existing NodeAttribute type, based on the +// unique name you give it. This allows producers and consumers to +// match their corresponding data values up by TypeHandle number (and +// hence by name); this matching happens more or less transparently by +// the graph traversal logic. +// +//////////////////////////////////////////////////////////////////// + +#include + +#include + +class NodeAttributes; + +//////////////////////////////////////////////////////////////////// +// Class : DataNode +// Description : The main node of the data graph. This could +// represent a device that produces data, or an object +// that operates on data received from a device. +// +// The main difference between this and a normal node is +// the transmit_data() function. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DataNode : public NamedNode { +public: + DataNode(const string &name = ""); + + virtual void + transmit_data(NodeAttributes &data)=0; + + void set_spam_mode(bool flag); + bool get_spam_mode() const; + + +private: + bool _spam_mode; + + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NamedNode::init_type(); + register_type(_type_handle, "DataNode", + NamedNode::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + + +//////////////////////////////////////////////////////////////////// +// Function: register_data_transition +// Description: Defines a new data transition of the indicated name +// and inheriting from the indicated transition type. +// This will represent a channel of data, for instance a +// 3-component position from a tracker. +// +// All data transitions that have a common name and base +// class will share the same TypeHandle. This makes it +// possible to unify data producers and consumers based +// on the TypeHandle of the data they share. +//////////////////////////////////////////////////////////////////// +void EXPCL_PANDA +register_data_transition(TypeHandle &type_handle, const string &name, + TypeHandle derived_from); + +#endif + diff --git a/panda/src/dgraph/dataRelation.I b/panda/src/dgraph/dataRelation.I new file mode 100644 index 0000000000..0668f829fb --- /dev/null +++ b/panda/src/dgraph/dataRelation.I @@ -0,0 +1,42 @@ +// Filename: dataRelation.I +// Created by: drose (08Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: DataRelation::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DataRelation:: +DataRelation(Node *from, Node *to, int sort) : + NodeRelation(from, to, sort, get_class_type()) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DataRelation::Constructor +// Access: Protected +// Description: Creates a new, unattached arc. This constructor is +// only intended for passing through the factory to +// create an arc based on a particular type using +// create_typed_arc(), below. You shouldn't, in +// general, attempt to create an unattached arc. +//////////////////////////////////////////////////////////////////// +INLINE DataRelation:: +DataRelation() : + NodeRelation(get_class_type()) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DataRelation::register_with_factory +// Access: Public, Static +// Description: Registers the type DataRelation with the factory. +// This is only intended to be called at initialization +// time; don't call it directly. +//////////////////////////////////////////////////////////////////// +INLINE void DataRelation:: +register_with_factory() { + get_factory().register_factory(get_class_type(), make_arc); +} diff --git a/panda/src/dgraph/dataRelation.N b/panda/src/dgraph/dataRelation.N new file mode 100644 index 0000000000..4899282a65 --- /dev/null +++ b/panda/src/dgraph/dataRelation.N @@ -0,0 +1 @@ +ignoremember register_with_factory diff --git a/panda/src/dgraph/dataRelation.cxx b/panda/src/dgraph/dataRelation.cxx new file mode 100644 index 0000000000..563244931d --- /dev/null +++ b/panda/src/dgraph/dataRelation.cxx @@ -0,0 +1,21 @@ +// Filename: dataRelation.cxx +// Created by: drose (08Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "dataRelation.h" +#include "transformTransition.h" + +TypeHandle DataRelation::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: DataRelation::make_arc +// Access: Public, Static +// Description: This function is passed to the Factory to make a new +// DataRelation by type. Don't try to call this +// function directly. +//////////////////////////////////////////////////////////////////// +NodeRelation *DataRelation:: +make_arc(const FactoryParams &) { + return new DataRelation; +} diff --git a/panda/src/dgraph/dataRelation.h b/panda/src/dgraph/dataRelation.h new file mode 100644 index 0000000000..0bf9d95cb7 --- /dev/null +++ b/panda/src/dgraph/dataRelation.h @@ -0,0 +1,54 @@ +// Filename: dataRelation.h +// Created by: drose (08Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DATARELATION_H +#define DATARELATION_H + +#include + +#include + +/////////////////////////////////////////////////////////////////// +// Class : DataRelation +// Description : The arc type specific to data graphs. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DataRelation : public NodeRelation { +public: + INLINE DataRelation(Node *from, Node *to, int sort = 0); + +protected: + // Normally, this should only be used for passing to the factory. + // Don't attempt to create an unattached arc directly. + INLINE DataRelation(); + +public: + // This is just to be called at initialization time; don't try to + // call this directly. + INLINE static void register_with_factory(); + +private: + static NodeRelation *make_arc(const FactoryParams ¶ms); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeRelation::init_type(); + register_type(_type_handle, "DataRelation", + NodeRelation::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "dataRelation.I" + +#endif diff --git a/panda/src/dgraph/describe_data_verbose.cxx b/panda/src/dgraph/describe_data_verbose.cxx new file mode 100644 index 0000000000..b8110994df --- /dev/null +++ b/panda/src/dgraph/describe_data_verbose.cxx @@ -0,0 +1,61 @@ +// Filename: describe_data_verbose.cxx +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "describe_data_verbose.h" +#include "dataGraphTraversal.h" + +#include +#include + +// The number of columns in from the start of the name to print the +// data value. +static const int data_indent_level = 12; + + +//////////////////////////////////////////////////////////////////// +// Function: describe_data_verbose +// Description: Writes to the indicated output stream a +// nicely-formatted, multi-line description of all the +// data values included in the indicated state. +//////////////////////////////////////////////////////////////////// +void +describe_data_verbose(ostream &out, const NodeAttributes &state, + int indent_level) { + NodeAttributes::const_iterator nai; + + for (nai = state.begin(); nai != state.end(); ++nai) { + // We'll exclude the spam flag. No one wants to see that. + TypeHandle type = (*nai).first; + if (type != DataGraphTraversal::_spam_flag_type) { + const PT(NodeAttribute) &attrib = (*nai).second; + if (attrib != (NodeAttribute *)NULL) { + // Now extract the type name out of the type flag. + string actual_name = type.get_name(); + string::size_type underscore = actual_name.rfind('_'); + string name; + if (underscore == NPOS) { + // There was no underscore, so this name wasn't created via + // register_data_transition(). Huh. Well, that's legitimate + // (if unexpected), so just print the whole name. + name = actual_name; + } else { + // Assume that, if the name was created via + // register_data_transition(), the original name was the part + // before the last underscore. + name = actual_name.substr(0, underscore); + } + + indent(out, indent_level) << name; + if (name.length() < data_indent_level) { + indent(out, data_indent_level - name.length()); + } else { + out << " "; + } + out << *attrib << "\n"; + } + } + } +} + diff --git a/panda/src/dgraph/describe_data_verbose.h b/panda/src/dgraph/describe_data_verbose.h new file mode 100644 index 0000000000..cdf7eab3a9 --- /dev/null +++ b/panda/src/dgraph/describe_data_verbose.h @@ -0,0 +1,22 @@ +// Filename: describe_data_verbose.h +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DESCRIBE_DATA_VERBOSE_H +#define DESCRIBE_DATA_VERBOSE_H + +#include + +class NodeAttributes; + +//////////////////////////////////////////////////////////////////// +// Function: describe_data_verbose +// Description: Writes to the indicated output stream a +// nicely-formatted, multi-line description of all the +// data values included in the indicated state. +//////////////////////////////////////////////////////////////////// +void describe_data_verbose(ostream &out, const NodeAttributes &state, + int indent_level = 0); + +#endif diff --git a/panda/src/dgraph/doubleDataAttribute.I b/panda/src/dgraph/doubleDataAttribute.I new file mode 100644 index 0000000000..3758932a88 --- /dev/null +++ b/panda/src/dgraph/doubleDataAttribute.I @@ -0,0 +1,23 @@ +// Filename: doubleDataAttribute.I +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: DoubleDataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DoubleDataAttribute:: +DoubleDataAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: DoubleDataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DoubleDataAttribute:: +DoubleDataAttribute(double value) : NumericDataAttribute(value) { +} + diff --git a/panda/src/dgraph/doubleDataAttribute.cxx b/panda/src/dgraph/doubleDataAttribute.cxx new file mode 100644 index 0000000000..4802ae94dc --- /dev/null +++ b/panda/src/dgraph/doubleDataAttribute.cxx @@ -0,0 +1,34 @@ +// Filename: doubleDataAttribute.cxx +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "doubleDataAttribute.h" +#include "doubleDataTransition.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif + +TypeHandle DoubleDataAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: DoubleDataAttribute::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *DoubleDataAttribute:: +make_copy() const { + return new DoubleDataAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: DoubleDataAttribute::make_initial +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *DoubleDataAttribute:: +make_initial() const { + return new DoubleDataAttribute; +} diff --git a/panda/src/dgraph/doubleDataAttribute.h b/panda/src/dgraph/doubleDataAttribute.h new file mode 100644 index 0000000000..ea9a5bb435 --- /dev/null +++ b/panda/src/dgraph/doubleDataAttribute.h @@ -0,0 +1,53 @@ +// Filename: doubleDataAttribute.h +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DOUBLEDATAATTRIBUTE_H +#define DOUBLEDATAATTRIBUTE_H + +#include + +#include "numericDataAttribute.h" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, NumericDataAttribute); + +//////////////////////////////////////////////////////////////////// +// Class : DoubleDataAttribute +// Description : A NumericDataAttribute templated on double types. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DoubleDataAttribute : + public NumericDataAttribute { +public: + INLINE DoubleDataAttribute(); + INLINE DoubleDataAttribute(double value); + + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NumericDataAttribute::init_type(); + register_type(_type_handle, "DoubleDataAttribute", + NumericDataAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "doubleDataAttribute.I" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/dgraph/doubleDataTransition.I b/panda/src/dgraph/doubleDataTransition.I new file mode 100644 index 0000000000..9732548e84 --- /dev/null +++ b/panda/src/dgraph/doubleDataTransition.I @@ -0,0 +1,25 @@ +// Filename: doubleDataTransition.I +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: DoubleDataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DoubleDataTransition:: +DoubleDataTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: DoubleDataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DoubleDataTransition:: +DoubleDataTransition(double scale, double offset) : + NumericDataTransition(scale, offset) +{ +} + diff --git a/panda/src/dgraph/doubleDataTransition.cxx b/panda/src/dgraph/doubleDataTransition.cxx new file mode 100644 index 0000000000..68df901ce7 --- /dev/null +++ b/panda/src/dgraph/doubleDataTransition.cxx @@ -0,0 +1,34 @@ +// Filename: doubleDataTransition.cxx +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "doubleDataTransition.h" +#include "doubleDataAttribute.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif + +TypeHandle DoubleDataTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: DoubleDataTransition::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeTransition *DoubleDataTransition:: +make_copy() const { + return new DoubleDataTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: DoubleDataTransition::make_attrib +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *DoubleDataTransition:: +make_attrib() const { + return new DoubleDataAttribute; +} diff --git a/panda/src/dgraph/doubleDataTransition.h b/panda/src/dgraph/doubleDataTransition.h new file mode 100644 index 0000000000..d999baab0e --- /dev/null +++ b/panda/src/dgraph/doubleDataTransition.h @@ -0,0 +1,53 @@ +// Filename: doubleDataTransition.h +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DOUBLEDATATRANSITION_H +#define DOUBLEDATATRANSITION_H + +#include + +#include "numericDataTransition.h" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, NumericDataTransition); + +//////////////////////////////////////////////////////////////////// +// Class : DoubleDataTransition +// Description : A NumericDataTransition templated on double types. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DoubleDataTransition : + public NumericDataTransition { +public: + INLINE DoubleDataTransition(); + INLINE DoubleDataTransition(double scale, double offset); + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NumericDataTransition::init_type(); + register_type(_type_handle, "DoubleDataTransition", + NumericDataTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "doubleDataTransition.I" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/dgraph/doublePtrDataAttribute.I b/panda/src/dgraph/doublePtrDataAttribute.I new file mode 100644 index 0000000000..e1b47e9cb3 --- /dev/null +++ b/panda/src/dgraph/doublePtrDataAttribute.I @@ -0,0 +1,22 @@ +// Filename: doublePtrDataAttribute.I +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: DoublePtrDataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DoublePtrDataAttribute:: +DoublePtrDataAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: DoublePtrDataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DoublePtrDataAttribute:: +DoublePtrDataAttribute(double* ptr) : PointerDataAttribute(ptr) { +} diff --git a/panda/src/dgraph/doublePtrDataAttribute.cxx b/panda/src/dgraph/doublePtrDataAttribute.cxx new file mode 100644 index 0000000000..db80bf0300 --- /dev/null +++ b/panda/src/dgraph/doublePtrDataAttribute.cxx @@ -0,0 +1,34 @@ +// Filename: doublePtrDataAttribute.cxx +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "doublePtrDataAttribute.h" +#include "doublePtrDataTransition.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif + +TypeHandle DoublePtrDataAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: DoublePtrDataAttribute::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *DoublePtrDataAttribute:: +make_copy() const { + return new DoublePtrDataAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: DoublePtrDataAttribute::make_initial +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *DoublePtrDataAttribute:: +make_initial() const { + return new DoublePtrDataAttribute; +} diff --git a/panda/src/dgraph/doublePtrDataAttribute.h b/panda/src/dgraph/doublePtrDataAttribute.h new file mode 100644 index 0000000000..4218283355 --- /dev/null +++ b/panda/src/dgraph/doublePtrDataAttribute.h @@ -0,0 +1,53 @@ +// Filename: doublePtrDataAttribute.h +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DOUBLEPTRDATAATTRIBUTE_H +#define DOUBLEPTRDATAATTRIBUTE_H + +#include + +#include "pointerDataAttribute.h" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerDataAttribute); + +//////////////////////////////////////////////////////////////////// +// Class : DoublePtrDataAttribute +// Description : A PointerDataAttribute templated on double types. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DoublePtrDataAttribute : + public PointerDataAttribute { +public: + INLINE DoublePtrDataAttribute(); + INLINE DoublePtrDataAttribute(double* ptr); + + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PointerDataAttribute::init_type(); + register_type(_type_handle, "DoublePtrDataAttribute", + PointerDataAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "doublePtrDataAttribute.I" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/dgraph/doublePtrDataTransition.I b/panda/src/dgraph/doublePtrDataTransition.I new file mode 100644 index 0000000000..680eed431d --- /dev/null +++ b/panda/src/dgraph/doublePtrDataTransition.I @@ -0,0 +1,24 @@ +// Filename: doublePtrDataTransition.I +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: DoublePtrDataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DoublePtrDataTransition:: +DoublePtrDataTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: DoublePtrDataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DoublePtrDataTransition:: +DoublePtrDataTransition(double* ptr) : + PointerDataTransition(ptr) +{ +} diff --git a/panda/src/dgraph/doublePtrDataTransition.cxx b/panda/src/dgraph/doublePtrDataTransition.cxx new file mode 100644 index 0000000000..aa5b229ec0 --- /dev/null +++ b/panda/src/dgraph/doublePtrDataTransition.cxx @@ -0,0 +1,34 @@ +// Filename: doublePtrDataTransition.cxx +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "doublePtrDataTransition.h" +#include "doublePtrDataAttribute.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif + +TypeHandle DoublePtrDataTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: DoublePtrDataTransition::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeTransition *DoublePtrDataTransition:: +make_copy() const { + return new DoublePtrDataTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: DoublePtrDataTransition::make_attrib +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *DoublePtrDataTransition:: +make_attrib() const { + return new DoublePtrDataAttribute; +} diff --git a/panda/src/dgraph/doublePtrDataTransition.h b/panda/src/dgraph/doublePtrDataTransition.h new file mode 100644 index 0000000000..74ff46f4d9 --- /dev/null +++ b/panda/src/dgraph/doublePtrDataTransition.h @@ -0,0 +1,53 @@ +// Filename: doublePtrDataTransition.h +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DOUBLEPTRDATATRANSITION_H +#define DOUBLEPTRDATATRANSITION_H + +#include + +#include "pointerDataTransition.h" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerDataTransition); + +//////////////////////////////////////////////////////////////////// +// Class : DoublePtrDataTransition +// Description : A PointerDataTransition templated on double types. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DoublePtrDataTransition : + public PointerDataTransition { +public: + INLINE DoublePtrDataTransition(); + INLINE DoublePtrDataTransition(double* ptr); + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PointerDataTransition::init_type(); + register_type(_type_handle, "DoublePtrDataTransition", + PointerDataTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "doublePtrDataTransition.I" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/dgraph/intDataAttribute.I b/panda/src/dgraph/intDataAttribute.I new file mode 100644 index 0000000000..6bf93d10a1 --- /dev/null +++ b/panda/src/dgraph/intDataAttribute.I @@ -0,0 +1,23 @@ +// Filename: intDataAttribute.I +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: IntDataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE IntDataAttribute:: +IntDataAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: IntDataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE IntDataAttribute:: +IntDataAttribute(int value) : NumericDataAttribute(value) { +} + diff --git a/panda/src/dgraph/intDataAttribute.cxx b/panda/src/dgraph/intDataAttribute.cxx new file mode 100644 index 0000000000..55565e0a5c --- /dev/null +++ b/panda/src/dgraph/intDataAttribute.cxx @@ -0,0 +1,34 @@ +// Filename: intDataAttribute.cxx +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "intDataAttribute.h" +#include "intDataTransition.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif + +TypeHandle IntDataAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: IntDataAttribute::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *IntDataAttribute:: +make_copy() const { + return new IntDataAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: IntDataAttribute::make_initial +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *IntDataAttribute:: +make_initial() const { + return new IntDataAttribute; +} diff --git a/panda/src/dgraph/intDataAttribute.h b/panda/src/dgraph/intDataAttribute.h new file mode 100644 index 0000000000..8474ad78b0 --- /dev/null +++ b/panda/src/dgraph/intDataAttribute.h @@ -0,0 +1,53 @@ +// Filename: intDataAttribute.h +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INTDATAATTRIBUTE_H +#define INTDATAATTRIBUTE_H + +#include + +#include "numericDataAttribute.h" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, NumericDataAttribute); + +//////////////////////////////////////////////////////////////////// +// Class : IntDataAttribute +// Description : A NumericDataAttribute templated on integer types. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA IntDataAttribute : + public NumericDataAttribute { +public: + INLINE IntDataAttribute(); + INLINE IntDataAttribute(int value); + + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NumericDataAttribute::init_type(); + register_type(_type_handle, "IntDataAttribute", + NumericDataAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "intDataAttribute.I" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/dgraph/intDataTransition.I b/panda/src/dgraph/intDataTransition.I new file mode 100644 index 0000000000..5e31fc0600 --- /dev/null +++ b/panda/src/dgraph/intDataTransition.I @@ -0,0 +1,25 @@ +// Filename: intDataTransition.I +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: IntDataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE IntDataTransition:: +IntDataTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: IntDataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE IntDataTransition:: +IntDataTransition(int scale, int offset) : + NumericDataTransition(scale, offset) +{ +} + diff --git a/panda/src/dgraph/intDataTransition.cxx b/panda/src/dgraph/intDataTransition.cxx new file mode 100644 index 0000000000..99df3a545a --- /dev/null +++ b/panda/src/dgraph/intDataTransition.cxx @@ -0,0 +1,34 @@ +// Filename: intDataTransition.cxx +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "intDataTransition.h" +#include "intDataAttribute.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif + +TypeHandle IntDataTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: IntDataTransition::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeTransition *IntDataTransition:: +make_copy() const { + return new IntDataTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: IntDataTransition::make_attrib +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *IntDataTransition:: +make_attrib() const { + return new IntDataAttribute; +} diff --git a/panda/src/dgraph/intDataTransition.h b/panda/src/dgraph/intDataTransition.h new file mode 100644 index 0000000000..ac102984f6 --- /dev/null +++ b/panda/src/dgraph/intDataTransition.h @@ -0,0 +1,53 @@ +// Filename: intDataTransition.h +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INTDATATRANSITION_H +#define INTDATATRANSITION_H + +#include + +#include "numericDataTransition.h" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, NumericDataTransition); + +//////////////////////////////////////////////////////////////////// +// Class : IntDataTransition +// Description : A NumericDataTransition templated on integer types. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA IntDataTransition : + public NumericDataTransition { +public: + INLINE IntDataTransition(); + INLINE IntDataTransition(int scale, int offset); + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NumericDataTransition::init_type(); + register_type(_type_handle, "IntDataTransition", + NumericDataTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "intDataTransition.I" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/dgraph/matrixDataAttribute.I b/panda/src/dgraph/matrixDataAttribute.I new file mode 100644 index 0000000000..9566807e3c --- /dev/null +++ b/panda/src/dgraph/matrixDataAttribute.I @@ -0,0 +1,24 @@ +// Filename: matrixDataAttribute.I +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: MatrixDataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MatrixDataAttribute:: +MatrixDataAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixDataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MatrixDataAttribute:: +MatrixDataAttribute(const LMatrix4f &value) : + VectorDataAttribute(value) +{ +} diff --git a/panda/src/dgraph/matrixDataAttribute.cxx b/panda/src/dgraph/matrixDataAttribute.cxx new file mode 100644 index 0000000000..d205423cb8 --- /dev/null +++ b/panda/src/dgraph/matrixDataAttribute.cxx @@ -0,0 +1,34 @@ +// Filename: matrixDataAttribute.cxx +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "matrixDataAttribute.h" +#include "matrixDataTransition.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif + +TypeHandle MatrixDataAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: MatrixDataAttribute::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *MatrixDataAttribute:: +make_copy() const { + return new MatrixDataAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixDataAttribute::make_initial +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *MatrixDataAttribute:: +make_initial() const { + return new MatrixDataAttribute; +} diff --git a/panda/src/dgraph/matrixDataAttribute.h b/panda/src/dgraph/matrixDataAttribute.h new file mode 100644 index 0000000000..73af978a27 --- /dev/null +++ b/panda/src/dgraph/matrixDataAttribute.h @@ -0,0 +1,58 @@ +// Filename: matrixDataAttribute.h +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MATRIXDATAATTRIBUTE_H +#define MATRIXDATAATTRIBUTE_H + +#include + +#include "vectorDataAttribute.h" + +#include + +// We need to define this temporary macro so we can pass a parameter +// containing a comma through the macro. +#define VECTORDATAATTRIBUTE_LMATRIX4F VectorDataAttribute +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, VECTORDATAATTRIBUTE_LMATRIX4F); + +//////////////////////////////////////////////////////////////////// +// Class : MatrixDataAttribute +// Description : A VectorDataAttribute templated on LMatrix4f. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA MatrixDataAttribute : + public VectorDataAttribute { +public: + INLINE MatrixDataAttribute(); + INLINE MatrixDataAttribute(const LMatrix4f &value); + + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + VectorDataAttribute::init_type(); + register_type(_type_handle, "MatrixDataAttribute", + VectorDataAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "matrixDataAttribute.I" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/dgraph/matrixDataTransition.I b/panda/src/dgraph/matrixDataTransition.I new file mode 100644 index 0000000000..e05a7cac01 --- /dev/null +++ b/panda/src/dgraph/matrixDataTransition.I @@ -0,0 +1,25 @@ +// Filename: matrixDataTransition.I +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: MatrixDataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MatrixDataTransition:: +MatrixDataTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixDataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MatrixDataTransition:: +MatrixDataTransition(const LMatrix4f &matrix) : + VectorDataTransition(matrix) +{ +} + diff --git a/panda/src/dgraph/matrixDataTransition.cxx b/panda/src/dgraph/matrixDataTransition.cxx new file mode 100644 index 0000000000..d48b51ad0a --- /dev/null +++ b/panda/src/dgraph/matrixDataTransition.cxx @@ -0,0 +1,34 @@ +// Filename: matrixDataTransition.cxx +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "matrixDataTransition.h" +#include "matrixDataAttribute.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif + +TypeHandle MatrixDataTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: MatrixDataTransition::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeTransition *MatrixDataTransition:: +make_copy() const { + return new MatrixDataTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixDataTransition::make_attrib +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *MatrixDataTransition:: +make_attrib() const { + return new MatrixDataAttribute; +} diff --git a/panda/src/dgraph/matrixDataTransition.h b/panda/src/dgraph/matrixDataTransition.h new file mode 100644 index 0000000000..46dd7146ec --- /dev/null +++ b/panda/src/dgraph/matrixDataTransition.h @@ -0,0 +1,58 @@ +// Filename: matrixDataTransition.h +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MATRIXDATATRANSITION_H +#define MATRIXDATATRANSITION_H + +#include + +#include "vectorDataTransition.h" + +#include + +// We need to define this temporary macro so we can pass a parameter +// containing a comma through the macro. +#define VECTORDATATRANSITION_LMATRIX4F VectorDataTransition +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, VECTORDATATRANSITION_LMATRIX4F); + +//////////////////////////////////////////////////////////////////// +// Class : MatrixDataTransition +// Description : A VectorDataTransition templated on LMatrix4f. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA MatrixDataTransition : + public VectorDataTransition { +public: + INLINE MatrixDataTransition(); + INLINE MatrixDataTransition(const LMatrix4f &matrix); + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + VectorDataTransition::init_type(); + register_type(_type_handle, "MatrixDataTransition", + VectorDataTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "matrixDataTransition.I" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/dgraph/modifierButtonDataAttribute.I b/panda/src/dgraph/modifierButtonDataAttribute.I new file mode 100644 index 0000000000..ddfc4948c6 --- /dev/null +++ b/panda/src/dgraph/modifierButtonDataAttribute.I @@ -0,0 +1,56 @@ +// Filename: modifierButtonDataAttribute.I +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ModifierButtonDataAttribute:: +ModifierButtonDataAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataAttribute::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ModifierButtonDataAttribute:: +ModifierButtonDataAttribute(const ModifierButtonDataAttribute ©) : + NodeAttribute(copy), + _mods(copy._mods) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataAttribute::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ModifierButtonDataAttribute:: +operator = (const ModifierButtonDataAttribute ©) { + NodeAttribute::operator = (copy); + _mods = copy._mods; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataAttribute::set_mods +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ModifierButtonDataAttribute:: +set_mods(const ModifierButtons &mods) { + _mods = mods; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataAttribute::set_mods +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const ModifierButtons &ModifierButtonDataAttribute:: +get_mods() const { + return _mods; +} diff --git a/panda/src/dgraph/modifierButtonDataAttribute.cxx b/panda/src/dgraph/modifierButtonDataAttribute.cxx new file mode 100644 index 0000000000..1ffc44d96b --- /dev/null +++ b/panda/src/dgraph/modifierButtonDataAttribute.cxx @@ -0,0 +1,78 @@ +// Filename: modifierButtonDataAttribute.cxx +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "modifierButtonDataAttribute.h" +#include "modifierButtonDataTransition.h" + +#include + +TypeHandle ModifierButtonDataAttribute::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataAttribute::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *ModifierButtonDataAttribute:: +make_copy() const { + return new ModifierButtonDataAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataAttribute::make_initial +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *ModifierButtonDataAttribute:: +make_initial() const { + return new ModifierButtonDataAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataAttribute::get_handle +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +TypeHandle ModifierButtonDataAttribute:: +get_handle() const { + return ModifierButtonDataTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataAttribute::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void ModifierButtonDataAttribute:: +output(ostream &out) const { + out << _mods; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataAttribute::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void ModifierButtonDataAttribute:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << _mods << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataAttribute::internal_compare_to +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int ModifierButtonDataAttribute:: +internal_compare_to(const NodeAttribute *other) const { + const ModifierButtonDataAttribute *ot; + DCAST_INTO_R(ot, other, false); + + if (_mods != ot->_mods) { + return (_mods < ot->_mods) ? -1 : 1; + } + return 0; +} diff --git a/panda/src/dgraph/modifierButtonDataAttribute.h b/panda/src/dgraph/modifierButtonDataAttribute.h new file mode 100644 index 0000000000..d792922fd6 --- /dev/null +++ b/panda/src/dgraph/modifierButtonDataAttribute.h @@ -0,0 +1,68 @@ +// Filename: modifierButtonDataAttribute.h +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MODIFIERBUTTONDATAATTRIBUTE_H +#define MODIFIERBUTTONDATAATTRIBUTE_H + +#include + +#include +#include + +class ModifierButtonDataTransition; + +//////////////////////////////////////////////////////////////////// +// Class : ModifierButtonDataAttribute +// Description : This data graph attribute stores the current state of +// some various modifier buttons (e.g. shift, control, +// alt, and the mouse buttons), as generated for +// instance by a GraphicsWindow. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ModifierButtonDataAttribute : public NodeAttribute { +public: + INLINE ModifierButtonDataAttribute(); + INLINE ModifierButtonDataAttribute(const ModifierButtonDataAttribute ©); + INLINE void operator = (const ModifierButtonDataAttribute ©); + +public: + INLINE void set_mods(const ModifierButtons &mods); + INLINE const ModifierButtons &get_mods() const; + + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual TypeHandle get_handle() const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeAttribute *other) const; + +private: + ModifierButtons _mods; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeAttribute::init_type(); + register_type(_type_handle, "ModifierButtonDataAttribute", + NodeAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +friend class ModifierButtonDataTransition; +}; + +#include "modifierButtonDataAttribute.I" + +#endif diff --git a/panda/src/dgraph/modifierButtonDataTransition.I b/panda/src/dgraph/modifierButtonDataTransition.I new file mode 100644 index 0000000000..f9ee79e84e --- /dev/null +++ b/panda/src/dgraph/modifierButtonDataTransition.I @@ -0,0 +1,34 @@ +// Filename: modifierButtonDataTransition.I +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ModifierButtonDataTransition:: +ModifierButtonDataTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataTransition::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ModifierButtonDataTransition:: +ModifierButtonDataTransition(const ModifierButtonDataTransition ©) : + NodeTransition(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataTransition::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ModifierButtonDataTransition:: +operator = (const ModifierButtonDataTransition ©) { + NodeTransition::operator = (copy); +} diff --git a/panda/src/dgraph/modifierButtonDataTransition.cxx b/panda/src/dgraph/modifierButtonDataTransition.cxx new file mode 100644 index 0000000000..60d211e9f9 --- /dev/null +++ b/panda/src/dgraph/modifierButtonDataTransition.cxx @@ -0,0 +1,80 @@ +// Filename: modifierButtonDataTransition.cxx +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "modifierButtonDataTransition.h" +#include "modifierButtonDataAttribute.h" + +TypeHandle ModifierButtonDataTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataTransition::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeTransition *ModifierButtonDataTransition:: +make_copy() const { + return new ModifierButtonDataTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataTransition::make_attrib +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *ModifierButtonDataTransition:: +make_attrib() const { + return new ModifierButtonDataAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataTransition::compose +// Access: Public, Virtual +// Description: Returns a new transition that corresponds to the +// composition of this transition with the second +// transition (which must be of an equivalent type). +// This may return the same pointer as either source +// transition. Applying the transition returned from +// this function to an attribute attribute will produce +// the same effect as applying each transition +// separately. +//////////////////////////////////////////////////////////////////// +NodeTransition *ModifierButtonDataTransition:: +compose(const NodeTransition *) const { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataTransition::invert +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeTransition *ModifierButtonDataTransition:: +invert() const { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataTransition::apply +// Access: Public, Virtual +// Description: Returns a new attribute (or possibly the same +// attribute) that represents the effect of applying this +// indicated transition to the indicated attribute. The +// source attribute may be NULL, indicating the initial +// attribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ModifierButtonDataTransition:: +apply(const NodeAttribute *attrib) const { + return (NodeAttribute *)attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtonDataTransition::internal_compare_to +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int ModifierButtonDataTransition:: +internal_compare_to(const NodeTransition *) const { + return 0; +} diff --git a/panda/src/dgraph/modifierButtonDataTransition.h b/panda/src/dgraph/modifierButtonDataTransition.h new file mode 100644 index 0000000000..870eac6a49 --- /dev/null +++ b/panda/src/dgraph/modifierButtonDataTransition.h @@ -0,0 +1,54 @@ +// Filename: modifierButtonDataTransition.h +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MODIFIERBUTTONDATATRANSITION_H +#define MODIFIERBUTTONDATATRANSITION_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : ModifierButtonDataTransition +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ModifierButtonDataTransition : public NodeTransition { +public: + INLINE ModifierButtonDataTransition(); + INLINE ModifierButtonDataTransition(const ModifierButtonDataTransition ©); + INLINE void operator = (const ModifierButtonDataTransition ©); + +public: + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + + virtual NodeTransition *compose(const NodeTransition *other) const; + virtual NodeTransition *invert() const; + virtual NodeAttribute *apply(const NodeAttribute *attrib) const; + +protected: + virtual int internal_compare_to(const NodeTransition *other) const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeTransition::init_type(); + register_type(_type_handle, "ModifierButtonDataTransition", + NodeTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "modifierButtonDataTransition.I" + +#endif diff --git a/panda/src/dgraph/numericDataAttribute.I b/panda/src/dgraph/numericDataAttribute.I new file mode 100644 index 0000000000..7e07622406 --- /dev/null +++ b/panda/src/dgraph/numericDataAttribute.I @@ -0,0 +1,132 @@ +// Filename: numericDataAttribute.I +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "numericDataTransition.h" + +#include + +template +TypeHandle NumericDataAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumericDataAttribute:: +NumericDataAttribute() { + _value = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumericDataAttribute:: +NumericDataAttribute(NumType value) : + _value(value) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataAttribute::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumericDataAttribute:: +NumericDataAttribute(const NumericDataAttribute ©) : + NodeAttribute(copy), + _value(copy._value) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataAttribute::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void NumericDataAttribute:: +operator = (const NumericDataAttribute ©) { + NodeAttribute::operator = (copy); + _value = copy._value; +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataAttribute::set_value +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void NumericDataAttribute:: +set_value(NumType value) { + _value = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataAttribute::get_value +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType NumericDataAttribute:: +get_value() const { + return _value; +} + + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataAttribute::get_handle +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +TypeHandle NumericDataAttribute:: +get_handle() const { + return NumericDataTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataAttribute::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void NumericDataAttribute:: +output(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataAttribute::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void NumericDataAttribute:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataAttribute::internal_compare_to +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +int NumericDataAttribute:: +internal_compare_to(const NodeAttribute *other) const { + const NumericDataAttribute *ot; + DCAST_INTO_R(ot, other, false); + + if (_value != ot->_value) { + return (_value < ot->_value) ? -1 : 1; + } + return 0; +} diff --git a/panda/src/dgraph/numericDataAttribute.h b/panda/src/dgraph/numericDataAttribute.h new file mode 100644 index 0000000000..e171cb076e --- /dev/null +++ b/panda/src/dgraph/numericDataAttribute.h @@ -0,0 +1,66 @@ +// Filename: numericDataAttribute.h +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NUMERICDATAATTRIBUTE_H +#define NUMERICDATAATTRIBUTE_H + +#include + +#include + +template +class NumericDataTransition; + +//////////////////////////////////////////////////////////////////// +// Class : NumericDataAttribute +// Description : +//////////////////////////////////////////////////////////////////// +template +class NumericDataAttribute : public NodeAttribute { +public: + INLINE NumericDataAttribute(); + INLINE NumericDataAttribute(NumType value); + INLINE NumericDataAttribute(const NumericDataAttribute ©); + INLINE void operator = (const NumericDataAttribute ©); + +public: + INLINE void set_value(NumType value); + INLINE NumType get_value() const; + + virtual TypeHandle get_handle() const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeAttribute *other) const; + +private: + NumType _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeAttribute::init_type(); + register_type(_type_handle, + string("NumericDataAttribute<") + + get_type_handle(NumType).get_name() + ">", + NodeAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +friend class NumericDataTransition; +}; + +#include "numericDataAttribute.I" + +#endif diff --git a/panda/src/dgraph/numericDataTransition.I b/panda/src/dgraph/numericDataTransition.I new file mode 100644 index 0000000000..fe92048027 --- /dev/null +++ b/panda/src/dgraph/numericDataTransition.I @@ -0,0 +1,252 @@ +// Filename: numericDataTransition.I +// Created by: drose (25Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "numericDataAttribute.h" + +#include + +template +TypeHandle NumericDataTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumericDataTransition:: +NumericDataTransition() : + _scale(1), + _offset(0) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumericDataTransition:: +NumericDataTransition(NumType scale, NumType offset) : + _scale(scale), + _offset(offset) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataTransition::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumericDataTransition:: +NumericDataTransition(const NumericDataTransition ©) : + NodeTransition(copy), + _scale(copy._scale), + _offset(copy._offset) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataTransition::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void NumericDataTransition:: +operator = (const NumericDataTransition ©) { + NodeTransition::operator = (copy); + _scale = copy._scale; + _offset = copy._offset; +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataTransition::is_identity +// Access: Public, Virtual +// Description: Returns true if this transition does not affect any +// numbers going through it. +//////////////////////////////////////////////////////////////////// +template +INLINE bool NumericDataTransition:: +is_identity() const { + return (_scale == 1.0 && _offset == 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataTransition::set_scale +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void NumericDataTransition:: +set_scale(NumType scale) { + _scale = scale; + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataTransition::get_scale +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +NumType NumericDataTransition:: +get_scale() const { + return _scale; +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataTransition::set_offset +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void NumericDataTransition:: +set_offset(NumType offset) { + _offset = offset; + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataTransition::get_offset +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +NumType NumericDataTransition:: +get_offset() const { + return _offset; +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataTransition::compose +// Access: Public, Virtual +// Description: Returns a new transition that corresponds to the +// composition of this transition with the second +// transition (which must be of an equivalent type). +// This may return the same pointer as either source +// transition. Applying the transition returned from +// this function to an attribute attribute will produce +// the same effect as applying each transition +// separately. +//////////////////////////////////////////////////////////////////// +template +NodeTransition *NumericDataTransition:: +compose(const NodeTransition *other) const { + const NumericDataTransition *ot; + DCAST_INTO_R(ot, other, NULL); + + if (is_identity()) { + return (NumericDataTransition *)ot; + + } else if (ot->is_identity()) { + return (NumericDataTransition *)this; + + } else { + NodeTransition *c = make_copy(); + NumericDataTransition *result; + DCAST_INTO_R(result, c, NULL); + + result->_offset = _offset + _scale * ot->_offset; + result->_scale = _scale * ot->_scale; + return result; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataTransition::invert +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +NodeTransition *NumericDataTransition:: +invert() const { + if (is_identity()) { + return (NumericDataTransition *)this; + + } else if (_scale == 0) { + // Singular; cannot invert. + return NULL; + + } else { + NodeTransition *c = make_copy(); + NumericDataTransition *result; + DCAST_INTO_R(result, c, NULL); + + result->_offset = - _offset / _scale; + result->_scale = (NumType)1 / _scale; + return result; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataTransition::apply +// Access: Public, Virtual +// Description: Returns a new attribute (or possibly the same +// attribute) that represents the effect of applying this +// indicated transition to the indicated attribute. The +// source attribute may be NULL, indicating the initial +// attribute. +//////////////////////////////////////////////////////////////////// +template +NodeAttribute *NumericDataTransition:: +apply(const NodeAttribute *attrib) const { + const NumericDataAttribute *at; + DCAST_INTO_R(at, attrib, NULL); + + if (is_identity()) { + return (NumericDataAttribute *)attrib; + } + + NodeAttribute *c = make_attrib(); + NumericDataAttribute *result; + DCAST_INTO_R(result, c, NULL); + + result->_value = at->_value * _scale + _offset; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataTransition::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void NumericDataTransition:: +output(ostream &out) const { + out << _scale << "x + " << _offset; +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataTransition::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void NumericDataTransition:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << _scale << "x + " << _offset << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataTransition::internal_compare_to +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +int NumericDataTransition:: +internal_compare_to(const NodeTransition *other) const { + const NumericDataTransition *ot; + DCAST_INTO_R(ot, other, false); + + if (_scale != ot->_scale) { + return (_scale < ot->_scale) ? -1 : 1; + } + if (_offset != ot->_offset) { + return (_offset < ot->_offset) ? -1 : 1; + } + return 0; +} diff --git a/panda/src/dgraph/numericDataTransition.h b/panda/src/dgraph/numericDataTransition.h new file mode 100644 index 0000000000..a4a5a7ea4c --- /dev/null +++ b/panda/src/dgraph/numericDataTransition.h @@ -0,0 +1,75 @@ +// Filename: numericDataTransition.h +// Created by: drose (25Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NUMERICDATATRANSITION_H +#define NUMERICDATATRANSITION_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : NumericDataTransition +// Description : A NumericDataAttribute is a special data graph +// attribute that is used to pass around a single +// number. A NumericDataTransition isn't often used, +// but when it is it may transform the number by a scale +// and/or offset. +//////////////////////////////////////////////////////////////////// +template +class NumericDataTransition : public NodeTransition { +public: + INLINE NumericDataTransition(); + INLINE NumericDataTransition(NumType scale, NumType offset); + INLINE NumericDataTransition(const NumericDataTransition ©); + INLINE void operator = (const NumericDataTransition ©); + +public: + + INLINE bool is_identity() const; + + INLINE void set_scale(NumType scale); + INLINE NumType get_scale() const; + + INLINE void set_offset(NumType offset); + INLINE NumType get_offset() const; + + virtual NodeTransition *compose(const NodeTransition *other) const; + virtual NodeTransition *invert() const; + virtual NodeAttribute *apply(const NodeAttribute *attrib) const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeTransition *other) const; + +private: + NumType _scale; + NumType _offset; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeTransition::init_type(); + register_type(_type_handle, + string("NumericDataTransition<") + + get_type_handle(NumType).get_name() + ">", + NodeTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "numericDataTransition.I" + +#endif diff --git a/panda/src/dgraph/pointerDataAttribute.I b/panda/src/dgraph/pointerDataAttribute.I new file mode 100644 index 0000000000..4239ac2e7f --- /dev/null +++ b/panda/src/dgraph/pointerDataAttribute.I @@ -0,0 +1,136 @@ +// Filename: pointerDataAttribute.I +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "pointerDataTransition.h" + +#include + +template +TypeHandle PointerDataAttribute::_type_handle; + +template +PtrType* PointerDataAttribute::_null_ptr = (PtrType*)NULL; + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerDataAttribute:: +PointerDataAttribute() : + _ptr(_null_ptr) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerDataAttribute:: +PointerDataAttribute(PtrType* ptr) : + _ptr(ptr) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataAttribute::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerDataAttribute:: +PointerDataAttribute(const PointerDataAttribute ©) : + NodeAttribute(copy), _ptr(copy._ptr) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataAttribute::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void PointerDataAttribute:: +operator = (const PointerDataAttribute ©) { + NodeAttribute::operator = (copy); + _ptr = copy._ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataAttribute::set_value +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void PointerDataAttribute:: +set_value(PtrType* ptr) { + _ptr = ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataAttribute::get_value +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +PtrType* PointerDataAttribute:: +get_value() const { + return _ptr; +} + + +//////////////////////////////////////////////////////////////////// +// Function: NumericDataAttribute::get_handle +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +TypeHandle PointerDataAttribute:: +get_handle() const { + return PointerDataTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataAttribute::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void PointerDataAttribute:: +output(ostream &out) const { + out << (void*)_ptr << endl; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataAttribute::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void PointerDataAttribute:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << (void*)_ptr << endl; +} + +/////////////////////////////////////////////////////////////////// +// Function: PointerDataAttribute::internal_compare_to +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +int PointerDataAttribute:: +internal_compare_to(const NodeAttribute *other) const { + const PointerDataAttribute *ot; + DCAST_INTO_R(ot, other, false); + + if (_ptr != ot->_ptr) { + return (_ptr > ot->_ptr) ? 1 : -1; + } + + return 0; +} diff --git a/panda/src/dgraph/pointerDataAttribute.h b/panda/src/dgraph/pointerDataAttribute.h new file mode 100644 index 0000000000..580acf673c --- /dev/null +++ b/panda/src/dgraph/pointerDataAttribute.h @@ -0,0 +1,70 @@ +// Filename: pointerDataAttribute.h +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef POINTERDATAATTRIBUTE_H +#define POINTERDATAATTRIBUTE_H + +#include + +#include + +template +class PointerDataTransition; + +//////////////////////////////////////////////////////////////////// +// Class : PointerDataTransition +// Description : +//////////////////////////////////////////////////////////////////// +template +class PointerDataAttribute : public NodeAttribute { +public: + INLINE PointerDataAttribute(); + INLINE PointerDataAttribute(PtrType* ptr); + INLINE PointerDataAttribute(const PointerDataAttribute ©); + INLINE void operator = (const PointerDataAttribute ©); + +public: + INLINE void set_value(PtrType* ptr); + INLINE PtrType* get_value() const; + + virtual TypeHandle get_handle() const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeAttribute *other) const; + +private: + PtrType* _ptr; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeAttribute::init_type(); + register_type(_type_handle, + string("PointerDataAttribute<") + + get_type_handle(PtrType).get_name() + ">", + NodeAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class PointerDataTransition; + + //Variable is just to make the compiler happy for the default + //constructor + static PtrType* _null_ptr; +}; + +#include "pointerDataAttribute.I" + +#endif diff --git a/panda/src/dgraph/pointerDataTransition.I b/panda/src/dgraph/pointerDataTransition.I new file mode 100644 index 0000000000..5d0427b583 --- /dev/null +++ b/panda/src/dgraph/pointerDataTransition.I @@ -0,0 +1,182 @@ +// Filename: pointerDataTransition.I +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "pointerDataAttribute.h" + +#include + +template +TypeHandle PointerDataTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerDataTransition:: +PointerDataTransition() : + _ptr(PointerDataAttribute::_null_ptr) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerDataTransition:: +PointerDataTransition(PtrType* ptr) : + _ptr(ptr) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataTransition::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerDataTransition:: +PointerDataTransition(const PointerDataTransition ©) : + NodeTransition(copy), _ptr(copy._ptr) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataTransition::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void PointerDataTransition:: +operator = (const PointerDataTransition ©) { + NodeTransition::operator = (copy); + _ptr = copy._ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataTransition::set_value +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void PointerDataTransition:: +set_value(PtrType* ptr) { + _ptr = ptr; + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataTransition::get_value +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +PtrType* PointerDataTransition:: +get_value() const { + return _ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataTransition::compose +// Access: Public, Virtual +// Description: Returns a new transition that corresponds to the +// composition of this transition with the second +// transition (which must be of an equivalent type). +// This may return the same pointer as either source +// transition. Applying the transition returned from +// this function to an attribute attribute will produce +// the same effect as applying each transition +// separately. +//////////////////////////////////////////////////////////////////// +template +NodeTransition *PointerDataTransition:: +compose(const NodeTransition *other) const { + const PointerDataTransition *ot; + DCAST_INTO_R(ot, other, NULL); + + return (NodeTransition*)other; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataTransition::invert +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +NodeTransition *PointerDataTransition:: +invert() const { + return (NodeTransition*)this; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataTransition::apply +// Access: Public, Virtual +// Description: Returns a new attribute (or possibly the same +// attribute) that represents the effect of applying this +// indicated transition to the indicated attribute. The +// source attribute may be NULL, indicating the initial +// attribute. +//////////////////////////////////////////////////////////////////// +template +NodeAttribute *PointerDataTransition:: +apply(const NodeAttribute *attrib) const { + const PointerDataAttribute *at; + DCAST_INTO_R(at, attrib, NULL); + + return (NodeAttribute*)attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataTransition::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void PointerDataTransition:: +output(ostream &out) const { + out << (void*)_ptr << endl; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerDataTransition::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void PointerDataTransition:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << (void*)_ptr << endl; +} + +/////////////////////////////////////////////////////////////////// +// Function: PointerDataTransition::internal_compare_to +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +int PointerDataTransition:: +internal_compare_to(const NodeTransition *other) const { + const PointerDataTransition *ot; + DCAST_INTO_R(ot, other, false); + + if (_ptr != ot->_ptr) { + return (_ptr > ot->_ptr) ? 1 : -1; + } + + return 0; +} + + + + + + + + + + diff --git a/panda/src/dgraph/pointerDataTransition.h b/panda/src/dgraph/pointerDataTransition.h new file mode 100644 index 0000000000..430fc30bca --- /dev/null +++ b/panda/src/dgraph/pointerDataTransition.h @@ -0,0 +1,66 @@ +// Filename: pointerDataTransition.h +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef POINTERDATATRANSITION_H +#define POINTERDATATRANSITION_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PointerDataTransition +// Description : A PointerDataAttribute is a special data graph +// attribute that is used to pass around a single +// pointer. +//////////////////////////////////////////////////////////////////// +template +class PointerDataTransition : public NodeTransition { +public: + INLINE PointerDataTransition(); + INLINE PointerDataTransition(PtrType* ptr); + INLINE PointerDataTransition(const PointerDataTransition ©); + INLINE void operator = (const PointerDataTransition ©); + +public: + INLINE void set_value(PtrType* ptr); + INLINE PtrType* get_value() const; + + virtual NodeTransition *compose(const NodeTransition *other) const; + virtual NodeTransition *invert() const; + virtual NodeAttribute *apply(const NodeAttribute *attrib) const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeTransition *other) const; + +private: + PtrType* _ptr; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeTransition::init_type(); + register_type(_type_handle, + string("PointerDataTransition<") + + get_type_handle(PtrType).get_name() + ">", + NodeTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "pointerDataTransition.I" + +#endif diff --git a/panda/src/dgraph/test_dgraph.cxx b/panda/src/dgraph/test_dgraph.cxx new file mode 100644 index 0000000000..93fac30528 --- /dev/null +++ b/panda/src/dgraph/test_dgraph.cxx @@ -0,0 +1,186 @@ +// Filename: test_dgraph.cxx +// Created by: drose (25Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "dataNode.h" +#include "dataRelation.h" +#include "dataGraphTraversal.h" +#include "doubleDataTransition.h" +#include "doubleDataAttribute.h" +#include "vec3DataTransition.h" +#include "vec3DataAttribute.h" + +#include + + +//////////////////////////////////////////////////////////////////// +// Class : Producer +// Description : Simulates some data-generating device, such as a +// mouse. +//////////////////////////////////////////////////////////////////// + +class Producer : public DataNode { +public: + Producer(const string &name); + + virtual void + transmit_data(NodeAttributes &data); + + NodeAttributes _attrib; + PT(DoubleDataAttribute) _t; + PT(Vec3DataAttribute) _xyz; + + static TypeHandle _t_type; + static TypeHandle _xyz_type; + + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + DataNode::init_type(); + register_type(_type_handle, "Producer", + DataNode::get_class_type()); + + DoubleDataTransition::init_type(); + register_data_transition(_t_type, "t", + DoubleDataTransition::get_class_type()); + Vec3DataTransition::init_type(); + register_data_transition(_xyz_type, "xyz", + Vec3DataTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + + +TypeHandle Producer::_type_handle; + +TypeHandle Producer::_t_type; +TypeHandle Producer::_xyz_type; + +Producer:: +Producer(const string &name) : DataNode(name) { + _t = new DoubleDataAttribute(0.0); + _xyz = new Vec3DataAttribute(LPoint3f(0.0, 0.0, 0.0)); + + // Set up our _attrib member to directly reference our data members. + // This way we can simply return a reference to our _attrib member, + // without bothering to construct a new one each time. + _attrib.set_attribute(_t_type, _t); + _attrib.set_attribute(_xyz_type, _xyz); +} + +void Producer:: +transmit_data(NodeAttributes &data) { + nout << get_name() << " sending xyz " << *_xyz << " t " << *_t << "\n"; + data = _attrib; +} + + + + +//////////////////////////////////////////////////////////////////// +// Class : Consumer +// Description : Simulates some data-consuming object, such as a mouse +// Tformer. +//////////////////////////////////////////////////////////////////// + +class Consumer : public DataNode { +public: + Consumer(const string &name) : DataNode(name) { } + + virtual void + transmit_data(NodeAttributes &data); + + + static TypeHandle _s_type; + static TypeHandle _t_type; + static TypeHandle _xyz_type; + + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + DataNode::init_type(); + register_type(_type_handle, "Consumer", + DataNode::get_class_type()); + + DoubleDataTransition::init_type(); + register_data_transition(_s_type, "s", + DoubleDataTransition::get_class_type()); + register_data_transition(_t_type, "t", + DoubleDataTransition::get_class_type()); + Vec3DataTransition::init_type(); + register_data_transition(_xyz_type, "xyz", + Vec3DataTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + + +TypeHandle Consumer::_type_handle; + +TypeHandle Consumer::_s_type; +TypeHandle Consumer::_t_type; +TypeHandle Consumer::_xyz_type; + +void Consumer:: +transmit_data(NodeAttributes &data) { + const DoubleDataAttribute *s; + const DoubleDataAttribute *t; + const Vec3DataAttribute *xyz; + + if (get_attribute_into(s, data, _s_type) && + get_attribute_into(t, data, _t_type) && + get_attribute_into(xyz, data, _xyz_type)) { + nout << get_name() << " got xyz " << *xyz << " s " << *s + << " t " << *t << "\n"; + } else { + nout << get_name() << " didn't get all data.\n"; + } +} + + + +//////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// + +int +main() { + Producer::init_type(); + Consumer::init_type(); + PT_NamedNode root = new NamedNode("root"); + + PT(Producer) producer = new Producer("a"); + PT(Consumer) consumer = new Consumer("b"); + new DataRelation(root, producer); + DataRelation *arc1 = new DataRelation(producer, consumer); + + producer->_t->set_value(10.0); + producer->_xyz->set_value(LPoint3f(1.0, 2.0, 3.0)); + + arc1->set_transition(Producer::_xyz_type, + new Vec3DataTransition(LMatrix4f::scale_mat(2.0))); + + producer->set_spam_mode(true); + + traverse_data_graph(root); + + return (0); +} diff --git a/panda/src/dgraph/vec3DataAttribute.I b/panda/src/dgraph/vec3DataAttribute.I new file mode 100644 index 0000000000..9ac30f5bdb --- /dev/null +++ b/panda/src/dgraph/vec3DataAttribute.I @@ -0,0 +1,25 @@ +// Filename: vec3DataAttribute.I +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Vec3DataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Vec3DataAttribute:: +Vec3DataAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: Vec3DataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Vec3DataAttribute:: +Vec3DataAttribute(const LVecBase3f &value) : + VectorDataAttribute(value) +{ +} + diff --git a/panda/src/dgraph/vec3DataAttribute.cxx b/panda/src/dgraph/vec3DataAttribute.cxx new file mode 100644 index 0000000000..3695fc415f --- /dev/null +++ b/panda/src/dgraph/vec3DataAttribute.cxx @@ -0,0 +1,34 @@ +// Filename: vec3DataAttribute.cxx +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "vec3DataAttribute.h" +#include "vec3DataTransition.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif + +TypeHandle Vec3DataAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: Vec3DataAttribute::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *Vec3DataAttribute:: +make_copy() const { + return new Vec3DataAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: Vec3DataAttribute::make_initial +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *Vec3DataAttribute:: +make_initial() const { + return new Vec3DataAttribute; +} diff --git a/panda/src/dgraph/vec3DataAttribute.h b/panda/src/dgraph/vec3DataAttribute.h new file mode 100644 index 0000000000..94ce2baf4d --- /dev/null +++ b/panda/src/dgraph/vec3DataAttribute.h @@ -0,0 +1,58 @@ +// Filename: vec3DataAttribute.h +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VEC3DATAATTRIBUTE_H +#define VEC3DATAATTRIBUTE_H + +#include + +#include "vectorDataAttribute.h" + +#include + +// We need to define this temporary macro so we can pass a parameter +// containing a comma through the macro. +#define VECTORDATAATTRIBUTE_LPOINT3F VectorDataAttribute +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, VECTORDATAATTRIBUTE_LPOINT3F); + +//////////////////////////////////////////////////////////////////// +// Class : Vec3DataAttribute +// Description : A VectorDataAttribute templated on LPoint3f. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Vec3DataAttribute : + public VectorDataAttribute { +public: + INLINE Vec3DataAttribute(); + INLINE Vec3DataAttribute(const LVecBase3f &value); + + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + VectorDataAttribute::init_type(); + register_type(_type_handle, "Vec3DataAttribute", + VectorDataAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "vec3DataAttribute.I" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/dgraph/vec3DataTransition.I b/panda/src/dgraph/vec3DataTransition.I new file mode 100644 index 0000000000..680bf2ce91 --- /dev/null +++ b/panda/src/dgraph/vec3DataTransition.I @@ -0,0 +1,25 @@ +// Filename: vec3DataTransition.I +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Vec3DataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Vec3DataTransition:: +Vec3DataTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: Vec3DataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Vec3DataTransition:: +Vec3DataTransition(const LMatrix4f &matrix) : + VectorDataTransition(matrix) +{ +} + diff --git a/panda/src/dgraph/vec3DataTransition.cxx b/panda/src/dgraph/vec3DataTransition.cxx new file mode 100644 index 0000000000..a2a3b5886d --- /dev/null +++ b/panda/src/dgraph/vec3DataTransition.cxx @@ -0,0 +1,34 @@ +// Filename: vec3DataTransition.cxx +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "vec3DataTransition.h" +#include "vec3DataAttribute.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif + +TypeHandle Vec3DataTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: Vec3DataTransition::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeTransition *Vec3DataTransition:: +make_copy() const { + return new Vec3DataTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: Vec3DataTransition::make_attrib +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *Vec3DataTransition:: +make_attrib() const { + return new Vec3DataAttribute; +} diff --git a/panda/src/dgraph/vec3DataTransition.h b/panda/src/dgraph/vec3DataTransition.h new file mode 100644 index 0000000000..3c5b8c0fcf --- /dev/null +++ b/panda/src/dgraph/vec3DataTransition.h @@ -0,0 +1,58 @@ +// Filename: vec3DataTransition.h +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VEC3DATATRANSITION_H +#define VEC3DATATRANSITION_H + +#include + +#include "vectorDataTransition.h" + +#include + +// We need to define this temporary macro so we can pass a parameter +// containing a comma through the macro. +#define VECTORDATATRANSITION_LPOINT3F VectorDataTransition +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, VECTORDATATRANSITION_LPOINT3F); + +//////////////////////////////////////////////////////////////////// +// Class : Vec3DataTransition +// Description : A VectorDataTransition templated on LPoint3f. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Vec3DataTransition : + public VectorDataTransition { +public: + INLINE Vec3DataTransition(); + INLINE Vec3DataTransition(const LMatrix4f &matrix); + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + VectorDataTransition::init_type(); + register_type(_type_handle, "Vec3DataTransition", + VectorDataTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "vec3DataTransition.I" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/dgraph/vec4DataAttribute.I b/panda/src/dgraph/vec4DataAttribute.I new file mode 100644 index 0000000000..8504d5a5bd --- /dev/null +++ b/panda/src/dgraph/vec4DataAttribute.I @@ -0,0 +1,24 @@ +// Filename: vec4DataAttribute.I +// Created by: jason (03Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Vec4DataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Vec4DataAttribute:: +Vec4DataAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: Vec4DataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Vec4DataAttribute:: +Vec4DataAttribute(const LVecBase4f &value) : + VectorDataAttribute(value) +{ +} diff --git a/panda/src/dgraph/vec4DataAttribute.cxx b/panda/src/dgraph/vec4DataAttribute.cxx new file mode 100644 index 0000000000..f634063f93 --- /dev/null +++ b/panda/src/dgraph/vec4DataAttribute.cxx @@ -0,0 +1,34 @@ +// Filename: vec4DataAttribute.cxx +// Created by: jason (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "vec4DataAttribute.h" +#include "vec4DataTransition.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif + +TypeHandle Vec4DataAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: Vec4DataAttribute::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *Vec4DataAttribute:: +make_copy() const { + return new Vec4DataAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: Vec4DataAttribute::make_initial +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *Vec4DataAttribute:: +make_initial() const { + return new Vec4DataAttribute; +} diff --git a/panda/src/dgraph/vec4DataAttribute.h b/panda/src/dgraph/vec4DataAttribute.h new file mode 100644 index 0000000000..e1cb807ff9 --- /dev/null +++ b/panda/src/dgraph/vec4DataAttribute.h @@ -0,0 +1,58 @@ +// Filename: vec4DataAttribute.h +// Created by: jason (03Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VEC4DATAATTRIBUTE_H +#define VEC4DATAATTRIBUTE_H + +#include + +#include "vectorDataAttribute.h" + +#include + +// We need to define this temporary macro so we can pass a parameter +// containing a comma through the macro. +#define VECTORDATAATTRIBUTE_LPOINT4F VectorDataAttribute +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, VECTORDATAATTRIBUTE_LPOINT4F); + +//////////////////////////////////////////////////////////////////// +// Class : Vec4DataAttribute +// Description : A VectorDataAttribute templated on LPoint4f. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Vec4DataAttribute : + public VectorDataAttribute { +public: + INLINE Vec4DataAttribute(); + INLINE Vec4DataAttribute(const LVecBase4f &value); + + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + VectorDataAttribute::init_type(); + register_type(_type_handle, "Vec4DataAttribute", + VectorDataAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "vec4DataAttribute.I" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/dgraph/vec4DataTransition.I b/panda/src/dgraph/vec4DataTransition.I new file mode 100644 index 0000000000..13bdf7bc74 --- /dev/null +++ b/panda/src/dgraph/vec4DataTransition.I @@ -0,0 +1,24 @@ +// Filename: vec4DataTransition.I +// Created by: jason (03Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Vec4DataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Vec4DataTransition:: +Vec4DataTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: Vec4DataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Vec4DataTransition:: +Vec4DataTransition(const LMatrix4f &matrix) : + VectorDataTransition(matrix) +{ +} diff --git a/panda/src/dgraph/vec4DataTransition.cxx b/panda/src/dgraph/vec4DataTransition.cxx new file mode 100644 index 0000000000..d04e763c31 --- /dev/null +++ b/panda/src/dgraph/vec4DataTransition.cxx @@ -0,0 +1,34 @@ +// Filename: vec4DataTransition.cxx +// Created by: jason (03Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "vec4DataTransition.h" +#include "vec4DataAttribute.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif + +TypeHandle Vec4DataTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: Vec4DataTransition::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeTransition *Vec4DataTransition:: +make_copy() const { + return new Vec4DataTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: Vec4DataTransition::make_attrib +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *Vec4DataTransition:: +make_attrib() const { + return new Vec4DataAttribute; +} diff --git a/panda/src/dgraph/vec4DataTransition.h b/panda/src/dgraph/vec4DataTransition.h new file mode 100644 index 0000000000..30bc0eb92b --- /dev/null +++ b/panda/src/dgraph/vec4DataTransition.h @@ -0,0 +1,58 @@ +// Filename: vec4DataTransition.h +// Created by: jason (03Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VEC4DATATRANSITION_H +#define VEC4DATATRANSITION_H + +#include + +#include "vectorDataTransition.h" + +#include + +// We need to define this temporary macro so we can pass a parameter +// containing a comma through the macro. +#define VECTORDATATRANSITION_LPOINT4F VectorDataTransition +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, VECTORDATATRANSITION_LPOINT4F); + +//////////////////////////////////////////////////////////////////// +// Class : Vec4DataTransition +// Description : A VectorDataTransition templated on LPoint4f. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Vec4DataTransition : + public VectorDataTransition { +public: + INLINE Vec4DataTransition(); + INLINE Vec4DataTransition(const LMatrix4f &matrix); + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + VectorDataTransition::init_type(); + register_type(_type_handle, "Vec4DataTransition", + VectorDataTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "vec4DataTransition.I" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/dgraph/vectorDataAttribute.I b/panda/src/dgraph/vectorDataAttribute.I new file mode 100644 index 0000000000..0844569036 --- /dev/null +++ b/panda/src/dgraph/vectorDataAttribute.I @@ -0,0 +1,129 @@ +// Filename: vectorDataAttribute.I +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "vectorDataTransition.h" + +#include + +template +TypeHandle VectorDataAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE VectorDataAttribute:: +VectorDataAttribute() { + _value = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE VectorDataAttribute:: +VectorDataAttribute(const VecType &value) : + _value(value) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataAttribute::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE VectorDataAttribute:: +VectorDataAttribute(const VectorDataAttribute ©) : + NodeAttribute(copy), + _value(copy._value) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataAttribute::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void VectorDataAttribute:: +operator = (const VectorDataAttribute ©) { + NodeAttribute::operator = (copy); + _value = copy._value; +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataAttribute::set_value +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void VectorDataAttribute:: +set_value(const VecType &value) { + _value = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataAttribute::get_value +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE const VecType &VectorDataAttribute:: +get_value() const { + return _value; +} + + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataAttribute::get_handle +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +TypeHandle VectorDataAttribute:: +get_handle() const { + return VectorDataTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataAttribute::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void VectorDataAttribute:: +output(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataAttribute::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void VectorDataAttribute:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataAttribute::internal_compare_to +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +int VectorDataAttribute:: +internal_compare_to(const NodeAttribute *other) const { + const VectorDataAttribute *ot; + DCAST_INTO_R(ot, other, false); + + return _value.compare_to(ot->_value); +} diff --git a/panda/src/dgraph/vectorDataAttribute.h b/panda/src/dgraph/vectorDataAttribute.h new file mode 100644 index 0000000000..84a7ca1958 --- /dev/null +++ b/panda/src/dgraph/vectorDataAttribute.h @@ -0,0 +1,67 @@ +// Filename: vectorDataAttribute.h +// Created by: drose (27Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTORDATAATTRIBUTE_H +#define VECTORDATAATTRIBUTE_H + +#include + +#include + +template +class VectorDataTransition; + +//////////////////////////////////////////////////////////////////// +// Class : VectorDataAttribute +// Description : +//////////////////////////////////////////////////////////////////// +template +class VectorDataAttribute : public NodeAttribute { +public: + INLINE VectorDataAttribute(); + INLINE VectorDataAttribute(const VecType &value); + INLINE VectorDataAttribute(const VectorDataAttribute ©); + INLINE void operator = (const VectorDataAttribute ©); + +public: + INLINE void set_value(const VecType &value); + INLINE const VecType &get_value() const; + + virtual TypeHandle get_handle() const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeAttribute *other) const; + +private: + VecType _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeAttribute::init_type(); + register_type(_type_handle, + string("VectorDataAttribute<") + + get_type_handle(VecType).get_name() + "," + + get_type_handle(MatType).get_name() + ">", + NodeAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +friend class VectorDataTransition; +}; + +#include "vectorDataAttribute.I" + +#endif diff --git a/panda/src/dgraph/vectorDataTransition.I b/panda/src/dgraph/vectorDataTransition.I new file mode 100644 index 0000000000..9c9b0ba7d6 --- /dev/null +++ b/panda/src/dgraph/vectorDataTransition.I @@ -0,0 +1,216 @@ +// Filename: vectorDataTransition.I +// Created by: drose (25Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "vectorDataAttribute.h" + +#include +#include + +template +TypeHandle VectorDataTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE VectorDataTransition:: +VectorDataTransition() { + _matrix = MatType::ident_mat(); +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE VectorDataTransition:: +VectorDataTransition(const MatType &matrix) : + _matrix(matrix) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataTransition::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE VectorDataTransition:: +VectorDataTransition(const VectorDataTransition ©) : + NodeTransition(copy), + _matrix(copy._matrix) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataTransition::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void VectorDataTransition:: +operator = (const VectorDataTransition ©) { + NodeTransition::operator = (copy); + _matrix = copy._matrix; +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataTransition::is_identity +// Access: Public, Virtual +// Description: Returns true if this transition does not affect any +// numbers going through it. +//////////////////////////////////////////////////////////////////// +template +INLINE bool VectorDataTransition:: +is_identity() const { + return _matrix.almost_equal(MatType::ident_mat()); +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataTransition::set_matrix +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void VectorDataTransition:: +set_matrix(const MatType &matrix) { + _matrix = matrix; + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataTransition::get_matrix +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +const MatType &VectorDataTransition:: +get_matrix() const { + return _matrix; +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataTransition::compose +// Access: Public, Virtual +// Description: Returns a new transition that corresponds to the +// composition of this transition with the second +// transition (which must be of an equivalent type). +// This may return the same pointer as either source +// transition. Applying the transition returned from +// this function to an attribute attribute will produce +// the same effect as applying each transition +// separately. +//////////////////////////////////////////////////////////////////// +template +NodeTransition *VectorDataTransition:: +compose(const NodeTransition *other) const { + const VectorDataTransition *ot; + DCAST_INTO_R(ot, other, NULL); + + if (is_identity()) { + return (VectorDataTransition *)ot; + + } else if (ot->is_identity()) { + return (VectorDataTransition *)this; + + } else { + NodeTransition *c = make_copy(); + VectorDataTransition *result; + DCAST_INTO_R(result, c, NULL); + + result->_matrix = ot->_matrix * _matrix; + return result; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataTransition::invert +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +NodeTransition *VectorDataTransition:: +invert() const { + if (is_identity()) { + return (VectorDataTransition *)this; + + } else { + PT(NodeTransition) c = make_copy(); + VectorDataTransition *result; + DCAST_INTO_R(result, c, NULL); + + bool invertible = result->_matrix.invert_from(_matrix); + if (!invertible) { + return NULL; + } + return result; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataTransition::apply +// Access: Public, Virtual +// Description: Returns a new attribute (or possibly the same +// attribute) that represents the effect of applying this +// indicated transition to the indicated attribute. The +// source attribute may be NULL, indicating the initial +// attribute. +//////////////////////////////////////////////////////////////////// +template +NodeAttribute *VectorDataTransition:: +apply(const NodeAttribute *attrib) const { + const VectorDataAttribute *at; + DCAST_INTO_R(at, attrib, NULL); + + if (is_identity()) { + return (VectorDataAttribute *)attrib; + } + + NodeAttribute *c = make_attrib(); + VectorDataAttribute *result; + DCAST_INTO_R(result, c, NULL); + + result->_value = at->_value * _matrix; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataTransition::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void VectorDataTransition:: +output(ostream &out) const { + out << _matrix; +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataTransition::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void VectorDataTransition:: +write(ostream &out, int indent_level) const { + _matrix.write(out, indent_level); +} + +//////////////////////////////////////////////////////////////////// +// Function: VectorDataTransition::internal_compare_to +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +int VectorDataTransition:: +internal_compare_to(const NodeTransition *other) const { + const VectorDataTransition *ot; + DCAST_INTO_R(ot, other, false); + + return _matrix.compare_to(ot->_matrix); +} diff --git a/panda/src/dgraph/vectorDataTransition.h b/panda/src/dgraph/vectorDataTransition.h new file mode 100644 index 0000000000..89eaea3f27 --- /dev/null +++ b/panda/src/dgraph/vectorDataTransition.h @@ -0,0 +1,78 @@ +// Filename: vectorDataTransition.h +// Created by: drose (25Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTORDATATRANSITION_H +#define VECTORDATATRANSITION_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : VectorDataTransition +// Description : A VectorDataAttribute is a special data graph +// attribute that is used to pass around a complex +// number like a vector or a matrix. The Transition +// isn't often used, but it may contain a matrix that +// modifies the vector in the attribute. (Note that a +// LMatrix4DataAttribute actually derives from +// VectorDataAttribute, even though its base type is a +// matrix, not a vector.) +//////////////////////////////////////////////////////////////////// +template +class VectorDataTransition : public NodeTransition { +public: + INLINE VectorDataTransition(); + INLINE VectorDataTransition(const MatType &matrix); + INLINE VectorDataTransition(const VectorDataTransition ©); + INLINE void operator = (const VectorDataTransition ©); + +public: + + INLINE bool is_identity() const; + + INLINE void set_matrix(const MatType &matrix); + INLINE const MatType &get_matrix() const; + + virtual NodeTransition *compose(const NodeTransition *other) const; + virtual NodeTransition *invert() const; + virtual NodeAttribute *apply(const NodeAttribute *attrib) const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeTransition *other) const; + +private: + MatType _matrix; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeTransition::init_type(); + do_init_type(VecType); + do_init_type(MatType); + register_type(_type_handle, + string("VectorDataTransition<") + + get_type_handle(VecType).get_name() + "," + + get_type_handle(MatType).get_name() + ">", + NodeTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "vectorDataTransition.I" + +#endif diff --git a/panda/src/display/Sources.pp b/panda/src/display/Sources.pp new file mode 100644 index 0000000000..c624e09db2 --- /dev/null +++ b/panda/src/display/Sources.pp @@ -0,0 +1,52 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET display + #define LOCAL_LIBS \ + putil gsgbase gobj linmath graph mathutil sgraph pstatclient + + #define SOURCES \ + config_display.cxx config_display.h displayRegion.I \ + displayRegion.cxx displayRegion.h graphicsChannel.I \ + graphicsChannel.cxx graphicsChannel.h graphicsLayer.I \ + graphicsLayer.cxx graphicsLayer.h graphicsPipe.I graphicsPipe.N \ + graphicsPipe.cxx graphicsPipe.h graphicsStateGuardian.I \ + graphicsStateGuardian.N graphicsStateGuardian.cxx \ + graphicsStateGuardian.h graphicsWindow.I graphicsWindow.N \ + graphicsWindow.cxx graphicsWindow.h graphicsWindowInputDevice.I \ + graphicsWindowInputDevice.cxx graphicsWindowInputDevice.h \ + hardwareChannel.I hardwareChannel.cxx hardwareChannel.h \ + interactiveGraphicsPipe.I interactiveGraphicsPipe.cxx \ + interactiveGraphicsPipe.h noninteractiveGraphicsPipe.I \ + noninteractiveGraphicsPipe.cxx noninteractiveGraphicsPipe.h \ + pipeSpec.I pipeSpec.cxx pipeSpec.h savedFrameBuffer.I \ + savedFrameBuffer.cxx savedFrameBuffer.h textureContext.I \ + textureContext.cxx textureContext.h + + #define INSTALL_HEADERS \ + displayRegion.I displayRegion.h displayRegionStack.I \ + displayRegionStack.h frameBufferStack.I frameBufferStack.h \ + graphicsChannel.I graphicsChannel.h graphicsLayer.I graphicsLayer.h \ + graphicsPipe.I graphicsPipe.h graphicsStateGuardian.I \ + graphicsStateGuardian.h graphicsWindow.I graphicsWindow.h \ + graphicsWindowInputDevice.I graphicsWindowInputDevice.h \ + hardwareChannel.I hardwareChannel.h interactiveGraphicsPipe.I \ + interactiveGraphicsPipe.h noninteractiveGraphicsPipe.I \ + noninteractiveGraphicsPipe.h pipeSpec.I pipeSpec.h renderBuffer.h \ + savedFrameBuffer.I savedFrameBuffer.h textureContext.I \ + textureContext.h + + #define IGATESCAN all + +#end lib_target + +#begin test_bin_target + #define TARGET test_display + #define LOCAL_LIBS \ + display putil + + #define SOURCES \ + test_display.cxx + +#end test_bin_target + diff --git a/panda/src/display/config_display.cxx b/panda/src/display/config_display.cxx new file mode 100644 index 0000000000..6ac254d4aa --- /dev/null +++ b/panda/src/display/config_display.cxx @@ -0,0 +1,109 @@ +// Filename: config_display.cxx +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_display.h" +#include "graphicsStateGuardian.h" +#include "savedFrameBuffer.h" +#include "graphicsPipe.h" +#include "interactiveGraphicsPipe.h" +#include "noninteractiveGraphicsPipe.h" +#include "graphicsWindow.h" +#include "graphicsChannel.h" +#include "hardwareChannel.h" +#include "textureContext.h" + +Configure(config_display); +NotifyCategoryDef(display, ""); +NotifyCategoryDef(gsg, display_cat); + +static Config::ConfigTable::Symbol *disp; +static Config::ConfigTable::Symbol *guard; +static Config::ConfigTable::Symbol *preferred_pipe; +static Config::ConfigTable::Symbol *preferred_window; +static Config::ConfigTable::Symbol *preferred_gsg; + +ConfigureFn(config_display) { + GraphicsStateGuardian::init_type(); + GraphicsStateGuardian::GsgParam::init_type(); + GraphicsStateGuardian::GsgWindow::init_type(); + SavedFrameBuffer::init_type(); + GraphicsPipe::init_type(); + GraphicsPipe::PipeParam::init_type(); + GraphicsPipe::PipeSpec::init_type(); + InteractiveGraphicsPipe::init_type(); + NoninteractiveGraphicsPipe::init_type(); + GraphicsWindow::init_type(); + GraphicsWindow::WindowParam::init_type(); + GraphicsWindow::WindowProps::init_type(); + GraphicsWindow::WindowPipe::init_type(); + GraphicsChannel::init_type(); + HardwareChannel::init_type(); + TextureContext::init_type(); + + disp = new Config::ConfigTable::Symbol; + guard = new Config::ConfigTable::Symbol; + preferred_pipe = new Config::ConfigTable::Symbol; + preferred_window = new Config::ConfigTable::Symbol; + preferred_gsg = new Config::ConfigTable::Symbol; + + config_display.GetAll("load-display", *disp); + config_display.GetAll("load-gsg", *guard); + + config_display.GetAll("preferred-pipe", *preferred_pipe); + config_display.GetAll("preferred-window", *preferred_window); + config_display.GetAll("preferred-gsg", *preferred_gsg); +} + +const string pipe_spec_machine = config_display.GetString("pipe-machine", ""); +const string pipe_spec_filename = config_display.GetString("pipe-filename", + "outfile-%03f.rib"); +const int pipe_spec_pipe_number = config_display.GetInt("pipe-number", -1); +const bool pipe_spec_is_file = config_display.Defined("pipe-filename") + || config_display.GetBool("pipe-file", false); +const bool pipe_spec_is_remote = config_display.Defined("pipe-machine") + || config_display.GetBool("pipe-remote", + false); + + +Config::ConfigTable::Symbol::iterator pipe_modules_begin(void) { + return disp->begin(); +} + +Config::ConfigTable::Symbol::iterator pipe_modules_end(void) { + return disp->end(); +} + +Config::ConfigTable::Symbol::iterator gsg_modules_begin(void) { + return guard->begin(); +} + +Config::ConfigTable::Symbol::iterator gsg_modules_end(void) { + return guard->end(); +} + + +Config::ConfigTable::Symbol::iterator preferred_pipe_begin(void) { + return preferred_pipe->begin(); +} + +Config::ConfigTable::Symbol::iterator preferred_pipe_end(void) { + return preferred_pipe->end(); +} + +Config::ConfigTable::Symbol::iterator preferred_window_begin(void) { + return preferred_window->begin(); +} + +Config::ConfigTable::Symbol::iterator preferred_window_end(void) { + return preferred_window->end(); +} + +Config::ConfigTable::Symbol::iterator preferred_gsg_begin(void) { + return preferred_gsg->begin(); +} + +Config::ConfigTable::Symbol::iterator preferred_gsg_end(void) { + return preferred_gsg->end(); +} diff --git a/panda/src/display/config_display.h b/panda/src/display/config_display.h new file mode 100644 index 0000000000..a7b08e2f55 --- /dev/null +++ b/panda/src/display/config_display.h @@ -0,0 +1,37 @@ +// Filename: config_display.h +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_DISPLAY_H +#define CONFIG_DISPLAY_H + +#include +#include +#include + +#include +#include + +NotifyCategoryDecl(display, EXPCL_PANDA, EXPTP_PANDA); +NotifyCategoryDecl(gsg, EXPCL_PANDA, EXPTP_PANDA); + +extern const string pipe_spec_machine; +extern const string pipe_spec_filename; +extern const int pipe_spec_pipe_number; +extern const bool pipe_spec_is_file; +extern const bool pipe_spec_is_remote; + +extern Config::ConfigTable::Symbol::iterator pipe_modules_begin(void); +extern Config::ConfigTable::Symbol::iterator pipe_modules_end(void); +extern Config::ConfigTable::Symbol::iterator gsg_modules_begin(void); +extern Config::ConfigTable::Symbol::iterator gsg_modules_end(void); + +extern Config::ConfigTable::Symbol::iterator preferred_pipe_begin(); +extern Config::ConfigTable::Symbol::iterator preferred_pipe_end(); +extern Config::ConfigTable::Symbol::iterator preferred_window_begin(); +extern Config::ConfigTable::Symbol::iterator preferred_window_end(); +extern Config::ConfigTable::Symbol::iterator preferred_gsg_begin(); +extern Config::ConfigTable::Symbol::iterator preferred_gsg_end(); + +#endif /* CONFIG_DISPLAY_H */ diff --git a/panda/src/display/displayRegion.I b/panda/src/display/displayRegion.I new file mode 100644 index 0000000000..75422f8af2 --- /dev/null +++ b/panda/src/display/displayRegion.I @@ -0,0 +1,135 @@ +// Filename: displayRegion.I +// Created by: frang (07Mar99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::get_dimensions +// Access: Public +// Description: Retrieves the coordinates of the DisplayRegion's +// rectangle within its GraphicsLayer. These numbers +// will be in the range [0..1]. +//////////////////////////////////////////////////////////////////// +INLINE void DisplayRegion:: +get_dimensions(float &l, float &r, float &b, float &t) const { + l = _l; + r = _r; + b = _b; + t = _t; +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::get_layer +// Access: Public +// Description: Returns the layer associated with this particular +// DisplayRegion, or NULL if no layer is associated +// (or if the layer was deleted). +//////////////////////////////////////////////////////////////////// +INLINE GraphicsLayer *DisplayRegion:: +get_layer() const { + return _layer; +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::get_camera +// Access: Public +// Description: Returns the camera associated with this +// DisplayRegion, or NULL if no camera is associated. +//////////////////////////////////////////////////////////////////// +INLINE PT(Camera) DisplayRegion:: +get_camera() const { + return _camera; +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::set_active +// Access: Public +// Description: Sets the active flag associated with the +// DisplayRegion. If the DisplayRegion is marked +// inactive, nothing is rendered. +//////////////////////////////////////////////////////////////////// +INLINE void DisplayRegion:: +set_active(bool active) { + _active = active; +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::is_active +// Access: Public +// Description: Returns the active flag associated with the +// DisplayRegion. +//////////////////////////////////////////////////////////////////// +INLINE bool DisplayRegion:: +is_active() const { + return _active; +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::compute_pixels +// Access: Public +// Description: Computes the pixel locations of the DisplayRegion +// within its layer, given the size of the layer in +// pixels. +//////////////////////////////////////////////////////////////////// +INLINE void DisplayRegion:: +compute_pixels(const int x, const int y) { + _pl = int((_l * x) + 0.5); + _pr = int((_r * x) + 0.5); + _pb = int((_b * y) + 0.5); + _pt = int((_t * y) + 0.5); +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::get_pixels +// Access: Public +// Description: Retrieves the coordinates of the DisplayRegion within +// its layer, in pixels. +//////////////////////////////////////////////////////////////////// +INLINE void DisplayRegion:: +get_pixels(int &pl, int &pr, int &pb, int &pt) const { + pl = _pl; + pr = _pr; + pb = _pb; + pt = _pt; +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::get_region_pixels +// Access: Public +// Description: Retrieves the coordinates of the DisplayRegion within +// its layer, as the pixel location of its bottom-left +// corner, along with a pixel width and height. +//////////////////////////////////////////////////////////////////// +INLINE void DisplayRegion:: +get_region_pixels(int &xo, int &yo, int &w, int &h) const { + xo = _pl; + yo = _pb; + w = _pr - _pl; + h = _pt - _pb; +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::get_pixel_width +// Access: Public +// Description: Returns the width of the DisplayRegion in pixels. +//////////////////////////////////////////////////////////////////// +INLINE int DisplayRegion:: +get_pixel_width() const { + return _pr - _pl; +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::get_pixel_height +// Access: Public +// Description: Returns the height of the DisplayRegion in pixels. +//////////////////////////////////////////////////////////////////// +INLINE int DisplayRegion:: +get_pixel_height() const { + return _pt - _pb; +} + + +INLINE ostream &operator << (ostream &out, const DisplayRegion &dr) { + dr.output(out); + return out; +} diff --git a/panda/src/display/displayRegion.cxx b/panda/src/display/displayRegion.cxx new file mode 100644 index 0000000000..3b562aa36d --- /dev/null +++ b/panda/src/display/displayRegion.cxx @@ -0,0 +1,176 @@ +// Filename: displayRegion.cxx +// Created by: cary (10Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "displayRegion.h" +#include "graphicsLayer.h" +#include "graphicsChannel.h" +#include "graphicsWindow.h" +#include "config_display.h" + + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DisplayRegion:: +DisplayRegion(GraphicsLayer *layer) : _layer(layer), + _l(0.), _r(1.), _b(0.), _t(1.), _active(true) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DisplayRegion:: +DisplayRegion(GraphicsLayer *layer, const float l, + const float r, const float b, const float t) + : _layer(layer), _l(l), _r(r), _b(b), _t(t), _active(true) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::Constructor +// Access: Public +// Description: This constructor makes a DisplayRegion that is not +// associated with any particular layer; this is +// typically for rendering a temporary pass. +//////////////////////////////////////////////////////////////////// +DisplayRegion:: +DisplayRegion(int xsize, int ysize) : + _layer((GraphicsLayer *)0L), + _l(0.), _r(1.), _b(0.), _t(1.), + _pl(0), _pr(xsize), _pb(0), _pt(ysize), + _active(true) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::Copy Constructor +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +DisplayRegion:: +DisplayRegion(const DisplayRegion&) { + display_cat.error() + << "DisplayRegions should not be copied" << endl; +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::Copy Assignment Operator +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +DisplayRegion &DisplayRegion:: +operator=(const DisplayRegion&) { + display_cat.error() + << "DisplayRegions should not be assigned" << endl; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DisplayRegion:: +~DisplayRegion() { +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::set_dimensions +// Access: Public +// Description: Changes the portion of the framebuffer this +// DisplayRegion corresponds to. The parameters range +// from 0 to 1, where 0,0 is the lower left corner and +// 1,1 is the upper right; (0, 1, 0, 1) represents the +// whole screen. +//////////////////////////////////////////////////////////////////// +void DisplayRegion:: +set_dimensions(float l, float r, float b, float t) { + _l = l; + _r = r; + _b = b; + _t = t; + + const GraphicsWindow *win = get_window(); + if (win != (GraphicsWindow *)NULL) { + compute_pixels(win->get_width(), win->get_height()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::get_channel +// Access: Public +// Description: Returns the GraphicsChannel that this DisplayRegion is +// ultimately associated with, or NULL if no channel is +// associated. +//////////////////////////////////////////////////////////////////// +GraphicsChannel *DisplayRegion:: +get_channel() const { + return (_layer != (GraphicsLayer *)NULL) ? _layer->get_channel() : NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::get_window +// Access: Public +// Description: Returns the GraphicsWindow that this DisplayRegion is +// ultimately associated with, or NULL if no window is +// associated. +//////////////////////////////////////////////////////////////////// +GraphicsWindow *DisplayRegion:: +get_window() const { + return (_layer != (GraphicsLayer *)NULL) ? _layer->get_window() : NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::get_pipe +// Access: Public +// Description: Returns the GraphicsPipe that this DisplayRegion is +// ultimately associated with, or NULL if no pipe is +// associated. +//////////////////////////////////////////////////////////////////// +GraphicsPipe *DisplayRegion:: +get_pipe() const { + return (_layer != (GraphicsLayer *)NULL) ? _layer->get_pipe() : NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::set_camera +// Access: Public +// Description: Sets the camera that is associated with this +// DisplayRegion. Each DisplayRegion may have zero or +// one cameras associated. (If it has no camera, +// nothing is rendered.) A given camera may be shared +// between multiple DisplayRegions. +//////////////////////////////////////////////////////////////////// +void DisplayRegion:: +set_camera(const PT(Camera) &camera) { + if (camera != _camera) { + if (_camera != (Camera *)NULL) { + // We need to tell the old camera we're not using him anymore. + _camera->remove_display_region(this); + } + _camera = camera; + if (_camera != (Camera *)NULL) { + // Now tell the new camera we are using him. + _camera->add_display_region(this); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegion::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DisplayRegion:: +output(ostream &out) const { + out << "DisplayRegion(" << _l << " " << _r << " " << _b << " " << _t + << ")=pixels(" << _pl << " " << _pr << " " << _pb << " " << _pt + << ")"; +} diff --git a/panda/src/display/displayRegion.h b/panda/src/display/displayRegion.h new file mode 100644 index 0000000000..931c4e08d0 --- /dev/null +++ b/panda/src/display/displayRegion.h @@ -0,0 +1,92 @@ +// Filename: displayRegion.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef DISPLAYREGION_H +#define DISPLAYREGION_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +class GraphicsLayer; +class GraphicsChannel; +class GraphicsWindow; +class GraphicsPipe; + +//////////////////////////////////////////////////////////////////// +// Class : DisplayRegion +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DisplayRegion : public ReferenceCount { +public: + + DisplayRegion(GraphicsLayer *); + DisplayRegion(GraphicsLayer *, + const float l, const float r, + const float b, const float t); + DisplayRegion(int xsize, int ysize); + virtual ~DisplayRegion(); + + INLINE void get_dimensions(float &l, float &r, float &b, float &t) const; + void set_dimensions(float l, float r, float b, float t); + + INLINE GraphicsLayer *get_layer() const; + GraphicsChannel *get_channel() const; + GraphicsWindow *get_window() const; + GraphicsPipe *get_pipe() const; + + void set_camera(const PT(Camera) &camera); + INLINE PT(Camera) get_camera() const; + + INLINE void set_active(bool active); + INLINE bool is_active() const; + + INLINE void compute_pixels(const int x, const int y); + INLINE void get_pixels(int &pl, int &pr, int &pb, int &pt) const; + INLINE void get_region_pixels(int &xo, int &yo, int &w, int &h) const; + + INLINE int get_pixel_width() const; + INLINE int get_pixel_height() const; + + void output(ostream &out) const; + +protected: + + float _l; + float _r; + float _b; + float _t; + + int _pl; + int _pr; + int _pb; + int _pt; + + GraphicsLayer *_layer; + PT(Camera) _camera; + + bool _active; + +private: + DisplayRegion(const DisplayRegion &); + DisplayRegion& operator=(const DisplayRegion &); + + friend class GraphicsLayer; +}; + +INLINE ostream &operator << (ostream &out, const DisplayRegion &dr); + +#include "displayRegion.I" + +#endif /* DISPLAYREGION_H */ diff --git a/panda/src/display/displayRegionStack.I b/panda/src/display/displayRegionStack.I new file mode 100644 index 0000000000..ae3bfd9d0b --- /dev/null +++ b/panda/src/display/displayRegionStack.I @@ -0,0 +1,48 @@ +// Filename: displayRegionStack.I +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegionStack::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DisplayRegionStack:: +DisplayRegionStack() { + _stack_level = -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegionStack::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DisplayRegionStack:: +~DisplayRegionStack() { +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegionStack::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DisplayRegionStack:: +DisplayRegionStack(const DisplayRegionStack ©) : + _display_region(copy._display_region), + _stack_level(copy._stack_level) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DisplayRegionStack::Copy Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DisplayRegionStack:: +operator =(const DisplayRegionStack ©) { + _display_region = copy._display_region; + _stack_level = copy._stack_level; +} diff --git a/panda/src/display/displayRegionStack.h b/panda/src/display/displayRegionStack.h new file mode 100644 index 0000000000..18cb334a8c --- /dev/null +++ b/panda/src/display/displayRegionStack.h @@ -0,0 +1,38 @@ +// Filename: displayRegionStack.h +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DISPLAYREGIONSTACK_H +#define DISPLAYREGIONSTACK_H + +#include + +#include "displayRegion.h" + +class GraphicsStateGuardian; + +//////////////////////////////////////////////////////////////////// +// Class : DisplayRegionStack +// Description : An instance of this kind of object is returned by +// GraphicsStateGuardian::push_display_region(). It +// holds the information needed to restore the previous +// display region in the subsequent matching call to +// pop_display_region(). +//////////////////////////////////////////////////////////////////// +class DisplayRegionStack { +public: + INLINE DisplayRegionStack(); + INLINE ~DisplayRegionStack(); + INLINE DisplayRegionStack(const DisplayRegionStack ©); + INLINE void operator =(const DisplayRegionStack ©); + +private: + CPT(DisplayRegion) _display_region; + int _stack_level; + friend class GraphicsStateGuardian; +}; + +#include "displayRegionStack.I" + +#endif diff --git a/panda/src/display/frameBufferStack.I b/panda/src/display/frameBufferStack.I new file mode 100644 index 0000000000..d69d6e23ae --- /dev/null +++ b/panda/src/display/frameBufferStack.I @@ -0,0 +1,46 @@ +// Filename: frameBufferStack.I +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: FrameBufferStack::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FrameBufferStack:: +FrameBufferStack() { + _stack_level = -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: FrameBufferStack::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FrameBufferStack:: +~FrameBufferStack() { +} + +//////////////////////////////////////////////////////////////////// +// Function: FrameBufferStack::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FrameBufferStack:: +FrameBufferStack(const FrameBufferStack ©) : + _frame_buffer(copy._frame_buffer), + _stack_level(copy._stack_level) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: FrameBufferStack::Copy Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void FrameBufferStack:: +operator =(const FrameBufferStack ©) { + _frame_buffer = copy._frame_buffer; + _stack_level = copy._stack_level; +} diff --git a/panda/src/display/frameBufferStack.h b/panda/src/display/frameBufferStack.h new file mode 100644 index 0000000000..1b009f7646 --- /dev/null +++ b/panda/src/display/frameBufferStack.h @@ -0,0 +1,38 @@ +// Filename: frameBufferStack.h +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FRAMEBUFFERSTACK_H +#define FRAMEBUFFERSTACK_H + +#include + +#include "savedFrameBuffer.h" + +class GraphicsStateGuardian; + +//////////////////////////////////////////////////////////////////// +// Class : FrameBufferStack +// Description : An instance of this kind of object is returned by +// GraphicsStateGuardian::push_frame_buffer(). It +// holds the information needed to restore the previous +// frame buffer contents in the subsequent matching call +// to pop_frame_buffer(). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA FrameBufferStack { +public: + INLINE FrameBufferStack(); + INLINE ~FrameBufferStack(); + INLINE FrameBufferStack(const FrameBufferStack ©); + INLINE void operator =(const FrameBufferStack ©); + +private: + PT(SavedFrameBuffer) _frame_buffer; + int _stack_level; + friend class GraphicsStateGuardian; +}; + +#include "frameBufferStack.I" + +#endif diff --git a/panda/src/display/graphicsChannel.I b/panda/src/display/graphicsChannel.I new file mode 100644 index 0000000000..46af9fec38 --- /dev/null +++ b/panda/src/display/graphicsChannel.I @@ -0,0 +1,26 @@ +// Filename: graphicsChannel.I +// Created by: frang (07Mar99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsChannel::set_active +// Access: Public +// Description: Sets the active flag on the channel. If the channel +// is marked as inactive, nothing will be rendered. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsChannel:: +set_active(bool active) { + _is_active = active; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsChannel::is_active +// Access: Public +// Description: Returns the active flag on the channel. +//////////////////////////////////////////////////////////////////// +INLINE bool GraphicsChannel:: +is_active() const { + return _is_active; +} + diff --git a/panda/src/display/graphicsChannel.cxx b/panda/src/display/graphicsChannel.cxx new file mode 100644 index 0000000000..cf372cc200 --- /dev/null +++ b/panda/src/display/graphicsChannel.cxx @@ -0,0 +1,218 @@ +// Filename: graphicsChannel.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "graphicsChannel.h" +#include "graphicsWindow.h" +#include "config_display.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle GraphicsChannel::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsChannel::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsChannel:: +GraphicsChannel() { + _window = NULL; + _is_active = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsChannel::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsChannel:: +GraphicsChannel(GraphicsWindow *window) + : _window(window) +{ + _is_active = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsChannel::Copy Constructor +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GraphicsChannel:: +GraphicsChannel(const GraphicsChannel&) { + display_cat.error() + << "GraphicsChannels should never be copied" << endl; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsChannel::Copy Assignment Operator +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GraphicsChannel &GraphicsChannel:: +operator=(const GraphicsChannel&) { + display_cat.error() + << "GraphicsChannels should never be assigned" << endl; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsChannel::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsChannel:: +~GraphicsChannel() { + // We don't have to destruct our child display regions explicitly, + // since they are all reference-counted and will go away when their + // pointers do. However, we do need to zero out their pointers to + // us. + GraphicsLayers::iterator li; + for (li = _layers.begin(); + li != _layers.end(); + ++li) { + (*li)->_channel = NULL; + } + + // We don't need to remove ourself from the windows's list of + // channels. We must have already been removed, or we wouldn't be + // destructing! +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsChannel::make_layer +// Access: Public +// Description: Creates a new GraphicsLayer, associated with the +// window, at the indicated index position. If the +// index position negative or past the end of the array, +// the end of the array is assumed. The layers will be +// rendered on top of each other, in increasing order by +// index, from back to front. +//////////////////////////////////////////////////////////////////// +GraphicsLayer *GraphicsChannel:: +make_layer(int index) { + PT(GraphicsLayer) layer = new GraphicsLayer(this); + if (index < 0 || index >= (int)_layers.size()) { + _layers.push_back(layer); + } else { + _layers.insert(_layers.begin() + index, layer); + } + return layer; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsChannel::get_num_layers +// Access: Public +// Description: Returns the number of layers currently associated +// with the channel. +//////////////////////////////////////////////////////////////////// +int GraphicsChannel:: +get_num_layers() const { + return _layers.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsChannel::get_layer +// Access: Public +// Description: Returns the nth layer associated with the channel. +//////////////////////////////////////////////////////////////////// +GraphicsLayer *GraphicsChannel:: +get_layer(int index) const { + nassertr(index >= 0 && index < (int)_layers.size(), NULL); + return _layers[index]; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsChannel::move_layer +// Access: Public +// Description: Changes the ordering of the layers so that the +// indicated layer will move to the indicated position. +// If to_index is negative or past the end of the array, +// the end of the array is assumed. +//////////////////////////////////////////////////////////////////// +void GraphicsChannel:: +move_layer(int from_index, int to_index) { + nassertv(from_index >= 0 && from_index < (int)_layers.size()); + PT(GraphicsLayer) layer = _layers[from_index]; + + if (to_index < 0 || to_index >= (int)_layers.size()) { + _layers.erase(_layers.begin() + from_index); + _layers.push_back(layer); + + } else if (to_index > from_index) { + // Move the layer later in the list. + _layers.insert(_layers.begin() + to_index, layer); + _layers.erase(_layers.begin() + from_index); + + } else if (to_index < from_index) { + // Move the layer earlier in the list. + _layers.erase(_layers.begin() + from_index); + _layers.insert(_layers.begin() + to_index, layer); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsChannel::remove_layer +// Access: Public +// Description: Removes the nth layer. This changes the numbers of +// all subsequent layers. +//////////////////////////////////////////////////////////////////// +void GraphicsChannel:: +remove_layer(int index) { + nassertv(index >= 0 && index < (int)_layers.size()); + _layers.erase(_layers.begin() + index); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsChannel::get_window +// Access: Public +// Description: Returns the GraphicsWindow that this channel is +// associated with. It is possible that the +// GraphicsWindow might have been deleted while an +// outstanding PT(GraphicsChannel) prevented all of its +// children channels from also being deleted; in this +// unlikely case, get_window() may return NULL. +//////////////////////////////////////////////////////////////////// +GraphicsWindow *GraphicsChannel:: +get_window() const { + return _window; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsChannel::get_pipe +// Access: Public +// Description: Returns the GraphicsPipe that this channel is +// ultimately associated with, or NULL if no pipe is +// associated. +//////////////////////////////////////////////////////////////////// +GraphicsPipe *GraphicsChannel:: +get_pipe() const { + return (_window != (GraphicsWindow *)NULL) ? _window->get_pipe() : NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsChannel::window_resized +// Access: Public, Virtual +// Description: This is called whenever the parent window has been +// resized; it should do whatever needs to be done to +// adjust the channel to account for it. +//////////////////////////////////////////////////////////////////// +void GraphicsChannel:: +window_resized(int x, int y) { + // By default, a normal GraphicsChannel fills the whole window, and + // so when the window resizes so does the channel, by the same + // amount. + GraphicsLayers::iterator li; + for (li = _layers.begin(); + li != _layers.end(); + ++li) { + (*li)->channel_resized(x, y); + } +} diff --git a/panda/src/display/graphicsChannel.h b/panda/src/display/graphicsChannel.h new file mode 100644 index 0000000000..01e71c7845 --- /dev/null +++ b/panda/src/display/graphicsChannel.h @@ -0,0 +1,94 @@ +// Filename: graphicsChannel.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef GRAPHICSCHANNEL_H +#define GRAPHICSCHANNEL_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "graphicsLayer.h" + +#include +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +class GraphicsChannel; +class GraphicsPipe; +class GraphicsWindow; + +//////////////////////////////////////////////////////////////////// +// Class : GraphicsChannel +// Description : This represents a single hardware output. Typically +// there is exactly one channel per window, but some +// implementations (e.g. SGI) support potentially +// several different video channel ports connected to +// different parts within a window. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GraphicsChannel : public TypedReferenceCount { +protected: + + GraphicsChannel(); + +public: + GraphicsChannel(GraphicsWindow *); + virtual ~GraphicsChannel(); + + GraphicsLayer *make_layer(int index = -1); + int get_num_layers() const; + GraphicsLayer *get_layer(int index) const; + void move_layer(int from_index, int to_index); + void remove_layer(int index); + + GraphicsWindow *get_window() const; + GraphicsPipe *get_pipe() const; + + virtual void window_resized(int x, int y); + + INLINE void set_active(bool active); + INLINE bool is_active() const; + +private: + GraphicsWindow *_window; + bool _is_active; + + typedef vector GraphicsLayers; + GraphicsLayers _layers; + +private: + + GraphicsChannel(const GraphicsChannel&); + GraphicsChannel& operator=(const GraphicsChannel&); + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "GraphicsChannel", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend class GraphicsWindow; +}; + +#include "graphicsChannel.I" + +#endif /* GRAPHICSCHANNEL_H */ diff --git a/panda/src/display/graphicsLayer.I b/panda/src/display/graphicsLayer.I new file mode 100644 index 0000000000..447281b48a --- /dev/null +++ b/panda/src/display/graphicsLayer.I @@ -0,0 +1,41 @@ +// Filename: graphicsLayer.I +// Created by: drose (18Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsLayer::get_channel +// Access: Public +// Description: Returns the GraphicsChannel that this layer is +// associated with. It is possible that the +// GraphicsChannel might have been deleted while an +// outstanding PT(GraphicsLayer) prevented all of its +// children layers from also being deleted; in this +// unlikely case, get_channel() may return NULL. +//////////////////////////////////////////////////////////////////// +INLINE GraphicsChannel *GraphicsLayer:: +get_channel() const { + return _channel; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsLayer::set_active +// Access: Public +// Description: Sets the active flag on the layer. If the layer +// is marked as inactive, nothing will be rendered. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsLayer:: +set_active(bool active) { + _is_active = active; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsLayer::is_active +// Access: Public +// Description: Returns the active flag on the layer. +//////////////////////////////////////////////////////////////////// +INLINE bool GraphicsLayer:: +is_active() const { + return _is_active; +} + diff --git a/panda/src/display/graphicsLayer.cxx b/panda/src/display/graphicsLayer.cxx new file mode 100644 index 0000000000..8e68e7c678 --- /dev/null +++ b/panda/src/display/graphicsLayer.cxx @@ -0,0 +1,230 @@ +// Filename: graphicsLayer.cxx +// Created by: drose (18Apr00) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "graphicsLayer.h" +#include "graphicsChannel.h" +#include "graphicsWindow.h" +#include "config_display.h" + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle GraphicsLayer::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsLayer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsLayer:: +GraphicsLayer() { + _channel = NULL; + _is_active = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsLayer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsLayer:: +GraphicsLayer(GraphicsChannel *channel) + : _channel(channel) +{ + _is_active = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsLayer::Copy Constructor +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GraphicsLayer:: +GraphicsLayer(const GraphicsLayer&) { + display_cat.error() + << "GraphicsLayers should never be copied" << endl; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsLayer::Copy Assignment Operator +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GraphicsLayer &GraphicsLayer:: +operator=(const GraphicsLayer&) { + display_cat.error() + << "GraphicsLayers should never be assigned" << endl; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsLayer::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsLayer:: +~GraphicsLayer() { + // We don't have to destruct our child display regions explicitly, + // since they are all reference-counted and will go away when their + // pointers do. However, we do need to zero out their pointers to + // us. + DisplayRegions::iterator dri; + for (dri = _display_regions.begin(); + dri != _display_regions.end(); + ++dri) { + (*dri)->_layer = NULL; + } + + // We don't need to remove ourself from the channel's list of + // layers. We must have already been removed, or we wouldn't be + // destructing! +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsLayer::make_display_region +// Access: Public +// Description: Creates a new DisplayRegion that covers the entire +// layer. +//////////////////////////////////////////////////////////////////// +DisplayRegion *GraphicsLayer:: +make_display_region() { + PT(DisplayRegion) dr = new DisplayRegion(this); + const GraphicsWindow *win = get_window(); + if (win != (GraphicsWindow *)NULL) { + dr->compute_pixels(win->get_width(), win->get_height()); + } + _display_regions.push_back(dr); + return dr; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsLayer::make_display_region +// Access: Public +// Description: Creates a new DisplayRegion that covers the indicated +// sub-rectangle within the layer. +//////////////////////////////////////////////////////////////////// +DisplayRegion *GraphicsLayer:: +make_display_region(float l, float r, float b, float t) { + nassertr(this != (GraphicsLayer *)NULL, NULL); + PT(DisplayRegion) dr = new DisplayRegion(this, l, r, b, t); + const GraphicsWindow *win = get_window(); + if (win != (GraphicsWindow *)NULL) { + dr->compute_pixels(win->get_width(), win->get_height()); + } + _display_regions.push_back(dr); + return dr; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsLayer::get_num_drs +// Access: Public +// Description: Returns the number of DisplayRegions associated with +// the layer. +//////////////////////////////////////////////////////////////////// +int GraphicsLayer:: +get_num_drs() const { + return _display_regions.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsLayer::get_dr +// Access: Public +// Description: Returns the nth DisplayRegion associated with the +// layer. +//////////////////////////////////////////////////////////////////// +DisplayRegion *GraphicsLayer:: +get_dr(int index) const { + nassertr(index >= 0 && index < (int)_display_regions.size(), NULL); + return _display_regions[index]; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsLayer::remove_dr +// Access: Public +// Description: Removes (and possibly deletes) the nth DisplayRegion +// associated with the layer. All subsequent index +// numbers will shift down one. +//////////////////////////////////////////////////////////////////// +void GraphicsLayer:: +remove_dr(int index) { + nassertv(index >= 0 && index < (int)_display_regions.size()); + _display_regions[index]->_layer = NULL; + _display_regions.erase(_display_regions.begin() + index); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsLayer::remove_dr +// Access: Public +// Description: Removes (and possibly deletes) the indicated +// DisplayRegion associated with the layer. All +// subsequent index numbers will shift down one. +// Returns true if the DisplayRegion was removed, false +// if it was not a member of the layer. +//////////////////////////////////////////////////////////////////// +bool GraphicsLayer:: +remove_dr(DisplayRegion *display_region) { + // For whatever reason, VC++ considers == ambiguous unless we + // compare it to a PT(DisplayRegion) instead of a DisplayRegion*. + PT(DisplayRegion) ptdr = display_region; + DisplayRegions::iterator dri = + find(_display_regions.begin(), _display_regions.end(), ptdr); + if (dri != _display_regions.end()) { + display_region->_layer = NULL; + _display_regions.erase(dri); + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsLayer::get_window +// Access: Public +// Description: Returns the GraphicsWindow that this layer is +// ultimately associated with, or NULL if no window is +// associated. +//////////////////////////////////////////////////////////////////// +GraphicsWindow *GraphicsLayer:: +get_window() const { + nassertr(this != (GraphicsLayer *)NULL, NULL); + return (_channel != (GraphicsChannel *)NULL) ? _channel->get_window() : NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsLayer::get_pipe +// Access: Public +// Description: Returns the GraphicsPipe that this layer is +// ultimately associated with, or NULL if no pipe is +// associated. +//////////////////////////////////////////////////////////////////// +GraphicsPipe *GraphicsLayer:: +get_pipe() const { + return (_channel != (GraphicsChannel *)NULL) ? _channel->get_pipe() : NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsLayer::channel_resized +// Access: Public, Virtual +// Description: This is called whenever the parent channel has been +// resized; it should do whatever needs to be done to +// adjust the layer to account for it. +//////////////////////////////////////////////////////////////////// +void GraphicsLayer:: +channel_resized(int x, int y) { + // Since a layer always fills the whole channel, when the channel + // resizes so does the layer, by the same amount. + DisplayRegions::iterator dri; + for (dri = _display_regions.begin(); + dri != _display_regions.end(); + ++dri) { + (*dri)->compute_pixels(x, y); + } +} diff --git a/panda/src/display/graphicsLayer.h b/panda/src/display/graphicsLayer.h new file mode 100644 index 0000000000..3777282695 --- /dev/null +++ b/panda/src/display/graphicsLayer.h @@ -0,0 +1,100 @@ +// Filename: graphicsLayer.h +// Created by: drose (18Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GRAPHICSLAYER_H +#define GRAPHICSLAYER_H + +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "displayRegion.h" + +#include +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +class GraphicsChannel; +class GraphicsWindow; +class GraphicsPipe; + +//////////////////////////////////////////////////////////////////// +// Class : GraphicsLayer +// Description : A layer is a collection of non-overlapping +// DisplayRegions within a Channel that will be rendered +// together. When the channel renders, it will render +// all of its layers in index number order; each layer +// may overlap some or all of its DisplayRegions with +// other layers, and they will be drawn sequentially +// without clearing the framebuffer between layers. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GraphicsLayer : public TypedReferenceCount { +protected: + + GraphicsLayer(); + GraphicsLayer(GraphicsChannel *); + +public: + virtual ~GraphicsLayer(); + + DisplayRegion *make_display_region(); + DisplayRegion *make_display_region(float l, float r, + float b, float t); + + int get_num_drs() const; + DisplayRegion *get_dr(int index) const; + void remove_dr(int index); + bool remove_dr(DisplayRegion *display_region); + + INLINE GraphicsChannel *get_channel() const; + GraphicsWindow *get_window() const; + GraphicsPipe *get_pipe() const; + + void channel_resized(int x, int y); + + INLINE void set_active(bool active); + INLINE bool is_active() const; + +private: + GraphicsChannel *_channel; + bool _is_active; + + typedef vector DisplayRegions; + DisplayRegions _display_regions; + +private: + + GraphicsLayer(const GraphicsLayer&); + GraphicsLayer& operator=(const GraphicsLayer&); + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "GraphicsLayer", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend class GraphicsChannel; +}; + +#include "graphicsLayer.I" + +#endif /* GRAPHICSLAYER_H */ diff --git a/panda/src/display/graphicsPipe.I b/panda/src/display/graphicsPipe.I new file mode 100644 index 0000000000..fb5ce192c8 --- /dev/null +++ b/panda/src/display/graphicsPipe.I @@ -0,0 +1,23 @@ +// Filename: graphicsPipe.I +// Created by: frang (07Mar99) +// +//////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsPipe::get_all_pipes +// Access: Private, Static +// Description: Returns a reference to the static _all_pipes +// container. This wrapper function allows us to +// guarantee creation of the container before we attempt +// to reference it. +//////////////////////////////////////////////////////////////////// +INLINE GraphicsPipe::Pipes &GraphicsPipe:: +get_all_pipes() { + if (_all_pipes == NULL) { + _all_pipes = new Pipes; + } + return *_all_pipes; +} diff --git a/panda/src/display/graphicsPipe.N b/panda/src/display/graphicsPipe.N new file mode 100644 index 0000000000..48153e2abe --- /dev/null +++ b/panda/src/display/graphicsPipe.N @@ -0,0 +1,13 @@ +ignorefile config_display.h displayRegion.h displayRegion.I +ignorefile displayRegionStack.h displayRegionStack.I frameBufferStack.h +ignorefile frameBufferStack.I graphicsChannel.h graphicsChannel.I +ignorefile graphicsStateGuardian.h graphicsStateGuardian.I +ignorefile hardwareChannel.h hardwareChannel.I interactiveGraphicsPipe.h +ignorefile interactiveGraphicsPipe.I noninteractiveGraphicsPipe.h +ignorefile noninteractiveGraphicsPipe.I renderBuffer.h savedFrameBuffer.h +ignorefile savedFrameBuffer.I textureContext.h textureContext.I + +ignoremember _factory +ignoremember make_window + +forcetype PointerTo diff --git a/panda/src/display/graphicsPipe.cxx b/panda/src/display/graphicsPipe.cxx new file mode 100644 index 0000000000..1fa3bffdf0 --- /dev/null +++ b/panda/src/display/graphicsPipe.cxx @@ -0,0 +1,324 @@ +// Filename: graphicsPipe.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "graphicsPipe.h" +#include "config_display.h" + +#include +#include + +#include + +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle GraphicsPipe::_type_handle; +TypeHandle GraphicsPipe::PipeSpec::_type_handle; + +GraphicsPipe::PipeFactory GraphicsPipe::_factory; + +// These static members are pointers rather than concrete objects so +// we can guarantee order of creation at static init time. +GraphicsPipe::Pipes *GraphicsPipe::_all_pipes = NULL; + +GraphicsPipe::PipeSpec::~PipeSpec(void) {} + +TypeHandle GraphicsPipe::PipeSpec::get_class_type(void) { + return _type_handle; +} + +void GraphicsPipe::PipeSpec::init_type(void) { + PipeParam::init_type(); + register_type(_type_handle, "GraphicsPipe::PipeSpec", + PipeParam::get_class_type()); +} + +TypeHandle GraphicsPipe::PipeSpec::get_type(void) const { + return get_class_type(); +} + +TypeHandle GraphicsPipe::PipeSpec::force_init_type(void) { + init_type(); + return get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsPipe::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsPipe:: +GraphicsPipe(const PipeSpecifier &spec) : + Namable(spec.get_name()) +{ + // Add ourself to the global list of pipes. + get_all_pipes().push_back(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsPipe::Default Constructor +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsPipe:: +GraphicsPipe() { + display_cat.error() + << "GraphicsPipes should not be called with default constructor" << endl; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsPipe::Copy Constructor +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsPipe:: +GraphicsPipe(const GraphicsPipe&) { + display_cat.error() + << "GraphicsPipes should not be copied" << endl; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsPipe::Copy Assignment Operator +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsPipe &GraphicsPipe:: +operator=(const GraphicsPipe&) { + display_cat.error() + << "GraphicsPipes should not be assigned" << endl; + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsPipe::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsPipe:: +~GraphicsPipe() { + // We don't have to destruct our child windows explicitly, since + // they are all reference-counted and will go away when their + // pointers do. However, we do need to zero out their pointers to + // us. + Windows::const_iterator wi; + for (wi = _windows.begin(); wi != _windows.end(); ++wi) { + (*wi)->_pipe = NULL; + } + + // Remove ourself from the global list. + Pipes &all_pipes = get_all_pipes(); + Pipes::iterator pi = + find(all_pipes.begin(), all_pipes.end(), this); + if (pi != all_pipes.end()) { + all_pipes.erase(pi); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsPipe::make_window +// Access: Public +// Description: Creates and returns a new window on the pipe. The +// window will automatically be added to the pipe's +// reference-countling list of windows; it must later be +// explicitly removed to destroy it (you should not +// attempt to delete it). +//////////////////////////////////////////////////////////////////// +GraphicsWindow *GraphicsPipe:: +make_window() { + FactoryParams params; + params.add_param(new GraphicsWindow::WindowPipe(this)); + + GraphicsWindow *win = GraphicsWindow::_factory. + make_instance(get_window_type(), params); + nassertr(win != (GraphicsWindow *)NULL, NULL); + + add_window(win); + return win; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsPipe::make_window +// Access: Public +// Description: Creates and returns a new window on the pipe. The +// window will automatically be added to the pipe's +// reference-countling list of windows; it must later be +// explicitly removed to destroy it (you should not +// attempt to delete it). +//////////////////////////////////////////////////////////////////// +GraphicsWindow *GraphicsPipe:: +make_window(const GraphicsWindow::Properties &props) { + FactoryParams params; + params.add_param(new GraphicsWindow::WindowPipe(this)); + params.add_param(new GraphicsWindow::WindowProps(props)); + + GraphicsWindow *win = GraphicsWindow::_factory. + make_instance(get_window_type(), params); + nassertr(win != (GraphicsWindow *)NULL, NULL); + + add_window(win); + return win; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsPipe::remove_window +// Access: Public +// Description: Deletes a previously-created window by removing it +// from the reference-counting list of windows owned by +// the pipe. Note that the window will not actually be +// deleted until all pointers to it (for instance, on +// mouse objects, etc.) are cleared. +//////////////////////////////////////////////////////////////////// +void GraphicsPipe:: +remove_window(GraphicsWindow *window) { + // For whatever reason, VC++ considers == ambiguous unless we + // compare it to a PT(GraphicsWindow) instead of a GraphicsWindow*. + PT(GraphicsWindow) ptwin = window; + Windows::iterator wi = + find(_windows.begin(), _windows.end(), ptwin); + if (wi != _windows.end()) { + _windows.erase(wi); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsPipe::get_num_windows +// Access: Public +// Description: Returns the number of windows that have been created +// on this pipe. +//////////////////////////////////////////////////////////////////// +int GraphicsPipe:: +get_num_windows() const { + return _windows.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsPipe::get_window +// Access: Public +// Description: Returns the nth window associated with this pipe. n +// must be between 0 and get_num_windows(). +//////////////////////////////////////////////////////////////////// +GraphicsWindow *GraphicsPipe:: +get_window(int n) const { + nassertr(n >= 0 && n < get_num_windows(), NULL); + return _windows[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsPipe::get_num_pipes +// Access: Public, Static +// Description: Returns the total number of pipes in the universe. +//////////////////////////////////////////////////////////////////// +int GraphicsPipe:: +get_num_pipes() { + return get_all_pipes().size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsPipe::get_pipe +// Access: Public, Static +// Description: Returns the nth pipe in the universe. n must be +// between 0 and get_num_pipes(). +//////////////////////////////////////////////////////////////////// +GraphicsPipe *GraphicsPipe:: +get_pipe(int n) { + nassertr(n >= 0 && n < get_num_pipes(), NULL); + return get_all_pipes()[n]; +} + + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsPipe::get_num_hw_channels +// Access: Protected, Virtual +// Description: Returns the number of hardware channels available for +// pipes of this type. See get_hw_channel(). +//////////////////////////////////////////////////////////////////// +int GraphicsPipe:: +get_num_hw_channels() { + return 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsPipe::get_hw_channel +// Access: Public, Virtual +// Description: Creates and returns an accessor to the +// HardwareChannel at the given index number, which must +// be in the range 0 <= index < get_num_hw_channels(). +// This function will return NULL if the index number is +// out of range or the hardware channel at that index is +// unavailable. +// +// Most kinds of GraphicsPipes do not have any special +// hardware channels available, and this function will +// always return NULL. +//////////////////////////////////////////////////////////////////// +HardwareChannel *GraphicsPipe:: +get_hw_channel(GraphicsWindow*, int) { + return (HardwareChannel*)0L; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsPipe::add_window +// Access: Protected +// Description: Adds a newly-created window to the set of windows +// owned by the pipe. This function is intended to be +// called from a derived class' make_window() function. +//////////////////////////////////////////////////////////////////// +void GraphicsPipe:: +add_window(GraphicsWindow *win) { + _windows.push_back(win); +} + +void GraphicsPipe::read_priorities(void) { + if (_factory.get_num_preferred() == 0) { + Config::ConfigTable::Symbol::iterator i; + for (i = preferred_pipe_begin(); i != preferred_pipe_end(); ++i) { + ConfigString type_name = (*i).Val(); + TypeHandle type = TypeRegistry::ptr()->find_type(type_name); + if (type == TypeHandle::none()) { + display_cat.warning() + << "Unknown type requested for pipe preference: " << type_name + << "\n"; + } else { + display_cat.debug() + << "Specifying type " << type << " for pipe preference.\n"; + _factory.add_preferred(type); + } + } + } +} + +void GraphicsPipe::resolve_modules(void) { + Config::ConfigTable::Symbol::iterator i; + + for (i=pipe_modules_begin(); i!=pipe_modules_end(); ++i) { + Filename dlname = Filename::dso_filename("lib" + (*i).Val() + ".so"); + display_cat.info() + << "loading display module: " << dlname.to_os_specific() << endl; + void *tmp = load_dso(dlname.to_os_specific()); + if (tmp == (void*)0L) { + display_cat.info() + << "Unable to load: " << load_dso_error() << endl; + } + } + for (i=gsg_modules_begin(); i!=gsg_modules_end(); ++i) { + Filename dlname = Filename::dso_filename("lib" + (*i).Val() + ".so"); + display_cat.info() + << "loading GSG module: " << dlname.to_os_specific() << endl; + void *tmp = load_dso(dlname.to_os_specific()); + if (tmp == (void*)0L) { + display_cat.info() + << "Unable to load: " << load_dso_error() << endl; + } + } + + read_priorities(); + GraphicsWindow::read_priorities(); + GraphicsPipe::read_priorities(); +} diff --git a/panda/src/display/graphicsPipe.h b/panda/src/display/graphicsPipe.h new file mode 100644 index 0000000000..90eaeffc8b --- /dev/null +++ b/panda/src/display/graphicsPipe.h @@ -0,0 +1,134 @@ +// Filename: graphicsPipe.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef GRAPHICSPIPE_H +#define GRAPHICSPIPE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "graphicsWindow.h" +#include "hardwareChannel.h" +#include "pipeSpec.h" + +#include +#include +#include +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +class GraphicsPipe; + +//////////////////////////////////////////////////////////////////// +// Class : GraphicsPipe +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GraphicsPipe : public TypedReferenceCount, public Namable { +public: + GraphicsPipe(const PipeSpecifier &spec); + virtual ~GraphicsPipe(); + + GraphicsWindow *make_window(); + GraphicsWindow *make_window(const GraphicsWindow::Properties&); + + void remove_window(GraphicsWindow *window); + + virtual TypeHandle get_window_type() const=0; + + // Functions for obtaining the set of windows associated with this + // pipe, and the set of all GraphicsPipes in the world. + + int get_num_windows() const; + GraphicsWindow *get_window(int n) const; + + static int get_num_pipes(); + static GraphicsPipe *get_pipe(int n); + +protected: + virtual int get_num_hw_channels(); + virtual HardwareChannel *get_hw_channel(GraphicsWindow *, int); + + void add_window(GraphicsWindow *win); + +public: + // Factory stuff + typedef Factory PipeFactory; + typedef FactoryParam PipeParam; + + // Make a factory parameter type for the pipe specifier + class EXPCL_PANDA PipeSpec : public PipeParam { + public: + INLINE PipeSpec(void) : PipeParam() {} + INLINE PipeSpec(PipeSpecifier& p) : _p(p), PipeParam() {} + virtual ~PipeSpec(void); + INLINE const PipeSpecifier &get_specifier(void) { return _p; } + public: + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type(void); + private: + PipeSpecifier _p; + + static TypeHandle _type_handle; + }; + + static PipeFactory _factory; + + static void resolve_modules(void); + +private: + static void read_priorities(void); + +protected: + + GraphicsPipe(); + GraphicsPipe(const GraphicsPipe ©); + GraphicsPipe &operator = (const GraphicsPipe ©); + +private: + // Some private type declarations. These must be declared here so + // we can declare the public iterator types, below. + typedef vector Windows; + typedef vector Pipes; + + Windows _windows; + + static Pipes *_all_pipes; + INLINE static Pipes &get_all_pipes(); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + Namable::init_type(); + register_type(_type_handle, "GraphicsPipe", + TypedReferenceCount::get_class_type(), + Namable::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + // this is so it can call get_hw_channel + friend class GraphicsWindow; +}; + +#include "graphicsPipe.I" + +#endif /* GRAPHICSPIPE_H */ diff --git a/panda/src/display/graphicsStateGuardian.I b/panda/src/display/graphicsStateGuardian.I new file mode 100644 index 0000000000..9bca29c955 --- /dev/null +++ b/panda/src/display/graphicsStateGuardian.I @@ -0,0 +1,184 @@ +// Filename: graphicsStateGuardian.I +// Created by: drose (24Sep99) +// +//////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::set_render_traverser +// Access: Public +// Description: Sets the traverser that will be used to render the +// scene graph. If this is unset, nothing will be +// rendered. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsStateGuardian:: +set_render_traverser(RenderTraverser *rt) { + _render_traverser = rt; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::get_render_traverser +// Access: Public +// Description: Returns the traverser that will be used to render the +// scene graph. +//////////////////////////////////////////////////////////////////// +INLINE RenderTraverser *GraphicsStateGuardian:: +get_render_traverser() const { + return _render_traverser; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::get_state +// Access: Public +// Description: Returns the current state of the GSG. This state may +// be saved and used to restore the GSG to its current +// state later; however, this save/restore operation +// should be used with caution, as it could grow to be +// expensive. +//////////////////////////////////////////////////////////////////// +INLINE const NodeAttributes &GraphicsStateGuardian:: +get_state() const { + return _state; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::get_current_projection_node +// Access: Public +// Description: Returns the node currently being used as the +// projection node (i.e. the camera) for this scene, as +// set by the last call to render_subgraph(). +//////////////////////////////////////////////////////////////////// +INLINE const ProjectionNode *GraphicsStateGuardian:: +get_current_projection_node(void) const { + return _current_projection_node; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::get_current_root_node +// Access: Public +// Description: Returns the root node of the entire scene graph (not +// the current subgraph) currently being rendered, as +// set by the last call to render_scene(). +//////////////////////////////////////////////////////////////////// +INLINE const Node *GraphicsStateGuardian:: +get_current_root_node(void) const { + return _current_root_node; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::get_current_display_region +// Access: Public +// Description: Returns the current display region being rendered to, +// as set by the last call to push_display_region() (or +// restored by pop_display_region()). This display +// region will be made active (if it is not already) by +// a call to prepare_display_region(). +//////////////////////////////////////////////////////////////////// +INLINE CPT(DisplayRegion) GraphicsStateGuardian:: +get_current_display_region(void) const { + return _current_display_region; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::push_display_region +// Access: Public +// Description: Saves the current display region information and sets +// up a new display region for rendering. The return +// value from this function must eventually be passed to +// a matching pop_display_region() call. +// +// The new display region will not actually be made +// active for rendering until the next call to +// prepare_display_region(). This is a state-changing +// optimization. +//////////////////////////////////////////////////////////////////// +INLINE DisplayRegionStack GraphicsStateGuardian:: +push_display_region(CPT(DisplayRegion) dr) { + DisplayRegionStack old; + old._display_region = _current_display_region; + old._stack_level = _display_region_stack_level; + _display_region_stack_level++; + _current_display_region = dr; + return old; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::pop_display_region +// Access: Public +// Description: Restores the display region previously in effect, +// before the matching call to push_display_region(). +// +// The newly-restored display region will not actually +// be made active for rendering until the next call to +// prepare_display_region(). This is a state-changing +// optimization. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsStateGuardian:: +pop_display_region(DisplayRegionStack &node) { + nassertv(_display_region_stack_level > 0); + _display_region_stack_level--; + nassertv(node._stack_level == _display_region_stack_level); + _current_display_region = node._display_region; + node._stack_level = -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::push_frame_buffer +// Access: Public +// Description: Saves the contents of the frame buffer (within the +// indicated display region only) so that rendering may +// be performed (for instance, to render a partial in a +// multipass algorithm) and the frame buffer later +// restored via a matching call to pop_frame_buffer(). +//////////////////////////////////////////////////////////////////// +INLINE FrameBufferStack GraphicsStateGuardian:: +push_frame_buffer(const RenderBuffer &buffer, + CPT(DisplayRegion) dr) { + FrameBufferStack old; + old._frame_buffer = save_frame_buffer(buffer, dr); + old._stack_level = _frame_buffer_stack_level; + _frame_buffer_stack_level++; + return old; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::pop_frame_buffer +// Access: Public +// Description: Restores the contents of the frame buffer as saved by +// a previous call to push_frame_buffer(). +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsStateGuardian:: +pop_frame_buffer(FrameBufferStack &node) { + nassertv(_frame_buffer_stack_level > 0); + _frame_buffer_stack_level--; + nassertv(node._stack_level == _frame_buffer_stack_level); + restore_frame_buffer(node._frame_buffer); + node._stack_level = -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::set_coordinate_system +// Access: Public +// Description: Changes the coordinate system in effect on this +// particular gsg. Normally, this will be the default +// coordinate system, but it might be set differently at +// runtime. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsStateGuardian:: +set_coordinate_system(CoordinateSystem cs) { + _coordinate_system = cs; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::get_coordinate_system +// Access: Public +// Description: Returns the coordinate system in effect on this +// particular gsg. Normally, this will be the default +// coordinate system, but it might be set differently at +// runtime. +//////////////////////////////////////////////////////////////////// +INLINE CoordinateSystem GraphicsStateGuardian:: +get_coordinate_system() const { + return _coordinate_system; +} diff --git a/panda/src/display/graphicsStateGuardian.N b/panda/src/display/graphicsStateGuardian.N new file mode 100644 index 0000000000..0b1272b28c --- /dev/null +++ b/panda/src/display/graphicsStateGuardian.N @@ -0,0 +1 @@ +forcetype GraphicsStateGuardian diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx new file mode 100644 index 0000000000..eaea0211b8 --- /dev/null +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -0,0 +1,435 @@ +// Filename: graphicsStateGuardian.cxx +// Created by: drose (02Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "graphicsStateGuardian.h" +#include "renderBuffer.h" +#include "config_display.h" + +#include +#include + +#include + +TypeHandle GraphicsStateGuardian::_type_handle; +TypeHandle GraphicsStateGuardian::GsgWindow::_type_handle; + +GraphicsStateGuardian::GsgFactory GraphicsStateGuardian::_factory; + +GraphicsStateGuardian::GsgWindow::~GsgWindow(void) {} + +TypeHandle GraphicsStateGuardian::GsgWindow::get_class_type(void) { + return _type_handle; +} + +void GraphicsStateGuardian::GsgWindow::init_type(void) { + GsgParam::init_type(); + register_type(_type_handle, "GraphicsStateGuardian::GsgWindow", + GsgParam::get_class_type()); +} + +TypeHandle GraphicsStateGuardian::GsgWindow::get_type(void) const { + return get_class_type(); +} + +TypeHandle GraphicsStateGuardian::GsgWindow::force_init_type(void) { + init_type(); + return get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsStateGuardian:: +GraphicsStateGuardian(GraphicsWindow *win) { + _win = win; + _coordinate_system = default_coordinate_system; + _current_display_region = (DisplayRegion*)0L; + reset(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::release_all_textures +// Access: Public +// Description: Frees the resources for all textures associated with +// this GSG. +//////////////////////////////////////////////////////////////////// +void GraphicsStateGuardian:: +release_all_textures() { + // We must get a copy of the _prepared_textures list first, because + // each call to release_texture() will remove that texture from the + // list, and we don't want to traverse a list while we're modifying + // it! + Textures temp = _prepared_textures; + for (Textures::const_iterator ti = temp.begin(); + ti != temp.end(); + ++ti) { + release_texture(*ti); + } + + // Now that we've released all of the textures, the + // _prepared_textures list should have completely emptied itself. + nassertv(_prepared_textures.empty()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::reset +// Access: Public, Virtual +// Description: Resets all internal state as if the gsg were newly +// created. +//////////////////////////////////////////////////////////////////// +void GraphicsStateGuardian:: +reset() { + _display_region_stack_level = 0; + _frame_buffer_stack_level = 0; + + _state.clear(); + + _buffer_mask = 0; + _color_clear_value.set(0.0, 0.0, 0.0, 0.0); + _depth_clear_value = 1.0; + _stencil_clear_value = 0.0; + _accum_clear_value.set(0.0, 0.0, 0.0, 0.0); + _normals_enabled = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::set_state +// Access: Public +// Description: Sets the graphics backend to the state represented by +// the indicated set of attributes. Only the minimal +// number of graphics commands are issued--attributes +// which have not changed since the last call to +// set_state are detected and not issued again. +// +// If complete is true, it means that the supplied state +// is a complete description of the desired state--if an +// attribute is absent, it should be taken to be the +// same as the initial value for that attribute. If +// complete is false, it means that the supplied state +// specifies only a subset of the desired state, and +// that absent attributes should remain unchanged. +//////////////////////////////////////////////////////////////////// +void GraphicsStateGuardian:: +set_state(const NodeAttributes &new_state, bool complete) { + if (gsg_cat.is_debug()) { + gsg_cat.debug() << "\n"; + gsg_cat.debug() + << "Frame " << ClockObject::get_global_clock()->get_frame_count() + << ", setting to (complete = " << complete << ")\n"; + new_state.write(gsg_cat.debug(false), 10); + } + + NodeAttributes::const_iterator new_i; + NodeAttributes::iterator current_i; + + new_i = new_state.begin(); + current_i = _state.begin(); + + while (new_i != new_state.end() && current_i != _state.end()) { + if ((*new_i).first < (*current_i).first) { + // The user requested setting an attribute that we've never set + // before. Issue the command. + + if ((*new_i).second != (NodeAttribute *)NULL) { + if (gsg_cat.is_debug()) { + gsg_cat.debug() + << "Issuing new attrib " << *(*new_i).second << "\n"; + } + + (*new_i).second->issue(this); + + // And store the new value. + current_i = _state.insert(current_i, *new_i); + ++current_i; + } + + ++new_i; + + } else if ((*current_i).first < (*new_i).first) { + // Here's an attribute that we've set previously, but the user + // didn't specify this time. + + if (complete) { + // If we're in the "complete state" model, that means this + // attribute should now get the default initial value. + + if (gsg_cat.is_debug()) { + gsg_cat.debug() + << "Unissuing attrib " << *(*current_i).second + << " (previously set, not now)\n"; + } + + PT(NodeAttribute) initial = (*current_i).second->make_initial(); + initial->issue(this); + + NodeAttributes::iterator erase_i = current_i; + ++current_i; + + _state.erase(erase_i); + + } else { + ++current_i; + } + + } else { // (*current_i).first == (*new_i).first) + + if ((*new_i).second == (NodeAttribute *)NULL) { + // Here's an attribute that we've set previously, which + // appears in the new list, but is NULL indicating it should + // be removed. + + if (complete) { + // Only remove it if we're in the "complete state" model. + + if (gsg_cat.is_debug()) { + gsg_cat.debug() + << "Unissuing attrib " << *(*current_i).second + << " (previously set, now NULL)\n"; + } + + // Issue the initial attribute before clearing the state. + PT(NodeAttribute) initial = (*current_i).second->make_initial(); + initial->issue(this); + + NodeAttributes::iterator erase_i = current_i; + ++current_i; + + _state.erase(erase_i); + + } else { + ++current_i; + } + ++new_i; + + } else { + // Here's an attribute that we've set previously, and the user + // asked us to set it again. Issue the command only if the new + // attribute is different from that which we'd set before. + if ((*new_i).second->compare_to(*(*current_i).second) != 0) { + if (gsg_cat.is_debug()) { + gsg_cat.debug() + << "Reissuing attrib " << *(*new_i).second << "\n"; + } + (*new_i).second->issue(this); + + // And store the new value. + (*current_i).second = (*new_i).second; + + } else if (gsg_cat.is_debug()) { + gsg_cat.debug() + << "Not reissuing unchanged attrib " << *(*new_i).second << "\n"; + } + + ++current_i; + ++new_i; + } + } + } + + while (new_i != new_state.end()) { + // The user requested setting an attribute that we've never set + // before. Issue the command. + + if ((*new_i).second != (NodeAttribute *)NULL) { + if (gsg_cat.is_debug()) { + gsg_cat.debug() + << "Issuing new attrib " << *(*new_i).second << "\n"; + } + + (*new_i).second->issue(this); + + // And store the new value. + _state.insert(_state.end(), *new_i); + } + ++new_i; + } + + if (complete) { + while (current_i != _state.end()) { + // Here's an attribute that we've set previously, but the user + // didn't specify this time. + + if (gsg_cat.is_debug()) { + gsg_cat.debug() + << "Unissuing attrib " << *(*current_i).second + << " (previously set, end of list)\n"; + } + + // If we're in the "complete state" model, that means this + // attribute should now get the default initial value. + PT(NodeAttribute) initial = (*current_i).second->make_initial(); + initial->issue(this); + + NodeAttributes::iterator erase_i = current_i; + ++current_i; + + _state.erase(erase_i); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::get_render_buffer +// Access: Public +// Description: Returns a RenderBuffer object suitable for operating +// on the requested set of buffers. buffer_type is the +// union of all the desired RenderBuffer::Type values. +//////////////////////////////////////////////////////////////////// +RenderBuffer GraphicsStateGuardian:: +get_render_buffer(int buffer_type) { + return RenderBuffer(this, buffer_type & _buffer_mask); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::set_color_clear_value +// Access: Public +// Description: Sets the color that the next clear() command will set +// the color buffer to +//////////////////////////////////////////////////////////////////// +void GraphicsStateGuardian:: +set_color_clear_value(const Colorf& value) { + _color_clear_value = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::set_depth_clear_value +// Access: Public +// Description: Sets the depth that the next clear() command will set +// the depth buffer to +//////////////////////////////////////////////////////////////////// +void GraphicsStateGuardian:: +set_depth_clear_value(const float value) { + _depth_clear_value = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::set_stencil_clear_value +// Access: Public +// Description: Sets the value that the next clear() command will set +// the stencil buffer to +//////////////////////////////////////////////////////////////////// +void GraphicsStateGuardian:: +set_stencil_clear_value(const bool value) { + _stencil_clear_value = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::set_accum_clear_value +// Access: Public +// Description: Sets the color that the next clear() command will set +// the accumulation buffer to +//////////////////////////////////////////////////////////////////// +void GraphicsStateGuardian:: +set_accum_clear_value(const Colorf& value) { + _accum_clear_value = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::wants_normals +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool GraphicsStateGuardian:: +wants_normals() const { + return _normals_enabled; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::wants_texcoords +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool GraphicsStateGuardian:: +wants_texcoords() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::wants_colors +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool GraphicsStateGuardian:: +wants_colors() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::begin_decal +// Access: Public, Virtual +// Description: This will be called to initiate decaling mode. It is +// passed the pointer to the GeomNode that will be the +// destination of the decals, which it is expected that +// the GSG will render normally; subsequent geometry +// rendered up until the next call of end_decal() should +// be rendered as decals of the base_geom. +//////////////////////////////////////////////////////////////////// +void GraphicsStateGuardian:: +begin_decal(GeomNode *base_geom) { + base_geom->draw(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::end_decal +// Access: Public, Virtual +// Description: This will be called to terminate decaling mode. It +// is passed the same base_geom that was passed to +// begin_decal(). +//////////////////////////////////////////////////////////////////// +void GraphicsStateGuardian:: +end_decal(GeomNode *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::mark_prepared_texture +// Access: Protected +// Description: This is intended to be called from within +// prepare_texture(). It adds the indicated +// TextureContext pointer to the _prepared_textures set, +// and returns true if it was successfully added +// (i.e. it was not already in the set). +//////////////////////////////////////////////////////////////////// +bool GraphicsStateGuardian:: +mark_prepared_texture(TextureContext *tc) { + return _prepared_textures.insert(tc).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::unmark_prepared_texture +// Access: Protected +// Description: This is intended to be called from within +// release_texture(). It removes the indicated +// TextureContext pointer from the _prepared_textures set, +// and returns true if it was successfully removed +// (i.e. it had been in the set). +//////////////////////////////////////////////////////////////////// +bool GraphicsStateGuardian:: +unmark_prepared_texture(TextureContext *tc) { + return (_prepared_textures.erase(tc) != 0); +} + + +void GraphicsStateGuardian::read_priorities(void) +{ + if (_factory.get_num_preferred() == 0) { + Config::ConfigTable::Symbol::iterator i; + for (i = preferred_gsg_begin(); i != preferred_gsg_end(); ++i) { + ConfigString type_name = (*i).Val(); + TypeHandle type = TypeRegistry::ptr()->find_type(type_name); + if (type == TypeHandle::none()) { + display_cat.warning() + << "Unknown type requested for GSG preference: " << type_name + << "\n"; + } else { + display_cat.debug() + << "Specifying type " << type << " for GSG preference.\n"; + _factory.add_preferred(type); + } + } + } +} diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h new file mode 100644 index 0000000000..34c3e1073d --- /dev/null +++ b/panda/src/display/graphicsStateGuardian.h @@ -0,0 +1,213 @@ +// Filename: graphicsStateGuardian.h +// Created by: drose (02Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GRAPHICSSTATEGUARDIAN_H +#define GRAPHICSSTATEGUARDIAN_H + +#include + +#include "savedFrameBuffer.h" +#include "frameBufferStack.h" +#include "displayRegionStack.h" + +#include +#include +#include +#include +#include +#include + +#include + +class AllAttributesWrapper; +class AllTransitionsWrapper; + +//////////////////////////////////////////////////////////////////// +// Class : GraphicsStateGuardian +// Description : Encapsulates all the communication with a particular +// instance of a given rendering backend. Tries to +// guarantee that redundant state-change requests are +// not issued (hence "state guardian"). +// +// There will be one of these objects for each different +// graphics context active in the system. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GraphicsStateGuardian : public GraphicsStateGuardianBase { + // + // Interfaces all GSGs should have + // +public: + GraphicsStateGuardian(GraphicsWindow *win); + + INLINE void set_render_traverser(RenderTraverser *rt); + INLINE RenderTraverser *get_render_traverser() const; + + void release_all_textures(); + + virtual void set_color_clear_value(const Colorf& value); + virtual void set_depth_clear_value(const float value); + virtual void set_stencil_clear_value(const bool value); + virtual void set_accum_clear_value(const Colorf& value); + INLINE Colorf get_color_clear_value(void) const { + return _color_clear_value; + } + INLINE float get_depth_clear_value(void) const { + return _depth_clear_value; + } + INLINE bool get_stencil_clear_value(void) const { + return _stencil_clear_value; + } + INLINE Colorf get_accum_clear_value(void) const { + return _accum_clear_value; + } + virtual void clear(const RenderBuffer &buffer)=0; + virtual void clear(const RenderBuffer &buffer, const DisplayRegion* region)=0; + + virtual void prepare_display_region()=0; + + virtual void render_frame(const AllAttributesWrapper &initial_state)=0; + virtual void render_scene(Node *root, const ProjectionNode *projnode, + const AllAttributesWrapper &initial_state)=0; + virtual void render_subgraph(RenderTraverser *traverser, + Node *subgraph, const ProjectionNode *projnode, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans)=0; + virtual void render_subgraph(RenderTraverser *traverser, + Node *subgraph, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans)=0; + + INLINE void enable_normals(bool val) { _normals_enabled = val; } + + + // These functions will be queried by the GeomIssuer to determine if + // it should issue normals, texcoords, and/or colors, based on the + // GSG's current state. + virtual bool wants_normals(void) const; + virtual bool wants_texcoords(void) const; + virtual bool wants_colors(void) const; + + virtual void begin_decal(GeomNode *base_geom); + virtual void end_decal(GeomNode *base_geom); + + virtual void reset(); + + void set_state(const NodeAttributes &new_state, bool complete); + INLINE const NodeAttributes &get_state() const; + + RenderBuffer get_render_buffer(int buffer_type); + + INLINE const ProjectionNode* get_current_projection_node(void) const ; + INLINE const Node* get_current_root_node(void) const; + + INLINE CPT(DisplayRegion) get_current_display_region(void) const; + + INLINE DisplayRegionStack push_display_region(CPT(DisplayRegion) dr); + INLINE void pop_display_region(DisplayRegionStack &node); + INLINE FrameBufferStack push_frame_buffer(const RenderBuffer &buffer, + CPT(DisplayRegion) dr); + INLINE void pop_frame_buffer(FrameBufferStack &node); + + INLINE void set_coordinate_system(CoordinateSystem cs); + INLINE CoordinateSystem get_coordinate_system() const; + + // This function may only be called during a render traversal; it + // will compute the distance to the indicated point, assumed to be + // in modelview coordinates, from the camera plane. This is a + // virtual function because different GSG's may define the modelview + // coordinate space differently. + virtual float compute_distance_to(const LPoint3f &point) const=0; + +protected: + virtual PT(SavedFrameBuffer) save_frame_buffer(const RenderBuffer &buffer, + CPT(DisplayRegion) dr)=0; + virtual void restore_frame_buffer(SavedFrameBuffer *frame_buffer)=0; + + bool mark_prepared_texture(TextureContext *tc); + bool unmark_prepared_texture(TextureContext *tc); + +protected: + int _buffer_mask; + NodeAttributes _state; + Colorf _color_clear_value; + float _depth_clear_value; + bool _stencil_clear_value; + Colorf _accum_clear_value; + + int _display_region_stack_level; + int _frame_buffer_stack_level; + + GraphicsWindow *_win; + PT(RenderTraverser) _render_traverser; + + // These must be set by render_scene(). + Node *_current_root_node; + const ProjectionNode *_current_projection_node; + CPT(DisplayRegion) _current_display_region; + + // This is used by wants_normals() + bool _normals_enabled; + + CoordinateSystem _coordinate_system; + +private: + typedef set Textures; + Textures _prepared_textures; + +// factory stuff +public: + typedef Factory GsgFactory; + typedef FactoryParam GsgParam; + + // Make a factory parameter type for the window pointer + class EXPCL_PANDA GsgWindow : public GsgParam { + public: + INLINE GsgWindow(GraphicsWindow* w) : _w(w), GsgParam() {} + virtual ~GsgWindow(void); + INLINE GraphicsWindow* get_window(void) { return _w; } + public: + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type(void); + private: + GraphicsWindow* _w; + static TypeHandle _type_handle; + + INLINE GsgWindow(void) : GsgParam() {} + }; + + static GsgFactory _factory; + +private: + static void read_priorities(void); + +public: + INLINE GraphicsWindow* get_window(void) const { return _win; } + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GraphicsStateGuardianBase::init_type(); + register_type(_type_handle, "GraphicsStateGuardian", + GraphicsStateGuardianBase::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + +friend class GraphicsPipe; +}; + +#include "graphicsStateGuardian.I" + +#endif diff --git a/panda/src/display/graphicsWindow.I b/panda/src/display/graphicsWindow.I new file mode 100644 index 0000000000..3b2adc8cd9 --- /dev/null +++ b/panda/src/display/graphicsWindow.I @@ -0,0 +1,273 @@ +// Filename: graphicsWindow.I +// Created by: frang (07Mar99) +// +//////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_properties +// Access: Public +// Description: Returns the full Properties structure that describes +// the window. +//////////////////////////////////////////////////////////////////// +INLINE const GraphicsWindow::Properties &GraphicsWindow:: +get_properties() const { + return _props; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_width +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int GraphicsWindow:: +get_width() const { + return (_props._xsize); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_height +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int GraphicsWindow:: +get_height() const { + return (_props._ysize); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_xorg +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int GraphicsWindow:: +get_xorg() const { + return (_props._xorg); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_yorg +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int GraphicsWindow:: +get_yorg() const { + return (_props._yorg); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_gsg +// Access: Public +// Description: Returns the GSG that is associated with this window. +// There is a one-to-one association between windows and +// GSG's. +//////////////////////////////////////////////////////////////////// +INLINE GraphicsStateGuardian *GraphicsWindow:: +get_gsg() const { + return _gsg; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_pipe +// Access: Public +// Description: Returns the GraphicsPipe that this window is +// associated with. It is possible that the +// GraphicsPipe might have been deleted while an +// outstanding PT(GraphicsWindow) prevented all of its +// children windows from also being deleted; in this +// unlikely case, get_pipe() may return NULL. +//////////////////////////////////////////////////////////////////// +INLINE GraphicsPipe *GraphicsWindow:: +get_pipe() const { + return _pipe; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_num_input_devices +// Access: Public +// Description: Returns the number of separate input devices +// associated with the window. Typically, a window will +// have exactly one input device: the keyboard/mouse +// pair. However, some windows may have no input +// devices, and others may add additional devices, for +// instance for a joystick. +//////////////////////////////////////////////////////////////////// +INLINE int GraphicsWindow:: +get_num_input_devices() const { + return _input_devices.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_input_device_name +// Access: Public +// Description: Returns the name of the nth input device. +//////////////////////////////////////////////////////////////////// +INLINE string GraphicsWindow:: +get_input_device_name(int device) const { + nassertr(device >= 0 && device < (int)_input_devices.size(), ""); + return _input_devices[device].get_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::has_pointer +// Access: Public +// Description: Returns true if the nth input device has a +// screen-space pointer (for instance, a mouse), false +// otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool GraphicsWindow:: +has_pointer(int device) const { + nassertr(device >= 0 && device < (int)_input_devices.size(), false); + return _input_devices[device].has_pointer(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::has_keyboard +// Access: Public +// Description: Returns true if the nth input device has a keyboard, +// false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool GraphicsWindow:: +has_keyboard(int device) const { + nassertr(device >= 0 && device < (int)_input_devices.size(), false); + return _input_devices[device].has_keyboard(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_mouse_data +// Access: Public +// Description: Returns the MouseData associated with the nth input +// device. +//////////////////////////////////////////////////////////////////// +INLINE const MouseData &GraphicsWindow:: +get_mouse_data(int device) const { + nassertr(device >= 0 && device < (int)_input_devices.size(), + *(new MouseData)); + return _input_devices[device].get_mouse_data(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_modifier_buttons +// Access: Public +// Description: Returns the set of ModifierButtons currently being +// monitored for the nth input device. +//////////////////////////////////////////////////////////////////// +INLINE const ModifierButtons &GraphicsWindow:: +get_modifier_buttons(int device) const { + nassertr(device >= 0 && device < (int)_input_devices.size(), + *(new ModifierButtons)); + return _input_devices[device].get_modifier_buttons(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::set_modifier_buttons +// Access: Public +// Description: Changes the set of ModifierButtons that will be +// monitored for the nth input device. It is +// recommended that you first retrieve the existing set +// via get_modifier_buttons(), so that any current +// button state will not be lost. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindow:: +set_modifier_buttons(int device, const ModifierButtons &mods) { + nassertv(device >= 0 && device < (int)_input_devices.size()); + _input_devices[device].set_modifier_buttons(mods); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::has_button_event +// Access: Public +// Description: Returns true if the indicated device has a pending +// button event (a mouse button or keyboard button +// down/up), false otherwise. If this returns true, the +// particular event may be extracted via +// get_button_event(). +//////////////////////////////////////////////////////////////////// +INLINE bool GraphicsWindow:: +has_button_event(int device) const { + nassertr(device >= 0 && device < (int)_input_devices.size(), false); + return _input_devices[device].has_button_event(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_button_event +// Access: Public +// Description: Assuming a previous call to has_button_event() +// returned true, this returns the pending button event. +//////////////////////////////////////////////////////////////////// +INLINE ButtonEvent GraphicsWindow:: +get_button_event(int device) { + nassertr(has_button_event(device), ButtonEvent()); + return _input_devices[device].get_button_event(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::set_frame_number +// Access: Public +// Description: Sets the current frame number of the window. This +// affects the frame numbers written for %f in a RIB or +// image filename template. The frame number is +// initially zero, and it increments at each call to +// end_frame(). +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindow:: +set_frame_number(const int f) { + _frame_number = f; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_frame_number +// Access: Public +// Description: Returns the current frame number of the window. See +// set_frame_number(). +//////////////////////////////////////////////////////////////////// +INLINE int GraphicsWindow:: +get_frame_number() const { + return _frame_number; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::set_draw_callback +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindow:: +set_draw_callback(Callback *c) { + _draw_callback = c; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::set_idle_callback +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindow:: +set_idle_callback(Callback *c) { + _idle_callback = c; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::call_draw_callback +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindow:: +call_draw_callback(bool force_redraw) { + if (_draw_callback) { + _draw_callback->draw(force_redraw); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::call_idle_callback +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindow:: +call_idle_callback() { + if (_idle_callback) { + _idle_callback->idle(); + } +} + diff --git a/panda/src/display/graphicsWindow.N b/panda/src/display/graphicsWindow.N new file mode 100644 index 0000000000..56a37260ef --- /dev/null +++ b/panda/src/display/graphicsWindow.N @@ -0,0 +1,13 @@ +ignoreconstruct GraphicsWindow +ignoremember get_properties +ignoremember set_draw_callback +ignoremember set_idle_callback +ignoremember call_draw_callback +ignoremember call_idle_callback +ignoremember register_draw_function +ignoremember register_idle_function +ignoremember register_resize_function +ignoremember remove_channel +ignoreinvolved KeyEvent + +forcetype PointerTo diff --git a/panda/src/display/graphicsWindow.cxx b/panda/src/display/graphicsWindow.cxx new file mode 100644 index 0000000000..c97a28e32a --- /dev/null +++ b/panda/src/display/graphicsWindow.cxx @@ -0,0 +1,523 @@ +// Filename: graphicsWindow.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "graphicsWindow.h" +#include "graphicsPipe.h" +#include "config_display.h" + +#include +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle GraphicsWindow::_type_handle; +TypeHandle GraphicsWindow::WindowProps::_type_handle; +TypeHandle GraphicsWindow::WindowPipe::_type_handle; + +GraphicsWindow::WindowFactory GraphicsWindow::_factory; + +PStatCollector GraphicsWindow::_app_pcollector = + PStatCollector("App", RGBColorf(0,1,1)); +PStatCollector GraphicsWindow::_show_code_pcollector = + PStatCollector("App:Show Code", RGBColorf(0.8,0.2,1)); +PStatCollector GraphicsWindow::_swap_pcollector = + PStatCollector("Draw:Swap Buffers", RGBColorf(0.5,1,0.8)); +PStatCollector GraphicsWindow::_make_current_pcollector = + PStatCollector("Draw:Make Current", RGBColorf(1,0.6,0.3)); + + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::Properties::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsWindow::Properties:: +Properties() { + _xorg = 0; + _yorg = 0; + _xsize = 512; + _ysize = 512; + _title = ""; + _border = true; + _fullscreen = false; + _mask = W_RGBA | W_DOUBLE | W_DEPTH; + _want_depth_bits = 1; + _want_color_bits = 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::Callback::draw +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void GraphicsWindow::Callback:: +draw(bool) { + display_cat.error() + << "Callback::draw() - no class defined for this" << endl; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::Callback::idle +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void GraphicsWindow::Callback:: +idle() { + display_cat.error() + << "Callback::idle() - no class defined for this" << endl; +} + +GraphicsWindow::WindowProps::~WindowProps(void) {} + +TypeHandle GraphicsWindow::WindowProps::get_class_type(void) { + return _type_handle; +} + +void GraphicsWindow::WindowProps::init_type(void) { + WindowParam::init_type(); + register_type(_type_handle, "GraphicsWindow::WindowProps", + WindowParam::get_class_type()); +} + +TypeHandle GraphicsWindow::WindowProps::get_type(void) const { + return get_class_type(); +} + +TypeHandle GraphicsWindow::WindowProps::force_init_type(void) { + init_type(); + return get_class_type(); +} + +GraphicsWindow::WindowPipe::~WindowPipe(void) {} + +TypeHandle GraphicsWindow::WindowPipe::get_class_type(void) { + return _type_handle; +} + +void GraphicsWindow::WindowPipe::init_type(void) { + WindowParam::init_type(); + register_type(_type_handle, "GraphicsWindow::WindowPipe", + WindowParam::get_class_type()); +} + +TypeHandle GraphicsWindow::WindowPipe::get_type(void) const { + return get_class_type(); +} + +TypeHandle GraphicsWindow::WindowPipe::force_init_type(void) { + init_type(); + return get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsWindow:: +GraphicsWindow(GraphicsPipe *pipe) : Configurable() { + _pipe = pipe; + + _draw_callback = NULL; + _idle_callback = NULL; + _resize_callback = NULL; + _frame_number = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsWindow:: +GraphicsWindow(GraphicsPipe *pipe, + const GraphicsWindow::Properties& props) : Configurable() { + _pipe = pipe; + _props = props; + + _draw_callback = NULL; + _idle_callback = NULL; + _resize_callback = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsWindow:: +GraphicsWindow(const GraphicsWindow&) { + display_cat.error() + << "GraphicsWindows should not be copied" << endl; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsWindow& GraphicsWindow:: +operator=(const GraphicsWindow&) { + display_cat.error() + << "GraphicsWindows should not be assigned" << endl; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsWindow:: +~GraphicsWindow() { + // We don't have to destruct our child channels explicitly, since + // they are all reference-counted and will go away when their + // pointers do. However, we do need to zero out their pointers to + // us. + Channels::iterator ci; + for (ci = _channels.begin(); ci != _channels.end(); ++ci) { + (*ci)->_window = NULL; + } + + // We don't need to remove ourself from the pipe's list of windows. + // We must have already been removed, or we wouldn't be destructing! +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_channel +// Access: Public +// Description: Returns a GraphicsChannel pointer that can be used to +// access the indicated channel number. All windows +// have at least one channel, channel 0, which +// corresponds to the entire window. If the hardware +// supports it, some kinds of windows may also have a +// number of hardware channels available at indices +// 1..n, which will correspond to a subregion of the +// window. +// +// This function returns a GraphicsChannel pointer if a +// channel is available, or NULL if it is not. If +// called twice with the same index number, it will +// return the same pointer. +//////////////////////////////////////////////////////////////////// +GraphicsChannel *GraphicsWindow:: +get_channel(int index) { + nassertr(index >= 0, NULL); + + if (index < (int)_channels.size()) { + if (_channels[index] != (GraphicsChannel *)NULL) { + return _channels[index]; + } + } + + // This channel has never been requested before; define it. + + PT(GraphicsChannel) chan; + if (index == 0) { + // Channel 0 is the default channel: the entire screen. + chan = new GraphicsChannel(this); + } else { + // Any other channel is some hardware-specific channel. + nassertr(_pipe != NULL, NULL); + chan = _pipe->get_hw_channel(this, index); + if (chan == NULL) { + display_cat.error() + << "GraphicsWindow::get_channel() - got a NULL channel" << endl; + } else { + if (chan->get_window() != this) { + chan = NULL; + } + } + } + + if (chan != (GraphicsChannel *)NULL) { + declare_channel(index, chan); + } + + return chan; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::remove_channel +// Access: Public +// Description: Deletes a GraphicsChannel that was previously created +// via a call to get_channel(). Note that the channel +// is not actually deleted until all pointers to it are +// cleared. +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +remove_channel(int index) { + if (index >= 0 && index < (int)_channels.size()) { + _channels[index].clear(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_max_channel_index +// Access: Public +// Description: Returns the largest channel index number yet created, +// plus 1. All channels associated with this window +// will have an index number in the range [0, +// get_max_channel_index()). This function, in +// conjunction with is_channel_defined(), below, may be +// used to determine the complete set of channels +// associated with the window. +//////////////////////////////////////////////////////////////////// +int GraphicsWindow:: +get_max_channel_index() const { + return _channels.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::is_channel_defined +// Access: Public +// Description: Returns true if the channel with the given index +// number has already been defined, false if it hasn't. +// If this returns true, calling get_channel() on the +// given index number will return the channel pointer. +// If it returns false, calling get_channel() will +// create and return a new channel pointer. +//////////////////////////////////////////////////////////////////// +bool GraphicsWindow:: +is_channel_defined(int index) const { + if (index < 0 || index >= (int)_channels.size()) { + return false; + } + return (_channels[index] != (GraphicsChannel *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::flag_redisplay +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +flag_redisplay() { +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::declare_channel +// Access: Protected +// Description: An internal function to add the indicated +// newly-created channel to the list at the indicated +// channel number. +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +declare_channel(int index, GraphicsChannel *chan) { + nassertv(index >= 0); + if (index >= _channels.size()) { + _channels.reserve(index); + while (index >= _channels.size()) { + _channels.push_back(NULL); + } + } + + nassertv(index < (int)_channels.size()); + _channels[index] = chan; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::register_draw_function +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +register_draw_function(GraphicsWindow::vfn f) { + _draw_function = f; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::register_idle_function +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +register_idle_function(GraphicsWindow::vfn f) { + _idle_function = f; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::register_resize_function +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +register_resize_function(GraphicsWindow::vfnii f) { + _resize_function = f; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::main_loop +// Access: Public, Virtual +// Description: Yields the application over to the window entirely +// for rendering. The window will wait for keyboard and +// mouse input, and repeatedly call update() on itself. +// For some kinds of window API's (notably glut), this +// is the only way to use the window--see +// supports_update(). +// +// This function does not return. +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +main_loop() { + while (true) { + update(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::supports_update +// Access: Public, Virtual +// Description: Returns true if this particular kind of +// GraphicsWindow supports use of the update() function +// to update the graphics one frame at a time, so that +// the window does not need to be the program's main +// loop. Returns false if the only way to update the +// window is to call main_loop(). +//////////////////////////////////////////////////////////////////// +bool GraphicsWindow:: +supports_update() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::update +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +update() { +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::begin_frame +// Access: Public, Virtual +// Description: This function will be called by the GSG before +// beginning processing for a given frame. It should do +// whatever setup is required. +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +begin_frame() { +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::end_frame +// Access: Public, Virtual +// Description: This function will be called by the GSG after +// processing is completed for a given frame. It should +// do whatever finalization is required. +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +end_frame() { + _frame_number++; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::resized +// Access: Public, Virtual +// Description: Called whenever the window gets the resize event. +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +resized(const int x, const int y) { + Channels::iterator ci; + for (ci = _channels.begin(); ci != _channels.end(); ++ci) { + GraphicsChannel *chan = (*ci); + chan->window_resized(x, y); + } + _props._xsize = x; + _props._ysize = y; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::make_scratch_display_region +// Access: Public +// Description: Allocates and returns a temporary DisplayRegion that +// may be used to render offscreen into. This +// DisplayRegion is not associated with any layer. +// +// To allocate a normal DisplayRegion for rendering, use +// the interface provded in GraphicsLayer. +//////////////////////////////////////////////////////////////////// +PT(DisplayRegion) GraphicsWindow:: +make_scratch_display_region(int xsize, int ysize) const { + if (xsize > _props._xsize) { + display_cat.error() + << "GraphicsWindow::make_scratch_display_region() - x size is larger " + << "than window x size" << endl; + xsize = _props._xsize; + } + if (ysize > _props._ysize) { + display_cat.error() + << "GraphicsWindow::make_scratch_display_region() - y size is larger " + << "than window y size" << endl; + ysize = _props._ysize; + } + + return new DisplayRegion(xsize, ysize); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::make_current +// Access: Public, Virtual +// Description: Makes the window's graphics context the currently +// active context that will be next rendered into by the +// GSG, if this makes sense for the particular type of +// GraphicsWindow. +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +make_current(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::unmake_current +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +unmake_current(void) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::make_gsg +// Access: Protected +// Description: Creates a new GSG for the window and stores it in the +// _gsg pointer. +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +make_gsg() { + FactoryParams params; + params.add_param(new GraphicsStateGuardian::GsgWindow(this)); + + _gsg = GraphicsStateGuardian::_factory. + make_instance(get_gsg_type(), params); + + nassertv(_gsg != (GraphicsStateGuardian *)NULL); +} + +void GraphicsWindow::read_priorities(void) { + if (_factory.get_num_preferred() == 0) { + Config::ConfigTable::Symbol::iterator i; + for (i = preferred_window_begin(); i != preferred_window_end(); ++i) { + ConfigString type_name = (*i).Val(); + TypeHandle type = TypeRegistry::ptr()->find_type(type_name); + if (type == TypeHandle::none()) { + display_cat.warning() + << "Unknown type requested for window preference: " << type_name + << "\n"; + } else { + display_cat.debug() + << "Specifying type " << type << " for window preference.\n"; + _factory.add_preferred(type); + } + } + } +} diff --git a/panda/src/display/graphicsWindow.h b/panda/src/display/graphicsWindow.h new file mode 100644 index 0000000000..18a0540aea --- /dev/null +++ b/panda/src/display/graphicsWindow.h @@ -0,0 +1,268 @@ +// Filename: graphicsWindow.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef GRAPHICSWINDOW_H +#define GRAPHICSWINDOW_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "graphicsWindowInputDevice.h" +#include "graphicsChannel.h" +#include "displayRegion.h" +#include "graphicsStateGuardian.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +enum WindowModeType +{ + W_RGBA = 0, + W_RGB = 0, + W_INDEX = 1, + W_SINGLE = 0, + W_DOUBLE = 2, + W_ACCUM = 4, + W_ALPHA = 8, + W_DEPTH = 16, + W_STENCIL = 32, + W_MULTISAMPLE = 128, + W_STEREO = 256, + W_LUMINANCE = 512 +}; + +class GraphicsPipe; +class GraphicsWindow; + +//////////////////////////////////////////////////////////////////// +// Class : GraphicsWindow +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GraphicsWindow : public Configurable, public ReferenceCount { +public: + class EXPCL_PANDA Properties { + public: + Properties(); + public: + int _xorg; + int _yorg; + int _xsize; + int _ysize; + string _title; + bool _border; + bool _fullscreen; + uint _mask; + int _want_depth_bits; + int _want_color_bits; + }; + + class EXPCL_PANDA Callback { + public: + virtual void draw(bool); + virtual void idle(); + }; + typedef void (*vfn)(); + typedef void (*vfnii)(int, int); + +public: + + GraphicsWindow(GraphicsPipe*); +#ifdef WIN32_VC + GraphicsWindow(GraphicsPipe*, const Properties&); +#else + GraphicsWindow(GraphicsPipe*, const GraphicsWindow::Properties&); +#endif + virtual ~GraphicsWindow(); + + INLINE const GraphicsWindow::Properties& get_properties() const; + INLINE int get_width() const; + INLINE int get_height() const; + INLINE int get_xorg() const; + INLINE int get_yorg() const; + + INLINE GraphicsStateGuardian *get_gsg() const; + + INLINE GraphicsPipe *get_pipe() const; + + INLINE void set_frame_number(const int); + INLINE int get_frame_number() const; + + virtual void resized(const int, const int); + + INLINE virtual void set_draw_callback(Callback *c); + INLINE virtual void set_idle_callback(Callback *c); + + INLINE void call_draw_callback(bool force_redraw); + INLINE void call_idle_callback(); + + PT(DisplayRegion) make_scratch_display_region(int xsize, + int ysize) const; + + virtual TypeHandle get_gsg_type() const=0; + +public: + // context setting + virtual void make_current(); + virtual void unmake_current(); + +public: + // Mouse and keyboard routines + INLINE int get_num_input_devices() const; + INLINE string get_input_device_name(int device) const; + INLINE bool has_pointer(int device) const; + INLINE bool has_keyboard(int device) const; + + INLINE const MouseData &get_mouse_data(int device) const; + INLINE const ModifierButtons &get_modifier_buttons(int device) const; + INLINE void set_modifier_buttons(int device, const ModifierButtons &mods); + + INLINE bool has_button_event(int device) const; + INLINE ButtonEvent get_button_event(int device); + +public: + // GUI glue methods + virtual void flag_redisplay(); + virtual void register_draw_function(GraphicsWindow::vfn); + virtual void register_idle_function(GraphicsWindow::vfn); + virtual void register_resize_function(GraphicsWindow::vfnii); + + virtual void main_loop(); + virtual bool supports_update() const; + virtual void update(); + + virtual void begin_frame(); + virtual void end_frame(); + + // Statistics + static PStatCollector _app_pcollector; + static PStatCollector _show_code_pcollector; + static PStatCollector _swap_pcollector; // dxgsg needs access so this is public + static PStatCollector _make_current_pcollector; + +protected: + void make_gsg(); + + typedef vector_GraphicsWindowInputDevice InputDevices; + InputDevices _input_devices; + + PT(GraphicsStateGuardian) _gsg; + Properties _props; + + GraphicsPipe *_pipe; + vfn _draw_function; + vfn _idle_function; + vfnii _resize_function; + int _frame_number; + +protected: + + Callback *_draw_callback; + Callback *_idle_callback; + Callback *_resize_callback; + +public: + virtual GraphicsChannel *get_channel(int index); + void remove_channel(int index); + + int get_max_channel_index() const; + bool is_channel_defined(int index) const; + +protected: + void declare_channel(int index, GraphicsChannel *chan); + +private: + typedef vector Channels; + Channels _channels; + +public: + + // factory stuff + typedef Factory WindowFactory; + typedef FactoryParam WindowParam; + + // make a factory parameter type for the window properties + class EXPCL_PANDA WindowProps : public FactoryParam { + public: + INLINE WindowProps(void) : WindowParam() {} + INLINE WindowProps(const Properties& p) : _p(p), WindowParam() {} + virtual ~WindowProps(void); + INLINE Properties get_properties(void) { return _p; } + public: + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type(void); + private: + Properties _p; + static TypeHandle _type_handle; + }; + // make a factory parameter type for the GraphicsPipe* + class EXPCL_PANDA WindowPipe : public FactoryParam { + public: + INLINE WindowPipe(GraphicsPipe* p) : _p(p), WindowParam() {} + virtual ~WindowPipe(void); + INLINE GraphicsPipe* get_pipe(void) { return _p; } + public: + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type(void); + private: + GraphicsPipe* _p; + static TypeHandle _type_handle; + + INLINE WindowPipe(void) : WindowParam() {} + }; + + static WindowFactory _factory; + +private: + + static void read_priorities(void); + + GraphicsWindow(const GraphicsWindow&); + GraphicsWindow &operator=(const GraphicsWindow&); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Configurable::init_type(); + ReferenceCount::init_type(); + register_type(_type_handle, "GraphicsWindow", + Configurable::get_class_type(), + ReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend class GraphicsPipe; +}; + +#include "graphicsWindow.I" + +#endif /* GRAPHICSWINDOW_H */ diff --git a/panda/src/display/graphicsWindowInputDevice.I b/panda/src/display/graphicsWindowInputDevice.I new file mode 100644 index 0000000000..d054936b89 --- /dev/null +++ b/panda/src/display/graphicsWindowInputDevice.I @@ -0,0 +1,126 @@ +// Filename: graphicsWindowInputDevice.I +// Created by: drose (24May00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::get_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE string GraphicsWindowInputDevice:: +get_name() const { + return _name; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::has_pointer +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool GraphicsWindowInputDevice:: +has_pointer() const { + return ((_flags & IDF_has_pointer) != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::has_keyboard +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool GraphicsWindowInputDevice:: +has_keyboard() const { + return ((_flags & IDF_has_keyboard) != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::get_mouse_data +// Access: Public +// Description: Returns the MouseData associated with the nth input +// device. +//////////////////////////////////////////////////////////////////// +INLINE const MouseData &GraphicsWindowInputDevice:: +get_mouse_data() const { + return _mouse_data; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::get_modifier_buttons +// Access: Public +// Description: Returns the set of ModifierButtons currently being +// monitored for the nth input device. +//////////////////////////////////////////////////////////////////// +INLINE const ModifierButtons &GraphicsWindowInputDevice:: +get_modifier_buttons() const { + return _mods; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::set_modifier_buttons +// Access: Public +// Description: Changes the set of ModifierButtons that will be +// monitored for the nth input device. It is +// recommended that you first retrieve the existing set +// via get_modifier_buttons(), so that any current +// button state will not be lost. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindowInputDevice:: +set_modifier_buttons(const ModifierButtons &mods) { + _mods = mods; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::set_pointer_in_window +// Access: Public +// Description: To be called by a particular kind of GraphicsWindow +// to indicate that the pointer is within the window, at +// the given pixel coordinates. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindowInputDevice:: +set_pointer_in_window(int x, int y) { + _mouse_data._in_window = true; + _mouse_data._xpos = x; + _mouse_data._ypos = y; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::set_pointer_out_of_window +// Access: Public +// Description: To be called by a particular kind of GraphicsWindow +// to indicate that the pointer is no longer within the +// window. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindowInputDevice:: +set_pointer_out_of_window() { + _mouse_data._in_window = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::operator == +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool GraphicsWindowInputDevice:: +operator == (const GraphicsWindowInputDevice &) const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::operator != +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool GraphicsWindowInputDevice:: +operator != (const GraphicsWindowInputDevice &) const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::operator < +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool GraphicsWindowInputDevice:: +operator < (const GraphicsWindowInputDevice &) const { + return false; +} diff --git a/panda/src/display/graphicsWindowInputDevice.cxx b/panda/src/display/graphicsWindowInputDevice.cxx new file mode 100644 index 0000000000..2f38e16dc5 --- /dev/null +++ b/panda/src/display/graphicsWindowInputDevice.cxx @@ -0,0 +1,163 @@ +// Filename: graphicsWindowInputDevice.cxx +// Created by: drose (24May00) +// +//////////////////////////////////////////////////////////////////// + +#include "graphicsWindowInputDevice.h" +#include +#include + + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::Constructor +// Access: Private +// Description: Defines a new InputDevice for the window. Most +// windows will have exactly one InputDevice: a +// keyboard/mouse pair. Some may also add joystick +// data, or additional mice or something. +// +// This private constructor is only used internally by +// the named constructors, below. +//////////////////////////////////////////////////////////////////// +GraphicsWindowInputDevice:: +GraphicsWindowInputDevice(const string &name, int flags) : + _name(name), + _flags(flags) +{ + // We'll define the mouse buttons and the traditional modifier keys + // as the default modifiers. Individual GraphicsWindows can change + // this if they want. + + _mods.add_button(MouseButton::one()); + _mods.add_button(MouseButton::two()); + _mods.add_button(MouseButton::three()); + _mods.add_button(KeyboardButton::shift()); + _mods.add_button(KeyboardButton::control()); + _mods.add_button(KeyboardButton::alt()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::pointer_only +// Access: Public +// Description: This named constructor returns an input device that +// only has a pointing device, no keyboard. +//////////////////////////////////////////////////////////////////// +GraphicsWindowInputDevice GraphicsWindowInputDevice:: +pointer_only(const string &name) { + return GraphicsWindowInputDevice(name, IDF_has_pointer); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::keyboard_only +// Access: Public +// Description: This named constructor returns an input device that +// only has a keyboard, no pointing device. +//////////////////////////////////////////////////////////////////// +GraphicsWindowInputDevice GraphicsWindowInputDevice:: +keyboard_only(const string &name) { + return GraphicsWindowInputDevice(name, IDF_has_keyboard); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::pointer_and_keyboard +// Access: Public +// Description: This named constructor returns an input device that +// has both a keyboard and pointer. +//////////////////////////////////////////////////////////////////// +GraphicsWindowInputDevice GraphicsWindowInputDevice:: +pointer_and_keyboard(const string &name) { + return + GraphicsWindowInputDevice(name, IDF_has_pointer | IDF_has_keyboard); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsWindowInputDevice:: +GraphicsWindowInputDevice(const GraphicsWindowInputDevice ©) : + _name(copy._name), + _flags(copy._flags), + _mouse_data(copy._mouse_data), + _mods(copy._mods), + _button_events(copy._button_events) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void GraphicsWindowInputDevice:: +operator = (const GraphicsWindowInputDevice ©) { + _name = copy._name; + _flags = copy._flags; + _mouse_data = copy._mouse_data; + _mods = copy._mods; + _button_events = copy._button_events; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphicsWindowInputDevice:: +~GraphicsWindowInputDevice() { +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::has_button_event +// Access: Public +// Description: Returns true if this device has a pending button +// event (a mouse button or keyboard button down/up), +// false otherwise. If this returns true, the +// particular event may be extracted via +// get_button_event(). +//////////////////////////////////////////////////////////////////// +bool GraphicsWindowInputDevice:: +has_button_event() const { + return !_button_events.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::get_button_event +// Access: Public +// Description: Assuming a previous call to has_button_event() +// returned true, this returns the pending button event. +//////////////////////////////////////////////////////////////////// +ButtonEvent GraphicsWindowInputDevice:: +get_button_event() { + ButtonEvent be = _button_events.front(); + _button_events.pop_front(); + return be; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::button_down +// Access: Public +// Description: Records that the indicated button has been depressed. +//////////////////////////////////////////////////////////////////// +void GraphicsWindowInputDevice:: +button_down(ButtonHandle button) { + _button_events.push_back(ButtonEvent(button, true, _mods)); + _mods.button_down(button); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::button_up +// Access: Public +// Description: Records that the indicated button has been released. +//////////////////////////////////////////////////////////////////// +void GraphicsWindowInputDevice:: +button_up(ButtonHandle button) { + _mods.button_up(button); + _button_events.push_back(ButtonEvent(button, false, _mods)); +} diff --git a/panda/src/display/graphicsWindowInputDevice.h b/panda/src/display/graphicsWindowInputDevice.h new file mode 100644 index 0000000000..71f04aae44 --- /dev/null +++ b/panda/src/display/graphicsWindowInputDevice.h @@ -0,0 +1,90 @@ +// Filename: graphicsWindowInputDevice.h +// Created by: drose (24May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GRAPHICSWINDOWINPUTDEVICE_H +#define GRAPHICSWINDOWINPUTDEVICE_H + +#include + +#include +#include +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : GraphicsWindowInputDevice +// Description : This is a structure representing a single input +// device that may be associated with a window. +// Typically this will be a keyboard/mouse pair, and +// there will be exactly one of these associated with +// each window, but other variants are possible. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GraphicsWindowInputDevice { +private: + GraphicsWindowInputDevice(const string &name, int flags); + +public: + static GraphicsWindowInputDevice pointer_only(const string &name); + static GraphicsWindowInputDevice keyboard_only(const string &name); + static GraphicsWindowInputDevice pointer_and_keyboard(const string &name); + + GraphicsWindowInputDevice(const GraphicsWindowInputDevice ©); + void operator = (const GraphicsWindowInputDevice ©); + ~GraphicsWindowInputDevice(); + + INLINE string get_name() const; + INLINE bool has_pointer() const; + INLINE bool has_keyboard() const; + + INLINE const MouseData &get_mouse_data() const; + INLINE const ModifierButtons &get_modifier_buttons() const; + INLINE void set_modifier_buttons(const ModifierButtons &mods); + + bool has_button_event() const; + ButtonEvent get_button_event(); + +public: + // The following interface is for the various kinds of + // GraphicsWindows to record the data incoming on the device. + void button_down(ButtonHandle button); + void button_up(ButtonHandle button); + INLINE void set_pointer_in_window(int x, int y); + INLINE void set_pointer_out_of_window(); + +public: + // We need these methods to make VC++ happy when we try to + // instantiate a vector. They don't do + // anything useful. + INLINE bool operator == (const GraphicsWindowInputDevice &other) const; + INLINE bool operator != (const GraphicsWindowInputDevice &other) const; + INLINE bool operator < (const GraphicsWindowInputDevice &other) const; + +private: + enum InputDeviceFlags { + IDF_has_pointer = 0x01, + IDF_has_keyboard = 0x02 + }; + typedef deque ButtonEvents; + + string _name; + int _flags; + MouseData _mouse_data; + ModifierButtons _mods; + ButtonEvents _button_events; +}; + +#include "graphicsWindowInputDevice.I" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, std::vector) +typedef vector vector_GraphicsWindowInputDevice; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/display/hardwareChannel.I b/panda/src/display/hardwareChannel.I new file mode 100644 index 0000000000..c30a8606b3 --- /dev/null +++ b/panda/src/display/hardwareChannel.I @@ -0,0 +1,54 @@ +// Filename: graphicsChannel.I +// Created by: mike (04Feb99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: get_id +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int HardwareChannel::get_id( void ) const +{ + return _id; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_xorg +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int HardwareChannel::get_xorg( void ) const +{ + return _xorg; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_yorg +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int HardwareChannel::get_yorg( void ) const +{ + return _yorg; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_xsize +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int HardwareChannel::get_xsize( void ) const +{ + return _xsize; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_ysize +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int HardwareChannel::get_ysize( void ) const +{ + return _ysize; +} diff --git a/panda/src/display/hardwareChannel.cxx b/panda/src/display/hardwareChannel.cxx new file mode 100644 index 0000000000..f9a79bf40a --- /dev/null +++ b/panda/src/display/hardwareChannel.cxx @@ -0,0 +1,55 @@ +// Filename: hardwareChannel.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "hardwareChannel.h" +#include "config_display.h" + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle HardwareChannel::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: HardwareChannel::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +HardwareChannel:: +HardwareChannel( GraphicsWindow* window ) : + GraphicsChannel( window ) { + _id = 0; + _xorg = 0; + _yorg = 0; + _xsize = 0; + _ysize = 0; + + // Why do we do this? + set_active(false); +} + +//////////////////////////////////////////////////////////////////// +// Function: HardwareChannel::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +HardwareChannel:: +~HardwareChannel(void) { + return; +} + +//////////////////////////////////////////////////////////////////// +// Function: HardwareChannel::window_resized +// Access: Public, Virtual +// Description: This is called whenever the parent window has been +// resized; it should do whatever needs to be done to +// adjust the channel to account for it. +//////////////////////////////////////////////////////////////////// +void HardwareChannel:: +window_resized(int, int) { + // A HardwareChannel ignores window resize messages. +} diff --git a/panda/src/display/hardwareChannel.h b/panda/src/display/hardwareChannel.h new file mode 100644 index 0000000000..3c428fb874 --- /dev/null +++ b/panda/src/display/hardwareChannel.h @@ -0,0 +1,72 @@ +// Filename: hardwareChannel.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef HARDWARECHANNEL_H +#define HARDWARECHANNEL_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "graphicsChannel.h" + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : HardwareChannel +// Description : Video output channels if available on the current +// platform +// NOTE: hardware channels belong to a pipe rather +// than to a particular window +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA HardwareChannel : public GraphicsChannel +{ + public: + + HardwareChannel( GraphicsWindow* window ); + ~HardwareChannel( void ); + + virtual void window_resized(int x, int y); + + INLINE int get_id( void ) const; + INLINE int get_xorg( void ) const; + INLINE int get_yorg( void ) const; + INLINE int get_xsize( void ) const; + INLINE int get_ysize( void ) const; + + protected: + + int _id; + int _xorg; + int _yorg; + int _xsize; + int _ysize; + + public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GraphicsChannel::init_type(); + register_type(_type_handle, "HardwareChannel", + GraphicsChannel::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + + private: + + static TypeHandle _type_handle; +}; + +#include "hardwareChannel.I" + +#endif diff --git a/panda/src/display/interactiveGraphicsPipe.I b/panda/src/display/interactiveGraphicsPipe.I new file mode 100644 index 0000000000..76d7f3b1ba --- /dev/null +++ b/panda/src/display/interactiveGraphicsPipe.I @@ -0,0 +1,5 @@ +// Filename: interactiveGraphicsPipe.I +// Created by: cary (10Mar99) +// +//////////////////////////////////////////////////////////////////// + diff --git a/panda/src/display/interactiveGraphicsPipe.cxx b/panda/src/display/interactiveGraphicsPipe.cxx new file mode 100644 index 0000000000..8ea23c3090 --- /dev/null +++ b/panda/src/display/interactiveGraphicsPipe.cxx @@ -0,0 +1,47 @@ +// Filename: interativeGraphicsPipe.cxx +// Created by: cary (10Mar99) +// +//////////////////////////////////////////////////////////////////// + +#include "interactiveGraphicsPipe.h" +#include "config_display.h" + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle InteractiveGraphicsPipe::_type_handle; + +InteractiveGraphicsPipe::InteractiveGraphicsPipe(const PipeSpecifier& spec) + : GraphicsPipe(spec) {} + +InteractiveGraphicsPipe::InteractiveGraphicsPipe(void) { + display_cat.error() + << "InteractiveGraphicsPipe should not be created with default constructor" << endl; +} + +InteractiveGraphicsPipe::InteractiveGraphicsPipe(const InteractiveGraphicsPipe&) { + display_cat.error() + << "InteractiveGraphicsPipes should not be copied" << endl; +} + +InteractiveGraphicsPipe& InteractiveGraphicsPipe::operator=(const InteractiveGraphicsPipe&) { + display_cat.error() + << "InteractiveGraphicsPipes should not be assigned" << endl; + return *this; +} + +InteractiveGraphicsPipe::~InteractiveGraphicsPipe(void) {} + +TypeHandle InteractiveGraphicsPipe::get_class_type(void) { + return _type_handle; +} + +void InteractiveGraphicsPipe::init_type(void) { + GraphicsPipe::init_type(); + register_type(_type_handle, "InteractiveGraphicsPipe", + GraphicsPipe::get_class_type()); +} + +TypeHandle InteractiveGraphicsPipe::get_type(void) const { + return get_class_type(); +} diff --git a/panda/src/display/interactiveGraphicsPipe.h b/panda/src/display/interactiveGraphicsPipe.h new file mode 100644 index 0000000000..7e0d38a3cc --- /dev/null +++ b/panda/src/display/interactiveGraphicsPipe.h @@ -0,0 +1,39 @@ +// Filename: interactiveGraphicsPipe.h +// Created by: cary (10Mar99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __INTERACTIVEGRAPHICSPIPE_H__ +#define __INTERACTIVEGRAPHICSPIPE_H__ + +#include + +#include "graphicsPipe.h" + +class EXPCL_PANDA InteractiveGraphicsPipe : public GraphicsPipe { +public: + + InteractiveGraphicsPipe( const PipeSpecifier& ); + virtual ~InteractiveGraphicsPipe(void) = 0; + +public: + + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + + static TypeHandle _type_handle; + +protected: + + InteractiveGraphicsPipe(void); + InteractiveGraphicsPipe(const InteractiveGraphicsPipe&); + InteractiveGraphicsPipe& operator=(const InteractiveGraphicsPipe&); +}; + +#include "interactiveGraphicsPipe.I" + +#endif /* __INTERACTIVEGRAPHICSPIPE_H__ */ diff --git a/panda/src/display/noninteractiveGraphicsPipe.I b/panda/src/display/noninteractiveGraphicsPipe.I new file mode 100644 index 0000000000..60663f4266 --- /dev/null +++ b/panda/src/display/noninteractiveGraphicsPipe.I @@ -0,0 +1,4 @@ +// Filename: noninteractiveGraphicsPipe.I +// Created by: cary (10Mar99) +// +//////////////////////////////////////////////////////////////////// diff --git a/panda/src/display/noninteractiveGraphicsPipe.cxx b/panda/src/display/noninteractiveGraphicsPipe.cxx new file mode 100644 index 0000000000..6260941419 --- /dev/null +++ b/panda/src/display/noninteractiveGraphicsPipe.cxx @@ -0,0 +1,47 @@ +// Filename: noninteractiveGraphicsPipe.cxx +// Created by: cary (10Mar99) +// +//////////////////////////////////////////////////////////////////// + +#include "noninteractiveGraphicsPipe.h" +#include "config_display.h" + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle NoninteractiveGraphicsPipe::_type_handle; + +NoninteractiveGraphicsPipe::NoninteractiveGraphicsPipe(const PipeSpecifier& spec) + : GraphicsPipe(spec) {} + +NoninteractiveGraphicsPipe::~NoninteractiveGraphicsPipe(void) {} + +NoninteractiveGraphicsPipe::NoninteractiveGraphicsPipe(void) { + display_cat.error() + << "NoninteractiveGraphicsPipe should not be created with default constructor" << endl; +} + +NoninteractiveGraphicsPipe::NoninteractiveGraphicsPipe(const NoninteractiveGraphicsPipe&) { + display_cat.error() + << "NoninteractiveGraphicsPipes should not be copied" << endl; +} + +NoninteractiveGraphicsPipe& NoninteractiveGraphicsPipe::operator=(const NoninteractiveGraphicsPipe&) { + display_cat.error() + << "NoninteractiveGraphicsPipes should not be assigned" << endl; + return *this; +} + +TypeHandle NoninteractiveGraphicsPipe::get_class_type(void) { + return _type_handle; +} + +void NoninteractiveGraphicsPipe::init_type(void) { + GraphicsPipe::init_type(); + register_type(_type_handle, "NoninteractiveGraphicsPipe", + GraphicsPipe::get_class_type()); +} + +TypeHandle NoninteractiveGraphicsPipe::get_type(void) const { + return get_class_type(); +} diff --git a/panda/src/display/noninteractiveGraphicsPipe.h b/panda/src/display/noninteractiveGraphicsPipe.h new file mode 100644 index 0000000000..20c271641e --- /dev/null +++ b/panda/src/display/noninteractiveGraphicsPipe.h @@ -0,0 +1,39 @@ +// Filename: noninteractiveGraphicsPipe.h +// Created by: cary (10Mar99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __NONINTERACTIVEGRAPHICSPIPE_H__ +#define __NONINTERACTIVEGRAPHICSPIPE_H__ + +#include + +#include "graphicsPipe.h" + +class EXPCL_PANDA NoninteractiveGraphicsPipe : public GraphicsPipe { +public: + + NoninteractiveGraphicsPipe( const PipeSpecifier& ); + virtual ~NoninteractiveGraphicsPipe(void) = 0; + +public: + + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + + static TypeHandle _type_handle; + +protected: + + NoninteractiveGraphicsPipe(void); + NoninteractiveGraphicsPipe(const NoninteractiveGraphicsPipe&); + NoninteractiveGraphicsPipe& operator=(const NoninteractiveGraphicsPipe&); +}; + +#include "noninteractiveGraphicsPipe.I" + +#endif /* __NONINTERACTIVEGRAPHICSPIPE_H__ */ diff --git a/panda/src/display/pipeSpec.I b/panda/src/display/pipeSpec.I new file mode 100644 index 0000000000..235fc67ca0 --- /dev/null +++ b/panda/src/display/pipeSpec.I @@ -0,0 +1,65 @@ +// Filename: pipeSpec.I +// Created by: frang (07Mar99) +// +//////////////////////////////////////////////////////////////////// + +INLINE void PipeSpecifier::set_name(const std::string& name) { + _name = name; +} + +INLINE std::string PipeSpecifier::get_name(void) const { + return _name; +} + +INLINE void PipeSpecifier::unset_machine(void) { + if (!_is_file) { + _is_file = true; + _machine = ""; + _is_remote = false; + } +} + +INLINE void PipeSpecifier::unset_file(void) { + if (_is_file) { + _is_file = false; + _filename = ""; + } +} + +INLINE void PipeSpecifier::set_machine_name(const std::string& name) { + _machine = name; + if (_machine.empty()) + _is_remote = false; + else + _is_remote = true; + unset_file(); +} + +INLINE std::string PipeSpecifier::get_machine_name(void) const { + return _machine; +} + +INLINE void PipeSpecifier::set_file_name(const std::string& name) { + _filename = name; + unset_machine(); +} + +INLINE std::string PipeSpecifier::get_file_name(void) const { + return _filename; +} + +INLINE void PipeSpecifier::set_pipe_number(const int p) { + _pipe_number = p; +} + +INLINE int PipeSpecifier::get_pipe_number(void) const { + return _pipe_number; +} + +INLINE bool PipeSpecifier::is_file(void) const { + return _is_file; +} + +INLINE bool PipeSpecifier::is_remote(void) const { + return _is_remote; +} diff --git a/panda/src/display/pipeSpec.cxx b/panda/src/display/pipeSpec.cxx new file mode 100644 index 0000000000..23bacfdbf2 --- /dev/null +++ b/panda/src/display/pipeSpec.cxx @@ -0,0 +1,36 @@ +// Filename: pipeSpec.cxx +// Created by: frang (07Mar99) +// +//////////////////////////////////////////////////////////////////// + +#include "pipeSpec.h" +#include "config_display.h" + +PipeSpecifier::PipeSpecifier(void) + : _machine(pipe_spec_machine), + _filename(pipe_spec_filename), + _pipe_number(pipe_spec_pipe_number), + _is_file(pipe_spec_is_file), + _is_remote(pipe_spec_is_remote) {} + +PipeSpecifier::PipeSpecifier(const PipeSpecifier& c) + : _name(c._name), _machine(c._machine), _filename(c._filename), + _pipe_number(c._pipe_number), _is_file(c._is_file), + _is_remote(c._is_remote) {} + +PipeSpecifier::~PipeSpecifier(void) {} + +std::string PipeSpecifier::get_X_specifier(void) const { + std::string ret; + + if (!_is_file) { + if (getenv("DISPLAY")) { + ret = getenv("DISPLAY"); + } else { + ostringstream ss; + ss << _machine << ":" << ((_pipe_number<0)?0:_pipe_number) << ".0"; + ret = ss.str(); + } + } + return ret; +} diff --git a/panda/src/display/pipeSpec.h b/panda/src/display/pipeSpec.h new file mode 100644 index 0000000000..facabd5ae1 --- /dev/null +++ b/panda/src/display/pipeSpec.h @@ -0,0 +1,47 @@ +// Filename: pipeSpec.h +// Created by: frang (07Mar99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __PIPESPEC_H__ +#define __PIPESPEC_H__ + +#include + +#include + +class EXPCL_PANDA PipeSpecifier { +public: + PipeSpecifier(void); + PipeSpecifier(const PipeSpecifier&); + ~PipeSpecifier(void); + + INLINE void set_name(const std::string&); + INLINE std::string get_name(void) const; + INLINE void set_machine_name(const std::string&); + INLINE std::string get_machine_name(void) const; + INLINE void set_file_name(const std::string&); + INLINE std::string get_file_name(void) const; + INLINE void set_pipe_number(const int); + INLINE int get_pipe_number(void) const; + + INLINE bool is_file(void) const; + INLINE bool is_remote(void) const; + + std::string get_X_specifier(void) const; + +private: + std::string _name; + std::string _machine; + std::string _filename; + int _pipe_number; + bool _is_file; + bool _is_remote; + + INLINE void unset_machine(void); + INLINE void unset_file(void); +}; + +#include "pipeSpec.I" + +#endif /* __PIPESPEC_H__ */ diff --git a/panda/src/display/renderBuffer.h b/panda/src/display/renderBuffer.h new file mode 100644 index 0000000000..21d1859d58 --- /dev/null +++ b/panda/src/display/renderBuffer.h @@ -0,0 +1,48 @@ +// Filename: renderBuffer.h +// Created by: drose (02Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef RENDERBUFFER_H +#define RENDERBUFFER_H + + +class GraphicsStateGuardian; + +//////////////////////////////////////////////////////////////////// +// Class : RenderBuffer +// Description : A RenderBuffer is an arbitrary subset of the various +// layers (depth buffer, color buffer, etc.) of a +// drawing region. It consists of a +// GraphicsStateGuardian pointer, along with a bitmask +// of the layers we're interested in. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA RenderBuffer { +public: + enum Type { + T_front_left = 0x0001, + T_back_left = 0x0002, + T_front_right = 0x0004, + T_back_right = 0x0008, + + T_front = 0x0005, + T_back = 0x000a, + T_left = 0x0003, + T_right = 0x000c, + + T_color = 0x000f, + + T_depth = 0x0010, + T_stencil = 0x0020, + T_accum = 0x0040, + }; + + + RenderBuffer(GraphicsStateGuardian *gsg, int buffer_type) + : _gsg(gsg), _buffer_type(buffer_type) { } + + GraphicsStateGuardian *_gsg; + int _buffer_type; +}; + +#endif diff --git a/panda/src/display/savedFrameBuffer.I b/panda/src/display/savedFrameBuffer.I new file mode 100644 index 0000000000..84810f70aa --- /dev/null +++ b/panda/src/display/savedFrameBuffer.I @@ -0,0 +1,15 @@ +// Filename: savedFrameBuffer.I +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: SavedFrameBuffer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SavedFrameBuffer:: +SavedFrameBuffer(const RenderBuffer &buffer, CPT(DisplayRegion) dr) : + _buffer(buffer), _display_region(dr) +{ +} diff --git a/panda/src/display/savedFrameBuffer.cxx b/panda/src/display/savedFrameBuffer.cxx new file mode 100644 index 0000000000..4f9526f95d --- /dev/null +++ b/panda/src/display/savedFrameBuffer.cxx @@ -0,0 +1,8 @@ +// Filename: savedFrameBuffer.cxx +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "savedFrameBuffer.h" + +TypeHandle SavedFrameBuffer::_type_handle; diff --git a/panda/src/display/savedFrameBuffer.h b/panda/src/display/savedFrameBuffer.h new file mode 100644 index 0000000000..bffc612e65 --- /dev/null +++ b/panda/src/display/savedFrameBuffer.h @@ -0,0 +1,67 @@ +// Filename: savedFrameBuffer.h +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SAVEDFRAMEBUFFER_H +#define SAVEDFRAMEBUFFER_H + +#include + +#include "renderBuffer.h" +#include "displayRegion.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : SavedFrameBuffer +// Description : Occasionally we need to save the contents of the +// frame buffer for restoring later, particularly when +// we are doing multipass effects. The precise form in +// which the frame buffer is optimally saved may vary +// from one platform to another; hence, we have the +// SavedFrameBuffer class, which is a placeholder +// structure to store the frame buffer in whichever way +// a particular GSG would prefer to do it. +// +// Each specific GSG will also derive a new kind of +// SavedFrameBuffer object that it will use to store its +// frame buffer contents meaningfully. +// +// This class is not meant to be used directly; it is +// used within the FrameBufferStack class to support +// GraphicsStateGuardian::push_frame_buffer() and +// pop_frame_buffer(). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA SavedFrameBuffer : public TypedReferenceCount { +public: + INLINE SavedFrameBuffer(const RenderBuffer &buffer, + CPT(DisplayRegion) dr); + + RenderBuffer _buffer; + CPT(DisplayRegion) _display_region; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "SavedFrameBuffer", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "savedFrameBuffer.I" + +#endif + diff --git a/panda/src/display/test_display.cxx b/panda/src/display/test_display.cxx new file mode 100644 index 0000000000..d371aac816 --- /dev/null +++ b/panda/src/display/test_display.cxx @@ -0,0 +1,10 @@ +// Filename: test_display.cxx +// Created by: shochet (02Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "graphicsWindow.h" + +int main() { + return 0; +} diff --git a/panda/src/display/textureContext.I b/panda/src/display/textureContext.I new file mode 100644 index 0000000000..9c6137842d --- /dev/null +++ b/panda/src/display/textureContext.I @@ -0,0 +1,16 @@ +// Filename: textureContext.I +// Created by: drose (07Oct99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: TextureContext::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TextureContext:: +TextureContext(Texture *tex) : + _texture(tex) +{ +} diff --git a/panda/src/display/textureContext.cxx b/panda/src/display/textureContext.cxx new file mode 100644 index 0000000000..59cc2f9151 --- /dev/null +++ b/panda/src/display/textureContext.cxx @@ -0,0 +1,8 @@ +// Filename: textureContext.cxx +// Created by: drose (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "textureContext.h" + +TypeHandle TextureContext::_type_handle; diff --git a/panda/src/display/textureContext.h b/panda/src/display/textureContext.h new file mode 100644 index 0000000000..840211c9a1 --- /dev/null +++ b/panda/src/display/textureContext.h @@ -0,0 +1,58 @@ +// Filename: textureContext.h +// Created by: drose (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TEXTURECONTEXT_H +#define TEXTURECONTEXT_H + +#include + +#include + +class Texture; + +//////////////////////////////////////////////////////////////////// +// Class : TextureContext +// Description : This is a special class object that holds all the +// information returned by a particular GSG to indicate +// the texture's internal context identifier. +// +// Textures typically have an immediate-mode and a +// retained-mode operation. When using textures in +// retained-mode (in response to +// Texture::prepare_texture()), the GSG will create some +// internal handle for the texture and store it here. +// The texture stores all of these handles internally. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TextureContext : public TypedObject { +public: + INLINE TextureContext(Texture *tex); + + // This cannot be a PT(Texture), because the texture and the GSG + // both own their TextureContexts! That would create a circular + // reference count. + Texture *_texture; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedObject::init_type(); + register_type(_type_handle, "TextureContext", + TypedObject::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "textureContext.I" + +#endif + diff --git a/panda/src/doc/Panda_Whitepaper.doc b/panda/src/doc/Panda_Whitepaper.doc new file mode 100644 index 0000000000000000000000000000000000000000..d9b94688f48b25610e0dc0950b0f7d847a94b6a3 GIT binary patch literal 581632 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ȁ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
<;uS9sA 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�f}b(maD(D z);F@TY|l8du2GUq_50le3}K>8H4ti1-dPdrQ*D0}i@k3inILRXtBIa{&CG`Y9uJZI z*7;}zf#KM^w+PhR`c7XS3@)>{kyP7k{Lq17orq7;Q50P?pi-g5HXELsF{nizl>;68n# z|H0DgP@$pY!1jIxuB{INGzT;;i4#y&-4I>{K%LF{W> zOn710&`uDe)ShA(vLjfN*>aAW=QT`D$k@PlEku-29A<>z#l-Eaq9%MLpr6wP{?5I4 z!O1Cbdgy?5dE3A6j&l3%Qf}tq_X36_;_b`J?k^W)IOmV|Sp3O7%x4W}b`-@!N@j01 zoJ*o!%lKW;eLO%XfNo@sHm+FLEF1B;naNHB^5N`YdvY!E4||0P^9kxWprZInf?760=v}cS zN|rU>Rd5)LY()n&5j~--hI6J8fs<$t9y$fRN-1M;m=uf6Q$p{)&HpoZw{ z|3TUYT6FEaD{ghq9$FgDaQ_A$bhJeMR01n``=jUE^(0xJ$F3s!&OlyHYWw1nLGw2YI&5 zrSUj3!uTUjKsJLkUz4WkFNB}(qlgL&G+$62_y-9C>UTxz1*|a&i>^fvd(i2g!_7NR zUbQVBY>74izt>)Jz`2&a#yNF~cKCy2Nc1=fx!pVba!+X+1=4ZYv%Bp|oA-itwXX7a zY3O0#Wfu+nERf|qTM$`YEP&k-ls%z41$S;%xo)f$Tpv0|BtI`p4)uTM8{0E=cl+sH zzHLcw-sY{Y&Hjdre~z!%hDc1w!K0VTUAC(AJ$ksX$-z|+In75K$G4xpvs68-b~-uE z)@k>_smz|fdmOw(=t^Fb{TNN3xK;19*%S$#iV=yS94(DII!Oopn44#5ZKK6Vw zo+ovD@YVg9q94wE&%?lq{==2pMtq!r_Fygql4?_|P3VwUqvp zS5WUX@@+jRtO0^qM4KM^>p~kJh@a5}5y6Z1`h;XPgJ>fqw*iC=ppB2Tj}P{)otc*3?H%uHUCE;- zRg%ERSn~MJ2=}`_pVxhhf@W!tnz}c2-uFQK+{-Myoi~yW?ze^b?Js!T{au--1W#4H z{iDA@SfG5-6+tY_0l{}o+qBiOVetBWO~8RA)L{Q=TX*D*R(%1qK;;2d`wf(JbPJ=a zE5)vVx#=82yQrB}Gq;#q+K4H^mxrSgKdjD<34;jr^SrWnEu~JsffS7NO0+t{?s~y0 z@r;AxX;mT-Bc+`(!zv8ao)ojZvYIx8fiKmGtNctipQN}Ff3vKi?+?;XSun47^vA)+ zM3~^$XgxDhp?y_!bsu3`!Q%h-W;fQI|ab1&a#hU zI3I64bq%`KnX_>mHVB8^AT~R6QGM?%-b_`G*cg$}czr=20dmgkb1$np&R(|2yDtcZ zuPTebWhAgJFL;I1JOi#LxE`ZrX92vn^^x@X+vitDdq+e=bjALL)EJ+u&nvzNFJ=QS zQo#7$I_Dh$6vjYnCcX@0q=;0kkMyH! zW=d&rXI%Z|F?P-m!Jw}G1(J@2T9LWm-7s>7^hICU1mC!&9tT8d)%BV9-Zxj&M|!KR zXDIQ5H9~f#X1S`a!RW=GVz&&W#Vtw;gJ%gTcMx5Z^E!z6rRXC@m1}=Nn%3)d>$nQD zW^Sm2l+_=k*IFXGE2W$5BC%DYwn@p<3@XKCZrFlB^D5MhBNrQ7crJQ?A+vz7RG%<|B~29*7W=`tE|b}{oN_!4o8Ca1HC!3 zyzZI@eexEnz%a!&KlWlgXi>Y{4z^tTLcV3mw*!jhJIjq9zp+rocnEN(sGJQ%n|ALX zo=W3=T+nnTG-WbhmbzY5JM3O?GC@H zm_jcC>&ghBtk9HU(-xrW*&3?gQ*^qk6W!zfK%Iwj+k9p;R!Fv+c@p2N6p$5y2{%Z$ znIT<$%|iZzy>+qAac473^~VxpdCAU0E>!&7V6b< zCP8ceGQt2tjGMsO=NpfFQT--z^vXPf5*VqvOcCq7fbH_T;IHK>g!C9RU;otE2wu7m z{SUsC|Dz1|pS?M)i+;`Sa1}H?WVUOi<8xUCItH) z^UB;Vd03w`V2FGl9}(FHK$T%r@{noqml$?0{GM(0k+mWxN`70p*LJvEKLDNw6KSn| zv*1V3L4QLsbQF#%G3JBXt|9E%hu3qp+Tl`JEkPeN6`x{2foej9Qlb zwin_W-_vS*u}^ulnA_Rnh=hm)9{ZDJ4HS5$Nq%3W6?Ig1`PoFHL@nAgxqdmtmG`^? zh>VAJK8?kU{U#d+o}0HXX(Pq87RRs~c;^>krd?>JT+D*_YAKU`6lPTopZk7nHueq8 zKUm?)V^J{L*?2$4Bav2Ur7Gy(sNqr^SNWM82*lPX-6Ai=et6^EI#R4k#{>ifBH;`d zK)RfF)qv`u$<}oDn~-z(o#>1s#$b zzCtBn`q2+#E$mb)IouK>7p^zlD^emCN2wiC?JGPakS7by$_0cv&s?aWAIYFDVdNzq>u&c}Lic9L*6r+ui}r_^h0tuJTh#&dCu; zWMmMb3a-Ru3OB^#E)SKKR^*ykt2hP?sQkRX5Psz?B_)zvG(5RIqbknBk6S)-OeaC0 zuA*kRC3=Bc)Mewm``>#z{|ono{+a%N!0>88e{t?OdV_DzQb*Pu9TZP8ho!Zo{bP0I zO`i!eMS1SMC)EQA$!fWCwrw#1d=giV4ruT#XR2tBXX6m>@~-5rGF7#Yi)g;IF-FoNdm^o4b@A? z1JtuA$x8rbhPxg69XA&WG|+c^HiZ@a4Jz!=P~?mLD1U=sKdt{lW=>+f+mSp#7ZR$d zeM}gCwNi@SLOiJOBcF56fW$wQ;CoYD&kFku&cqH6X8+Rzy8N;|Q*pu1#5uXok-zN6 z<1rYDz#urolNG(57+cZm-jRm~6y1l*-O_4NN#TMS6$Bqod$j^$`YTPU)Tz*B`sG&h zqZE=_12q<{8*PL6@VYsSlE7Or-W|mDrl28vNd>wu!S<3b-J|N$5XP&_8@`XL?wX8c zD|eU6+Wz^XA0Vf5-iZ_L1?3fbFEt~u$k;oxx+xCfGt@h_P7sHVT~yRJu5Ji2RORr#Kp7G zZlDPa%RF|()=$=N&xEy%-n@QG0ec;lHc^=QqTmMO1aoZg$_tj6CmXw_{dD|utJ+n# zAJ-?d{Hbi_2fJQb%v3fkEO8pP57wWtES@Q8@vxW7r6c?ME!$W)M@oI)>i(=Q@g4WdjTi6~!`}PgD1HcOCE;TZ%b&w?L23$`R=etImM^OtPJm9l4`apd>N1 z0@4)MSeM1a5PZv(?gjnMYCB+a?=r%b>K^MX9Ndt9cgYFUPt_LVRl81I@=kFyv2S=X zOe^Wd5JWo#EIfsX?_OO?IEz^}&+QCBLpAN0LF(OJN{vfz{~$db_+%MytF7Q8m?mI# z@{2wx#b;={tc(}sd2?OGlq!OTDd8@BGp9AWmd|!0LQhCU!p?MDh00&v3_hD5e*ox+ z-2q3)GU*8^3>~f^;8-zVC!jt2p2J2L8fp4T4f1-Q+$4m>3l2kV9zbQYYdqoFM5$Pp zD;kfvdn}uja6u`(sljYQdHFrUUX)^ex$!+V3mOlVKN>jjC6?#Q6Xptecgi=S#peTi zUB@yR*DtmQY{~;~j{-fvDvQ&F0y^sUm&IsC_aJGH*DQzbpqZmzaU2cu zGrLd|T(Jw(a^oBR*z2s%4R4NJ$Cpa0?c>;0tJjqR9{a_I1oft2^55r1`J6-&-@5nG z+401d37LyxTKQsqr#P3jRP0ZyWZK47*KN=Iu|H=d;h8@P8zwm@DEao(IVef zh)Wyc%*@bBTE5UcHGK54J*01&;Vxbx!^l>TF}EHas20cYrugow`WJalki^K`TpjHm zUl_Y$yO6|4=mEx4A3i?txzHkoML_o zB5v~XVjoBG)Jce`iYrnzNlaP56$S#e#eC=|Cu<9jy4lIwm|&91S$5_DlfZ0$>YeXu zr!I;vQK7*yvTq)UCU<-wpVn;Iez^ z34han2s{lp*q^~;ncY{#D3lxjh2oIj>*ri!Q#5tt@{VJ3jz}t(I~zi^vyW{SEm8~HO`jWLvglRS z3Be7DKilO6O}!XQp8DP3oL_1IHv9kG^gD+N1wy?;?hO&hV{d_o&qTzG{_XnVZY31> z_FqLAnoFed`hQONWD}*O7q`39j6gKvSdYTveKNHaJQw)auS|t0DL*GwBpN>uDnYO0 zvNum&2_SlH0ZHMTa-5?*12^Kv<<#TPJV>vd@m%?bDBAbg+hX&BTa(xlwdF3@-W6wJ5W=3D;V5vbCP=e8mpFXte?Xk>CO}QGfO|5 z;Ka67HJvwn5G>8`eOBPZb4W`!EhGOWsXs_W7f3lmWNk-jZDYtMQJwo;P|)p!RVs9K2i+K&fbQGQxPP!s*C!$npdHIBJjo+e zbMrIfJw|jzXXk+?Hpn(42hYMq;&}P0J|ShqMl3BgFHG~}5lG&6fMPE0fT<4x0V*k* zm1e%fm8R|}@^A0H&bN7yQLT8TMa{oXQDj@NB$}FdbA@AH)GTy~)=4%hb-)6WOnJPg zTe*H%+?6k#(olL0=eY~Gz8tUieR1STlJ4QUv(~5lg3fa&uqug8Hol@kxCiGN@dMW@ z#}w^lpRC2teqy%O8o&qC=&LP711YtPd5)UAJ<(PJHupBY9SzHVF+IgYTIMKAbp;OiqRw5tCbM(uEDi!gLxi6auR$J0KitALoAkzBdS;2# zl^ULiq;?E1r-p~OH*S>o8?}G6;3B?DdoAOBvaeib$}rOr1n4jx;suNsbt8-*1e5)l zoy;c3j=qy=o*p<F&yylc(ABE(Xo5lp$R-)Ka^-Ig6 z?Sm4#a+EnoYti_ZcMWMkeGF=pp>CYFEFxTxZ1JZT_6z3e8Ej>bR_gi}(IYCm6Bxcw zwsO7?Sxv07i;YpLnm|Ica5n3Hv+jF@Px#T@!i?#&P~HpFJaacMtppxhG!bx_N}caJ z*J$+_+2>wQR4>o!2+9h=wgdaXQChaB4|qq~W^Gz46-L(g&W7YY%HcVCsLt+lWhLs$ zq9~gF;Jo=vtL~zEV0H8QhnG*$#**BJi&Sl{w&w5(O6o`h_2~=%X3YHOA#=RWs9oZ zxoonxBlor0aQU;mr<+BI81Do-fu}~TJX_8Bwwzib(*r2f?_RMiDhP{TSB5$0fW^B?W zN*0nL=2gODL4%I<1DSeqRPAsQ2kq%E;sf;QvR8ilubWl;cq=NsoN_q-?-Q~SdSmW0Q-nY}>WDY^}DjMUWUArRDm)4p!`}|28^WVN+si z&SH>CTqe#UMIs-{v`~ptOvIdFuIH8Jn zncW18iGcHd9qQ7=S+KChTY#UvJyw^Td(2G{zIyTWn0&CyE81}*Z(vHYsV31;Nm3TG zGIk&VwxQ<3c$r;7nLSGR$qQ8dY2NWsWAYn6o?DH;uoi`Q*6+Ud zzORyf0wA95H@tpG{h}{S<1{#_OF&*L_jZ+bSIB1u&^e%-sO)e$8wg-SRAx7cq`LKi zYOr~0%Ou-NZm`q@sa^d;P~uDu4?~+Tdoj7C`mSrUjcgSct|M1nGx^g38=5ROa|%AA zlp-IzC=?`i3}KTUc}TD?7--aX(^`swgx0)Vd0CX6VMgxr*(VGL%lu0q=Ki>_vs63j z3SMPx1U-AEr$OD`Dd46)^{sd82fh4fi7p41-;%iEV)7{O>x64ZirH{x5L2IQ4%?Rc z8-#P0Z8bld+68k_Pv|_xkV_r`DY%Oq{2i6MpX*b!hu!dF@+%S}w?0JB;=BnA7NMT) z7oz_f{~~E*ojzzkzkB=q-E3jphRo-JQuBouPaaFi9=_VzK;2dVqi0XHEUkFCy$|CC zC9Pf!#Hl{5?+nJFDI9xUhmEQ6EM#D^_z%*Y$l80LbADm3?aT#0Et@`sDDEsi>$FcS z?R8dvFO5~SPjxLhLYRAKodjej00^rLd%E0^=loD<7jCPsKp~XD$b7HHk%U5?O-%+% zR1*Q2jCL2hJh#c%t9VJ@;D>YpR@*c4amcfVd+NkBZCv3Ty|cN3>@ z@s=!&5y(jU+I|LEz0v<$UDr=kI;x)s_$B@z;lWVfclZ2r-y!ZgFxOX=ENNQ#Vx+-O zH2vA(p#0YQ@PoHxey^{TzlBpo>wkC*934!}?N(JCM-gp4(`Z zk6)B6`s*w$pH_Iu(FSyyNlVs-RF=MN#{b;Ufm)FyF7B*PZ1u#`;pGFmaEzG03qX@f; z^QiDJc4%pGjgne()liX8ev+fIC3?6AxMk33Zx7>Ol-sy7K3^KmCnRt8AplU$`KpIL z3zVo;`+TyJXOxuVzuS``D(L1;oDE5)@R^WEZf+=6>5=}2K~FGAyt`1GfyAsSEv6>lKT@-nm9^Cm({riNh z(6HQrNUgD>a`sOm50slWNe<4RX-{KiU;N|pIHzxQelV|N{B;^}^ip|kU z}+kbZ@ON!NRo31`9)Cxs@ zy$KW8kJ#n=ndA|?j-SQZ5mYK4A2Co6nH_xyBdYiMDFF^p`|#SCw=+sMLOe^fpd_o5 zP?c+7&$qM(3Mpz&Qz;SMW3jiG#c@4cX1Y$A8#*EH4qr_9ByGh_A~V2D9gJ^g^f`u_ zPdgoBby?S+`SbbV_gChEbI5e|0Ls|O=5Efs&-{SrV7R0<6=+Vgfb>)*w%=~!i{!O}-f zZunGa(??W?HDZAST1$AM#?>r!xVjBmbu3m>@837iUh!R3^gZfuo_0d;b8RaZ3^Fk( zRWNL%^lcG+eNqI)o5V@A_fPJ*Zsb-^LE!f^=~kY@qd1b z#DhzQlFFQ6Q3fKqoi$z9f&i@{oo!%Q zUd~kIS8yQdOUb6EWhIgX%1okbqCZePolR|3*K2VU1UPHN!qiq@5P({&5qZ82-cA3pjUboR9H!`5Lo1=aRky+n48o6}8pVLEk zYz-}-lY)M)3kC)RieZ5Oq%05Zw&Wo-)iEW~^s%KTP51y}1giD4=jh;1yX#5Ie zd<-aXlYeAeO`ihtYk03zHPvxP%6Q^9(`b0%_)+Y~Vy$fpd|M*&ub3&R5qhzjHtA}u zaPgsBs!{C7C_6isE~zzoIa31kU$S^a6>&_Dm3v*qQcDjgIwy{+OK{$lsea-$@JfT* zsB6+MAVmBt2{sE-|AJ>Z$KuiBs)@;NQFyL-b*xZe7)gR^kTBQNmF%0YtB$Bf`AloS z=9ZI`yXT&P3uSthj+JRa0-JYkP?w)KTDjC12t|LXsGENBKOx{z2dm?%9;UT$v z%b%ph@Oq+8udbwkr+RX98=(aBS!`Nh49dr>C<>mqE}<0)u22@6HyceM6%<+9u^FJ4VG9h zt6r{|YJ=Fbdpps!p!BIIZmaF+GC(^D)HpT|3KeI-*#oDK#hx>=yONI#5y-p@#@^mCOT5x7Y}0ntPT>s{@A4wEVP}q#1xpQn z(R)=?Lg$ zm9w|T>MIz>;KF=noi0R(hF%BHCleAZ&z;8dUD%NYkmZZL0te4htbrZ3stEbRkJ+(V zxBGW+G_OX<0(=@;nwAa6dek)CXrt8}7FN`9iZAv?u9^&b6N-YF^Xpqx2|ho@T9H0< zAKPz}xZb74I?TLrqT~eSmk)7`{Xs$*=4NH?=)bxllqQwvg}hf+4v{_-X}qt}4Y2b) z{OA(D>c2yS4zaJ#97Q=!@;_mrhdM8zIZY3gzH3&qt*qp$QMkQy2kqQk z9bG>W+m#<%**3U+1SuW!?^CGvYOfkM-+V6Ps-mxN9eo{dbwF=|47b8@EP4y`m&I>E z4h491M&-oqqDaHi3_<>B5=!tw>6P9k49M|aUE(5WjQj(n z7XeHARwnA-OkY$9#)Yipe~GpJ>ve3B^eOZkBfJ{O>OZa@Klz${X1liPB9|lcxvZqC z3jYd1`6$_m?=|nc7~SGk4hvT}M4TT9{@}38&~YMP3b~#0{VDkor-7L4XY^j-D)4WC zyS2Fq{EJ45@zUpbZ;M1l>TJI(JAkhR%N&3~WZH8W=WY&A6%HeAj`vvBoR9Puu#+;Y zy?spKb_#iq9)x6G!<;q}nW>QB^5X4hg+Itag0vt>g^S~iz+c}#e8=fO5cZ8S3N{h-UDoT6WTfmJhzu&{IL&z*O{-0;CYgq^$=l^N)nXeh+rApGW{Fr^ zs!m+UT+jW9Pa7&GqWyXP;A(Jy(nL|Wb*WBhbU5~Y|1BtON_<1GjrLPK0-_wRfYv8hyBsn(A=UH%^?-COmn;x;g}M+Nn{`W|RzbPsdsuHqH0OXe=~Go)!=gt} zP)A#YP7{&yZN?uYHfVe!21g^#`faXSh2=vcvHnSV5lxuP_|1 zgRY2jiP|!3BIICD>Hw+4UWwqWg9WCUGfg2Hes`*BQxrb;fWz-siQa9NY>lKsF8FF; z%-6U|<6Ad+QGb&Xj{lEm^goc^zdyA9`QXpY{{yzS0P@Ie+>8aau*quAugdOkI(1gP z;<+CvV#om_-oHeAjH``55^ZH}+J-j*$6`-NbF&Bx(}5kz{?WO|XHp1?g$gSC!Xl@ID@m5cEzC0N_-dZ;&_D)0N?6SIgV9 zmf+FjUD)Bu1+i93k@rVHWoA#gmnT@}~pm?k?UDeYsFP&9%19fY8O+%EK>aT~|FK z(wq2JS_;p#4@mE#x2xr(Lwba=hk~r2X9yE zJ2i@O?-2E8XR~*@HxQRm(ZpdJxdsNpN~y3xTcUVd!@QzYF`r%@UM6`(B!BdGT@)iW zY@i=Ig5txj5yrOA@#AO2(Q40A_VOH-JF>UF&5|89cf#DiO-zhk2RyR<%x$Hd0 z?W{kICrM+$cyZ)l2)kjmfsgl`^9kiU%1frO=11jrtS%pWuhj7N*t088f2A6Ytt`jk z2*~P2$mqXxQf!046H^Wke!MOx5aQ%jiVkN`+J|CpSj9t?y1?6dO1m6RTO{6k3d@z& zv%hIJG|8YC{VW}-cYczh(s#)Y?NrB47-<7us{Oda41%%*OnY4yox8(25*SMNY!PYZ zMxWgqyNs2lOp13xb;8}xiNcH+&yUc6SBM$e5B{3YgQuXwEp4HQqT41=J_hMVKOFa$ zi3ZpNZKgwpEaxWt#wqq^ca0q$sCEp)Dt@zD^j1|7_pauk;vw0v8iAQ*I1MX zc53L_(>~T2ne)+AS+nH?&4;iiP)5=7ojI$(#}Q-J3;I@{Tf=i&^D9XQSY>xXG*%1W zg@zcG@hIfkr(XO-^txUiZvB2L%EHHhBMjK}?x={Htz+93Ao*r9TbQ(uG-#1TdHE$w zJB1?PQ;yMy24-%;K4m*uN3~beP+7;h&w?&7hBO}a4?!ogh)nttQ9aZ$BZMX82<2zf z*0LAX_g?m*8qzIXgR#FsNMWyu5?#ciiZv0zTs~s=4@18VYPz=Nb7)e_z(;}oG!4+6 zURE0Z*u>QL7n6D|x5e_^;xvhG(zvr33GivR#byc@BY43wt)8VJ9$IP7ONy&5(=DM7!N&JK3%dDCZPZwDwWgu(^I5l9_njj zWhqbiEj#7%Ak0hB@ER=Zv2FTG$>h*duTm{Yk9k>%caF_q3FnN;YoPMel50R?U!%B% z=qHtmcS0tthDK>0!e60Mq?q4}RUb^tX}i&gA-%`1%dVQ(h13w8U^wguvRiqTC={Y( zWtSiXuASEi7CkVu(=L+hyur=WB)pcSib17&CBjx4M^g(yV{WcAj-xi--TDmx3N;~)j&}WiVNpM-aIy_3$@7rap zQoi!#Gm4bwd6zLG5k|fdL5f1aF~M{hz_R>o^pY-m|A^0HGwm)3QaHy>f^6KARE#9M$Y+Sx%OYG>&m%k%(ID_hDQS+(CeJot8)fdzgVLl}-;;+z)!sv*I$;TGZd1VQn; z+$Xsz{bDgt;F1MS3tob7?I-9|E$H^bG{CC{y z@>uV%;4$H|H%!*;VZ&`KUreq2qCr$y5tS`*Ob6B2es_nMf$eeal|4ypJ#mn&ct?Oq zF=m1#zRAseUU^ei7XP6K`ZI(s_Tu?k47B*B_Q~e7r_x#z z7=o&erAw|lCXE-2LlzahcIH^LK>Dk5<(QT%9LuxDM|yp5sWT+cqt@5*ns4;Myw^tD zhD}^lJvDnm>}g�t^8T1WB$MTeH$WaD4 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 z

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�($gB3=f6t+_707(i|OBZ#%8hVZ8w( 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 literal 0 HcmV?d00001 diff --git a/panda/src/doc/Sources.pp b/panda/src/doc/Sources.pp new file mode 100644 index 0000000000..ab1b3e35bc --- /dev/null +++ b/panda/src/doc/Sources.pp @@ -0,0 +1,2 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + diff --git a/panda/src/doc/channel.flo b/panda/src/doc/channel.flo new file mode 100644 index 0000000000000000000000000000000000000000..d2af042f4b3999a61ae50f1581362a7e1d193722 GIT binary patch literal 16384 zcmeI22Y4058iwZ_5|T&>0aUJfWw9VCs1#$pdXOMiP(Z~FN)Ql11B#$RRO~1aK(T{j zK}AIcyTo3>2KEB>4k~ux-0$7}_he5D@dScbp6lfK_MMrXKJ(Ac?#W9p7A%@JYlFqE zlH1MIaVwH}u5KhgCVxB(H*&h4OcD%PM}9^9$&VSo6jUuxL;(FHj^~bnad=7ZKqkA>#xoE=lS3*JMQTb`^+zxwbZ#s z&mPu>vG!(;X#DfKb7ST0PxBd_|4v$cth-|WI<&@9)7ZshH68yf)pGszHLhx<8ERVp zN()-W8>>@)n3p->S&*$Z@XXJVa-}?J9jTsFUuqyVl=3AV0bGI9SZX3QmDZKklh&7- zN&k{IkT#SylKw4iEdBG$2}k~aOHD<`Jv!;?+y^(79_^asxEOZq$~4_EJMPs{)HBSq z#G*(Y1U2(40>XPqea+8m2;&vy{8J){v_kr+3=WZv1mEP>n9&lHBLU7_{2JlWh+^tNgeHAsF%EdsxT zWQVj8vYSAPhFA~8f*^f_SQ4Ze5Q~CX6~wY2)&;RJ$Us8IA7X6~i-U9pVtEkjgIFNM z3L%yVu||kRLaY*EnGoxQ&5fQIZQ~{i)3!||%G}JjxyUfLlqd^7eQqU@eoNz4B7NCf zqAjf@%I8r*I;Ms2w#My5>S=3iXKZg|db!Vr4hxMt7f0#u34wq=p5ym5pM~SSd zqb1taTVh`NNQ93u9xKuhjGp*nV?U9#e4K^*8wZH2{ei~gMb_vb3!fmeb_Pq7H^g|N z@g!r3NPC7#)IUt3A16!XJ4GUXxWpPh)xsl0Y-FVIG?8*nm$1b%EIdkNJ&cxGNTtRz zjc1AU<7|og#z>TNj>LGMD-nO5@qCf{7f9T{P+}e~l87HG5r45neU})=8OIwZh}2tV zoG6lil5w)}Qjz*ElPGtJ#C%+C;VVSO=SqqES4mq*S4-4?jqzHMc21S*OVcFExlW?| z>y6WmHyCde8PA(6e6vVBGbGBtMJkYPl_=*niSeE(k?wYhbazPHzf)=~&9d-aBI)in z-ea6CQqCNynRKs2yYG`of4}hov61wkMEMU%n@bNHA2H4qH7*7%+Ad*d?W55^yjKN)`(>Blb?{?+)K@pt2Lab0PJg_9N*{QWRI zu*4KbaVa>kcy;8JBQ|vJuEv1{YxJZXDNCYURxqnH!vhN)CL5%okXAy9gxSWrMyew| z*O+Hq$B0pLpUHx32C$)#CKATDAVv-`Uf9Ig)W~2H&mw{A8!=wOOgdx{!%!QUpT$bt z#>VDGPo&K)B-*@*M4K@h>fX%AaFVB`MBOY}#-No%`Ynx22YuXHqRp)(%5Ni)ZW{}4 zYurwxp0>tz#`eYz#_f%T#vP118h0}8EK=Vt#$83)@gL)E#@$8gO-S^yqeQ>zn1iko>AOkfKR_bgffnvAa=(W} zd{2q`4w8sJSR(!qi8<d#Q2vQ&orJT(vPzx>Kh|b&N&j}eXd0O zdB*ca?q48nCS52oKNm^FkCn)Gu|$2B7{?jM8z+d=TV|Xnl7EtMvhh-p`Y)3xcZyU` zy4=E7h>Xvb68W!^uc7T#tw=kkN{sI`iE^%!DF1rnbmI-i8%4(RCJWyzQqK&D z^?8fbK)O|;oZBSEd!|IX+a=Q7AvKZilp0F2EPR(py1R|{7-x%=H%B7hy%O!dPa^&O z#s@^|e^4U*LlW!mVdEplxgzZ_`LB2 zk#^2A&Nsejd`YC7myNH8%+ssJ1;&NO*Nm@=wEqnYzbR7ATgJDI?-<_|Y3F;!_eJtA zk{GAO#t)1i8b30AZ2UxQE`2Jo&OVdK|GDuCW*cE*VCU6O9fZJ@YQ0!*S&w6$YX#hzB8Zrsb{_E~ujj`xxz~!(TC4L2da; z!zkmzPjx{kxF(wiIqIobkE-IVMlt_{MH{js{<;x=j#|FE?#Eo+u0u*r=rd^51|V0( zjP;9YP*mKn&&h*^c|H2~Ik7lsSn8V5aD-i387Yl>m4~C1#dD7?{~g*yTS7;h@-$8t zi7{6{9H;#T3{D#+k}E}OhNIaP9bH8=UNx0JbyH6UO`=*_4^4i+F00j~uzS%0!-d>H zr_gzo`dm7> z)APaos{5>bOa1$`RZiFtSlQdM1_`a?~yv*gkT;`hXTjs{j zE(?>2`R)j%&zG-Ae}$=eqiW>bvKUohFo6p>(rRrVmt}Tntos<9?*cO^*gme;l)^#E z>-jZU0YVr5tXJa4_VM30PV}$+{p$ePxO2z)b1>Auc2Wt>1$Tq=gZ-4jwb=I$@^jUT z3|%AnX?xBAB}0xa9y+u@VZViqL(lq_T8UhEE?E`xZ&yR}PbAxZpIQ^Ddr@-9#R(c= zE;Ro|$;+-vgpm=hjpkoIY~mZyK6`FyTAwS-e@c~oR`-4LudQ+-!&z(QpDHtA{%vN? zNp&0ZPmUf^l$`rf2IkLRT4VDs(F(81{P$W?=1zUC%)N8ogj6dr>j6stSDXJ{zBvbm z9Bcx8eG?w;zXC<$1JnMi%|B0@FM}Twv%Nom`nRv4`Fq8~e@v|j)#Jr-(@deuxtD!)>VTVtK$sGGLvSF>~ZYA~~tBW8OQGomQpReq5x ztl?IkIDXboDf?E5y;qD&+tyMiIhPoh2&$}nUF)`Tmv!)a(LOIqruVsGE3c-{j6!Xd z6CKgod6A2=4t*Cn$;D4woa#0wx$j5!Ey{5tGdRgNors5w+W^H<5nL0-U#SB^)tf%R2;m*1t+%PJlEZMN*u)W^Iwnye{kjt{J|?+@Uj=g1gE_r{^Q|f z{~Dwj=5MFren9J2{RQuR?dEI?uZx*%3p>Z{<)zX1NRqW~ zTSzP(xgcd9en-;#T(K=w(`V(Yt#Tq`Sz9N(Dz}9(MN?AU-qruU)3Hd$gbcO?Jy8F+ zEyT65tJ@aXHmcedt}2@3e!hE>`|SCNk)GFcO_aW#VyfR3_~jsQAv4+*db+}%DxJTm ztA1PP=70S;#y9PO{#Tp8@PkdDHqHE7GxN_gH1i#6Xy)a+6n~qVdksM1$fjk9$Uv0Z zi(JCpSEZTvUJF0tmQU;aTC~qP0jKvl^%=M7K5KY_?>MzoPGq>(sG09D_J&lqdHR2G z-T0zdd3FY7&S5_7Ov}^1sqCyeGw-VVRhjveQ^vWM#!PmnKNdCfhHg)#Z>Wl@f01jr zYG&Tg74}o<{6*d0_xYa5JI&}U{riH%i`$(#?^X9sqm6&nU)cDeC~f}3GYn(7hF;jF zYsdRqCmcG-?=fqvb9}1*t2@_Tv-OoacZNFm@tW)$Pqo_WT%J}?E0xFdHT>yt83DQW zT_*d-QG)n`G(md*-N8@AZ!y8IX<_;>Z!|rJ3MK^?rl0XmC%=5+mM)*@>OVeFZKx3_LST`?ime#!~o z)~aexRq3^Ed`Eg*w7-?plO&p+Uo*q>nasroA|AnL~wn@F*^Jd8T!_fj~^u!Uq5JVP>cSj Y!v9#6qwi6~(;i$hKWs%1UL)840-GgAp8x;= literal 0 HcmV?d00001 diff --git a/panda/src/doc/channel.gif b/panda/src/doc/channel.gif new file mode 100644 index 0000000000000000000000000000000000000000..ea648c1d3b8120073791d3aa1eff703a1f888bff GIT binary patch literal 3671 zcmc(ei93|*|Hns&)TvYU99k?P6~?|K(HMm6J4Z)DNEnPgjAh2o*v6X3ShHl`3YBf5 z(AYU*L@Fm@P(0sz=KH&@-|zedzvp_c`?~M@`P}#C{d&KzM@JW-r0lc+Q#wL}(H}S* zjzXb`L?W3?h9HOt7H~Kjtigyvk-=ve31CbSb|%?CluI$Lu4XEgprAGB3P19WFi>BCXC4FL}qj%gMCIP6gUow$uI~4 zFC2nUKoLam1S67RpdXP8M}YzeMTB4|a3^Stf}y}|5S&bJ59V+<5iEfS7>ceHlz|#h z398{JP!AOU8wn18^u_=t!U?LmWdrJt8_1U?$UQ;=SHAUhqzhUP%*+=EFW(sVVrH0tdQ~f2g-|GSgHZi?gQbCo7W(={P?pGnH<>v5*sS zyJvzFr^kzt3MU&c=vsx|5)SY7Ub$-}rTO}^+r{ASg?r z>T)TRj}ct-@M4aflB<-A`xVNRT&dDlDvsaA_bw5BL79^7Ghdc(%WR`lmaEBXRa}VS zP+m=YG`(km)PAo_D!GK1ih9f?$ud5>%m$G>+lf8xLhaeeUd--)9&oI+gUu$`PdH6=CU7)^TC(+KQ>-r5|%RKC=YCDoyy7k_E{8vQA$cB5mV^XIExEbp(NzIIEsu7S%WpRIR_CqmqZ zlI=o&(CkMxe~7y!AJB5|ZyXGN#A=l9wVWDJ-~TdZVe_-C`g$03t2fzlX?rHQ<8Ws4 z)STn`R$&oKzu1v8QjcnnKV~8-K4U!@W6!SUz#=Hl%i8oXl8c2mPIk4+`E?R@@rhZk zRKKRHL|oJnu5=FLRSi~|!Dy+51yinD=lLRQrQvE$zu5<9bua%Ob0guxE)cHNe? zzs2(Lw0w!S_7&sn+94O`@&Yt#s}m*d(YCWarHy)>Z{0n#Y|R@2M5K2r98A$UXDcak z&mXf|dZL8|CR{HU@Uvk>)1L?}SqiFM?@RtUQ+!@s9y!3Cni74xN@c7!6xDHvWTeGu^T3On<+!GR9`dx0Ygdm*bSS-DleRp*6C3s6x%#wKRA??qBe337usHLw zt+jh@t_mviiJVHPrfHrM4eHRoy9`+YX!MU>X!(>q%w zyW=bJP{08EsAqajgXsSLMN^9~whWQ_`^~;K$h?y&q?are=R!`ZhlxYUeqyj z6x%Mp^Ppnu@nGgt6Zv2Zc@m3L&<7!uU#a8QdzO7Fx1|PB-Dcz}ZN>sAa(Y*i{L(+0 zO>kH?ZxWul^l~{AQV?F7*Q%UNJ^z`-vsr$xGZwus3)szzVp`wX&!YWuwO4p#o=?2?;8GRP9+5oVpulpC zyHj85eI}*>!QymgVrYJ+jn?_k!Lz=P`V0Hhw5}N{Ul?s@;qV#FWh~z?d}`e87uRkW z8$vpqvnr1XitXNtl-?FmTV%Bz>E?dr6(bRH^(1Dp=RBn*Mt|8UA!G8@1)lGCOWBXR z)k>;Y946vb<^DQL+P%A$;gHbRxYW38)hAz?8TasZhtMVQ{+m~2)3j8aFG9opI%1oF z`Fgj9xm+`(9b_X^a!e(KeMt%h8ySt$qa#OugjgHajh>;B6<$a{Ia%M!awD8a9S(NG z8zSrU*7qrJ_xpMN87^rNWT`@?^t`+{%Tc-gJ7P-#md?K6M8``iFe7cirm zZWBjSQ4M8@Jf92%vWJ2Xw*;u;hAK(ZBc%!gcZX-hYE#TglIDG?I|7XCjkbnQnNFZ0 zs#U#zQ>Ae1)U~I~{dwi*{`+Au|CQ`Hk4qZXL!$)>P33}4?n_fM-Shqpt@ch{?mv4z zBAQ!AuB>>Ig6Gbm6+4#HH$qcq1~)yM+qUgDqUHn^4tAPb4)p(t+`x1nHEQXZi~km_ z`fG8?yv8lRx*2bq*T}1J<+Wqn_at|AYv7*fGhff(aC#mpG+9gO^tc3_NtrAL99VCpKta>DfZu$i!!J zl1frtmtQq!^136f?Z?aO{&i5&7Z=5L8J#e{MvW6+-8J^crwaVqFmvCW`husn3;etK z)Ts|>KWFBKjk|ddP1pvlWa|>fJA}h3{ydb;5qLrCW2=uS5=xd{_Y8F0YBOn8-rv_5J$>xyx?~&HgDa?pD3$7e)cn>Z|y^UviLVY>t0JHJZ)!| zl)o?=^kAQ`*?TSHo1@t~2kh}Nf?>lyOY?Uu_%%N8A9O+18edI3IMF(PN^ZoYOBkPC z;Xjz9h2V}{!|XE`Y|&l>AAHxq9DG{%4p{~=;ewfWF$ZknY~ta^km1K&!*@vGr~1QB zuZI7+6v2&*IAn|9RSJk{4icP%d-X;9%!=R?k7RX?B+6(o^ZH3;d+ERO6;MJ8@%k8& ztfY~V*A%g$mRQ|BY>Xe4ebPsz1*;_8V;1r`-)Jlp=Sm*KW*bnb?AH zp@0@;|LoXDtFfW|vB!L4quFs-t62PMOsqIA5kle`vAExVz`0PnwE*t)pW&BuF{9tAvx37M6S=_zn z5~5ULhcUNO#UT45E+osK^;dCr+a4i24t0tOzOsn|^1*-e%6G9PFjJ*5KJgxn!8MAZ zL(O8m_kw5pO?rML#_fcBhzj|Z9Ri8mKPj5rhK!paS&s51jrX~?WhGDXrrbm)zhF=C zrHa0#g@#bv-mHc$XT{gx5J@KPxTB9o?gsG^!{k`+^YEu^Dn82Qix*zS=k}p}tUQ-{ z@kM-TBy?IlE;)mpP;8YVqf3DAC0vm!Pg9nNyIhV}9Y`x(N|VPEG|LIM1_&B!1f65) eCBo^7Q;A09Nm|D;)OFKz?`P<*@iH;NRR04XDivP< literal 0 HcmV?d00001 diff --git a/panda/src/doc/drawable.flo b/panda/src/doc/drawable.flo new file mode 100644 index 0000000000000000000000000000000000000000..d697ecca0cb793fce922f31d3b22fdd36118166c GIT binary patch literal 30208 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 literal 0 HcmV?d00001 diff --git a/panda/src/doc/drawable.gif b/panda/src/doc/drawable.gif new file mode 100644 index 0000000000000000000000000000000000000000..5b6323af8011497ef71984f98796ef7452386a9c GIT binary patch literal 8714 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 literal 0 HcmV?d00001 diff --git a/panda/src/doc/eggSyntax.txt b/panda/src/doc/eggSyntax.txt new file mode 100644 index 0000000000..9928637808 --- /dev/null +++ b/panda/src/doc/eggSyntax.txt @@ -0,0 +1,1264 @@ + +Egg files can, naturally, describe many properties of a scene: simple +geometry, including special effects and collision surfaces, characters +including skeletons, morphs, and multiple-joint assignments, and +character animation tables. Traditionally the scene descriptions, +character descriptions, and animation tables have been stored in three +different kinds of egg files (and, in the old player, parsed by +different egg loaders, which didn't even accept the same syntax); this +distinction is no longer as important. There is only one egg loader, +and a single egg file can contain elements of all three. + + +Egg files consist of a series of sequential and hierarchically-nested +entries. In general, the syntax of each entry is: + + name { contents } + +Where the name is optional (and in many cases, ignored anyway) and the +syntax of the contents is determined by the entry-type. The name (and +strings in general) may be either quoted with double quotes or +unquoted. Newlines are treated like any other whitespace, and case is +not significant. The angle brackets are literally a part of the entry +keyword. (Square brackets and ellipses in this document are used to +indicate optional pieces, and are not literally part of the syntax.) + +The name field is always syntactically allowed between an entry +keyword and its opening brace, even if it will be ignored. In the +syntax lines given below, the name is not shown if it will be ignored. + +Comments may be delimited using either the C++-style // ... or the +C-style /* ... */. C comments do not nest. There is also a +entry type, of the form: + + { text } + + entries are slightly different, in that tools which read and +write egg files will preserve the text within entries, but +they may not preserve comments delimited by // or /* */. Special +characters and keywords within a entry should be quoted; +it's safest to quote the entire comment. + + + +LOCAL INFORMATION ENTRIES + +These nodes contain information relevant to the current level of +nesting only. + + name { value } + name { value } + + Scalars can appear in various contexts. They are always optional, + and specify some attribute value relevant to the current context. + The scalar name is the name of the attribute; different attribute + names are meaningful in different contexts. The value is either a + numeric or a (quoted or unquoted) string value; the interpretation + as a number or as a string depends on the nature of the named + attribute. Because of a syntactic accident with the way Egg + evolved, and are lexically the same and both can + represent either a string or a number. + + + +GLOBAL INFORMATION ENTRIES + +These nodes contain information relevant to the file as a whole. They +can be nested along with geometry nodes, but this nesting is +irrelevant and the only significant placement rule is that they should +appear before they are referenced. + + + { string } + + This is a new kind of node introduced for the new player. The old + player used a Y-up coordinate system, so all modeling was done with + Y-up and egg files used Y-up implicitly. Since we have adopted a + Z-up coordinate system with the new player, it becomes necessary to + make a distinction. The contents of this entry are either the + string "Y-up" or "Z-up" to indicate the coordinate system used in + the egg file; the egg loader will automatically make a conversion if + necessary. By convention, this entry should only appear at the + beginning of the file. If it is omitted, Y-up is assumed. + + + name { filename [scalars] } + + This describes a texture file that can be referenced later with + { name }. It is not necessary to make a entry for + each texture to be used; a texture may also be referenced directly + by the geometry via an abbreviated inline entry, but a + separate entry is the only way to specify anything other + than the default texture attributes. + + The filename may be a relative path, in which case it is relative to + the directory in which the current egg file was found. If no + directory prefix is specified, the current egg file's directory is + searched first, and then $PFPATH is searched. + + The following attributes are presently implemented for textures: + + format { format-definition } + + This defines the load format of the image file. The + format-definition is one of: + + RGBA, RGBA12, RGBA8, RGBA4, + RGB, RGB12, RGB8, RGB5, RGB332, + LUMINANCE_ALPHA, + RED, GREEN, BLUE, ALPHA, LUMINANCE + + RGB12 and RGBA12 specify 48-bit texels with or without alpha; + RGB8 and RGBA8 specify 32-bit texels, and RGB5 and RGBA4 specify + 16-bit texels. Otherwise, the size of the texels is determined by + the width of the components in the image file. + + The number of components of the image file must match the format + specified; otherwise, it is an error and the format specification + is ignored. + + wrap { repeat-definition } + wrapu { repeat-definition } + wrapv { repeat-definition } + + This defines the behavior of the texture image outside of the + normal (u,v) range 0.0 - 1.0. It is "REPEAT" to repeat the + texture to infinity, "CLAMP" not to. The wrapping behavior may be + specified independently for each axis via "wrapu" and "wrapv", or + it may be specified for both simultaneously via "wrap". + + minfilter { filter-type } + magfilter { filter-type } + magfilteralpha { filter-type } + magfiltercolor { filter-type } + + This specifies the type of filter applied when minimizing or + maximizing. Filter-type may be one of: + + POINT + LINEAR + BILINEAR + TRILINEAR + MIPMAP_POINT + MIPMAP_LINEAR + MIPMAP_BILINEAR + MIPMAP_TRILINEAR + ADD_DETAIL + MODULATE_DETAIL + + envtype { environment-type } + + This specifies the type of texture environment to create; i.e. it + controls the way in which textures apply to models. + Environment-type may be one of: + + MODULATE + DECAL + + alpha { alpha-type } + + This specifies whether and what type of transparency will be + performed. Alpha-type may be one of: + + OFF + ON + BLEND + BLEND_NO_OCCLUDE + MS + MS_MASK + + If alpha-type is OFF, it means not to enable transparency, even if + the image filename ends in .rgba or the format is RGBA12. If + alpha-type is ON, it means to enable the default transparency, + even if the image filename does not end in .rgba. If alpha-type + is any of the other options, it specifies the type of transparency + to be enabled. + + draw_order { number } + + This specifies the fixed drawing order of all polygons with this + texture applied, in the absence of a drawing order specified on + the polygon itself. See the description for draw-order under + polygon attributes. + + { transform-definition } + + This specifies a 2-d transformation that is applied to the UV's of + a surface to generate the texture coordinates. + + The transform syntax is similar to that for groups, except it + defines a 2-d 3x3 matrix. The definition may be any sequence of + zero or more of the following. Transformations are post + multiplied in the order they are encountered to produce a net + transformation matrix. + + { x y } + { degrees } + { x y } + + { + 00 01 02 + 10 11 12 + 20 21 22 + } + + The transform definition might also include the following scalar: + + animated { 1 } + + If this is set, it indicates the matrix may be animated by a + matrix channel, like a joint. At present, this flag is not used; + eventually, it may be used to define an arbitrary texture + coordinate animation. + + + name { [scalars] } + + This defines a set of material attributes that may later be + referenced with { name }. + + At present, no material attributes have been implemented. + + + name { vertices } + + A vertex pool is a set of vertices. All geometry is created by + referring to vertices by number in a particular vertex pool. There + may be one or several vertex pools in an egg file, but all vertices + that make up a single polygon must come from the same vertex pool. + The body of a entry is simply a list of one or more + entries, as follows: + + + number { x [y [z [w]]] [attributes] } + + A entry is only valid within a vertex pool definition. + The number is the index by which this vertex will be referenced. + It is optional; if it is omitted, the vertices are implicitly + numbered consecutively beginning at one. If the number is + supplied, the vertices need not be consecutive. + + Normally, vertices are three-dimensional (with coordinates x, y, + and z); however, in certain cases vertices may have fewer or more + dimensions, up to four. This is particularly true of vertices + used as control vertices of NURBS curves and surfaces. If more + coordinates are supplied than needed, the extra coordinates are + ignored; if fewer are supplied than needed, the missing + coordinates are assumed to be 0. + + The vertex's coordinates are always given in world space, + regardless of any transforms before the vertex pool or before the + referencing geometry. If the vertex is referenced by geometry + under a transform, the egg loader will do an inverse transform to + move the vertex into the proper coordinate space without changing + its position in world space. One exception is geometry under an + node; in this case the vertex coordinates are given in + the space of the node. (Another exception is a + ; see below.) + + In neither case does it make a difference whether the vertex pool + is itself declared under a transform or an node. + + While each vertex must at least have a position, it may also have + a color, normal, pair of UV coordinates, and/or a set of morph + offsets. Furthermore, the color, normal, and UV coordinates may + themselves have morph offsets. Thus, the [attributes] in the + syntax line above may be replaced with zero or more of the + following entries: + + + + target { x y z } + + This specifies the offset of this vertex for the named morph + target. See the "MORPH DESCRIPTION ENTRIES" header, below. + + + { x y z [morph-list] } + + This specifies the surface normal of the vertex. If omitted, the + vertex will have no normal. Normals may also be morphed; + morph-list here is thus an optional list of entries, + similar to the above. + + + { r g b a [morph-list] } + + This specifies the four-valued color of the vertex. Each + component is in the range 0.0 to 1.0. A vertex color, if + specified for all vertices of the polygon, overrides the polygon's + color. If neither color is given, the default is white + (1 1 1 1). The morph-list is an optional list of entries. + + + { u v [morph-list] } + + This gives the texture coordinates of the vertex. This must be + specified if a texture is to be mapped onto this geometry. As + before, morph-list is an optional list of entries. + + + In addition to the above, when the vertex serves as a control + vertex for a parametric curve the following attributes may be + supplied: + + + continuity-type { type } + + This has meaning for a Bezier curve only. It indicates the type + of continuity that should be forced for the given CV: cut, free, + g1, or smooth. This only applies to the end CV's of each curve + segment: every third CV. + + + name { vertices } + + A dynamic vertex pool is similar to a vertex pool in most respects, + except that each vertex might be animated by substituting in values + from a table. Also, the vertices defined within a + dynamic vertex pool are given in local coordinates, instead of world + coordinates. + + The presence of a dynamic vertex pool makes sense only within a + character model, and a single dynamic vertex pool may not span + multiple characters. Each dynamic vertex pool creates a DynVerts + object within the character by the same name; this name is used + later when matching up the corresponding . + + + + +GEOMETRY ENTRIES + + name { + [attributes] + { + indices + { pool-name } + } +} + + A polygon consists of a sequence of vertices from a single vertex + pool. Vertices are identified by pool-name and index number within + the pool; indices is a list of vertex numbers within the given + vertex pool. Vertices are listed in counterclockwise order. + Although the vertices must all come from the same vertex pool, they + may have been assigned to arbitrarily many different joints + regardless of joint connectivity (there is no "straddle-polygon" + limitation). See Joints, below. + + The polygon syntax is quite verbose, and there isn't any way to + specify a set of attributes that applies to a group of polygons--the + attributes list must be repeated for each polygon. This is why egg + files tend to be very large. + + The following attributes may be specified for polygons: + + { texture-name } + + This refers to a named entry given earlier. It applies + the given texture to the polygon. This requires that all the + polygon's vertices have been assigned texture coordinates. + + + { filename } + + This is another way to apply a texture to a polygon. The + entry is defined "inline" to the polygon, instead of + referring to a entry given earlier. There is no way to + specify texture attributes given this form. + + There's no advantage to this syntax for texture mapping. It's + supported only because it's required by some egg files. + + + { material-name } + + This applies the material properties defined in the earlier + entry to the polygon. + + + { x y z [morph-list] } + + This defines a polygon surface normal. The polygon normal will be + used unless all vertices also have a normal. If no normal is + defined, none will be supplied. The polygon normal, like the + vertex normal, may be morphed by specifying a series of + entries. + + + { r g b a [morph-list] } + + This defines the polygon's color, which will be used unless all + vertices also have a color. If no color is defined, the default + is white (1 1 1 1). The color may be morphed with a series of + entries. + + + { boolean-value } + + This defines whether the polygon will be rendered double-sided + (i.e. its back face will be visible). By default, this option is + disabled, and polygons are one-sided; specifying a nonzero value + disables backface culling for this particular polygon and allows + it to be viewed from either side. + + + draw_order { number } + + This defines the fixed drawing order of this polygon. Drawing + order is very important when using certain kinds of transparency. + Normally, Performer will draw all opaque objects first, then all + transparent objects from back to front. Sometimes the + back-to-front sorting can fail, especially in the case of a small + polygon immediately in front of a large polygon. In this case, it + may be necessary to explicitly give the order in which the + polygons should be drawn. + + All objects with a given draw-order will be drawn before all + objects with a higher draw-order. Objects with a zero or + unspecified draw-order will be drawn first if they are opaque, and + in order from back to front if they are transparent. A negative + draw-order is not allowed. + + + name { + [attributes] + { + indices + { pool-name } + } +} + + A PointLight is a set of single points. One point is drawn for each + vertex listed in the . Normals, textures, and colors may + be specified for PointLights, as well as draw-order, plus one + additional attribute valid only for PointLights and Lines: + + thick { number } + + This specifies the size of the PointLight (or the width of a + line), in pixels, when it is rendered. This may be a + floating-point number, which is meaningful only when antialiasing + is in effect. The default is 1.0. + + + + name { + [attributes] + { + indices + { pool-name } + } +} + + A Line is a connected set of line segments. The listed N vertices + define a series of N-1 line segments, drawn between vertex 0 and + vertex 1, vertex 1 and vertex 2, etc. As with a PointLight, + normals, textures, colors, draw-order, and the "thick" attribute are + all valid. + + + +PARAMETRIC DESCRIPTION ENTRIES + +The following entries define parametric curves and surfaces. +Generally, the player supports these only in the abstract; they're not +geometry in the true sense but do exist in the scene graph and may +have specific meaning to the show code. However, the player can +create visible representations of these parametrics to aid +visualization. + +These entries might also have meaning to tools outside of the player, +such as a smart polygon mesher. + +In general, dynamic attributes such as morphs and joint assignment are +legal for the control vertices of the following parametrics, but the +player doesn't support them and will always create static curves and +surfaces. Non-player tools, however, may respect them. + + + name { + [attributes] + + [ { t1 t2 t3 ... } ] + { indices { pool-name } } +} + + A Bezier curve will generally be used to describe a motion path + (though NURBS curves can be used for this purpose as well; see + below). The player translates entries into Hermite + curves internally. + + Bezier curves can have any number of dimensions from one to three. + Accordingly, the referenced vertices may be defined for x, x y, or x + y z. All vertices should be defined over the same number of + dimensions. + + A Bezier curve consists of a sequence of curve segments defined by + four vertices taken three at a time. The first four vertices define + the first curve segment, the vertices four through seven define the + second curve segment, and so on. The total number of vertices must + be one more than a multiple of three. + + is a list of (n-1)/3 values, where n is the number of + Bezier control vertices. Each value corresponds to the length in + parametric space of the corresponding curve segment; the total curve + is defined over the parametric range [0,sum(TLengths)]. If this + entry is omitted, the curve will be defined over the range [0,1]. + + The following attributes may be defined: + + type { curve-type } + + This defines the semanting meaning of this curve, either XYZ, HPR, + or T. If the type is XYZ, the curve will automatically be + transformed between Y-up and Z-up if necessary; otherwise, it will + be left alone. + + subdiv { num-segments } + + If this scalar is given and nonzero, the player will create a + visible representation of the curve when the scene is loaded. The + number represents the number of line segments to draw to + approximate the curve. + + { r g b a [morph-list] } + + This specifies the color of the overall curve. + + + Bezier control vertices may also be given color and/or morph + attributes (though the player ignores these), but and + entries do not apply to Bezier vertices. + + Each control vertex may optionally be given a continuity type, + defined with a " continuity-type { type }" within the + entry. The type may be one of Cut, Free, G1, or Smooth. + This enforces the respective continuity restriction on the + neighboring vertices. + + + + { + [attributes] + + { order } + { knot-list } + { indices { pool-name } } +} + + A NURBS curve is a more general parametric than a Bezier curve. It + can be used within the show anywhere a Bezier might have been used, + though a NURBS curve as the player creates it is more difficult to + modify at runtime. + + The order is equal to the degree of the polynomial basis plus 1. It + must be an integer in the range [1,4]. + + The number of vertices must be equal to the number of knots minus the + order. + + Each control vertex of a NURBS is defined in homogeneous space with + four coordinates x y z w (to convert to 3-space, divide x, y, and z + by w). The last coordinate is always the homogeneous coordinate; if + only three coordinates are given, it specifies a curve in two + dimensions plus a homogeneous coordinate (x y w). + + The valid attributes are the same for a as that for a + , above. Similarly, NURBS vertices may be given color + and/or morph attributes. + + + + name { + [attributes] + + { u-order v-order } + { u-knot-list } + { v-knot-list } + + { + indices + { pool-name } + } +} + + A NURBS surface is an extension of a NURBS curve into two parametric + dimensions, u and v. NURBS surfaces may be given the same set of + attributes assigned to polygons, except for normals: , + , , , and draw-order are all valid attributes + for NURBS. NURBS vertices, similarly, may be colored or morphed, + but and entries do not apply to NURBS vertices. The + attributes may also include and entries; see + below. + + To have the player create a visualization of a NURBS surface, the + following two attributes should be defined as well: + + U-subdiv { u-num-segments } + V-subdiv { v-num-segments } + + These define the number of subdivisions to make in the U and V + directions to represent the surface. A uniform subdivision is + always made, and trim curves are not respected (though they will + be drawn in if the trim curves themselves also have a subiv + parameter). This is only intended as a cheesy visualization. + + + The same sort of restrictions on order and knots applies to NURBS + surfaces as do to NURBS curves. The order and knot description may + be different in each dimension. + + The surface must have u-num * v-num vertices, where u-num is the + number of u-knots minus the u-order, and v-num is the number of + v-knots minus the v-order. All vertices must come from the same + vertex pool. The nth (zero-based) index number defines control + vertex (u, v) of the surface, where n = (v * u-num) + u. Thus, it + is the u coordinate which changes faster. + + As with the NURBS curve, each control vertex is defined in + homogeneous space with four coordinates x y z w. + + + A NURBS may also contain curves on its surface. These are one or + more nested entries included with the attributes; these + curves are defined in the two-dimensional parametric space of the + surface. Thus, these curve vertices should have only two dimensions + plus the homogeneous coordinate: u v w. A curve-on-surface has no + intrinsic meaning to the surface, unless it is defined within a + entry, below. + + Finally, a NURBS may be trimmed by one or more trim curves. These + are special curves on the surface which exclude certain areas from + the NURBS surface definition. The inside is specified using two + rules: an odd winding rule that states that the inside consists of + all regions for which an infinite ray from any point in the region + will intersect the trim curve an odd number of times, and a curve + orientation rule that states that the inside consists of the regions + to the left as the curve is traced. + + Each trim curve contains one or more loops, and each loop contains + one or more NURBS curves. The curves of a loop connect in a + head-to-tail fashion and must be explicitly closed. + + The trim curve syntax is as follows: + + { + { + { + { order } + { knot-list } + + { indices { pool-name } } + } + [ { ... } ... ] + } + [ { ... } ... ] + } + + + + +MORPH DESCRIPTION ENTRIES + +Morphs are linear interpolations of attribute values at run time, +according to values read from an animation table. In general, vertex +positions, surface normals, texture coordinates, and colors may be +morphed. + +A morph target is defined by giving a net morph offset for a series of +vertex or polygon attributes; this offset is the value that will be +added to the attribute when the morph target has the value 1.0. At +run time, the morph target's value may be animated to any scalar value +(but generally between 0.0 and 1.0); the corresponding fraction of the +offset is added to the attribute each frame. + +There is no explicit morph target definition; a morph target exists +solely as the set of all offsets that share the same target name. +Formerly, the target name must have been a nonnegative integer, but +this restriction no longer applies. + +The following types of morph offsets may be defined, within their +corresponding attribute entries (an older, still supported, syntax +defined the target name after the first curly brace, instead of before +it): + + target { x y z } + + A position delta, valid within a entry or a entry. + The given offset vector, scaled by the morph target's value, is + added to the vertex or CV position each frame. + + + target { x y z } + + A normal delta, similar to the position delta, valid within a + entry (for vertex or polygon normals). The given offset + vector, scaled by the morph target's value, is added to the normal + vector each frame. + + + target { u v } + + A texture-coordinate delta, valid within a entry (within a + entry). The given 2-valued offset vector, scaled by the + morph target's value, is added to the vertex's texture coordinates + each frame. + + + target { r g b a } + + A color delta, valid within an entry (for vertex or polygon + colors). The given 4-valued offset vector, scaled by the morph + target's value, is added to the color value each frame. + + + + +GROUPING ENTRIES + + name { group-body } + + A node is the primary means of providing structure to the egg + file. Groups can contain vertex pools and polygons, as well as + other groups. The egg loader translates nodes directly into + pfGroup nodes in the scene graph. In addition, the following + entries can be given specifically within a node to specify + attributes of the group: + + GROUP BINARY ATTRIBUTES + + These attributes may be either on or off; they are off by default. + They are turned on by specifying a non-zero "boolean-value". + + { boolean-value } + + This indicates that this group contains geometry that might need + to be repositioned interactively relative to the rest of the + scene. The egg loader interprets this by creating a pfDCS node + instead of a pfGroup node. This also implies , below. + + { boolean-value } + + This indicates that this group contains geometry whose textures + might need to be separately managed. A special xpfTexListNode is + created for this group. This also implies . + + { boolean-value } + + This indicates that the show code might need a pointer to this + particular group. If any nodes in the egg file have the + flag set, the egg loader creates an xpfModelRoot node as the root + node of the file, which keeps a short list of all the nodes with + the flag set. This allows quick access to these nodes by + the show code at run time. In the old player this also implied + , but it doesn't any more. + + + { boolean-value } + { boolean-value } + + These two attributes are lexically equivalent, and indicate that + this group begins a character. An xpfCharacter node will be created + for this group. + + { dart-type } + + This is another syntax for the flag. The dart-type string + should be one of either "async" or "sync". This tells the egg + loader to optimize the character for asynchronous or synchronous + animation, respectively. In the absence of this dart-type, the + egg loader will choose the most general. + + { boolean-value } + + This attribute indicates that the child nodes of this group + represent a series of animation frames that should be + consecutively displayed. In the absence of an "fps" scalar for + the group (see below), the egg loader creates a pfSwitch node, and + it the responsibility of the show code to perform the switching. + If an fps scalar is defined and is nonzero, the egg loader creates + a pfSequence node instead, which automatically cycles through its + children. + + + GROUP SCALARS + + fps { frame-rate } + + This specifies the rate of animation for a pfSequence (created + when the Switch flag is specified, see above). A value of zero + indicates a pfSwitch should be created instead. + + no_fog { boolean-value } + + This specifies that geometry at or below this node should not be + affected by fog. It will be created with the appropriate GeoState + to have fog disabled. + + draw_order { number } + + This specifies the drawing order for all polygons at or below this + node that do not explicitly set their own drawing order. See the + description of draw-order for geometry attributes, above. + + write_through { boolean-value } + + If this is present and boolean-value is non-zero, it places the + geometry at this level and below in a special drawing bin that is + drawn first and with the z-buffer set to write-through. This is + an optimization to avoid the initial screen clear when rendering a + completely enclosed scene; the flag should be set on the enclosing + geometry, which must be convex. + + post_lp { boolean-value } + + If this is present and boolean-value is non-zero, it places the + geometry at this level and below in a drawing bin that is drawn + after everything else in the scene, including light points. This + must be done particularly for semitransparent geometry which must + not obscure light points. + + decal { boolean-value } + + If this is present and boolean-value is non-zero, it indicates + that the geometry *below* this level is coplanar with the geometry + *at* this level, and the geometry below is to be drawn as a decal + onto the geometry at this level. This means the geometry below + this level will be rendered "on top of" this geometry, but without + the Z-fighting artifacts one might expect without the use of the + decal flag. + + collide-mask { value } + from-collide-mask { value } + into-collide-mask { value } + + Sets the CollideMasks on the collision nodes and geometry nodes + created at or below this group to the indicated values. These + are bits that indicate which objects can collide with which + other objects. Setting "collide-mask" is equivalent to setting + both "from-collide-mask" and "into-collide-mask" to the same + value. + + The value may be an ordinary decimal integer, or a hex number in + the form 0x000, or a binary number in the form 0b000. + + + OTHER GROUP ATTRIBUTES + + { type } + + This entry indicates that all geometry defined at or below this + group level is part of a billboard that will rotate to face the + camera. Type is either "axis" or "point", describing the type of + rotation. + + Billboards rotate about their local axis. In the case of a Y-up + file, the billboards rotate about the Y axis; in a Z-up file, they + rotate about the Z axis. Point-rotation billboards rotate about + the origin. + + There is an implicit around billboard geometry. This + means that the geometry within a billboard is not specified in + world coordinates, but in the local billboard space. Thus, a + vertex drawn at point 0,0,0 will appear to be at the pivot point + of the billboard, not at the origin of the scene. + + { + { + in out [fade] { x y z } + } + } + + The subtree beginning at this node and below represents a single + level of detail for a particular model. Sibling nodes represent + the additional levels of detail. The geometry at this node will + be visible when the point (x, y, z) is closer than "in" units, but + further than "out" units, from the camera. If "fade" is + specified, it enables fade-LOD, and specifies the distance over + which to fade between this level of detail and the next-closer + one. + + + name { type [flags] } + + This entry indicates that geometry defined at this group level is + actually an invisible collision surface, and is not true geometry. + The geometry is used to define the extents of the collision + surface. If there is no geometry defined at this level, then a + child is searched for with the same collision type specified, and + its geometry is used to define the extent of the collision + surface (unless the "descend" flag is given; see below). + + Valid types so far are: + + Plane + + The geometry represents an infinite plane. The first polygon + found in the group will define the plane. + + Polygon + + The geometry represents a single polygon. The first polygon is + used. + + Polyset + + The geometry represents a complex shape made up of several + polygons. This collision type should not be overused, as it + provides the least optimization benefit. + + Sphere + + The geometry represents a sphere. The vertices in the group are + averaged together to determine the sphere's center and radius. + + InverseSphere + + The geometry represents a sphere with the normal inverted, to + make a hollow spherical shell. + + Geode + + The geometry is not a collision surface after all, but is in + fact true geometry. This is intended to be used in conjunction + with one or more flags to define collision properties for normal + geometry. + + + + The flags may be any zero or more of: + + event + + Throws the name of the entry, or the name of the + surface if the entry has no name, as an event whenever + an avatar strikes the solid. This is the default if the + entry has a name. + + intangible + + Rather than being a solid collision surface, the defined surface + represents a boundary. The name of the surface will be thrown + as an event when an avatar crosses into the interior, and + name-out will be thrown when an avater exits. + + descend + + Instead of creating only one collision object of the given type, + each group descended from this node that contains geometry will + define a new collision object of the given type. The event + name, if any, will also be inherited from the top node and + shared among all the collision objects. + + keep + + Don't discard the visible geometry after using it to define a + collision surface; create both an invisible collision surface + and the visible geometry. + + solid + + This has meaning only when the type is Polygon or Polyset. This + creates the polygon(s) as "solid" polygons, meaning no mover is + allowed to occupy the space immediately behind them. In the + absence of this flag, once a mover passes completely through a + polygon it is considered no longer intersecting with it. + + center + + Sets the collide-center property on the collision solid, so that + it collides with the center point of each mover, disregarding + the mover's radius. This implies "solid". + + turnstile + + Sets the turnstile property on the collision solid, so that + movers approaching from behind the solid will smoothly pass + through instead of being forcefully yanked to the front. This + is intended to implement a one-way wall, through which players + can go forwards but not back. Presently, this is only + implemented for planes, polygons, and polysets. + + + { type } + + This is a short form to indicate one of several pre-canned sets of + attributes. Type may be any word, and a Config definition will be + searched for by the name "egg-object-type-word", where "word" is + the type word. This definition may contain any arbitrary egg + syntax to be parsed in at this group level. + + A number of predefined ObjectType definitions are provided: + + barrier + + This is equivalent to { Polyset descend }. The + geometry defined at this root and below defines an invisible + collision solid. + + solidpoly + + This is equivalent to { Polyset descend solid }. See + the description of the "solid" property, above. + + turnstile + + This is equivalent to { Polyset descend turnstile }. See + the description of the "turnstile" property, above. + + trigger + + This is equivalent to { Polyset descend intangible }. + The geometry defined at this root and below defines an invisible + trigger surface. + + eye-trigger + + Equivalent to { Polyset descend intangible center }. + This defines a trigger polygon whose point of collision is the + center of the mover; presumably this polygon will be used to + trigger visibility events. + + bubble + + Equivalent to { Sphere keep descend }. A collision + bubble is placed around the geometry, which is otherwise + unchanged. + + missile + + Equivalent to missile { Sphere keep descend event }. The + geometry defined at this root and below defines a missile + object, which will be surrounded by a collision sphere and named + "missile". + + ghost + + Equivalent to collide-mask { 0 }. It means that the + geometry beginning at this node and below should never be + collided with--characters will pass through it. + + backstage + + This has no equivalent; it is treated as a special case. It + means that the geometry at this node and below should not be + translated. This will normally be used on scale references and + other modelling tools. + + + + { transform-definition } + + This specifies a matrix transform at this group level. This + defines a local coordinate space for this group and its + descendents. Vertices are still specified in world coordinates + (in a vertex pool), but any geometry assigned to this group will + be inverse transformed to move its vertices to the local space. + + The transform definition may be any sequence of zero or more of + the following. Transformations are post multiplied in the order + they are encountered to produce a net transformation matrix. + + { x y z } + { degrees } + { degrees } + { degrees } + { degrees x y z } + { x y z } + + { + 00 01 02 + 10 11 12 + 20 21 22 + } + + { + 00 01 02 03 + 10 11 12 13 + 20 21 22 23 + 30 31 32 33 + } + + + { indices { pool-name } } + + This moves geometry created from the named vertices into the + current group, regardless of the group in which the geometry is + actually defined. See the description, below. + + + name { group-body } + + An node is exactly like a node, except that + vertices referenced by geometry created under the node + are not assumed to be given in world coordinates, but are instead + given in the local space of the node itself (including + any transforms given to the node). + + In other words, geometry under an node is defined in + local coordinates. In principle, similar geometry can be created + under several different nodes, and thus can be positioned + in a different place in the scene each instance. This doesn't + necessarily imply the use of shared geometry in the Performer scene + graph, but see the syntax, below. + + This is particularly useful in conjunction with a entry, to + load external file references at places other than the origin. + + A special syntax of entries does actually create shared + geometry in the scene graph. The syntax is: + + name { + { group-name } + [ { group-name } ... ] +} + + In this case, the referenced group name will appear as a duplicate + instance in this part of the tree. Local transforms can be applied + and are relative to the reference group's transform. + + + name { [transform] [ref-list] [joint-list] } + + A joint is a highly specialized kind of grouping node. A tree of + joints is used to specify the skeletal structure of an animated + character. + + A joint may only contain one of three things. It may contain a + entry, as above, which defines the joint's unanimated + (rest) position; it may contain lists of assigned vertices or CV's; + and it may contain other joints. + + A tree of nodes only makes sense within a character + definition, which is created by applying the flag to a group. + See , above. + + The vertex assignment is crucial. This is how the geometry of a + character is made to move with the joints. The character's geometry + is actually defined outside the joint tree, and each vertex must be + assigned to one or more joints within the tree. + + This is done with zero or more entries per joint, as the + following: + + { indices [ membership { m }] { pool-name } } + + This is syntactically similar to the way vertices are assigned to + polygons. Each entry can assign vertices from only one + vertex pool (but there may be many entries per joint). + Indices is a list of vertex numbers from the specied vertex pool, in + an arbitrary order. + + The membership scalar is optional. If specified, it is a value + between 0.0 and 1.0 that indicates the fraction of dominance this + joint has over the vertices. This is used to implement + soft-skinning, so that each vertex may have partial ownership in + several joints. + + The entry may also be given to ordinary nodes. + In this case, it treats the geometry as if it was parented under the + group in the first place. Non-total membership assignments are + meaningless. + + + name { table-list } + name { table-body } + + A table is a set of animated values for joints. A tree of tables + with the same structure as the corresponding tree of joints must be + defined for each character to be animated. Such a tree is placed + under a node, which provides a handle within the player to + the tree as a whole. + + Bundles may only contain tables; tables may contain more tables, + bundles, or any one of the following ( and entries + are optional, and default as shown): + + name { + fps { 24 } + { values } + } + + This is a table of scalar values, one per frame. This may be + applied to a morph slider, for instance. + + + name { + fps { 24 } + order { sphrt } + contents { ijkphrxyz } + { values } + } + + This is a table of matrix transforms, one per frame, such as may + be applied to a joint. The "contents" string consists of a subset + of the letters "ijkphrxyz", where each letter corresponds to a + column of the table; is a list of numbers of length(contents) + * num_frames. Each letter of the contents string corresponds to a + type of transformation: + + i, j, k - scale in x, y, z directions, respectively + p, h, r - rotate by pitch, heading, roll + x, y, z - translate in x, y, z directions + + The net transformation matrix specified by each row of the table + is defined as the net effect of each of the individual columns' + transform, according to the corresponding letter in the contents + string. The order the transforms are applied is defined by the + order string: + + s - all scale transforms + p, h, r - individual rotate transforms + t - all translation transforms + + + name { + fps { 24 } + order { sphrt } + i { ... } + j { ... } + ... + } + + This is a variant on the entry, where each column of + the table is entered as a separate table. This syntax + reflects an attempt in the old player development to save memory + by not requiring repetition of values for columns that did not + change value during an animation sequence. + + + name { + width { table-width } + fps { 24 } + { values } + } + + This is a table of vertex positions, normals, texture coordinates, + or colors. These values will be subsituted at runtime for the + corresponding values in a . The name of the + table should be "coords", "norms", "texCoords", or "colors", + according to the type of values defined. The number table-width + is the number of floats in each row of the table. In the case of + a coords or norms table, this must be 3 times the number of + vertices in the corresponding dynamic vertex pool. (For texCoords + and colors, this number must be 2 times and 4 times, respectively.) + + + +MISCELLANEOUS + + { filename } + + This includes a copy of the referenced egg file at the current + point. This is usually placed under an node, so that the + current transform will apply to the geometry in the external file. + The extension ".egg" is implied if it is omitted. + + As with texture filenames, the filename may be a relative path, in + which case it is relative to the directory in which the current egg + file was found. If no directory prefix is specified, the current + egg file's directory is searched first, and then $PFPATH is + searched. + diff --git a/panda/src/doc/graph-ex.flo b/panda/src/doc/graph-ex.flo new file mode 100644 index 0000000000000000000000000000000000000000..bd40e79aba1c49d3bb974c7415e339e553143a22 GIT binary patch literal 29184 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 N!K|ZaVW-RW`ahiz{W|~v literal 0 HcmV?d00001 diff --git a/panda/src/doc/graph-ex.gif b/panda/src/doc/graph-ex.gif new file mode 100644 index 0000000000000000000000000000000000000000..bccfff1a914474e1ea52832b7369a8e695bef991 GIT binary patch literal 3659 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 StJo1+SP{M6B0xojkoy-mD)VCi literal 0 HcmV?d00001 diff --git a/panda/src/doc/gsg.flo b/panda/src/doc/gsg.flo new file mode 100644 index 0000000000000000000000000000000000000000..a762b34ecf615e3847249ecd656090b6d5b869b1 GIT binary patch literal 19456 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 literal 0 HcmV?d00001 diff --git a/panda/src/doc/gsg.gif b/panda/src/doc/gsg.gif new file mode 100644 index 0000000000000000000000000000000000000000..36c8ea04271bd71ec28ee0d1eb82f6d84cc9167c GIT binary patch literal 5882 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 literal 0 HcmV?d00001 diff --git a/panda/src/doc/howto.install_panda_on_unix b/panda/src/doc/howto.install_panda_on_unix new file mode 100644 index 0000000000..c1db389b90 --- /dev/null +++ b/panda/src/doc/howto.install_panda_on_unix @@ -0,0 +1,74 @@ + 0) (be in tcsh) + 1) cd ~ + 2) anonymous ftp to ftp.digitigrade.com + 3) (in ftp) cd panda-new + 4) (in ftp) binary + 5) (in ftp) hash + 6) (in ftp) prompt + 7) (in ftp) mget * + 8) (in ftp) bye + 9) su root + 10) (enter root password) + 11) cd /usr/local + 12) mkdir etc + 13) mkdir trees + 14) chmod 777 etc trees + 15) cd bin + 16) ln -s `which bash` ./bash + 17) ln -s `which perl` ./perl + 18) exit + 19) cd /usr/local/etc + 20) tar -zxf ~/vspec.tgz + 21) cd ../trees + 22) tar -zxf ~/tool.tgz + 23) tar -zxf ~/panda.tgz + 24) tar -zxf ~/direct.tgz + 25) tar -zxf ~/egg.tgz + 26) setenv TOOL /usr/local/trees/tool + 27) source $TOOL/etc/tool.cshrc + 28) cd $TOOL + 29) ctmake + 30) (put the mozilla distribution in /usr/local/mozilla, + build nspr, make sure that if the directory in + /usr/local/mozilla/dist is not Linux2.2.10_x86_PTH_DBG.OBJ + that you make a link that is called that to whatever + name it installed to) + 31) (have some flavor of GL installed. Mesa is free + (http://www.mesa3d.org/), MetroGL is also good (though + commercial). If you have a modern Voodoo card, and want + to play with the DRI stuff, that will be a whole other + set of instructions. It is non-trivial.) + 32) cta panda + 33) cd $PANDA + 34) ctmake configfiles + 35) ctuna panda + 36) cta panda + 37) setenv LD_LIBRARY_PATH + "/usr/local/mozilla/dist/Linux2.2.10_x86_PTH_DBG.OBJ/lib:$LD_LIBRARY_PATH" + + 38) ctmake + 39) add the following to your ~/.login + if ( $?CTPROJS ) unsetenv CTPROJS + setenv TOOL /usr/local/trees/tool + source $TOOL/etc/tool.cshrc + + setenv LD_LIBRARY_PATH + "/usr/local/mozilla/dist/Linux2.2.10_x86_PTH_DBG.OBJ/lib:$LD_LIBRARY_PATH" + + setenv CONFIG_CONFIG ":configpath=ETC_PATH" + 40) add the following to your ~/.cshrc + if ( $?TOOL ) then + source $TOOL/etc/tool.alias + endif + 41) have the following in $PANDA/etc/Configrc + load-display glxdisplay + load-gsg glgsg + + egg-path /usr/local/trees/egg:. + chan-config-sanity-check #f + multipass-viz none + 42) cd testbed + 43) ./demo smiley.egg + + For info on what the hell to do with Squeak (other then ignore it) + you'll need to talk to Jesse and Joe. \ No newline at end of file diff --git a/panda/src/doc/howto.install_panda_on_windows b/panda/src/doc/howto.install_panda_on_windows new file mode 100644 index 0000000000..c6d9627205 --- /dev/null +++ b/panda/src/doc/howto.install_panda_on_windows @@ -0,0 +1,187 @@ +To install Panda3D on WinNT or Win98: +------------------------------------- +Notes: + Lines preceeded by "#" are comments. + Lines preceeded by ">" are instructions to be typed on the command line. + On WinNT, make sure your system clock is not set to GMT (only worry about + this if your makedepend seems out of sync, i.e. you constantly reinstall + old files over more current ones). + Opengl appears to be installed with the os for both WinNT and Win98, but I + read somewhere that we might want to do our own version control for + this eventually using "VerInstallFile" to install our own version of + OpenGl (At any rate, "HAVE_GL" defaults to true in the .cshrc file. + "Touch" doesn't work on WinNT or Win98 currently. + Make sure you run ftp in "binary" mode to fetch tarballs. + "ftp" doesn't currently read "backspace" in rxvt, so you'll just have to be + precise at the ftp prompt for now ("delete" doesn't work either). + If this really bugs you, you can create a shortcut on the Desktop to + "tcsh.exe" and run ftp properly from there rather than from rxvt. + Occasionally, a freshly opened shell will hang in the middle of attaching + or something. If this happens, go ahead and kill the tcsh process (or + ftp or rxvt process) using the Task Manager and try again. + Make sure your machine has enough disk space - I'd start with the largest + open space on any of your partitions. + Make sure your machine has networking (IP, gateway, etc.) so you can use + ftp to get to dumbo. + +Step 1: Install Visual C++ (v6.0) + # On WinNT, you will be prompted to register environment variables for + # running the compiler from the command line - do it. + # I also recommend installing the full MSDN Library documentation. + +Step 2: Install Cygwin (v1.0) + # Install Cygwin from the cd + # To get things started, you can run a bash shell by going to the "Start" + # button at the bottom left of your screen, selecting "Programs", + # "Cygwin" and "bash". I usually run "tcsh" at the bash prompt to make + # things easier. + # Set up the Cygwin environment: + # Check for directories /bin /etc /tmp /usr and create them using + # mkdir if they don't exist. Also make the following directories: + > mkdir /mscommon + > mkdir /msvc98 + # Mount directories (This assumes VC++ was installed in C:\Program Files): + > mount -s C:/Program\ Files/Microsoft\ Visual\ Studio/Common /mscommon + > mount -s C:/Program\ Files/Microsoft\ Visual\ Studio/VC98 /msvc98 + # Create some links expected by the attach scripts: + > mkdir /usr/local + > mkdir /usr/local/bin + > ln -s /contrib/bin/tcsh /bin/csh + > ln -s /contrib/bin/perl /usr/local/bin/perl + > ln -s /bin/bash /usr/local/bin/bash + +Step 3: Install winpandatools + # Winpandatools currently includes rxvt, glut, and nspr, as well as + # .cshrc, Configrc, the .vspec files, and Squeak. + # Get /for/program/tarballs/winpandatools.tgz using ftp. + # While your're here you can also grab tool.tgz, panda.tgz, and direct.tgz + > cd / + > tar zxvf winpandatools.tgz + # Create a HOME directory: + > mkdir /usr/ + > cp /usr/local/winpandatools/.cshrc /usr/ + > cp /usr/local/winpandatools/Configrc /usr/ + # On Windows for some reason I couldn't tar these links up properly so + # you'll need to create them by hand: + > ln -s /usr/local/squeak/Squeak.exe /usr/local/bin/squeak + > ln -s /usr/local/squeak/Squeak.exe /usr/local/bin/headlessSqueak + # On WinNT: + > cp /usr/local/winpandatools/winNT/libnspr3.* /usr/lib/ + # On Win98: + > cp /usr/local/winpandatools/win98/libnspr3.* /usr/lib/ + +Step 4: (Optional) Build NSPR + # Skip this step if you don't care to mess with nspr code. + # Winpandatools contains prebuilt nspr libraries, but if you want to build + # your own, do the following: + # Grab the tarball /for/program/tarballs/nspr.tgz and install it in + # /usr/local + > cd /usr/local + > tar zxvf nspr.tgz + > source /usr/local/mozilla/nsprpub/env.sh + > cd /usr/local/buildtools/windows + > install.bat + # To build optimized: + > setenv BUILD_OPT 1 + > unsetenv MOZ_DEBUG + # On WinNT only: + > cd /bin + > ln -s /usr/local/buildtools/windows/Cygwin/bin/nsinstall.exe . + > cd /usr/local/mozilla/nsprpub + # If you want nspr to use the debug runtime library: + > setenv USE_DEBUG_RTL 1 + > make release + > cd /usr/lib + # Remove the pre-built libs that came from winpandatools: + > rm libnspr3.dll libnspr3.lib + # On WinNT: + > ln -s /usr/local/mozilla/dist/WINNT4.0_OPT.OBJ/libnspr3.dll . + > ln -s /usr/local/mozilla/dist/WINNT4.0_OPT.OBJ/libnspr3.lib . + # On Win98: + > ln -s /usr/local/mozilla/dist/WIN954.0_OPT.OBJ/libnspr3.dll . + > ln -s /usr/local/mozilla/dist/WIN954.0_OPT.OBJ/libnspr3.lib . + +Step 5: Set $HOME environment variable + # On WinNT use the Registry: + # Select "My Computer" icon from the desktop. + # Select "Control Panel" icon. + # Select "System" icon. + # Select "Environment" tab. + # Create the variable "HOME" with the value "/usr/". + # Click the "Set" button. + # Click the "Apply" button. + # Now open a new shell to get the changes. + # On Win98 use AUTOEXEC.BAT: + # Change the "HOME=/usr/" line in + # /usr/local/winpandatools/AUTOEXEC.BAT + > cp /usr/local/winpandatools/AUTOEXEC.BAT C: + # Note: if you want to edit your existing AUTOEXEC.BAT, you need + # to use "edit.exe" in order to generate the correct "newline" + # character (^M). + # Note: We've seen a problem where we can't source ~/.cshrc if the home + # directory /usr/ does not match the user name exactly. To be safe, + # be sure and name your home directory to be /usr/. + +Step 6: Create a shortcut to rxvt + # Using Windows file access, "Find" rxvt.exe (it's in + # /usr/local/winpandatools/). + # Drag the rxvt.exe icon to the Desktop. + # Click the right mouse button on the rxvt.exe icon on the Desktop + # and select "Properties". + # Select the "Shortcut" tab. + # Set "Target" to be C:\Cygwin\usr\local\winpandatools\rxvt.exe -fn courier + # -sl 10000 -bg bisque -e tcsh -l + # Select "OK". + # Note: You need to install $TOOL before the shortcut will be able to + # attach to $TOOL correctly (Step 7). + +Step 7: Grab the tarballs + > mkdir /install + > cd /install + # Get tool.tgz, panda.tgz, and direct.tgz from /for/program/tarballs + # or copy them if they have already been ftp'd over. + > tar zxvf tool.tgz + > tar zxvf panda.tgz + > tar zxvf direct.tgz + +Step 8: Build $TOOL + # Now you must use the rxvt shortcut to open a shell - it should attach + # to $TOOL automatically. + # You can ignore the tcsh warnings (don't recognize "xterm") - this is + # a Cygwin - rxvt conflict that's not trivial to resolve. + # Choose which optimize level you want: + # setenv OPTIMIZE 1 (default) - fast build, runs slower + # setenv OPTIMIZE 2 (recommended) - slower build, faster, has asserts + # setenv OPTIMIZE 3 - debug symbols, but no asserts + # setenv OPTIMIZE 4 (release mode) - no debug symbols + > setenv OPTIMIZE 2 + > cd $TOOL + > ctmake + +Step 9: (Optional) Install DirectX + # Install DirectX 7.0 (by default into C:\mssdk). + > cp C:/mssdk/include/*.* /msvc98/Include/ + > cp C:/mssdk/lib/d3dim.lib /msvc98/Lib/ + > cp C:/mssdk/lib/ddraw.lib /msvc98/Lib/ + # Add the following to ~/.cshrc: + # setenv HAVE_DX yes + +Step 9: Build $PANDA + > cta panda + > cd $PANDA + > ctmake install-configfiles + # Now you need to attach again, so start over with a new shell. + > exit + # Open a new shell using the rxvt shortcut. + > cta panda + > cd $PANDA + > ctmake + # You can now run "demo" to test the install and build - you should + # see a window pop up with a triangle in it. To see the triangle with + # a texture on it, you'll need to be in $PANDA/src/all/testbed/ when + # you run "demo". + +Step 10: Build $DIRECT + > cta direct + > cd $DIRECT + > ctmake diff --git a/panda/src/doc/image-trinity.flo b/panda/src/doc/image-trinity.flo new file mode 100644 index 0000000000000000000000000000000000000000..fe514843736442d2080cbfaba1f8e2c1d4e53d57 GIT binary patch literal 22016 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 V^7Y#Y>KPBIrfo0^zr5dn{ui!^#;O1S literal 0 HcmV?d00001 diff --git a/panda/src/doc/image-trinity.gif b/panda/src/doc/image-trinity.gif new file mode 100644 index 0000000000000000000000000000000000000000..c59d8d3d615e9017db20c5b8535f05470a971a3a GIT binary patch literal 4951 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 literal 0 HcmV?d00001 diff --git a/panda/src/doc/light.flo b/panda/src/doc/light.flo new file mode 100644 index 0000000000000000000000000000000000000000..24e51366307f3f2b4bad953ae2616612d0946d4e GIT binary patch literal 19968 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 literal 0 HcmV?d00001 diff --git a/panda/src/doc/node.flo b/panda/src/doc/node.flo new file mode 100644 index 0000000000000000000000000000000000000000..730a650539c8ecaf91abf330ac5b6ff2ae496fc0 GIT binary patch literal 24064 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 literal 0 HcmV?d00001 diff --git a/panda/src/doc/node.gif b/panda/src/doc/node.gif new file mode 100644 index 0000000000000000000000000000000000000000..c8cdfc8f5fe0a9a4f422391f3095acb62bd45ffa GIT binary patch literal 6518 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�QM0b!vf9&*zWlN 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 RqCzf;Nfrz1=@Sg${{mb(Dcb-5 literal 0 HcmV?d00001 diff --git a/panda/src/doc/sampleClass.I b/panda/src/doc/sampleClass.I new file mode 100644 index 0000000000..df026a96e1 --- /dev/null +++ b/panda/src/doc/sampleClass.I @@ -0,0 +1,52 @@ +// Filename: sampleClass.I +// Created by: drose (10Jun00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: SampleClass::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SampleClass:: +SampleClass(const SampleClass ©) : + _public_data_member(copy._public_data_member), + _private_data_member(copy._private_data_member), + _flag(copy._flag) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: SampleClass::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SampleClass:: +~SampleClass() { +} + +//////////////////////////////////////////////////////////////////// +// Function: SampleClass::set_flag +// Access: Public +// Description: A few sentences describing the purpose of flag, the +// legal values it may be set to, and the implications +// of setting it to certain values. +//////////////////////////////////////////////////////////////////// +INLINE void SampleClass:: +set_flag(int flag) { + _flag = flag; +} + +//////////////////////////////////////////////////////////////////// +// Function: SampleClass::get_flag +// Access: Public +// Description: A few sentences describing the purpose of flag, the +// legal values it may be set to, and the implications +// of setting it to certain values. +//////////////////////////////////////////////////////////////////// +INLINE int SampleClass:: +get_flag() const { + return _flag; +} + diff --git a/panda/src/doc/sampleClass.cxx b/panda/src/doc/sampleClass.cxx new file mode 100644 index 0000000000..8489eac1f7 --- /dev/null +++ b/panda/src/doc/sampleClass.cxx @@ -0,0 +1,63 @@ +// Filename: sampleClass.cxx +// Created by: drose (10Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "sampleClass.h" + +TypeHandle SampleClass::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: SampleClass::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +SampleClass:: +SampleClass() { +} + +//////////////////////////////////////////////////////////////////// +// Function: SampleClass::public_method +// Access: Public +// Description: A few sentences describing what public_method is +// supposed to do and why you'd want to call it. +//////////////////////////////////////////////////////////////////// +int SampleClass:: +public_method() { + switch (_private_data_member) { + case NE_case_one: + return 0; + + case NE_case_two: + return _flag; + + default: + return -1; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SampleClass::protected_method +// Access: Protected +// Description: A few sentences describing what protected_method is +// supposed to do. +//////////////////////////////////////////////////////////////////// +bool SampleClass:: +protected_method() { + if (_flag > 0) { + _flag--; + return false; + } else { + return true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SampleClass::private_method +// Access: Private +// Description: A few sentences describing what private_method is +// supposed to do. +//////////////////////////////////////////////////////////////////// +void SampleClass:: +private_method() { +} diff --git a/panda/src/doc/sampleClass.h b/panda/src/doc/sampleClass.h new file mode 100644 index 0000000000..cb66c9c1a4 --- /dev/null +++ b/panda/src/doc/sampleClass.h @@ -0,0 +1,104 @@ +// Filename: sampleClass.h +// Created by: drose (10Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SAMPLECLASS_H +#define SAMPLECLASS_H + +// This file shows some sample code that illustrates our general +// naming and style conventions for Panda coding. Note that there is +// generally one .h file per class, with the .h file named after the +// class but the first letter lowercase. + +#include + +#include "localHeaderFile.h" +#include "anotherLocalHeaderFile.h" + +#include +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : SampleClass +// Description : A basic description of the function and purpose of +// SampleClass. Note that class names are generally +// mixed case, no underscore, beginning with a capital +// letter. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA SampleClass : public TypedObject { +public: + enum NestedEnum { + NE_case_one, + NE_case_two, + }; + + class EXPCL_PANDA NestedClass { + public: + int _data_member; + }; + + SampleClass(); + INLINE SampleClass(const SampleClass ©); + INLINE ~SampleClass(); + + // Note that inline function bodies are generally not given here in + // the .h file--they're defined in the associated .I file. + + // Method names are generally lower case, with underscores + // separating words. Accessors are generally of the form set_*() + // and get_*(). Respect the const convention for methods which + // should be const. + + INLINE void set_flag(int flag); + INLINE int get_flag() const; + + int public_method(); + +protected: + bool protected_method(); + +private: + void private_method(); + + +public: + // Data members, whether private or public, are generally lower + // case, with underscores separating words, and beginning with a + // leading underscore. + + bool _public_data_member; + +private: + + NestedEnumType _private_data_member; + int _flag; + + + // The TypeHandle stuff, below, need be present only for classes + // that inherit from TypedObject. Classes that do not inherit from + // TypedObject may optionally define just the non-virtual methods + // below: get_class_type(), init_type(). +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedObject::init_type(); + register_type(_type_handle, "SampleClass", + TypedObject::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "sampleClass.I" + +#endif diff --git a/panda/src/doc/shader.flo b/panda/src/doc/shader.flo new file mode 100644 index 0000000000000000000000000000000000000000..29a11cc10b1b15235d95d22fda671670648dcb62 GIT binary patch literal 31744 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 literal 0 HcmV?d00001 diff --git a/panda/src/doc/window.flo b/panda/src/doc/window.flo new file mode 100644 index 0000000000000000000000000000000000000000..cdac291b3590678e1475d98cca6e8004f8e356a1 GIT binary patch literal 19456 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 QUhhmw5At?==CTL=4*=h($ literal 0 HcmV?d00001 diff --git a/panda/src/doc/windowing.gif b/panda/src/doc/windowing.gif new file mode 100644 index 0000000000000000000000000000000000000000..451186e8776c1202d1e7921c9d6133a81d0aed85 GIT binary patch literal 7572 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 yWmS^)>|KD|VCs>>iaLv%I?N!NgLcXM?L7 + #include + #undef WINDOWS_LEAN_AND_MEAN +#else + #include +#endif + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: AsyncUtility::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +AsyncUtility:: +AsyncUtility(float frequency) : _frequency(frequency) { + _next_token = 1; + _shutdown = false; + _threaded = false; + _threads_enabled = true; + _request_cond = new condition_variable(_lock); +} + +//////////////////////////////////////////////////////////////////// +// Function: AsyncUtility::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +AsyncUtility:: +~AsyncUtility() { + delete _request_cond; +} + +//////////////////////////////////////////////////////////////////// +// Function: AsyncUtility::create_thread +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void AsyncUtility:: +create_thread(void) { + if (_threaded == false && _threads_enabled == true) { + downloader_cat.debug() + << "AsyncUtility::create_thread()" << endl; + _thread = thread::create(&st_callback, this); + _threaded = true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AsyncUtility::destroy_thread +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +void AsyncUtility:: +destroy_thread(void) { + if (_threaded == false) + return; + + // Tell the thread to shut itself down. + // We need to grab the lock in order to signal the condition variable + _lock.lock(); + _shutdown = true; + _request_cond->signal(); + _lock.unlock(); + + // Join the loader thread - calling process blocks until the loader + // thread returns. + void *ret; + _thread->join(&ret); +} + +//////////////////////////////////////////////////////////////////// +// Function: AsyncUtility::st_callback +// Access: Protected, Static +// Description: This is a static wrapper around the callback() +// method, below. It's static just so we can pass it to +// the thread-creation function. In addition, the +// function has a void* return type even though we +// don't actually return anything. This is necessary +// because ipc assumes a function that does not return +// anything indicates that the associated thread should +// be created as unjoinable (detached). +//////////////////////////////////////////////////////////////////// +void* AsyncUtility:: +st_callback(void *arg) { + nassertr(arg != NULL, NULL); + ((AsyncUtility *)arg)->callback(); + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: AsyncUtility::callback +// Access: Protected +// Description: This is the main body of the sub-thread. It waits +// forever for a request to show up, and then serves it. +//////////////////////////////////////////////////////////////////// +void AsyncUtility:: +callback(void) { + while (process_request()) { + // Sleep until a signal arrives + _lock.lock(); + _request_cond->wait(); + _lock.unlock(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AsyncUtility::nap +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +void AsyncUtility:: +nap(void) const { +#ifdef WIN32 + _sleep(1000 * _frequency); +#else + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = (long)(1000000 * _frequency); + select(0, NULL, NULL, NULL, &tv); +#endif +} diff --git a/panda/src/downloader/asyncUtility.h b/panda/src/downloader/asyncUtility.h new file mode 100644 index 0000000000..60d75938df --- /dev/null +++ b/panda/src/downloader/asyncUtility.h @@ -0,0 +1,53 @@ +// Filename: asyncUtility.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef ASYNCUTILITY_H +#define ASYNCUTILITY_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : AsyncUtility +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS AsyncUtility { +public: + AsyncUtility(float frequency = 0.2); + ~AsyncUtility(void); + + void create_thread(void); + + INLINE void set_frequency(float frequency); + INLINE float get_frequency(void) const; + +protected: + void destroy_thread(void); + static void* st_callback(void *arg); + void callback(void); + virtual bool process_request(void) = 0; + void nap(void) const; + +protected: + int _next_token; + bool _shutdown; + bool _threaded; + mutex _lock; + condition_variable *_request_cond; + thread *_thread; + float _frequency; + bool _threads_enabled; +}; + +#include "asyncUtility.I" + +#endif diff --git a/panda/src/downloader/config_downloader.cxx b/panda/src/downloader/config_downloader.cxx new file mode 100644 index 0000000000..55754154ef --- /dev/null +++ b/panda/src/downloader/config_downloader.cxx @@ -0,0 +1,45 @@ +// Filename: config_downloader.cxx +// Created by: mike (19Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_downloader.h" + +#include +#include + +Configure(config_downloader); +NotifyCategoryDef(downloader, ""); + +// We'd like this to be about 1 second worth of download assuming a +// 28.8Kb connection (28.8Kb / 8 = 3600 bytes per second). +const int downloader_buffer_size = + config_downloader.GetInt("downloader-buffer-size", 3600); + +// Frequency of download chunk requests in seconds (or fractions of) +// (Estimated 200 msec round-trip to server). +const float downloader_frequency = + config_downloader.GetFloat("downloader-frequency", 0.2); + +// Estimated available bandwidth (assume a 28.8Kb connection, so again +// we have 3600 bytes per second). +const float downloader_bandwidth = + config_downloader.GetFloat("downloader-bandwidth", 3600.0); + +const int decompressor_buffer_size = + config_downloader.GetInt("decompressor-buffer-size", 4096); + +const float decompressor_frequency = + config_downloader.GetFloat("decompressor-frequency", 0.2); + +const int extractor_buffer_size = + config_downloader.GetInt("extractor-buffer-size", 4096); + +const float extractor_frequency = + config_downloader.GetFloat("extractor-frequency", 0.2); + +const int patcher_buffer_size = + config_downloader.GetInt("patcher-buffer-size", 4096); + +ConfigureFn(config_downloader) { +} diff --git a/panda/src/downloader/config_downloader.h b/panda/src/downloader/config_downloader.h new file mode 100644 index 0000000000..cbd505e6e5 --- /dev/null +++ b/panda/src/downloader/config_downloader.h @@ -0,0 +1,26 @@ +// Filename: config_downloader.h +// Created by: mike (19Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_DOWNLOADER_H +#define CONFIG_DOWNLOADER_H + +#include +#include + +NotifyCategoryDecl(downloader, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS); + +extern const int downloader_buffer_size; +extern const float downloader_frequency; +extern const float downloader_bandwidth; + +extern const int decompressor_buffer_size; +extern const float decompressor_frequency; + +extern const int extractor_buffer_size; +extern const float extractor_frequency; + +extern const int patcher_buffer_size; + +#endif diff --git a/panda/src/downloader/decompressor.cxx b/panda/src/downloader/decompressor.cxx new file mode 100644 index 0000000000..86aff55166 --- /dev/null +++ b/panda/src/downloader/decompressor.cxx @@ -0,0 +1,319 @@ +// Filename: decompressor.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// + +// This file is compiled only if we have zlib installed. + +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "decompressor.h" +#include "config_downloader.h" + +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : DecompressorToken +// Description : Holds a request for the decompressor. +//////////////////////////////////////////////////////////////////// +class DecompressorToken : public ReferenceCount { +public: + INLINE DecompressorToken(uint id, const Filename &source_file, + const Filename &dest_file, const string &event_name) { + _id = id; + _source_file = source_file; + _dest_file = dest_file; + _event_name = event_name; + } + int _id; + Filename _source_file; + Filename _dest_file; + string _event_name; +}; + +//////////////////////////////////////////////////////////////////// +// Function: Decompressor::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Decompressor:: +Decompressor(void) : AsyncUtility() { + PT(Buffer) buffer = new Buffer(decompressor_buffer_size); + init(buffer); +} + +//////////////////////////////////////////////////////////////////// +// Function: Decompressor::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Decompressor:: +Decompressor(PT(Buffer) buffer) : AsyncUtility() { + init(buffer); +} + +//////////////////////////////////////////////////////////////////// +// Function: Decompressor::Constructor +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void Decompressor:: +init(PT(Buffer) buffer) { + nassertv(!buffer.is_null()); + _frequency = decompressor_frequency; + _token_board = new DecompressorTokenBoard; + _half_buffer_length = buffer->get_length()/2; + _buffer = buffer; + char *temp_name = tempnam(NULL, "dc"); + _temp_file_name = temp_name; + _temp_file_name.set_binary(); + delete temp_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: Decompressor::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Decompressor:: +~Decompressor(void) { + destroy_thread(); + + delete _token_board; + _temp_file_name.unlink(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Decompressor::request_decompress +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int Decompressor:: +request_decompress(const Filename &source_file, const string &event_name) { + Filename dest_file = source_file; + string extension = source_file.get_extension(); + if (extension == "pz") + dest_file = source_file.get_fullpath_wo_extension(); + else + downloader_cat.debug() + << "Decompressor::request_decompress() - Unknown file extension: ." + << extension << endl; + return request_decompress(source_file, dest_file, event_name); +} + +//////////////////////////////////////////////////////////////////// +// Function: Decompressor::request_decompress +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int Decompressor:: +request_decompress(const Filename &source_file, const Filename &dest_file, + const string &event_name) { + + PT(DecompressorToken) tok; + if (_threads_enabled) { + + // Make sure we actually are threaded + if (!_threaded) { + downloader_cat.info() + << "Decompressor::request_decompress() - create_thread() was " + << "never called! Calling it now..." << endl; + create_thread(); + } + + // We need to grab the lock in order to signal the condition variable + _lock.lock(); + + if (_token_board->_waiting.is_full()) { + downloader_cat.error() + << "Downloader::request_download() - Too many pending requests\n"; + return 0; + } + + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Decompress requested for file: " << source_file << endl; + } + + tok = new DecompressorToken(_next_token++, source_file, dest_file, + event_name); + _token_board->_waiting.insert(tok); + + _request_cond->signal(); + + _lock.unlock(); + + } else { + // If we're not running asynchronously, process the load request + // directly now. + if (_token_board->_waiting.is_full()) { + downloader_cat.error() + << "Downloader::request_download() - Too many pending requests\n"; + return 0; + } + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Decompress requested for file: " << source_file << endl; + } + + tok = new DecompressorToken(_next_token++, source_file, dest_file, + event_name); + _token_board->_waiting.insert(tok); + process_request(); + } + + return tok->_id; +} + +//////////////////////////////////////////////////////////////////// +// Function: Decompressor::process_request +// Access: Private +// Description: Serves any requests on the token board, moving them +// to the done queue. +//////////////////////////////////////////////////////////////////// +bool Decompressor:: +process_request() { + if (_shutdown) { + if (downloader_cat.is_debug()) + downloader_cat.debug() + << "Decompressor shutting down...\n"; + return false; + } + + // If there is actually a request token - process it + while (!_token_board->_waiting.is_empty()) { + PT(DecompressorToken) tok = _token_board->_waiting.extract(); + if (decompress(tok->_source_file, tok->_dest_file)) { + _token_board->_done.insert(tok); + + // Throw a "done" event now. + if (!tok->_event_name.empty()) { + PT_Event done = new Event(tok->_event_name); + done->add_parameter(EventParameter((int)tok->_id)); + throw_event(done); + } + + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Decompressor::process_request() - decompress complete for " + << tok->_source_file << "\n"; + } + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Decompressor::decompress +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool Decompressor:: +decompress(Filename &source_file) { + Filename dest_file = source_file; + string extension = source_file.get_extension(); + if (extension == "pz") + dest_file = source_file.get_fullpath_wo_extension(); + else { + downloader_cat.debug() + << "Decompressor::request_decompress() - Unknown file extension: ." + << extension << endl; + return false; + } + return decompress(source_file, dest_file); +} + +//////////////////////////////////////////////////////////////////// +// Function: Decompressor::decompress +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool Decompressor:: +decompress(Filename &source_file, Filename &dest_file) { + + // Open source file + ifstream read_stream; + source_file.set_binary(); + if (!source_file.open_read(read_stream)) { + downloader_cat.error() + << "Decompressor::decompress() - Error opening source file: " + << source_file << endl; + return false; + } + + // Determine source file length + read_stream.seekg(0, ios::end); + int source_file_length = read_stream.tellg(); + read_stream.seekg(0, ios::beg); + + // Open destination file + ofstream write_stream; + dest_file.set_binary(); + if (!dest_file.open_write(write_stream)) { + downloader_cat.error() + << "Decompressor::decompress() - Error opening dest file: " + << source_file << endl; + return false; + } + + // Read from the source file into the first half of the buffer, + // decompress into the second half of the buffer, write the second + // half of the buffer to disk, and repeat. + int total_bytes_read = 0; + bool read_all_input = false; + bool handled_all_input = false; + int source_buffer_length; + ZDecompressor decompressor; + while (handled_all_input == false) { + + // See if there is anything left in the source file + if (read_all_input == false) { + read_stream.read(_buffer->_buffer, _half_buffer_length); + source_buffer_length = read_stream.gcount(); + total_bytes_read += source_buffer_length; + if (read_stream.eof()) { + nassertr(total_bytes_read == source_file_length, false); + read_all_input = true; + } + } + + char *next_in = _buffer->_buffer; + int avail_in = source_buffer_length; + char *dest_buffer = _buffer->_buffer + source_buffer_length; + char *next_out = dest_buffer; + int dest_buffer_length = _buffer->get_length() - source_buffer_length; + int avail_out = dest_buffer_length; + nassertr(avail_out > 0 && avail_in > 0, false); + + while (avail_in > 0) { + int ret = decompressor.decompress_to_stream(next_in, avail_in, + next_out, avail_out, dest_buffer, + dest_buffer_length, write_stream); + if (ret == ZCompressorBase::S_error) + return false; + if (decompressor.get_total_in() == source_file_length && + avail_out == dest_buffer_length) + handled_all_input = true; + } + + nap(); + + } + + read_stream.close(); + write_stream.close(); + + source_file.unlink(); + + return true; +} diff --git a/panda/src/downloader/decompressor.h b/panda/src/downloader/decompressor.h new file mode 100644 index 0000000000..0f8bd9d580 --- /dev/null +++ b/panda/src/downloader/decompressor.h @@ -0,0 +1,52 @@ +// Filename: decompressor.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef DECOMPRESSOR_H +#define DECOMPRESSOR_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include "zcompressor.h" +#include "asyncUtility.h" + +class DecompressorToken; + +//////////////////////////////////////////////////////////////////// +// Class : Decompressor +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS Decompressor : public AsyncUtility { +public: + Decompressor(void); + Decompressor(PT(Buffer) buffer); + ~Decompressor(void); + + int request_decompress(const Filename &source_file, + const string &event_name); + int request_decompress(const Filename &source_file, + const Filename &dest_file, + const string &event_name); + + bool decompress(Filename &source_file); + bool decompress(Filename &source_file, Filename &dest_file); + +private: + void init(PT(Buffer) buffer); + virtual bool process_request(void); + + typedef TokenBoard DecompressorTokenBoard; + DecompressorTokenBoard *_token_board; + + PT(Buffer) _buffer; + int _half_buffer_length; + Filename _temp_file_name; +}; + +#endif diff --git a/panda/src/downloader/downloadDb.I b/panda/src/downloader/downloadDb.I new file mode 100644 index 0000000000..68dc47fcfa --- /dev/null +++ b/panda/src/downloader/downloadDb.I @@ -0,0 +1,251 @@ +// Filename: downloadDb.I +// Created by: shochet (08Sep00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int DownloadDb:: +get_client_num_multifiles(void) const { + return _client_db.get_num_multifiles(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int DownloadDb:: +get_server_num_multifiles(void) const { + return _server_db.get_num_multifiles(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE string DownloadDb:: +get_client_multifile_name(int index) const { + return _client_db.get_multifile_name(index); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE string DownloadDb:: +get_server_multifile_name(int index) const { + return _server_db.get_multifile_name(index); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Version DownloadDb:: +get_client_multifile_version(string mfname) const { + return (_client_db.get_multifile_record_named(mfname))->_version; +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DownloadDb:: +set_client_multifile_version(string mfname, Version version) { + (_client_db.get_multifile_record_named(mfname))->_version = version; + write_db(_client_db._filename, _client_db); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Version DownloadDb:: +get_server_multifile_version(string mfname) const { + return (_server_db.get_multifile_record_named(mfname))->_version; +} + + + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Version DownloadDb:: +get_client_multifile_phase(string mfname) const { + return (_client_db.get_multifile_record_named(mfname))->_phase; +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Version DownloadDb:: +get_server_multifile_phase(string mfname) const { + return (_server_db.get_multifile_record_named(mfname))->_phase; +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int DownloadDb:: +get_client_multifile_size(string mfname) const { + return (_client_db.get_multifile_record_named(mfname))->_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DownloadDb:: +set_client_multifile_size(string mfname, int size) { + (_client_db.get_multifile_record_named(mfname))->_size = size; + write_db(_client_db._filename, _client_db); +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DownloadDb:: +set_client_multifile_delta_size(string mfname, int size) { + (_client_db.get_multifile_record_named(mfname))->_size += size; + write_db(_client_db._filename, _client_db); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int DownloadDb:: +get_server_multifile_size(string mfname) const { + return (_server_db.get_multifile_record_named(mfname))->_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DownloadDb:: +set_client_multifile_complete(string mfname) { + (_client_db.get_multifile_record_named(mfname))->_status = Status_complete; + write_db(_client_db._filename, _client_db); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DownloadDb:: +set_client_multifile_decompressed(string mfname) { + (_client_db.get_multifile_record_named(mfname))->_status = Status_decompressed; + write_db(_client_db._filename, _client_db); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DownloadDb:: +set_client_multifile_expanded(string mfname) { + (_client_db.get_multifile_record_named(mfname))->_status = Status_expanded; + write_db(_client_db._filename, _client_db); +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int DownloadDb:: +get_client_num_files(string mfname) const { + return _client_db.get_multifile_record_named(mfname)->get_num_files(); +} +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int DownloadDb:: +get_server_num_files(string mfname) const { + return (_server_db.get_multifile_record_named(mfname))->get_num_files(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE string DownloadDb:: +get_client_file_name(string mfname, int index) const { + return (_client_db.get_multifile_record_named(mfname))->get_file_name(index); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE string DownloadDb:: +get_server_file_name(string mfname, int index) const { + return (_server_db.get_multifile_record_named(mfname))->get_file_name(index); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Version DownloadDb:: +get_client_file_version(string mfname, string fname) const { + return ((_client_db.get_multifile_record_named(mfname))->get_file_record_named(fname))->_version; +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Version DownloadDb:: +get_server_file_version(string mfname, string fname) const { + return ((_server_db.get_multifile_record_named(mfname))->get_file_record_named(fname))->_version; +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DownloadDb:: +set_client_file_version(string mfname, string fname, Version version) { + ((_client_db.get_multifile_record_named(mfname))->get_file_record_named(fname))->_version = version; + write_db(_client_db._filename, _client_db); +} + diff --git a/panda/src/downloader/downloadDb.cxx b/panda/src/downloader/downloadDb.cxx new file mode 100644 index 0000000000..cce1b1c84c --- /dev/null +++ b/panda/src/downloader/downloadDb.cxx @@ -0,0 +1,920 @@ +// Filename: downloader.cxx +// Created by: shochet (08Sep00) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "downloadDb.h" +#include "config_downloader.h" + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +PN_uint32 DownloadDb::_magic_number = 0xfeedfeed; + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::Constructor +// Access: Public +// Description: Create a download db with these client and server dbs +//////////////////////////////////////////////////////////////////// +DownloadDb:: +DownloadDb(Filename &server_file, Filename &client_file) { + _client_db = read_db(client_file); + _client_db._filename = client_file; + _server_db = read_db(server_file); + _server_db._filename = server_file; + +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::Constructor +// Access: Public +// Description: Primarily used for testing. +//////////////////////////////////////////////////////////////////// +DownloadDb:: +DownloadDb(void) { + _client_db = Db(); + _server_db = Db(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DownloadDb:: +~DownloadDb(void) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DownloadDb:: +output(ostream &out) const { + out << "DownloadDb" << endl; + out << "============================================================" << endl; + out << " Client DB file: " << _client_db._filename << endl; + out << "============================================================" << endl; + _client_db.output(out); + out << endl; + out << "============================================================" << endl; + out << " Server DB file: " << _server_db._filename << endl; + out << "============================================================" << endl; + _server_db.output(out); + out << endl; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool DownloadDb:: +write_client_db(Filename &file) { + return write_db(file, _client_db); +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool DownloadDb:: +write_server_db(Filename &file) { + return write_db(file, _server_db); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool DownloadDb:: +client_db_current_version(void) const { + return (_client_db._version == _server_db._version); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool DownloadDb:: +client_multifile_exists(string mfname) const { + return (_client_db.multifile_exists(mfname)); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: A multifile is complete when it is completely +// downloaded. Note: it may already be decompressed +// or expanded and it is still complete +//////////////////////////////////////////////////////////////////// +bool DownloadDb:: +client_multifile_complete(string mfname) const { + int client_status = _client_db.get_multifile_record_named(mfname)->_status; + return (client_status >= Status_complete); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool DownloadDb:: +client_multifile_decompressed(string mfname) const { + int client_status = _client_db.get_multifile_record_named(mfname)->_status; + return (client_status >= Status_decompressed); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool DownloadDb:: +client_multifile_expanded(string mfname) const { + int client_status = _client_db.get_multifile_record_named(mfname)->_status; + return (client_status >= Status_expanded); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool DownloadDb:: +client_multifile_version_correct(string mfname) const { + Version client_version = get_client_multifile_version(mfname); + Version server_version = get_server_multifile_version(mfname); + return (client_version == server_version); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool DownloadDb:: +client_file_version_correct(string mfname, string filename) const { + Version client_version = get_client_file_version(mfname, filename); + Version server_version = get_server_file_version(mfname, filename); + return (client_version == server_version); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool DownloadDb:: +client_file_crc_correct(string mfname, string filename) const { + return true; +} + +// Operations on multifiles + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DownloadDb:: +delete_client_multifile(string mfname) { +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DownloadDb:: +add_client_multifile(string server_mfname) { + PT(MultifileRecord) server_mfr = _server_db.get_multifile_record_named(server_mfname); + PT(MultifileRecord) client_mfr = new MultifileRecord; + client_mfr->_name = server_mfr->_name; + client_mfr->_phase = server_mfr->_phase; + client_mfr->_version = server_mfr->_version; + _client_db.add_multifile_record(client_mfr); +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DownloadDb:: +expand_client_multifile(string mfname) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DownloadDb::Db DownloadDb:: +read_db(Filename &file) { + // Open the multifile for reading + ifstream read_stream; + file.set_binary(); + + Db db; + + if (!file.open_read(read_stream)) { + downloader_cat.error() + << "DownloadDb::read() - Failed to open input file: " + << file << endl; + return db; + } + + if (!db.read(read_stream)) { + downloader_cat.error() + << "DownloadDb::read() - Read failed: " + << file << endl; + return db; + } + + return db; +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool DownloadDb:: +write_db(Filename &file, Db db) { + ofstream write_stream; + file.set_binary(); + if (!file.open_write(write_stream)) { + downloader_cat.error() + << "DownloadDb::write_db() - Failed to open output file: " + << file << endl; + return false; + } + + downloader_cat.debug() + << "Writing to file: " << file << endl; + + db.write(write_stream); + write_stream.close(); + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::create_new_server_db +// Access: Public +// Description: Used on the server side makefiles to create a +// new clean server db +//////////////////////////////////////////////////////////////////// +void DownloadDb:: +create_new_server_db(void) { + _server_db = Db(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DownloadDb:: +server_add_multifile(string mfname, Phase phase, Version version, int size, int status) { + PT(MultifileRecord) mfr = new MultifileRecord(mfname, phase, version, size, status); + _server_db.add_multifile_record(mfr); +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DownloadDb:: +server_add_file(string mfname, string fname, Version version) { + // Make the new file record + PT(FileRecord) fr = new FileRecord(fname, version); + + // Find the multifile with mfname + vector::iterator i = _server_db._mfile_records.begin(); + for(; i != _server_db._mfile_records.end(); ++i) { + if (mfname == (*i)->_name) { + (*i)->add_file_record(fr); + return; + } + } + + // Uh-oh, did not find it + downloader_cat.error() << "Could not find record named " + << mfname << " in database " << endl; + return; +} + + +//////////////////////////////////////////////////////////////////// +// Multifile methods +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::MultifileRecord::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DownloadDb::MultifileRecord:: +MultifileRecord(void) { + _name = ""; + _phase = 0; + _version = 0; + _size = 0; + _status = Status_incomplete; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::MultifileRecord::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DownloadDb::MultifileRecord:: +MultifileRecord(string name, Phase phase, Version version, int size, int status) { + _name = name; + _phase = phase; + _version = version; + _size = size; + _status = status; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::MultifileRecord::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DownloadDb::MultifileRecord:: +output(ostream &out) const { + out << "==================================================" << endl; + out << "MultifileRecord: " << _name << endl + << " phase: " << _phase << endl + << " version: " << _version << endl + << " size: " << _size << endl + << " status: " << _status << endl; + out << "--------------------------------------------------" << endl; + vector::const_iterator i = _file_records.begin(); + for(; i != _file_records.end(); ++i) { + (*i)->output(out); + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::MultifileRecord:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int DownloadDb::MultifileRecord:: +get_num_files(void) const { + return _file_records.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::MultifileRecord:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +string DownloadDb::MultifileRecord:: +get_file_name(int index) const { + return _file_records[index]->_name; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::MultifileRecord:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool DownloadDb::MultifileRecord:: +file_exists(string fname) const { + vector::const_iterator i = _file_records.begin(); + for(; i != _file_records.end(); ++i) { + if (fname == (*i)->_name) { + return true; + } + } + return false; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::MultifileRecord:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PT(DownloadDb::FileRecord) DownloadDb::MultifileRecord:: +get_file_record_named(string fname) const { + vector::const_iterator i = _file_records.begin(); + for(; i != _file_records.end(); ++i) { + if (fname == (*i)->_name) { + return (*i); + } + } + // Did not find it, just return an empty version + downloader_cat.error() << "Could not find record named " + << fname << " in multifile " << _name << endl; + PT(FileRecord) foo = new FileRecord; + return foo; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::MultifileRecord:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DownloadDb::MultifileRecord:: +add_file_record(PT(FileRecord) fr) { + _file_records.push_back(fr); +} + + + +//////////////////////////////////////////////////////////////////// +// Db methods +//////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::Db::constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DownloadDb::Db:: +Db(void) { + // The head is a magic number and the number of multifiles in the db + _header_length = sizeof(_magic_number) + sizeof(PN_int32); +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::Db::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DownloadDb::Db:: +output(ostream &out) const { + vector::const_iterator i = _mfile_records.begin(); + for(; i != _mfile_records.end(); ++i) { + (*i)->output(out); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::Db:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int DownloadDb::Db:: +get_num_multifiles(void) const { + return _mfile_records.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::Db:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +string DownloadDb::Db:: +get_multifile_name(int index) const { + return _mfile_records[index]->_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::Db:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool DownloadDb::Db:: +multifile_exists(string mfname) const { + vector::const_iterator i = _mfile_records.begin(); + for(; i != _mfile_records.end(); ++i) { + if (mfname == (*i)->_name) { + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::Db:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PT(DownloadDb::MultifileRecord) DownloadDb::Db:: +get_multifile_record_named(string mfname) const { + vector::const_iterator i = _mfile_records.begin(); + for(; i != _mfile_records.end(); ++i) { + if (mfname == (*i)->_name) { + return (*i); + } + } + // Did not find it, just return an empty version + downloader_cat.error() << "Could not find record named " + << mfname << " in database " << endl; + PT(MultifileRecord) foo = new MultifileRecord; + return foo; +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::Db:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DownloadDb::Db:: +add_multifile_record(PT(MultifileRecord) mfr) { + _mfile_records.push_back(mfr); +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::Db::parse_header +// Access: Private +// Description: Verifies magic number, returns the number of +// multifiles or -1 if invalid +//////////////////////////////////////////////////////////////////// +int DownloadDb::Db:: +parse_header(uchar *start, int size) { + _datagram.clear(); + _datagram.append_data(start, size); + + // Make sure we have a good header + DatagramIterator di(_datagram); + PN_uint32 magic_number = di.get_uint32(); + downloader_cat.debug() + << "Parsed magic number: " << magic_number << endl; + if (magic_number != _magic_number) { + downloader_cat.error() + << "DownloadDb::parse_header() - Invalid magic number: " + << magic_number << " expected: " << _magic_number << endl; + return -1; + } + + PN_int32 num_multifiles = di.get_int32(); + downloader_cat.debug() + << "Parsed number of multifiles: " << num_multifiles << endl; + + // If we got all the way here, must be done + return num_multifiles; +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::Db::parse_fr_header +// Access: Private +// Description: Parses a file record (fr) header and returns +// the length of the next file record +//////////////////////////////////////////////////////////////////// +int DownloadDb::Db:: +parse_record_header(uchar *start, int size) { + _datagram.clear(); + _datagram.append_data(start, size); + + DatagramIterator di(_datagram); + PN_int32 record_length = di.get_int32(); + downloader_cat.debug() + << "Parsed record header length: " << record_length << endl; + + // If we got all the way here, must be done + return record_length; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::Db::parse_mfr +// Access: Private +// Description: Parses a multifile record (mfr) and returns one +//////////////////////////////////////////////////////////////////// +PT(DownloadDb::MultifileRecord) DownloadDb::Db:: +parse_mfr(uchar *start, int size) { + + PT(DownloadDb::MultifileRecord) mfr = new DownloadDb::MultifileRecord; + + _datagram.clear(); + _datagram.append_data(start, size); + + DatagramIterator di(_datagram); + PN_int32 mfr_name_length = di.get_int32(); + mfr->_name = di.extract_bytes(mfr_name_length); + mfr->_phase = di.get_int32(); + mfr->_version = di.get_int32(); + mfr->_size = di.get_int32(); + mfr->_status = di.get_int32(); + mfr->_num_files = di.get_int32(); + + downloader_cat.debug() + << "Parsed multifile record: " << mfr->_name << " phase: " << mfr->_phase + << " version: " << mfr->_version << " size: " << mfr->_size + << " status: " << mfr->_status << " num_files: " << mfr->_num_files << endl; + + // Return the new MultifileRecord + return mfr; +} + + + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::Db::parse_fr +// Access: Private +// Description: Parses a file record (fr) and returns one +//////////////////////////////////////////////////////////////////// +PT(DownloadDb::FileRecord) DownloadDb::Db:: +parse_fr(uchar *start, int size) { + + PT(DownloadDb::FileRecord) fr = new DownloadDb::FileRecord; + + _datagram.clear(); + _datagram.append_data(start, size); + + DatagramIterator di(_datagram); + PN_int32 fr_name_length = di.get_int32(); + fr->_name = di.extract_bytes(fr_name_length); + fr->_version = di.get_int32(); + + downloader_cat.debug() + << "Parsed file record: " << fr->_name << " version: " << fr->_version << endl; + + // Return the new MultifileRecord + return fr; +} + + + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::Db::read +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +bool DownloadDb::Db:: +read(ifstream &read_stream) { + + // Make a little buffer to read the header into + uchar *header_buf = new uchar[_header_length]; + // Read the header + read_stream.read((char *)header_buf, _header_length); + + // Parse the header + int num_multifiles = parse_header(header_buf, _header_length); + if (num_multifiles == -1) { + delete header_buf; + downloader_cat.error() << "DownloadDb::read() - Invalid header" << endl; + return false; + } + + delete header_buf; + + // Now that we know how many multifiles this db has, we can iterate + // reading them off one by one + for (int i = 0; i < num_multifiles; i++) { + // The multifile record header is just one int which + // represents the size of the record + int mfr_header_length = sizeof(PN_int32); + + // Make a little buffer to read the multifile record header into + header_buf = new uchar[mfr_header_length]; + + // Read the header + read_stream.read((char *)header_buf, mfr_header_length); + + // Parse the header + int mfr_length = parse_record_header(header_buf, mfr_header_length); + delete header_buf; + + // Ok, now that we know the size of the mfr, read it in + // Make a buffer to read the multifile record into + header_buf = new uchar[mfr_length]; + + // Read the mfr -- do not count the header length twice + read_stream.read((char *)header_buf, (mfr_length - mfr_header_length)); + + // Parse the mfr + PT(DownloadDb::MultifileRecord) mfr = parse_mfr(header_buf, mfr_length); + delete header_buf; + + // Read off all the file records this multifile has + for (int j = 0; j_num_files; j++) { + // The file record header is just one int which + // represents the size of the record + int fr_header_length = sizeof(PN_int32); + + // Make a little buffer to read the file record header into + header_buf = new uchar[fr_header_length]; + + // Read the header + read_stream.read((char *)header_buf, fr_header_length); + + // Parse the header + int fr_length = parse_record_header(header_buf, fr_header_length); + delete header_buf; + + // Ok, now that we know the size of the mfr, read it in + // Make a buffer to read the file record into + header_buf = new uchar[fr_length]; + + // Read the file record -- do not count the header length twice + read_stream.read((char *)header_buf, (fr_length - fr_header_length)); + + // Parse the file recrod + PT(DownloadDb::FileRecord) fr = parse_fr(header_buf, fr_length); + + // Add this file record to the current multifilerecord + mfr->add_file_record(fr); + } + + // Add the current multifilerecord to our database + add_multifile_record(mfr); + + } + + return true; +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::Db::write +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +bool DownloadDb::Db:: +write(ofstream &write_stream) { + write_header(write_stream); + + + // Iterate over the multifiles writing them to the stream + vector::const_iterator i = _mfile_records.begin(); + for(; i != _mfile_records.end(); ++i) { + _datagram.clear(); + + // Cache some properties so we do not have to keep asking for them + PN_int32 phase = (*i)->_phase; + PN_int32 version = (*i)->_version; + PN_int32 size = (*i)->_size; + PN_int32 status = (*i)->_status; + PN_int32 num_files = (*i)->get_num_files(); + PN_int32 name_length = (*i)->_name.length(); + + // Compute the length of this datagram + PN_int32 header_length; + header_length = + sizeof(header_length) + // Size of this header length + sizeof(name_length) + // Size of the size of the name string + (*i)->_name.length() + // Size of the name string + sizeof(phase) + sizeof(version) + sizeof(size) + + sizeof(status) + sizeof(num_files); + + // Add the length of this entire datagram + _datagram.add_int32(header_length); + + // Add the length of the name + _datagram.add_int32(name_length); + // Add the name + _datagram.append_data((*i)->_name.c_str(), (*i)->_name.length()); + + // Add all the properties + _datagram.add_int32(phase); + _datagram.add_int32(version); + _datagram.add_int32(size); + _datagram.add_int32(status); + _datagram.add_int32(num_files); + + // Now put this datagram on the write stream + string msg = _datagram.get_message(); + write_stream.write(msg.data(), msg.length()); + + // Now iterate over this multifile's files writing them to the stream + // Iterate over the multifiles writing them to the stream + vector::const_iterator j = (*i)->_file_records.begin(); + for(; j != (*i)->_file_records.end(); ++j) { + // Clear the datagram before we jam a bunch of stuff on it + _datagram.clear(); + + version = (*j)->_version; + name_length = (*j)->_name.length(); + + // Compute the length of this datagram + header_length = + sizeof(header_length) + // Size of this header length + sizeof(name_length) + // Size of the size of the name string + (*j)->_name.length() + // Size of the name string + sizeof(version); + + // Add the length of this entire datagram + _datagram.add_int32(header_length); + + // Add the length of the name + _datagram.add_int32(name_length); + // Add the name + _datagram.append_data((*j)->_name.c_str(), (*j)->_name.length()); + + // Add all the properties + _datagram.add_int32(version); + + // Now put this datagram on the write stream + string msg = _datagram.get_message(); + write_stream.write(msg.data(), msg.length()); + } + + } + + return true; +} + + + + + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::Db::write_header +// Access: Private +// Description: Writes the header uncompressed with platform- +// independent byte ordering +//////////////////////////////////////////////////////////////////// +bool DownloadDb::Db:: +write_header(ofstream &write_stream) { + _datagram.clear(); + + // Write the db magic number + _datagram.add_uint32(_magic_number); + + // Write the number of multifiles + _datagram.add_int32(get_num_multifiles()); + + string msg = _datagram.get_message(); + write_stream.write(msg.data(), msg.length()); + return true; +} + + +//////////////////////////////////////////////////////////////////// +// FileRecord methods +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::FileRecord::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DownloadDb::FileRecord:: +FileRecord(void) { + _name = ""; + _version = 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::FileRecord::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DownloadDb::FileRecord:: +FileRecord(string name, Version version) { + _name = name; + _version = version; +} + +//////////////////////////////////////////////////////////////////// +// Function: DownloadDb::FileRecord::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DownloadDb::FileRecord:: +output(ostream &out) const { + out << " FileRecord: " << _name << " version: " << _version << endl; +} + + + + + + + + diff --git a/panda/src/downloader/downloadDb.h b/panda/src/downloader/downloadDb.h new file mode 100644 index 0000000000..2145754c4f --- /dev/null +++ b/panda/src/downloader/downloadDb.h @@ -0,0 +1,206 @@ +// Filename: downloadDb.h +// Created by: shochet (06Sep00) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef DOWNLOADDB_H +#define DOWNLOADDB_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include +#include +#include +#include + +#include +#include +#include + + +/* +////////////////////////////////////////////////// +// Database Format +////////////////////////////////////////////////// +magic_number +number_of_multifiles +header_length multifile_name phase version size status num_files + header_length file_name version + header_length file_name version +header_length multifile_name phase version size status num_files + header_length file_name version + header_length file_name version + ... +... + + +A Db is a Vector +MultifileRecord is a Vector +*/ + +typedef int Version; +typedef int Phase; + +class EXPCL_PANDAEXPRESS DownloadDb { +public: + + // Status of a multifile is stored in this enum + // Note these values are in increasing order of "doneness" + // So if you are decompressed, you are complete + // If you are expanded, you are decompressed and complete + enum Status { + Status_incomplete = 0, + Status_complete = 1, + Status_decompressed = 2, + Status_expanded = 3 + }; + + DownloadDb(void); + DownloadDb(Filename &server_file, Filename &client_file); + ~DownloadDb(void); + + void output(ostream &out) const; + + // Write a database file + bool write_client_db(Filename &file); + bool write_server_db(Filename &file); + + INLINE int get_client_num_multifiles(void) const; + INLINE int get_server_num_multifiles(void) const; + + INLINE string get_client_multifile_name(int index) const; + INLINE string get_server_multifile_name(int index) const; + + INLINE Version get_client_multifile_version(string mfname) const; + INLINE void set_client_multifile_version(string mfname, Version version); + INLINE Version get_server_multifile_version(string mfname) const; + + INLINE int get_client_multifile_size(string mfname) const; + INLINE void set_client_multifile_size(string mfname, int size); + INLINE void set_client_multifile_delta_size(string mfname, int size); + INLINE int get_server_multifile_size(string mfname) const; + + INLINE int get_client_multifile_phase(string mfname) const; + INLINE int get_server_multifile_phase(string mfname) const; + + INLINE void set_client_multifile_complete(string mfname); + INLINE void set_client_multifile_decompressed(string mfname); + INLINE void set_client_multifile_expanded(string mfname); + + INLINE int get_client_num_files(string mfname) const; + INLINE int get_server_num_files(string mfname) const; + + INLINE string get_client_file_name(string mfname, int index) const; + INLINE string get_server_file_name(string mfname, int index) const; + + INLINE Version get_client_file_version(string mfname, string fname) const; + INLINE Version get_server_file_version(string mfname, string fname) const; + INLINE void set_client_file_version(string mfname, string fname, Version version); + + // Check client db against server db + bool client_db_current_version(void) const; + + // Queries from the Launcher + bool client_multifile_exists(string mfname) const; + bool client_multifile_complete(string mfname) const; + bool client_multifile_decompressed(string mfname) const; + bool client_multifile_expanded(string mfname) const; + bool client_multifile_version_correct(string mfname) const; + bool client_file_version_correct(string mfname, string filename) const; + bool client_file_crc_correct(string mfname, string filename) const; + + // Operations on multifiles + void delete_client_multifile(string mfname); + void add_client_multifile(string server_mfname); + void expand_client_multifile(string mfname); + + // Server side operations to create multifile records + void create_new_server_db(); + void server_add_multifile(string mfname, Phase phase, Version version, int size, int status); + void server_add_file(string mfname, string fname, Version version); + +public: + + class EXPCL_PANDAEXPRESS FileRecord : public ReferenceCount { + public: + FileRecord(void); + FileRecord(string name, Version version); + void output(ostream &out) const; + string _name; + Version _version; + }; + + typedef vector FileRecords; + + class EXPCL_PANDAEXPRESS MultifileRecord : public ReferenceCount { + public: + MultifileRecord(void); + MultifileRecord(string name, Phase phase, Version version, int size, int status); + void output(ostream &out) const; + int get_num_files(void) const; + string get_file_name(int index) const; + bool file_exists(string fname) const; + PT(FileRecord) get_file_record_named(string fname) const; + void add_file_record(PT(FileRecord) fr); + string _name; + Phase _phase; + Version _version; + int _size; + int _status; + PN_int32 _num_files; + FileRecords _file_records; + }; + + typedef vector MultifileRecords; + + class EXPCL_PANDAEXPRESS Db { + public: + Db(void); + void output(ostream &out) const; + int get_num_multifiles(void) const; + string get_multifile_name(int index) const; + bool multifile_exists(string mfname) const; + PT(MultifileRecord) get_multifile_record_named(string mfname) const; + void add_multifile_record(PT(MultifileRecord) mfr); + int parse_header(uchar *start, int size); + int parse_record_header(uchar *start, int size); + PT(MultifileRecord) parse_mfr(uchar *start, int size); + PT(FileRecord) parse_fr(uchar *start, int size); + bool read(ifstream &read_stream); + bool write(ofstream &write_stream); + Version _version; + Filename _filename; + MultifileRecords _mfile_records; + private: + PN_int32 _header_length; + + // Datagram used for reading and writing to disk + Datagram _datagram; + + bool write_header(ofstream &write_stream); + }; + + Db read_db(Filename &file); + bool write_db(Filename &file, Db db); + + // The doenload db stores two databases, one that represents the client's state + // and one that represents the server state + Db _client_db; + Db _server_db; + +public: + // Magic number for knowing this is a download Db + static PN_uint32 _magic_number; + +}; + +INLINE ostream &operator << (ostream &out, const DownloadDb &dldb) { + dldb.output(out); + return out; +} + + +#include "downloadDb.I" + +#endif diff --git a/panda/src/downloader/download_utils.cxx b/panda/src/downloader/download_utils.cxx new file mode 100644 index 0000000000..9454e84f64 --- /dev/null +++ b/panda/src/downloader/download_utils.cxx @@ -0,0 +1,62 @@ +// Filename: download_utils.cxx +// Created by: mike (18Jan99) +// +//////////////////////////////////////////////////////////////////// + +// This file is compiled only if we have zlib installed. + +#include "download_utils.h" +#include "config_downloader.h" +#include + +ulong +check_crc(Filename name) { + ifstream read_stream; + name.set_binary(); + if (!name.open_read(read_stream)) { + downloader_cat.error() + << "check_crc() - Failed to open input file: " << name << endl; + return 0; + } + + // Determine the length of the file and read it into the buffer + read_stream.seekg(0, ios::end); + int buffer_length = read_stream.tellg(); + char *buffer = new char[buffer_length]; + read_stream.seekg(0, ios::beg); + read_stream.read(buffer, buffer_length); + + // Compute the crc + ulong crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, (uchar *)buffer, buffer_length); + + delete buffer; + + return crc; +} + +ulong +check_adler(Filename name) { + ifstream read_stream; + name.set_binary(); + if (!name.open_read(read_stream)) { + downloader_cat.error() + << "check_adler() - Failed to open input file: " << name << endl; + return 0; + } + + // Determine the length of the file and read it into the buffer + read_stream.seekg(0, ios::end); + int buffer_length = read_stream.tellg(); + char *buffer = new char[buffer_length]; + read_stream.seekg(0, ios::beg); + read_stream.read(buffer, buffer_length); + + // Compute the adler checksum + ulong adler = adler32(0L, Z_NULL, 0); + adler = adler32(adler, (uchar *)buffer, buffer_length); + + delete buffer; + + return adler; +} diff --git a/panda/src/downloader/download_utils.h b/panda/src/downloader/download_utils.h new file mode 100644 index 0000000000..79bb3aefd8 --- /dev/null +++ b/panda/src/downloader/download_utils.h @@ -0,0 +1,17 @@ +// Filename: download_utils.h +// Created by: mike (18Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DOWNLOAD_UTILS_H +#define DOWNLOAD_UTILS_H + +#include +#include +#include + +EXPCL_PANDAEXPRESS ulong check_crc(Filename name); +EXPCL_PANDAEXPRESS ulong check_adler(Filename name); + +#endif + diff --git a/panda/src/downloader/downloader.I b/panda/src/downloader/downloader.I new file mode 100644 index 0000000000..03dbbceb7f --- /dev/null +++ b/panda/src/downloader/downloader.I @@ -0,0 +1,48 @@ +// Filename: downloader.I +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// + +#include "config_downloader.h" + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::set_bandwidth +// Access: Public +// Description: Note: modem speeds are reported in bits, so you +// need to convert! +//////////////////////////////////////////////////////////////////// +INLINE void Downloader:: +set_bandwidth(float bytes) { + mutex_lock lock(_bandwidth_frequency_lock); + _bandwidth = bytes; +} + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::get_bandwidth +// Access: Public +// Description: Returns bandwidth in bytes. +//////////////////////////////////////////////////////////////////// +INLINE float Downloader:: +get_bandwidth(void) const { + return _bandwidth; +} + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::enable_download +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Downloader:: +enable_download(bool val) { + _download_enabled = val; +} + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::is_download_enabled +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool Downloader:: +is_download_enabled(void) const { + return _download_enabled; +} diff --git a/panda/src/downloader/downloader.cxx b/panda/src/downloader/downloader.cxx new file mode 100644 index 0000000000..1a9c1e72b8 --- /dev/null +++ b/panda/src/downloader/downloader.cxx @@ -0,0 +1,629 @@ +// Filename: downloader.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "downloader.h" +#include "config_downloader.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(WIN32) +// #define errno wsaGetLastError() + #include +#endif + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : DownloaderToken +// Description : Holds a request for the downloader. +//////////////////////////////////////////////////////////////////// +class DownloaderToken : public ReferenceCount { +public: + INLINE DownloaderToken(uint id, const string &file_name, + const Filename &file_dest, const string &event_name, + int first_byte, int last_byte, int total_bytes, + bool partial_content) : _id(id), _first_byte(first_byte), + _last_byte(last_byte), _total_bytes(total_bytes) { + _file_name = file_name; + _event_name = event_name; + _file_dest = file_dest; + _partial_content = partial_content; + } + uint _id; + string _file_name; + Filename _file_dest; + string _event_name; + int _first_byte; + int _last_byte; + int _total_bytes; + bool _partial_content; +}; + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Downloader:: +Downloader(void) : AsyncUtility() { + PT(Buffer) buffer = new Buffer(downloader_buffer_size); + init(buffer); +} + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Downloader:: +Downloader(PT(Buffer) buffer) : AsyncUtility() { + init(buffer); +} + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::init +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void Downloader:: +init(PT(Buffer) buffer) { + nassertv(!buffer.is_null()); + _frequency = downloader_frequency; + _bandwidth = downloader_bandwidth; + _connected = false; + _token_board = new DownloaderTokenBoard; + _buffer = buffer; + _download_enabled = true; + +#if defined(WIN32) + WSAData mydata; + int answer1 = WSAStartup(0x0101, &mydata); + if(answer1 != 0) { + downloader_cat.error() + << "Downloader::Downloader() - Error initializing TCP stack!" + << endl; + } +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Downloader:: +~Downloader() { + if (_connected) + disconnect_from_server(); + + destroy_thread(); + + delete _token_board; +} + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::connect_to_server +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool Downloader:: +connect_to_server(const string &name, uint port) { + downloader_cat.debug() + << "Downloader connecting to server: " << name + << " on port: " << port << endl; + + _server_name = name; + + _sin.sin_family = PF_INET; + _sin.sin_port = htons(port); + ulong addr = (ulong)inet_addr(name.c_str()); + struct hostent *hp = NULL; + + if (addr == INADDR_NONE) { + hp = gethostbyname(name.c_str()); + if (hp != NULL) + (void)memcpy(&_sin.sin_addr, hp->h_addr, (uint)hp->h_length); + else { + downloader_cat.error() + << "Downloader::connect_to_server() - gethostbyname() failed: " + << strerror(errno) << endl; + return false; + } + } else + (void)memcpy(&_sin.sin_addr, &addr, sizeof(addr)); + + return connect_to_server(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::connect_to_server +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +bool Downloader:: +connect_to_server(void) { + if (_connected == true) + return true; + + _socket = 0xffffffff; + _socket = socket(PF_INET, SOCK_STREAM, 0); + if (_socket == (int)0xffffffff) { + downloader_cat.error() + << "Downloader::connect_to_server() - socket failed: " + << strerror(errno) << endl; + return false; + } + + _connected = true; + + if (connect(_socket, (struct sockaddr *)&_sin, sizeof(_sin)) < 0) { + downloader_cat.error() + << "Downloader::connect_to_server() - connect() failed: " + << strerror(errno) << endl; + disconnect_from_server(); + _connected = false; + } + + // Make socket non blocking + //if (setsockopt(_socket, SOL_SOCKET, + + return _connected; +} + +/////////////////////////////////////////////////////////////////// +// Function: Downloader::disconnect_from_server +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Downloader:: +disconnect_from_server(void) { + downloader_cat.debug() + << "Downloader disconnecting from server..." << endl; + +#if defined(WIN32) + (void)closesocket(_socket); +#else + (void)close(_socket); +#endif + + _connected = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::request_download +// Access: Public +// Description: Requests the download of a complete file. +//////////////////////////////////////////////////////////////////// +int Downloader:: +request_download(const string &file_name, const Filename &file_dest, + const string &event_name) { + return request_download(file_name, file_dest, event_name, 0, 0, 0, + false); +} + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::request_download +// Access: Public +// Description: Requests an asynchronous load of a file. The request +// will be queued and served by the asynchronous thread. +// If event_name is nonempty, it is the name of the +// event that will be thrown (with the uint id as its +// single parameter) when the loading is completed later. +// +// The return value is an integer which can be used to +// identify this particular request later to +// fetch_load(), or 0 if there has been an error. +// +// Can be used to request a partial download of a file. +//////////////////////////////////////////////////////////////////// +int Downloader:: +request_download(const string &file_name, const Filename &file_dest, + const string &event_name, int first_byte, + int last_byte, int total_bytes, + bool partial_content) { + + nassertr(first_byte <= last_byte && last_byte <= total_bytes, 0); + + PT(DownloaderToken) tok; + if (_threads_enabled) { + + // Make sure we actually are threaded + if (!_threaded) { + downloader_cat.info() + << "Downloader::request_download() - create_thread() was " + << "never called! Calling it now..." << endl; + create_thread(); + } + + // We need to grab the lock in order to signal the condition variable + _lock.lock(); + + if (_token_board->_waiting.is_full()) { + downloader_cat.error() + << "Downloader::request_download() - Too many pending requests\n"; + return 0; + } + + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Download requested for file: " << file_name << "\n"; + } + + tok = new DownloaderToken(_next_token++, file_name, file_dest, + event_name, first_byte, last_byte, total_bytes, + partial_content); + _token_board->_waiting.insert(tok); + + _request_cond->signal(); + + _lock.unlock(); + + } else { + // If we're not running asynchronously, process the load request + // directly now. + if (_token_board->_waiting.is_full()) { + downloader_cat.error() + << "Downloader::request_download() - Too many pending requests\n"; + return 0; + } + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Load requested for file: " << file_name << "\n"; + } + + tok = new DownloaderToken(_next_token++, file_name, file_dest, + event_name, first_byte, last_byte, total_bytes, + partial_content); + _token_board->_waiting.insert(tok); + process_request(); + } + + return tok->_id; +} + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::process_request +// Access: Private +// Description: Serves any requests on the token board, moving them +// to the done queue. +//////////////////////////////////////////////////////////////////// +bool Downloader:: +process_request() { + if (_shutdown) { + if (downloader_cat.is_debug()) + downloader_cat.debug() + << "Downloader shutting down...\n"; + return false; + } + + // If there is actually a request token - process it + while (!_token_board->_waiting.is_empty()) { + PT(DownloaderToken) tok = _token_board->_waiting.extract(); + if (download(tok->_file_name, tok->_file_dest, tok->_event_name, + tok->_first_byte, tok->_last_byte, tok->_total_bytes, + tok->_partial_content, tok->_id)) { + _token_board->_done.insert(tok); + + // Throw a "done" event now. + if (!tok->_event_name.empty()) { + PT_Event done = new Event(tok->_event_name); + done->add_parameter(EventParameter((int)tok->_id)); + throw_event(done); + } + + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Downloader::process_request() - downloading complete for " + << tok->_file_name << "\n"; + } + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::download +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +bool Downloader:: +download(const string &file_name, Filename file_dest, + const string &event_name, int first_byte, int last_byte, + int total_bytes, bool partial_content, uint id) { + + // Make sure we are still connected to the server + if (connect_to_server() == false) + return false; + + // Attempt to open the destination file + file_dest.set_binary(); + bool result; + if (partial_content == true && first_byte > 0) + result = file_dest.open_append(_dest_stream); + else + result = file_dest.open_write(_dest_stream); + if (result == false) { + downloader_cat.error() + << "Downloader::download() - Error opening file: " << file_dest + << " for writing" << endl; + return false; + } + + // Send an HTTP request for the file to the server + string request = "GET "; + request += file_name; + request += " HTTP/1.1\012Host: "; + request += _server_name; + request += "\012Connection: close"; + if (partial_content == true) { + downloader_cat.debug() + << "Downloader::download() - Requesting byte range: " << first_byte + << "-" << last_byte << endl; + request += "\012Range: bytes="; + stringstream start_stream; + start_stream << first_byte << "-" << last_byte; + request += start_stream.str(); + } + request += "\012\012"; + int outlen = request.size(); + downloader_cat.debug() + << "Downloader::download() - Sending request:\n" << request << endl; + if (send(_socket, request.c_str(), outlen, 0) != outlen) { + downloader_cat.error() + << "Downloader::download() - Error sending HTTP request: " + << request << "\n" << "error: " << strerror(errno) << endl; + return false; + } + + // Loop at the requested frequency until the download completes + DownloadStatus status(_buffer->_buffer, event_name, first_byte, last_byte, + total_bytes, partial_content, id); + bool got_any_data = false; + + // MPG - Get *safe* socket read code from Roger + // send() can write fewer than the requested # of bytes, so we need to + // loop on send. + // recv() can block forever, so we probably want to use a non-blocking + // socket and then check for EWOULDBLOCK error (which wouldn't be an + // error for us in this case - we should just go back to sleep for + // a while). + for (;;) { + if (_download_enabled) { + // Ensure that these don't change while we're computing read_size + _bandwidth_frequency_lock.lock(); + int read_size = (int)(_bandwidth * _frequency); + _bandwidth_frequency_lock.unlock(); + + // Ensure we have enough room in the buffer to download read_size + // If we don't have enough room, write the buffer to disk + if (status._bytes_in_buffer + read_size > downloader_buffer_size) { + downloader_cat.debug() + << "Downloader::download() - Flushing buffer" << endl; + + if (write_to_disk(status) == false) + return false; + } + + // Grab the next chunk + int ans = recv(_socket, status._next_in, read_size, 0); + + if (ans < 0) { + downloader_cat.error() + << "Downloader::download() - Error reading from socket: " + << strerror(errno) << endl; + return false; + + } else if (ans == 0) { + + if (got_any_data == true) { + downloader_cat.debug() + << "Download for: " << file_name << " completed" << endl; + bool ret = write_to_disk(status); + _dest_stream.close(); + + // The "Connection: close" line tells server to close connection + // when the download is complete + _connected = false; + return ret; + } else { + downloader_cat.debug() + << "Downloader::download() - Received 0 bytes" << endl; + nap(); + } + + } else { + downloader_cat.debug() + << "Downloader::download() - Got: " << ans << " bytes" << endl; + status._bytes_in_buffer += ans; + status._next_in += ans; + got_any_data = true; + + // Sleep for the requested frequency + nap(); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::parse_header +// Access: Private +// Description: Looks for a valid header. If it finds one, it +// calculates the header length and strips it from +// the download status structure. Function returns false +// on an error condition, otherwise true. +//////////////////////////////////////////////////////////////////// +bool Downloader:: +parse_header(DownloadStatus &status) { + + if (status._header_is_complete == true) + return true; + + string bufstr((char *)status._start, status._bytes_in_buffer); + size_t p = 0; + while (p < bufstr.length()) { + // Server sends out CR LF (\r\n) as newline delimiter + size_t nl = bufstr.find("\015\012", p); + if (nl == string::npos) + return true; + + string component = bufstr.substr(p, nl - p); + + // The first line of the response should say whether + // got an error or not + if (status._first_line_complete == false) { + status._first_line_complete = true; + if ((status._partial_content == false && + component == "HTTP/1.1 200 OK") || + (status._partial_content == true && + component == "HTTP/1.1 206 Partial Content")) { + downloader_cat.debug() + << "Downloader::parse_header() - Header is valid: " + << component << endl; + status._header_is_valid = true; + } else { + downloader_cat.error() + << "Downloader::parse_header() - Invalid header: " + << component << endl; + return false; + } + } + + // Look for content length + size_t cpos = component.find(":"); + string tline = component.substr(0, cpos); + if (status._partial_content == true && tline == "Content-Length") { + tline = component.substr(cpos + 2, string::npos); + int server_download_bytes = atoi(tline.c_str()); + int client_download_bytes = status._last_byte - status._first_byte; + if (status._first_byte == 0) + client_download_bytes += 1; + if (client_download_bytes != server_download_bytes) { + downloader_cat.error() + << "Downloader::parse_header() - server size = " + << server_download_bytes << ", client size = " + << client_download_bytes << " (" + << status._last_byte << "-" << status._first_byte << ")" << endl; + return false; + } + } + + // Two consecutive (CR LF)s indicates end of HTTP header + if (nl == p) { + downloader_cat.debug() + << "Downloader::parse_header() - Header is complete" << endl; + status._header_is_complete = true; + + // Strip the header out of the status buffer + int header_length = nl + 2; + status._start += header_length; + status._bytes_in_buffer -= header_length; + + downloader_cat.debug() + << "Downloader::parse_header() - Stripping out header of size: " + << header_length << endl; + + return true; + } + + p = nl + 2; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::write_to_disk +// Access: Private +// Description: Writes a download to disk. If there is a header, +// the pointer and size are adjusted so the header +// is excluded. Function returns false on error +// condition. +//////////////////////////////////////////////////////////////////// +bool Downloader:: +write_to_disk(DownloadStatus &status) { + + // Ensure the header has been parsed successfully first + if (parse_header(status) == false) + return false; + + if (status._header_is_complete == false) { + downloader_cat.error() + << "Downloader::write_to_disk() - Incomplete HTTP header - " + << "(or header was larger than download buffer) - " + << "try increasing download-buffer-size" << endl; + return false; + } + + // Write what we have so far to disk + if (status._bytes_in_buffer > 0) { + downloader_cat.debug() + << "Downloader::write_to_disk() - Writing " + << status._bytes_in_buffer << " to disk" << endl; + + _dest_stream.write(status._start, status._bytes_in_buffer); + status._total_bytes_written += status._bytes_in_buffer; + + // Throw an event to indicate how many bytes have been written so far + if (!status._event_name.empty()) { + PT_Event write_event = new Event(status._event_name); + write_event->add_parameter(EventParameter((int)status._id)); + write_event->add_parameter(EventParameter(status._total_bytes_written)); + throw_event(write_event); + } + } + + status.reset(); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::DownloadStatus::constructor +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +Downloader::DownloadStatus:: +DownloadStatus(char *buffer, const string &event_name, int first_byte, + int last_byte, int total_bytes, bool partial_content, uint id) { + _first_line_complete = false; + _header_is_complete = false; + _header_is_valid = false; + _buffer = buffer; + _event_name = event_name; + _first_byte = first_byte; + _last_byte = last_byte; + _total_bytes = total_bytes; + _partial_content = partial_content; + _id = id; + reset(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Downloader::DownloadStatus::reset +// Access: Public +// Description: Resets the status buffer for more downloading after +// a write. +//////////////////////////////////////////////////////////////////// +void Downloader::DownloadStatus:: +reset(void) { + _start = _buffer; + _next_in = _start; + _bytes_in_buffer = 0; + _total_bytes_written = 0; +} diff --git a/panda/src/downloader/downloader.h b/panda/src/downloader/downloader.h new file mode 100644 index 0000000000..ff96aaf3bf --- /dev/null +++ b/panda/src/downloader/downloader.h @@ -0,0 +1,105 @@ +// Filename: downloader.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef DOWNLOADER_H +#define DOWNLOADER_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include "asyncUtility.h" + +#if defined(WIN32) + #include +#endif + +class DownloaderToken; + +//////////////////////////////////////////////////////////////////// +// Class : Downloader +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS Downloader : public AsyncUtility { +public: + Downloader(void); + Downloader(PT(Buffer) buffer); + ~Downloader(void); + + bool connect_to_server(const string &name, uint port=80); + void disconnect_from_server(void); + + int request_download(const string &file_name, const Filename &file_dest, + const string &event_name); + int request_download(const string &file_name, const Filename &file_dest, + const string &event_name, int first_byte, + int last_byte, int total_bytes, + bool partial_content = true); + + INLINE void set_bandwidth(float bytes); + INLINE float get_bandwidth(void) const; + INLINE void enable_download(bool val); + INLINE bool is_download_enabled(void) const; + +private: + class DownloadStatus { + public: + DownloadStatus(char *buffer, const string &event_name, int first_byte, + int last_byte, int total_bytes, bool partial_content, + uint id); + void reset(void); + + public: + bool _first_line_complete; + bool _header_is_complete; + bool _header_is_valid; + char *_start; + char *_next_in; + int _bytes_in_buffer; + string _event_name; + int _total_bytes_written; + int _first_byte; + int _last_byte; + int _total_bytes; + bool _partial_content; + uint _id; + + private: + char *_buffer; + }; + + void init(PT(Buffer) buffer); + bool download(const string &file_name, Filename file_dest, + const string &event_name, int first_byte, + int last_byte, int total_bytes, bool partial_content, + uint id); + virtual bool process_request(void); + bool parse_header(DownloadStatus &status); + bool write_to_disk(DownloadStatus &status); + bool connect_to_server(void); + + typedef TokenBoard DownloaderTokenBoard; + DownloaderTokenBoard *_token_board; + + bool _connected; + + mutex _bandwidth_frequency_lock; + int _socket; + PT(Buffer) _buffer; + float _bandwidth; + bool _download_enabled; + ofstream _dest_stream; + + string _server_name; + struct sockaddr_in _sin; +}; + +#include "downloader.I" + +#endif diff --git a/panda/src/downloader/extractor.cxx b/panda/src/downloader/extractor.cxx new file mode 100644 index 0000000000..65df13ffff --- /dev/null +++ b/panda/src/downloader/extractor.cxx @@ -0,0 +1,243 @@ +// Filename: extractor.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "extractor.h" +#include "config_downloader.h" + +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : ExtractorToken +// Description : Holds a request for the extractor. +//////////////////////////////////////////////////////////////////// +class ExtractorToken : public ReferenceCount { +public: + INLINE ExtractorToken(uint id, const Filename &source_file, + const string &event_name) { + _id = id; + _source_file = source_file; + _event_name = event_name; + } + int _id; + Filename _source_file; + string _event_name; +}; + +//////////////////////////////////////////////////////////////////// +// Function: Extractor::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Extractor:: +Extractor(void) : AsyncUtility() { + PT(Buffer) buffer = new Buffer(extractor_buffer_size); + init(buffer); +} + +//////////////////////////////////////////////////////////////////// +// Function: Extractor::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Extractor:: +Extractor(PT(Buffer) buffer) : AsyncUtility() { + init(buffer); +} + +//////////////////////////////////////////////////////////////////// +// Function: Extractor::Constructor +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void Extractor:: +init(PT(Buffer) buffer) { + nassertv(!buffer.is_null()); + _frequency = extractor_frequency; + _token_board = new ExtractorTokenBoard; + _buffer = buffer; +} + +//////////////////////////////////////////////////////////////////// +// Function: Extractor::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Extractor:: +~Extractor(void) { + destroy_thread(); + + delete _token_board; +} + +//////////////////////////////////////////////////////////////////// +// Function: Extractor::request_extract +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int Extractor:: +request_extract(const Filename &source_file, const string &event_name) { + + PT(ExtractorToken) tok; + if (_threads_enabled) { + + // Make sure we actually are threaded + if (!_threaded) { + downloader_cat.info() + << "Extractor::request_decompress() - create_thread() was " + << "never called! Calling it now..." << endl; + create_thread(); + } + + // We need to grab the lock in order to signal the condition variable + _lock.lock(); + + if (_token_board->_waiting.is_full()) { + downloader_cat.error() + << "Extractor::request_extract() - Too many pending requests\n"; + return 0; + } + + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Extract requested for file: " << source_file << endl; + } + + tok = new ExtractorToken(_next_token++, source_file, event_name); + _token_board->_waiting.insert(tok); + + _request_cond->signal(); + + _lock.unlock(); + + } else { + // If we're not running asynchronously, process the load request + // directly now. + if (_token_board->_waiting.is_full()) { + downloader_cat.error() + << "Extractor::request_extract() - Too many pending requests\n"; + return 0; + } + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Extract requested for file: " << source_file << endl; + } + + tok = new ExtractorToken(_next_token++, source_file, event_name); + _token_board->_waiting.insert(tok); + process_request(); + } + + return tok->_id; +} + +//////////////////////////////////////////////////////////////////// +// Function: Extractor::process_request +// Access: Private +// Description: Serves any requests on the token board, moving them +// to the done queue. +//////////////////////////////////////////////////////////////////// +bool Extractor:: +process_request() { + if (_shutdown) { + if (downloader_cat.is_debug()) + downloader_cat.debug() + << "Extractor shutting down...\n"; + return false; + } + + // If there is actually a request token - process it + while (!_token_board->_waiting.is_empty()) { + PT(ExtractorToken) tok = _token_board->_waiting.extract(); + if (extract(tok->_source_file)) { + _token_board->_done.insert(tok); + + // Throw a "done" event now. + if (!tok->_event_name.empty()) { + PT_Event done = new Event(tok->_event_name); + done->add_parameter(EventParameter((int)tok->_id)); + throw_event(done); + } + + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Extractor::process_request() - extract complete for " + << tok->_source_file << "\n"; + } + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Extractor::extract +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool Extractor:: +extract(Filename &source_file) { + + // Open source file + ifstream read_stream; + source_file.set_binary(); + if (!source_file.open_read(read_stream)) { + downloader_cat.error() + << "Extractor::extract() - Error opening source file: " + << source_file << endl; + return false; + } + + // Determine source file length + read_stream.seekg(0, ios::end); + int source_file_length = read_stream.tellg(); + read_stream.seekg(0, ios::beg); + + // Read the multifile header + Multifile mfile; + + // Read from the source file and write to the appropriate extracted file + int total_bytes_read = 0; + bool read_all_input = false; + bool handled_all_input = false; + int source_buffer_length; + while (handled_all_input == false) { + + // See if there is anything left in the source file + if (read_all_input == false) { + read_stream.read(_buffer->_buffer, _buffer->get_length()); + source_buffer_length = read_stream.gcount(); + total_bytes_read += source_buffer_length; + if (read_stream.eof()) { + nassertr(total_bytes_read == source_file_length, false); + read_all_input = true; + } + } + + // Write to the out file + char *start = _buffer->_buffer; + int size = source_buffer_length; + if (mfile.write_extract(start, size) == true) + handled_all_input = true; + + nap(); + + } + + read_stream.close(); + source_file.unlink(); + + return true; +} diff --git a/panda/src/downloader/extractor.h b/panda/src/downloader/extractor.h new file mode 100644 index 0000000000..2409b65999 --- /dev/null +++ b/panda/src/downloader/extractor.h @@ -0,0 +1,46 @@ +// Filename: extractor.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef EXTRACTOR_H +#define EXTRACTOR_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include "asyncUtility.h" + +class ExtractorToken; + +//////////////////////////////////////////////////////////////////// +// Class : Extractor +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS Extractor : public AsyncUtility { +public: + Extractor(void); + Extractor(PT(Buffer) buffer); + ~Extractor(void); + + int request_extract(const Filename &source_file, + const string &event_name); + + bool extract(Filename &source_file); + +private: + void init(PT(Buffer) buffer); + virtual bool process_request(void); + + typedef TokenBoard ExtractorTokenBoard; + ExtractorTokenBoard *_token_board; + + PT(Buffer) _buffer; +}; + +#endif diff --git a/panda/src/downloader/patcher.cxx b/panda/src/downloader/patcher.cxx new file mode 100644 index 0000000000..d0b9cc0c15 --- /dev/null +++ b/panda/src/downloader/patcher.cxx @@ -0,0 +1,195 @@ +// Filename: patcher.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "patcher.h" +#include "config_downloader.h" + +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : PatcherToken +// Description : Holds a request for the patcher. +//////////////////////////////////////////////////////////////////// +class PatcherToken : public ReferenceCount { +public: + INLINE PatcherToken(uint id, const Filename &patch, + const Filename &infile, const string &event_name) { + _id = id; + _patch = patch; + _infile = infile; + _event_name = event_name; + } + int _id; + Filename _patch; + Filename _infile; + string _event_name; +}; + +//////////////////////////////////////////////////////////////////// +// Function: Patcher::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Patcher:: +Patcher(void) : AsyncUtility() { + PT(Buffer) buffer = new Buffer(patcher_buffer_size); + init(buffer); +} + +//////////////////////////////////////////////////////////////////// +// Function: Patcher::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Patcher:: +Patcher(PT(Buffer) buffer) : AsyncUtility() { + init(buffer); +} + +//////////////////////////////////////////////////////////////////// +// Function: Patcher::Constructor +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void Patcher:: +init(PT(Buffer) buffer) { + nassertv(!buffer.is_null()); + _token_board = new PatcherTokenBoard; + _buffer = buffer; +} + +//////////////////////////////////////////////////////////////////// +// Function: Patcher::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Patcher:: +~Patcher(void) { + destroy_thread(); + + delete _token_board; +} + +//////////////////////////////////////////////////////////////////// +// Function: Patcher::request_patch +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int Patcher:: +request_patch(const Filename &patch, const Filename &infile, + const string &event_name) { + PT(PatcherToken) tok; + if (_threads_enabled) { + + // Make sure we actually are threaded + if (!_threaded) { + downloader_cat.info() + << "Patcher::request_patch() - create_thread() was " + << "never called! Calling it now..." << endl; + create_thread(); + } + + // We need to grab the lock in order to signal the condition variable + _lock.lock(); + + if (_token_board->_waiting.is_full()) { + downloader_cat.error() + << "Patcher::request_patch() - Too many pending requests\n"; + return 0; + } + + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Patch requested for file: " << infile << endl; + } + + tok = new PatcherToken(_next_token++, patch, infile, event_name); + _token_board->_waiting.insert(tok); + + _request_cond->signal(); + + _lock.unlock(); + + } else { + // If we're not running asynchronously, process the load request + // directly now. + if (_token_board->_waiting.is_full()) { + downloader_cat.error() + << "Patcher::request_patch() - Too many pending requests\n"; + return 0; + } + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Patch requested for file: " << infile << endl; + } + + tok = new PatcherToken(_next_token++, patch, infile, event_name); + _token_board->_waiting.insert(tok); + process_request(); + } + + return tok->_id; +} + +//////////////////////////////////////////////////////////////////// +// Function: Patcher::process_request +// Access: Private +// Description: Serves any requests on the token board, moving them +// to the done queue. +//////////////////////////////////////////////////////////////////// +bool Patcher:: +process_request() { + if (_shutdown) { + if (downloader_cat.is_debug()) + downloader_cat.debug() + << "Patcher shutting down...\n"; + return false; + } + + // If there is actually a request token - process it + while (!_token_board->_waiting.is_empty()) { + PT(PatcherToken) tok = _token_board->_waiting.extract(); + if (patch(tok->_patch, tok->_infile)) { + _token_board->_done.insert(tok); + + // Throw a "done" event now. + if (!tok->_event_name.empty()) { + PT_Event done = new Event(tok->_event_name); + done->add_parameter(EventParameter((int)tok->_id)); + throw_event(done); + } + + if (downloader_cat.is_debug()) { + downloader_cat.debug() + << "Patcher::process_request() - patching complete for " + << tok->_infile << "\n"; + } + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Patcher::patch +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool Patcher:: +patch(Filename &patch, Filename &infile) { + Patchfile pfile(_buffer); + return pfile.apply(patch, infile); +} diff --git a/panda/src/downloader/patcher.h b/panda/src/downloader/patcher.h new file mode 100644 index 0000000000..210a2f61b1 --- /dev/null +++ b/panda/src/downloader/patcher.h @@ -0,0 +1,46 @@ +// Filename: patcher.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef PATCHER_H +#define PATCHER_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include "asyncUtility.h" +#include + +class PatcherToken; + +//////////////////////////////////////////////////////////////////// +// Class : Patcher +// Description : Applys a patch asynchronously +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS Patcher : public AsyncUtility { +public: + Patcher(void); + Patcher(PT(Buffer) buffer); + ~Patcher(void); + + int request_patch(const Filename &patch, + const Filename &infile, const string &event_name); + + bool patch(Filename &patch, Filename &infile); + +private: + void init(PT(Buffer) buffer); + virtual bool process_request(void); + + typedef TokenBoard PatcherTokenBoard; + PatcherTokenBoard *_token_board; + + PT(Buffer) _buffer; +}; + +#endif diff --git a/panda/src/downloader/zcompressor.I b/panda/src/downloader/zcompressor.I new file mode 100644 index 0000000000..5742dfc568 --- /dev/null +++ b/panda/src/downloader/zcompressor.I @@ -0,0 +1,82 @@ +// Filename: zcompressor.I +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ZCompressorBase::get_total_in +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ulong ZCompressorBase:: +get_total_in(void) const { + return (_stream->total_in); +} + +//////////////////////////////////////////////////////////////////// +// Function: ZCompressorBase::get_total_out +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ulong ZCompressorBase:: +get_total_out(void) const { + return (_stream->total_out); +} + +//////////////////////////////////////////////////////////////////// +// Function: ZCompressorBase::reset +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ZCompressorBase:: +reset(void) { + reset_stream(_stream); +} + +//////////////////////////////////////////////////////////////////// +// Function: ZCompressorBase::reset_stream +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ZCompressorBase:: +reset_stream(z_stream *strm) const { + strm->next_in = Z_NULL; + strm->avail_in = 0; + strm->total_in = 0; + strm->next_out = Z_NULL; + strm->avail_out = 0; + strm->total_out = 0; + strm->zalloc = Z_NULL; + strm->zfree = Z_NULL; + strm->opaque = Z_NULL; +} + +#ifndef CPPPARSER +//////////////////////////////////////////////////////////////////// +// Function: ZCompressorBase::put_stream +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ZCompressorBase:: +put_stream(z_stream *strm, char *next_in, int avail_in, char *next_out, + int avail_out) const { + strm->next_in = (uchar *)next_in; + strm->avail_in = (ulong)avail_in; + strm->next_out = (uchar *)next_out; + strm->avail_out = (ulong)avail_out; +} + +//////////////////////////////////////////////////////////////////// +// Function: ZCompressorBase::get_stream +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ZCompressorBase:: +get_stream(z_stream *strm, char *&next_in, int &avail_in, char *&next_out, + int &avail_out) const { + next_in = (char *)strm->next_in; + avail_in = (int)strm->avail_in; + next_out = (char *)strm->next_out; + avail_out = (int)strm->avail_out; +} +#endif diff --git a/panda/src/downloader/zcompressor.cxx b/panda/src/downloader/zcompressor.cxx new file mode 100644 index 0000000000..06552495d2 --- /dev/null +++ b/panda/src/downloader/zcompressor.cxx @@ -0,0 +1,215 @@ +/// Filename: zcompressor.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// + +// This file is compiled only if we have zlib installed. + +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "zcompressor.h" +#include "config_downloader.h" +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ZCompressor::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ZCompressor:: +ZCompressor(void) { + _stream = new z_stream; + reset_stream(_stream); + int ret = deflateInit(_stream, 9); + if (ret == Z_OK) + downloader_cat.debug() + << "ZCompressor::ZCompressor() - Zlib deflate initialized" << endl; + else + handle_zerror(ret, _stream); +} + +//////////////////////////////////////////////////////////////////// +// Function: ZCompressor::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ZCompressor:: +~ZCompressor(void) { + handle_zerror(deflateEnd(_stream), _stream); +} + +//////////////////////////////////////////////////////////////////// +// Function: ZCompressor::compress +// Access: Public +// Description: Compress from next_in into next_out until avail_in +// is 0 or avail_out is 0 or an error occurs. +//////////////////////////////////////////////////////////////////// +int ZCompressor:: +compress(char *&next_in, int &avail_in, char *&next_out, int &avail_out, + bool finish) { + + int fin_flag = (finish == true) ? Z_FINISH : Z_NO_FLUSH; + + put_stream(_stream, next_in, avail_in, next_out, avail_out); + int ret = handle_zerror(deflate(_stream, fin_flag), _stream); + get_stream(_stream, next_in, avail_in, next_out, avail_out); + + return ret; +} + +//////////////////////////////////////////////////////////////////// +// Function: ZCompressor::compress_to_stream +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int ZCompressor:: +compress_to_stream(char *&next_in, int &avail_in, char *&next_out, + int &avail_out, char *out_buffer, + int out_buffer_length, ofstream &write_stream, + bool finish) { + int ret = compress(next_in, avail_in, next_out, avail_out, finish); + if (ret == S_error) + return ret; + + // See if there is any output to write + int out_bytes = out_buffer_length - avail_out; + if (out_bytes > 0) { + write_stream.write(out_buffer, out_bytes); + next_out = out_buffer; + avail_out = out_buffer_length; + } + + return ret; +} + +//////////////////////////////////////////////////////////////////// +// Function: ZDecompressor::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ZDecompressor:: +ZDecompressor(void) { + _stream = new z_stream; + reset_stream(_stream); + int ret = inflateInit(_stream); + if (ret == Z_OK) + downloader_cat.debug() + << "ZDecompressor::ZDecompressor() - Zlib inflate initialized" << endl; + else + handle_zerror(ret, _stream); +} + +//////////////////////////////////////////////////////////////////// +// Function: ZDecompressor::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ZDecompressor:: +~ZDecompressor(void) { + handle_zerror(inflateEnd(_stream), _stream); +} + +//////////////////////////////////////////////////////////////////// +// Function: ZDecompressor::decompress +// Access: Public +// Description: Decompress from next_in into next_out until avail_in +// is 0 or avail_out is 0 or an error occurs. +//////////////////////////////////////////////////////////////////// +int ZDecompressor:: +decompress(char *&next_in, int &avail_in, char *&next_out, int &avail_out, + bool finish) { + + int fin_flag = (finish == true) ? Z_FINISH : Z_NO_FLUSH; + + put_stream(_stream, next_in, avail_in, next_out, avail_out); + int ret = handle_zerror(inflate(_stream, fin_flag), _stream); + get_stream(_stream, next_in, avail_in, next_out, avail_out); + + return ret; +} + +//////////////////////////////////////////////////////////////////// +// Function: ZDecompressor::decompress_to_stream +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int ZDecompressor:: +decompress_to_stream(char *&next_in, int &avail_in, char *&next_out, + int &avail_out, char *out_buffer, + int out_buffer_length, ofstream &write_stream, + bool finish) { + int ret = decompress(next_in, avail_in, next_out, avail_out, finish); + if (ret == S_error) + return ret; + + // See if there is any output to write + int out_bytes = out_buffer_length - avail_out; + if (out_bytes > 0) { + write_stream.write(out_buffer, out_bytes); + next_out = out_buffer; + avail_out = out_buffer_length; + } + + return ret; +} + +//////////////////////////////////////////////////////////////////// +// Function: ZCompressorBase::handle_zerror +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +int ZCompressorBase:: +handle_zerror(int code, const z_stream *strm) const { + if (code == Z_OK) + return S_ok; + else if (code == Z_STREAM_END) + return S_finished; + else { + downloader_cat.error() + << "ZCompressorBase::handle_zerror() - "; + switch (code) { + case Z_DATA_ERROR: + downloader_cat.error() + << "corrupt data"; + break; + case Z_NEED_DICT: + downloader_cat.error() + << "need a dictionary"; + break; + case Z_STREAM_ERROR: + downloader_cat.error() + << "next_in or next_out = NULL"; + break; + case Z_MEM_ERROR: + downloader_cat.error() + << "not enough memory"; + break; + case Z_BUF_ERROR: + downloader_cat.error() + << "no progress is possible"; + break; + case Z_VERSION_ERROR: + downloader_cat.error() + << "zlib version conflict"; + break; + default: + downloader_cat.error() + << "unknown error: " << code; + break; + } + if (strm != NULL) { + if (strm->msg != NULL) { + downloader_cat.error() + << " - zlib message: " << strm->msg; + } + } + downloader_cat.error() + << endl; + } + return S_error; +} diff --git a/panda/src/downloader/zcompressor.h b/panda/src/downloader/zcompressor.h new file mode 100644 index 0000000000..8c6d6309f5 --- /dev/null +++ b/panda/src/downloader/zcompressor.h @@ -0,0 +1,78 @@ +// Filename: zcompressor.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef ZCOMPRESSOR_H +#define ZCOMPRESSOR_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include +#include +#include "typedef.h" + +//////////////////////////////////////////////////////////////////// +// Class : ZCompressorBase +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS ZCompressorBase { +public: + enum status { + S_ok, + S_finished, + S_error, + }; + + INLINE ulong get_total_in(void) const; + INLINE ulong get_total_out(void) const; + INLINE void reset(void); + INLINE void reset_stream(z_stream *strm) const; +#ifndef CPPPARSER + INLINE void put_stream(z_stream *strm, char *next_in, int avail_in, + char *next_out, int avail_out) const; + INLINE void get_stream(z_stream *strm, char *&next_in, int &avail_in, + char *&next_out, int &avail_out) const; +#endif + int handle_zerror(int code, const z_stream *strm = NULL) const; + +protected: + z_stream *_stream; +}; + +//////////////////////////////////////////////////////////////////// +// Class : ZCompressor +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS ZCompressor : public ZCompressorBase { +public: + ZCompressor(void); + ~ZCompressor(void); + + int compress(char *&next_in, int &avail_in, char *&next_out, + int &avail_out, bool finish = false); + int compress_to_stream(char *&next_in, int &avail_in, char *&next_out, + int &avail_out, char *out_buffer, int out_buffer_length, + ofstream &write_stream, bool finish = false); +}; + +//////////////////////////////////////////////////////////////////// +// Class : ZDecompressor +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS ZDecompressor : public ZCompressorBase { +public: + ZDecompressor(void); + ~ZDecompressor(void); + + int decompress(char *&next_in, int &avail_in, char *&next_out, + int &avail_out, bool finish = false); + int decompress_to_stream(char *&next_in, int &avail_in, char *&next_out, + int &avail_out, char *out_buffer, int out_buffer_length, + ofstream &write_stream, bool finish = false); +}; + +#include "zcompressor.I" + +#endif diff --git a/panda/src/downloadertools/Sources.pp b/panda/src/downloadertools/Sources.pp new file mode 100644 index 0000000000..af484f9ede --- /dev/null +++ b/panda/src/downloadertools/Sources.pp @@ -0,0 +1,61 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase pystub +#define LOCAL_LIBS downloader express event ipc + +#begin bin_target + #define TARGET apply_patch + + #define SOURCES \ + apply_patch.cxx + + #define INSTALL_HEADERS \ + +#end bin_target + +#begin bin_target + #define TARGET build_patch + + #define SOURCES \ + build_patch.cxx + +#end bin_target + +#begin bin_target + #define TARGET check_adler + + #define SOURCES \ + check_adler.cxx + +#end bin_target + +#begin bin_target + #define TARGET check_crc + + #define SOURCES \ + check_crc.cxx + +#end bin_target + +#begin bin_target + #define TARGET multify + + #define SOURCES \ + multify.cxx + +#end bin_target + +#begin bin_target + #define TARGET pcompress + + #define SOURCES \ + pcompress.cxx + +#end bin_target + +#begin bin_target + #define TARGET pdecompress + + #define SOURCES \ + pdecompress.cxx + +#end bin_target + diff --git a/panda/src/downloadertools/apply_patch.cxx b/panda/src/downloadertools/apply_patch.cxx new file mode 100644 index 0000000000..59b1e0d3c0 --- /dev/null +++ b/panda/src/downloadertools/apply_patch.cxx @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +int +main(int argc, char *argv[]) { + if (argc < 3) { + cerr << "Usage: apply_patch " << endl; + return 0; + } + + Filename patch = argv[1]; + patch.set_binary(); + + Filename file = argv[2]; + file.set_binary(); + + Patchfile pfile; + + cerr << "Applying patch file " << patch << " to " << file << endl; + if (pfile.apply(patch, file) == false) + cerr << "apply patch failed" << endl; + + return 1; +} diff --git a/panda/src/downloadertools/build_patch.cxx b/panda/src/downloadertools/build_patch.cxx new file mode 100644 index 0000000000..41dedea9c6 --- /dev/null +++ b/panda/src/downloadertools/build_patch.cxx @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +int +main(int argc, char *argv[]) { + if (argc < 3) { + cerr << "Usage: build_patch " << endl; + return 0; + } + + Filename src_file = argv[1]; + src_file.set_binary(); + + Filename dest_file = argv[2]; + dest_file.set_binary(); + + Patchfile pfile; + + cerr << "Building patch file to convert " << src_file << " to " + << dest_file << endl; + if (pfile.build(src_file, dest_file) == false) + cerr << "build patch failed" << endl; + + return 1; +} diff --git a/panda/src/downloadertools/check_adler.cxx b/panda/src/downloadertools/check_adler.cxx new file mode 100644 index 0000000000..ffcab1c1e6 --- /dev/null +++ b/panda/src/downloadertools/check_adler.cxx @@ -0,0 +1,15 @@ +#include + +int +main(int argc, char *argv[]) { + if (argc < 2) { + cerr << "Usage: check_adler " << endl; + return 0; + } + + Filename source_file = argv[1]; + + cout << check_adler(source_file); + + return 1; +} diff --git a/panda/src/downloadertools/check_crc.cxx b/panda/src/downloadertools/check_crc.cxx new file mode 100644 index 0000000000..33de2ce7f8 --- /dev/null +++ b/panda/src/downloadertools/check_crc.cxx @@ -0,0 +1,15 @@ +#include + +int +main(int argc, char *argv[]) { + if (argc < 2) { + cerr << "Usage: check_crc " << endl; + return 0; + } + + Filename source_file = argv[1]; + + cout << check_crc(source_file); + + return 1; +} diff --git a/panda/src/downloadertools/multify.cxx b/panda/src/downloadertools/multify.cxx new file mode 100644 index 0000000000..4c6dc1e356 --- /dev/null +++ b/panda/src/downloadertools/multify.cxx @@ -0,0 +1,65 @@ +#include +#include +#include +#include + +int +main(int argc, char *argv[]) { + if (argc < 3) { + cerr << "Usage: multify -[x,c|v] ..." << endl; + return 0; + } + + bool verbose = true; + bool extract = false; + + extern char *optarg; + extern int optind; + int flag = getopt(argc, argv, "x:c:v"); + while (flag != EOF) { + switch (flag) { + case 'x': + extract = true; + break; + case 'c': + break; + case 'v': + verbose = true; + break; + default: + cerr << "Unhandled switch: " << flag << endl; + break; + } + flag = getopt(argc, argv, "x:c:v"); + } + argc -= (optind - 1); + argv += (optind - 1); + + Filename dest_file = argv[0]; + dest_file.set_binary(); + + if (verbose == true) { + if (extract == true) + cerr << "Extracting from: " << dest_file << endl; + else + cerr << "Appending to: " << dest_file << endl; + } + + Multifile mfile; + + if (extract == false) { + for (int i = 1; i < argc; i++) { + Filename in_file = argv[i]; + in_file.set_binary(); + mfile.add(in_file); + } + + if (mfile.write(dest_file) == false) + cerr << "Failed to write: " << dest_file << endl; + } else { + mfile.read(dest_file); + mfile.extract_all(); + } + + return 1; +} diff --git a/panda/src/downloadertools/pcompress.cxx b/panda/src/downloadertools/pcompress.cxx new file mode 100644 index 0000000000..e377ef2144 --- /dev/null +++ b/panda/src/downloadertools/pcompress.cxx @@ -0,0 +1,101 @@ +#include +#include +#include + +int +main(int argc, char *argv[]) { + if (argc < 2) { + cerr << "Usage: pcompress " << endl; + return 0; + } + + Filename source_file = argv[1]; + string dname = argv[1]; + dname += ".pz"; + Filename dest_file = dname; + + // Open source file + ifstream read_stream; + source_file.set_binary(); + if (!source_file.open_read(read_stream)) { + cerr << "failed to open: " << source_file << endl; + return 0; + } + + // Determine source file length + read_stream.seekg(0, ios::end); + int source_file_length = read_stream.tellg(); + read_stream.seekg(0, ios::beg); + + // Open destination file + ofstream write_stream; + dest_file.set_binary(); + if (!dest_file.open_write(write_stream)) { + cerr << "failed to open: " << dest_file << endl; + return 0; + } + + ZCompressor compressor; + int buffer_length = 1000000; + int half_buffer_length = buffer_length/2; + char *buffer = new char[buffer_length]; + + // Read from the source file into the first half of the buffer, + // decompress into the second half of the buffer, write the second + // half of the buffer to disk, and repeat. + int total_bytes_read = 0; + bool read_all_input = false; + bool handled_all_input = false; + int source_buffer_length; + int ret; + while (handled_all_input == false) { + + // See if there is anything left in the source file + if (read_all_input == false) { + read_stream.read(buffer, half_buffer_length); + source_buffer_length = read_stream.gcount(); + total_bytes_read += source_buffer_length; + if (read_stream.eof()) { + nassertr(total_bytes_read == source_file_length, false); + read_all_input = true; + } + } + + char *next_in = buffer; + int avail_in = source_buffer_length; + char *dest_buffer = buffer + source_buffer_length; + char *next_out = dest_buffer; + int dest_buffer_length = buffer_length - source_buffer_length; + int avail_out = dest_buffer_length; + nassertr(avail_out > 0 && avail_in > 0, false); + + // Consume all the input from the input buffer, writing to disk + // as we go to free output buffer space + while (avail_in > 0) { + ret = compressor.compress_to_stream(next_in, avail_in, + next_out, avail_out, dest_buffer, + dest_buffer_length, write_stream); + if (ret == ZCompressorBase::S_error) + return 0; + } + + // If all the input has been consumed, we need to keep processing + // output until it says it's finished + if (compressor.get_total_in() == source_file_length) { + while (ret != ZCompressorBase::S_finished) { + ret = compressor.compress_to_stream(next_in, avail_in, + next_out, avail_out, dest_buffer, + dest_buffer_length, write_stream, true); + if (ret == ZCompressorBase::S_error) + return 0; + } + handled_all_input = true; + } + } + + read_stream.close(); + write_stream.close(); + source_file.unlink(); + + return 1; +} diff --git a/panda/src/downloadertools/pdecompress.cxx b/panda/src/downloadertools/pdecompress.cxx new file mode 100644 index 0000000000..200389fb09 --- /dev/null +++ b/panda/src/downloadertools/pdecompress.cxx @@ -0,0 +1,20 @@ +#include +#include +#include + +int +main(int argc, char *argv[]) { + if (argc < 2) { + cerr << "Usage: pdecompress " << endl; + return 0; + } + + Filename source_file = argv[1]; + + PT(Buffer) buffer = new Buffer(1000000); + Decompressor decompressor(buffer); + if (decompressor.decompress(source_file) == false) + cerr << "Decompress failed" << endl; + + return 1; +} diff --git a/panda/src/dxgsg/Sources.pp b/panda/src/dxgsg/Sources.pp new file mode 100644 index 0000000000..ac010cbb6d --- /dev/null +++ b/panda/src/dxgsg/Sources.pp @@ -0,0 +1,19 @@ +#define DIRECTORY_IF_DX yes + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET dxgsg + + #define SOURCES \ + config_dxgsg.cxx config_dxgsg.h dxGraphicsStateGuardian.I \ + dxGraphicsStateGuardian.cxx dxGraphicsStateGuardian.h \ + dxSavedFrameBuffer.I dxSavedFrameBuffer.cxx dxSavedFrameBuffer.h \ + dxTextureContext.I dxTextureContext.cxx dxTextureContext.h + + #define INSTALL_HEADERS \ + config_dxgsg.h dxGraphicsStateGuardian.I dxGraphicsStateGuardian.h \ + dxSavedFrameBuffer.I dxSavedFrameBuffer.h dxTextureContext.I \ + dxTextureContext.h + +#end lib_target diff --git a/panda/src/dxgsg/config_dxgsg.cxx b/panda/src/dxgsg/config_dxgsg.cxx new file mode 100644 index 0000000000..dc55414fb3 --- /dev/null +++ b/panda/src/dxgsg/config_dxgsg.cxx @@ -0,0 +1,66 @@ +// Filename: config_dxgsg.cxx +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_dxgsg.h" +#include "dxGraphicsStateGuardian.h" +#include "dxSavedFrameBuffer.h" +#include "dxTextureContext.h" + +#include + +Configure(config_dxgsg); +NotifyCategoryDef(dxgsg, ":display:gsg"); + +// Configure this variable true to cause the DXGSG to show each +// transform space it renders by drawing a little unit axis. This +// cannot be enabled when the player is compiled in NDEBUG mode. +bool dx_show_transforms = config_dxgsg.GetBool("dx-show-transforms", false); + +// Configure this to TRUE if you want DirectX to control the entire screen, +// If false, it will just blit into a window. +bool dx_full_screen = config_dxgsg.GetBool("dx-full-screen", false); + +// Configure this true to glHint the textures into the cheapest +// possible mode. +bool dx_cheap_textures = config_dxgsg.GetBool("dx-cheap-textures", false); + +// Configure this true to perform a cull traversal over the geometry +// by default, false otherwise. The cull traversal provides support +// for state-sorting, z-sorting, and binning. +bool dx_cull_traversal = config_dxgsg.GetBool("dx-cull-traversal", true); + +DXDecalType dx_decal_type = GDT_blend; + +static DXDecalType +parse_decal_type(const string &type) { + if (type == "mask") { + return GDT_mask; + } else if (type == "blend") { + return GDT_blend; + } else if (type == "offset") { + return GDT_offset; + } + dxgsg_cat.error() << "Invalid dx-decal-type: " << type << "\n"; + return GDT_offset; +} + +ConfigureFn(config_dxgsg) { + + string decal_type = config_dxgsg.GetString("dx-decal-type", ""); + if (!decal_type.empty()) { + dx_decal_type = parse_decal_type(decal_type); + } + + DXGraphicsStateGuardian::init_type(); + DXSavedFrameBuffer::init_type(); + DXTextureContext::init_type(); + + GraphicsStateGuardian::_factory.register_factory + (DXGraphicsStateGuardian::get_class_type(), + DXGraphicsStateGuardian::make_DXGraphicsStateGuardian); +} + + + diff --git a/panda/src/dxgsg/config_dxgsg.h b/panda/src/dxgsg/config_dxgsg.h new file mode 100644 index 0000000000..c343d26091 --- /dev/null +++ b/panda/src/dxgsg/config_dxgsg.h @@ -0,0 +1,28 @@ +// Filename: config_dxgsg.h +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_DXGSG_H +#define CONFIG_DXGSG_H + +#include +#include + +NotifyCategoryDecl(dxgsg, EXPCL_PANDADX, EXPTP_PANDADX); + +extern bool dx_show_transforms; +extern bool dx_full_screen; +extern bool dx_cheap_textures; +extern bool dx_cull_traversal; + +// Ways to implement decals. +enum DXDecalType { + GDT_mask, // GL 1.0 style, involving three steps + GDT_blend, // As above, but slower; a hack for broken nVidia driver + GDT_offset // The fastest, using GL 1.1 style glPolygonOffset +}; +extern DXDecalType dx_decal_type; + + +#endif diff --git a/panda/src/dxgsg/dxGraphicsStateGuardian.I b/panda/src/dxgsg/dxGraphicsStateGuardian.I new file mode 100644 index 0000000000..d6c50ac3a7 --- /dev/null +++ b/panda/src/dxgsg/dxGraphicsStateGuardian.I @@ -0,0 +1,888 @@ +// Filename: dxGraphicsStateGuardian.I +// Created by: mike (02Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_dxgsg.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: DxGraphicsStateGuardian::activate +// Access: Public +// Description: Sets this context to be the active context for future +// DX commands. +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +activate() { +// _win->make_current(); +} + + + +#ifdef WBD_GL_MODE +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glClearColor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glClearColor(GLclampf red, GLclampf green, GLclampf blue, + GLclampf alpha) { + if (red != _clear_color_red || + green != _clear_color_green || + blue != _clear_color_blue || + alpha != _clear_color_alpha) { + glClearColor(red, green, blue, alpha); + _clear_color_red = red; + _clear_color_green = green; + _clear_color_blue = blue; + _clear_color_alpha = alpha; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glClearDepth +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glClearDepth(GLclampd depth) { + if (depth != _clear_depth) { +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "glClearDepth(" << (double)depth << ")" << endl; +#endif + glClearDepth(depth); + _clear_depth = depth; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glClearStencil +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glClearStencil(GLint s) { + if (s != _clear_stencil) { + glClearStencil(s); + _clear_stencil = s; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glClearAccum +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glClearAccum(GLclampf red, GLclampf green, GLclampf blue, + GLclampf alpha) { + if (red != _clear_accum_red || + green != _clear_accum_green || + blue != _clear_accum_blue || + alpha != _clear_accum_alpha) { + glClearAccum(red, green, blue, alpha); + _clear_accum_red = red; + _clear_accum_green = green; + _clear_accum_blue = blue; + _clear_accum_alpha = alpha; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glDrawBuffer +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glDrawBuffer(GLenum mode) { + if (mode != _draw_buffer_mode) { +#ifdef GSG_VERBOSE + dxgsg_cat.debug() << "glDrawBuffer("; + switch (mode) { + case GL_FRONT: + dxgsg_cat.debug(false) << "GL_FRONT)"; + break; + case GL_BACK: + dxgsg_cat.debug(false) << "GL_BACK)"; + break; + case GL_RIGHT: + dxgsg_cat.debug(false) << "GL_RIGHT)"; + break; + case GL_LEFT: + dxgsg_cat.debug(false) << "GL_LEFT)"; + break; + case GL_FRONT_RIGHT: + dxgsg_cat.debug(false) << "GL_FRONT_RIGHT)"; + break; + case GL_FRONT_LEFT: + dxgsg_cat.debug(false) << "GL_FRONT_LEFT)"; + break; + case GL_BACK_RIGHT: + dxgsg_cat.debug(false) << "GL_BACK_RIGHT)"; + break; + case GL_BACK_LEFT: + dxgsg_cat.debug(false) << "GL_BACK_LEFT)"; + break; + case GL_FRONT_AND_BACK: + dxgsg_cat.debug(false) << "GL_FRONT_AND_BACK)"; + break; + } + dxgsg_cat.debug(false) << endl; +#endif + glDrawBuffer(mode); + _draw_buffer_mode = mode; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glReadBuffer +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glReadBuffer(GLenum mode) { + if (mode != _read_buffer_mode) { + glReadBuffer(mode); + _read_buffer_mode = mode; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glShadeModel +// Access: +// Description: Set the shading model to be either GL_FLAT or GL_SMOOTH +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glShadeModel(GLenum mode) { + if (_shade_model_mode != mode) { + glShadeModel(mode); + _shade_model_mode = mode; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glViewport +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glViewport(GLint x, GLint y, GLsizei width, GLsizei height) +{ + if ( _viewport_x != x || _viewport_y != y || + _viewport_width != width || _viewport_height != height ) + { + _viewport_x = x; _viewport_y = y; + _viewport_width = width; _viewport_height = height; + glViewport( x, y, width, height ); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glStencilFunc +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glStencilFunc(GLenum func) { + if (_stencil_func != func) { + _stencil_func = func; +#ifdef GSG_VERBOSE + dxgsg_cat.debug() << "glStencilFunc("; + switch (func) { + case GL_NEVER: + dxgsg_cat.debug(false) << "GL_NEVER, "; + break; + case GL_LESS: + dxgsg_cat.debug(false) << "GL_LESS, "; + break; + case GL_EQUAL: + dxgsg_cat.debug(false) << "GL_EQUAL, "; + break; + case GL_LEQUAL: + dxgsg_cat.debug(false) << "GL_LEQUAL, "; + break; + case GL_GREATER: + dxgsg_cat.debug(false) << "GL_GREATER, "; + break; + case GL_NOTEQUAL: + dxgsg_cat.debug(false) << "GL_NOTEQUAL, "; + break; + case GL_GEQUAL: + dxgsg_cat.debug(false) << "GL_GEQUAL, "; + break; + case GL_ALWAYS: + dxgsg_cat.debug(false) << "GL_ALWAYS, "; + break; + default: + dxgsg_cat.debug(false) << "unknown, "; + break; + } + dxgsg_cat.debug(false) << "1, 1)" << endl; +#endif + glStencilFunc(func, 1, 1); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glStencilOp +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glStencilOp(GLenum op) { + if (_stencil_op != op) { + _stencil_op = op; +#ifdef GSG_VERBOSE + dxgsg_cat.debug() << "glStencilOp(GL_KEEP, GL_KEEP, "; + switch (op) { + case GL_KEEP: + dxgsg_cat.debug(false) << "GL_KEEP)"; + break; + case GL_ZERO: + dxgsg_cat.debug(false) << "GL_ZERO)"; + break; + case GL_REPLACE: + dxgsg_cat.debug(false) << "GL_REPLACE)"; + break; + case GL_INCR: + dxgsg_cat.debug(false) << "GL_INCR)"; + break; + case GL_DECR: + dxgsg_cat.debug(false) << "GL_DECR)"; + break; + case GL_INVERT: + dxgsg_cat.debug(false) << "GL_INVERT)"; + break; + default: + dxgsg_cat.debug(false) << "unknown)"; + break; + } + dxgsg_cat.debug(false) << endl; +#endif + glStencilOp(GL_KEEP, GL_KEEP, op); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glLineWidth +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glLineWidth(GLfloat width) { + if (_line_width != width) { + _line_width = width; +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "glLineWidth(" << width << ")" << endl; +#endif + glLineWidth(width); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glPointSize +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glPointSize(GLfloat size) { + if (_point_size != size) { + _point_size = size; +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "glPointSize(" << size << ")" << endl; +#endif + glPointSize(size); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glFogMode +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glFogMode(GLint mode) { + if (_fog_mode != mode) { + _fog_mode = mode; +#ifdef GSG_VERBOSE + dxgsg_cat.debug() << "glFog(GL_FOG_MODE, "; + switch(mode) { + case GL_LINEAR: + dxgsg_cat.debug(false) << "GL_LINEAR)" << endl; + break; + case GL_EXP: + dxgsg_cat.debug(false) << "GL_EXP)" << endl; + break; + case GL_EXP2: + dxgsg_cat.debug(false) << "GL_EXP2)" << endl; + break; +#ifdef GL_FOG_FUNC_SGIS + case GL_FOG_FUNC_SGIS: + dxgsg_cat.debug(false) << "GL_FOG_FUNC_SGIS)" << endl; + break; +#endif + default: + dxgsg_cat.debug(false) << "unknown)" << endl; + break; + } +#endif + glFogi(GL_FOG_MODE, mode); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glFogStart +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glFogStart(GLfloat start) { + if (_fog_start != start) { + _fog_start = start; +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "glFog(GL_FOG_START, " << start << ")" << endl; +#endif + glFogf(GL_FOG_START, start); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glFogEnd +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glFogEnd(GLfloat end) { + if (_fog_end != end) { + _fog_end = end; +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "glFog(GL_FOG_END, " << end << ")" << endl; +#endif + glFogf(GL_FOG_END, end); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glFogDensity +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glFogDensity(GLfloat density) { + if (_fog_density != density) { + _fog_density = density; +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "glFog(GL_FOG_DENSITY, " << density << ")" << endl; +#endif + glFogf(GL_FOG_DENSITY, density); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glFogColor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glFogColor(const Colorf &color) { + if (_fog_color != color) { + _fog_color = color; +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "glFog(GL_FOG_COLOR, " << color << ")" << endl; +#endif + glFogfv(GL_FOG_COLOR, color.get_data()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glAlphaFunc +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_glAlphaFunc(GLenum func, GLclampf ref) { + if (_alpha_func != func || _alpha_func_ref != ref) { + _alpha_func = func; + _alpha_func_ref = ref; +#ifdef GSG_VERBOSE + dxgsg_cat.debug() << "glAlphaFunc("; + switch (func) { + case GL_NEVER: + dxgsg_cat.debug(false) << "GL_NEVER, "; + break; + case GL_LESS: + dxgsg_cat.debug(false) << "GL_LESS, "; + break; + case GL_EQUAL: + dxgsg_cat.debug(false) << "GL_EQUAL, "; + break; + case GL_LEQUAL: + dxgsg_cat.debug(false) << "GL_LEQUAL, "; + break; + case GL_GREATER: + dxgsg_cat.debug(false) << "GL_GREATER, "; + break; + case GL_NOTEQUAL: + dxgsg_cat.debug(false) << "GL_NOTEQUAL, "; + break; + case GL_GEQUAL: + dxgsg_cat.debug(false) << "GL_GEQUAL, "; + break; + case GL_ALWAYS: + dxgsg_cat.debug(false) << "GL_ALWAYS, "; + break; + } + dxgsg_cat.debug() << ref << ")" << endl; +#endif + glAlphaFunc(func, ref); + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::get_light_id +// Access: Public +// Description: Convert index to gl light id +//////////////////////////////////////////////////////////////////// +INLINE GLenum DXGraphicsStateGuardian::get_light_id(int index) const +{ + switch( index ) + { + case 0: return GL_LIGHT0; + case 1: return GL_LIGHT1; + case 2: return GL_LIGHT2; + case 3: return GL_LIGHT3; + case 4: return GL_LIGHT4; + case 5: return GL_LIGHT5; + case 6: return GL_LIGHT6; + case 7: return GL_LIGHT7; + default: + dxgsg_cat.error() + << "get_light_id() - we don't currently support ids " + << "> 8" << endl; + break; + } + return GL_LIGHT0; +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::get_clip_plane_id +// Access: Public +// Description: Convert index to gl clip plane id +//////////////////////////////////////////////////////////////////// +INLINE GLenum DXGraphicsStateGuardian:: +get_clip_plane_id(int index) const { + switch(index) { + case 0: return GL_CLIP_PLANE0; + case 1: return GL_CLIP_PLANE1; + case 2: return GL_CLIP_PLANE2; + case 3: return GL_CLIP_PLANE3; + case 4: return GL_CLIP_PLANE4; + case 5: return GL_CLIP_PLANE5; + default: + dxgsg_cat.error() + << "get_clip_plane_id() - we don't currently support ids " + << "> 5" << endl; + break; + } + return GL_CLIP_PLANE0; +} + + +#endif // WBD_GL_MODE + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::set_pack_alignment +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +set_pack_alignment(int alignment) { + if (_pack_alignment != alignment) { +// glPixelStorei(GL_PACK_ALIGNMENT, alignment); + _pack_alignment = alignment; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::set_unpack_alignment +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +set_unpack_alignment(int alignment) { + if (_unpack_alignment != alignment) { +// glPixelStorei(GL_UNPACK_ALIGNMENT, alignment); + _unpack_alignment = alignment; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::enable_multisample +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +enable_multisample(bool val) { + if (_multisample_enabled != val) { + _multisample_enabled = val; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::enable_line_smooth +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +enable_line_smooth(bool val) { + if (_line_smooth_enabled != val) { + _line_smooth_enabled = val; + _d3dDevice->SetRenderState(D3DRENDERSTATE_EDGEANTIALIAS, (DWORD)val); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::enable_point_smooth +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +enable_point_smooth(bool val) { + if (_point_smooth_enabled != val) { + _point_smooth_enabled = val; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::enable_lighting +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +enable_lighting(bool val) { + if (_lighting_enabled != val) { + _lighting_enabled = val; + _d3dDevice->SetRenderState(D3DRENDERSTATE_LIGHTING, (DWORD)val); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::enable_dither +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +enable_dither(bool val) { + if (_dither_enabled != val) { + _dither_enabled = val; + _d3dDevice->SetRenderState(D3DRENDERSTATE_DITHERENABLE, (DWORD)val); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::enable_stencil_test +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +enable_stencil_test(bool val) { + if (_stencil_test_enabled != val) { + _stencil_test_enabled = val; + _d3dDevice->SetRenderState(D3DRENDERSTATE_STENCILENABLE, (DWORD)val); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::enable_color_material +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +enable_color_material(bool val) { + if (_color_material_enabled != val) { + _color_material_enabled = val; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::enable_texturing +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +enable_texturing(bool val) { + if (_texturing_enabled != val) { + _texturing_enabled = val; + } + + if(val == FALSE) { + _d3dDevice->SetTexture(0,NULL); + } else { + if(_pCurTexContext!=NULL) { + _d3dDevice->SetTexture(0,_pCurTexContext->_surface); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::enable_clip_plane +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +enable_clip_plane(int clip_plane, bool val) +{ + if (_clip_plane_enabled[clip_plane] != val) + { + _clip_plane_enabled[clip_plane] = val; + DWORD ClipPlaneBits; + _d3dDevice->GetRenderState(D3DRENDERSTATE_CLIPPLANEENABLE , &ClipPlaneBits); + if (val) + ClipPlaneBits |= 1 << clip_plane; + else + ClipPlaneBits &= ~(1 << clip_plane); + _d3dDevice->SetRenderState(D3DRENDERSTATE_CLIPPLANEENABLE , ClipPlaneBits); + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::enable_multisample_alpha_one +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +enable_multisample_alpha_one(bool val) { + if (_multisample_alpha_one_enabled != val) { + _multisample_alpha_one_enabled = val; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::enable_multisample_alpha_mask +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +enable_multisample_alpha_mask(bool val) { + if (_multisample_alpha_mask_enabled != val) { + _multisample_alpha_mask_enabled = val; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::enable_blend +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +enable_blend(bool val) { + if (_blend_enabled != val) { + _blend_enabled = val; + _d3dDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, (DWORD)val); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::enable_fog +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +enable_fog(bool val) { + if (_fog_enabled != val) { + _fog_enabled = val; + _d3dDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, (DWORD)val); + } +} + + + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::enable_alpha_test +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +enable_alpha_test(bool val ) +{ + if (_alpha_test_enabled != val) { + _alpha_test_enabled = val; + _d3dDevice->SetRenderState(D3DRENDERSTATE_ALPHATESTENABLE, (DWORD)val); + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_dxLightModelAmbient +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_dxLightModelAmbient( const Colorf& color) +{ + if (_lmodel_ambient != color) { + _lmodel_ambient = color; +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "glLightModel(GL_LIGHT_MODEL_AMBIENT, " << color << ")" << endl; +#endif + _d3dDevice->SetRenderState( D3DRENDERSTATE_AMBIENT, + D3DRGBA(color[0], color[1], color[2], color[3])); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::call_glAlphaFunc +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +call_dxAlphaFunc(D3DCMPFUNC func, DWORD ref) +{ + if (_alpha_func != func || _alpha_func_ref != ref) { + _alpha_func = func; + _alpha_func_ref = ref; +#ifdef GSG_VERBOSE + dxgsg_cat.debug() << "glAlphaFunc("; + switch (func) { + case D3DCMP_NEVER: + dxgsg_cat.debug(false) << "D3DCMP_NEVER, "; + break; + case D3DCMP_LESS: + dxgsg_cat.debug(false) << "D3DCMP_LESS, "; + break; + case D3DCMP_EQUAL: + dxgsg_cat.debug(false) << "D3DCMP_EQUAL, "; + break; + case D3DCMP_LEQUAL: + dxgsg_cat.debug(false) << "D3DCMP_LEQUAL, "; + break; + case D3DCMP_GREATER: + dxgsg_cat.debug(false) << "D3DCMP_GREATER, "; + break; + case D3DCMP_NOTEQUAL: + dxgsg_cat.debug(false) << "D3DCMP_NOTEQUAL, "; + break; + case D3DCMP_GEQUAL: + dxgsg_cat.debug(false) << "D3DCMP_GEQUAL, "; + break; + case D3DCMP_ALWAYS: + dxgsg_cat.debug(false) << "D3DCMP_ALWAYS, "; + break; + } + dxgsg_cat.debug() << ref << ")" << endl; +#endif + _d3dDevice->SetRenderState(D3DRENDERSTATE_ALPHAFUNC, func); + _d3dDevice->SetRenderState(D3DRENDERSTATE_ALPHAREF, ref); + } +} + + +INLINE void DXGraphicsStateGuardian:: +call_dxBlendFunc(D3DBLEND sfunc, D3DBLEND dfunc ) +{ + if (_blend_source_func != sfunc) + { + _blend_source_func = sfunc; + _d3dDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, sfunc); +#ifdef GSG_VERBOSE + dxgsg_cat.debug() << "dxSrcBlendFunc("; + switch (sfunc) + { + case D3DBLEND_ZERO: + dxgsg_cat.debug(false) << "ZERO, "; + break; + case D3DBLEND_ONE: + dxgsg_cat.debug(false) << "ONE, "; + break; + case D3DBLEND_DESTCOLOR: + dxgsg_cat.debug(false) << "DESTCOLOR, "; + break; + case D3DBLEND_INVDESTCOLOR: + dxgsg_cat.debug(false) << "INVDESTCOLOR, "; + break; + case D3DBLEND_SRCALPHA: + dxgsg_cat.debug(false) << "SRCALPHA, "; + break; + case D3DBLEND_INVSRCALPHA: + dxgsg_cat.debug(false) << "INVSRCALPHA, "; + break; + case D3DBLEND_DESTALPHA: + dxgsg_cat.debug(false) << "DESTALPHA, "; + break; + case D3DBLEND_INVDESTALPHA: + dxgsg_cat.debug(false) << "INVDESTALPHA, "; + break; + case D3DBLEND_SRCALPHASAT: + dxgsg_cat.debug(false) << "SRCALPHASAT, "; + break; + default: + dxgsg_cat.debug(false) << "unknown, "; + break; + } + dxgsg_cat.debug(false) << endl; +#endif + } + if ( _blend_dest_func != dfunc) + { + _blend_dest_func = dfunc; + _d3dDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, dfunc); +#ifdef GSG_VERBOSE + dxgsg_cat.debug() << "dxDstBlendFunc("; + switch (dfunc) + { + case D3DBLEND_ZERO: + dxgsg_cat.debug(false) << "ZERO, "; + break; + case D3DBLEND_ONE: + dxgsg_cat.debug(false) << "ONE, "; + break; + case D3DBLEND_DESTCOLOR: + dxgsg_cat.debug(false) << "DESTCOLOR, "; + break; + case D3DBLEND_INVDESTCOLOR: + dxgsg_cat.debug(false) << "INVDESTCOLOR, "; + break; + case D3DBLEND_SRCALPHA: + dxgsg_cat.debug(false) << "SRCALPHA, "; + break; + case D3DBLEND_INVSRCALPHA: + dxgsg_cat.debug(false) << "INVSRCALPHA, "; + break; + case D3DBLEND_DESTALPHA: + dxgsg_cat.debug(false) << "DESTALPHA, "; + break; + case D3DBLEND_INVDESTALPHA: + dxgsg_cat.debug(false) << "INVDESTALPHA, "; + break; + case D3DBLEND_SRCALPHASAT: + dxgsg_cat.debug(false) << "SRCALPHASAT, "; + break; + } + dxgsg_cat.debug(false) << endl; +#endif + } +} + diff --git a/panda/src/dxgsg/dxGraphicsStateGuardian.cxx b/panda/src/dxgsg/dxGraphicsStateGuardian.cxx new file mode 100644 index 0000000000..d29416b710 --- /dev/null +++ b/panda/src/dxgsg/dxGraphicsStateGuardian.cxx @@ -0,0 +1,4460 @@ +// Filename: dxGraphicsStateGuardian.cxx +// Created by: mike (02Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "dxSavedFrameBuffer.h" +#include "config_dxgsg.h" +#include "dxGraphicsStateGuardian.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +TypeHandle DXGraphicsStateGuardian::_type_handle; + +// bit masks used for drawing primitives +#define PerTexcoord 8 +#define PerColor 4 +#define PerNormal 2 +#define PerCoord 1 + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DXGraphicsStateGuardian:: +DXGraphicsStateGuardian(GraphicsWindow *win) : GraphicsStateGuardian(win) { + _light_enabled = (bool *)NULL; + _cur_light_enabled = (bool *)NULL; + _clip_plane_enabled = (bool *)NULL; + _cur_clip_plane_enabled = (bool *)NULL; + _fvf_buf = NULL; + _sav_fvf = new char[VERT_BUFFER_SIZE]; // allocate storage for vertex info. + _dx_ready = false; + + _pri = _zbuf = _back = NULL; + _pDD = NULL; + _d3dDevice = NULL; + + _cNumTexPixFmts = 0; + _pTexPixFmts = NULL; + _pCurTexContext = NULL; + + // Create a default RenderTraverser. + if (dx_cull_traversal) { + _render_traverser = + new CullTraverser(this, RenderRelation::get_class_type()); + } else { + _render_traverser = + new DirectRenderTraverser(this, RenderRelation::get_class_type()); + } + + //Color and alpha transform variables + _color_transform_enabled = false; + _alpha_transform_enabled = false; + _current_color_mat = LMatrix4f::ident_mat(); + _current_alpha_offset = 0; + _current_alpha_scale = 1; + + reset(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DXGraphicsStateGuardian:: +~DXGraphicsStateGuardian() { + if (_d3dDevice != NULL) + _d3dDevice->SetTexture(0, NULL); + _pCurTexContext = NULL; + + free_pointers(); + delete [] _sav_fvf; +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::reset +// Access: Public, Virtual +// Description: Resets all internal state as if the gsg were newly +// created. +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +reset() { + free_pointers(); + activate(); + GraphicsStateGuardian::reset(); + _buffer_mask = 0; + + // All implementations have the following buffers. (?) + _buffer_mask = (RenderBuffer::T_color | + RenderBuffer::T_depth | + RenderBuffer::T_stencil | + RenderBuffer::T_accum ); + + // WBD for now, let's assume a back buffer too); + _buffer_mask |= RenderBuffer::T_back; + + _current_projection_mat = LMatrix4f::ident_mat(); + _projection_mat_stack_count = 0; + +#ifdef WBD_GL_MODE + + // Make sure the GL state matches all of our initial attribute + // states. + PT(DepthTestAttribute) dta = new DepthTestAttribute; + PT(DepthWriteAttribute) dwa = new DepthWriteAttribute; + PT(CullFaceAttribute) cfa = new CullFaceAttribute; + PT(LightAttribute) la = new LightAttribute; + PT(TextureAttribute) ta = new TextureAttribute; + + dta->issue(this); + dwa->issue(this); + cfa->issue(this); + la->issue(this); + ta->issue(this); + + + // Check to see if we have double-buffering. + GLboolean has_back; + glGetBooleanv(GL_DOUBLEBUFFER, &has_back); + if (!has_back) { + _buffer_mask &= ~RenderBuffer::T_back; + } + + // Check to see if we have stereo (and therefore a right buffer). + GLboolean has_stereo; + glGetBooleanv(GL_STEREO, &has_stereo); + if (!has_stereo) { + _buffer_mask &= ~RenderBuffer::T_right; + } + + // Set up our clear values to invalid values, so the glClear* calls + // will be made initially. + _clear_color_red = -1.0; + _clear_color_green = -1.0; + _clear_color_blue = -1.0; + _clear_color_alpha = -1.0; + _clear_depth = -1.0; + _clear_stencil = -1; + _clear_accum_red = -1.0; + _clear_accum_green = -1.0; + _clear_accum_blue = -1.0; + _clear_accum_alpha = -1.0; + + // Set up the specific state values to GL's known initial values. + _draw_buffer_mode = (has_back) ? GL_BACK : GL_FRONT; + _read_buffer_mode = (has_back) ? GL_BACK : GL_FRONT; + _shade_model_mode = GL_SMOOTH; + glFrontFace(GL_CCW); + + _line_width = 1.0; + _point_size = 1.0; + _depth_mask = false; + _fog_mode = GL_EXP; + _alpha_func = GL_ALWAYS; + _alpha_func_ref = 0; + + _pack_alignment = 4; + _unpack_alignment = 4; + + // Set up all the enabled/disabled flags to GL's known initial + // values: everything off. + _multisample_enabled = false; + _line_smooth_enabled = false; + _point_smooth_enabled = false; + _color_material_enabled = false; + _scissor_enabled = false; + _lighting_enabled = false; + _normals_enabled = false; + _texturing_enabled = false; + _multisample_alpha_one_enabled = false; + _multisample_alpha_mask_enabled = false; + _blend_enabled = false; + _depth_test_enabled = false; + _fog_enabled = false; + _alpha_test_enabled = false; + _decal_level = 0; + + _scissor_x = _scissor_y = _scissor_height = _scissor_width = 0; + + // Dither is on by default in GL, let's turn it off + _dither_enabled = true; + enable_dither(false); + + // Stencil test is off by default + _stencil_test_enabled = false; + _stencil_func = GL_NOTEQUAL; + _stencil_op = GL_REPLACE; + + // Antialiasing. + enable_line_smooth(false); + enable_multisample(true); + + // Set up the light id map + glGetIntegerv( GL_MAX_LIGHTS, &_max_lights ); + _available_light_ids = PTA(Light*)(_max_lights); + _light_enabled = new bool[_max_lights]; + _cur_light_enabled = new bool[_max_lights]; + int i; + for (i = 0; i < _max_lights; i++ ) { + _available_light_ids[i] = NULL; + _light_enabled[i] = false; + } + + // Set up the clip plane id map + glGetIntegerv(GL_MAX_CLIP_PLANES, &_max_clip_planes); + _available_clip_plane_ids = PTA(PlaneNode*)(_max_clip_planes); + _clip_plane_enabled = new bool[_max_clip_planes]; + _cur_clip_plane_enabled = new bool[_max_clip_planes]; + for (i = 0; i < _max_clip_planes; i++) { + _available_clip_plane_ids[i] = NULL; + _clip_plane_enabled[i] = false; + } + + if (dx_cheap_textures) { + dxgsg_cat.info() + << "Setting glHint() for fastest textures.\n"; + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + } + + +#else + _issued_color_enabled = false; + + _buffer_mask &= ~RenderBuffer::T_right; // test for these later + + // Set up our clear values to invalid values, so the glClear* calls + // will be made initially. + _clear_color_red = -1.0; + _clear_color_green = -1.0; + _clear_color_blue = -1.0; + _clear_color_alpha = -1.0; + _clear_depth = -1.0; + _clear_stencil = -1; + _clear_accum_red = -1.0; + _clear_accum_green = -1.0; + _clear_accum_blue = -1.0; + _clear_accum_alpha = -1.0; + _line_width = 1.0; + _point_size = 1.0; + _depth_mask = false; + _fog_mode = D3DFOG_EXP; + _alpha_func = D3DCMP_ALWAYS; + _alpha_func_ref = 0; +// _polygon_mode = GL_FILL; + + _pack_alignment = 4; + _unpack_alignment = 4; + + // Set up all the enabled/disabled flags to GL's known initial + // values: everything off. + _multisample_enabled = false; + _line_smooth_enabled = false; + _point_smooth_enabled = false; + _color_material_enabled = false; +// _scissor_enabled = false; + _lighting_enabled = false; + _normals_enabled = false; + _texturing_enabled = false; + _multisample_alpha_one_enabled = false; + _multisample_alpha_mask_enabled = false; + _blend_enabled = false; + _depth_test_enabled = false; + _fog_enabled = false; + _alpha_test_enabled = false; + _decal_level = 0; + + _dx_ready = false; + +#endif // WBD_GL_MODE +} + +HRESULT CALLBACK EnumTexFmtsCallback( LPDDPIXELFORMAT pddpf, VOID* param ) +{ + // wont build if its a member fn, so had to do this stuff + DXGraphicsStateGuardian *mystate = (DXGraphicsStateGuardian *) param; + assert(mystate->_cNumTexPixFmts < MAX_DX_TEXPIXFMTS); + memcpy( &(mystate->_pTexPixFmts[mystate->_cNumTexPixFmts]), pddpf, sizeof(DDPIXELFORMAT) ); + mystate->_cNumTexPixFmts++; + return DDENUMRET_OK; +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::reset +// Access: Public, Virtual +// Description: Handles initialization which assumes that DX has already been +// set up. +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +init_dx( LPDIRECTDRAW7 context, + LPDIRECTDRAWSURFACE7 pri, + LPDIRECTDRAWSURFACE7 back, + LPDIRECTDRAWSURFACE7 zbuf, + LPDIRECT3D7 d3d, + LPDIRECT3DDEVICE7 d3dDevice, + RECT viewrect) +{ + _pDD = context; + _pri = pri; + _back = back; + _zbuf = zbuf; + _d3d = d3d; + _d3dDevice = d3dDevice; + _view_rect = viewrect; + + _pTexPixFmts = new DDPIXELFORMAT[MAX_DX_TEXPIXFMTS]; + + assert(_pTexPixFmts!=NULL); + + if (d3dDevice->EnumTextureFormats(EnumTexFmtsCallback, this) != S_OK) { + dxgsg_cat.error() << "EnumTextureFormats failed!!\n"; + } + + SetRect(&clip_rect, 0,0,0,0); // no clip rect set + + _d3dDevice->SetRenderState(D3DRENDERSTATE_AMBIENTMATERIALSOURCE, D3DMCS_COLOR1); + + // Lighting, let's turn it off by default + _lighting_enabled = true; + enable_lighting(false); + + // Dither, let's turn it off + _dither_enabled = true; + enable_dither(false); + + // Stencil test is off by default + _stencil_test_enabled = false; +// _stencil_func = D3DCMP_NOTEQUAL; +// _stencil_op = D3DSTENCILOP_REPLACE; + + // Antialiasing. + enable_line_smooth(false); + enable_multisample(true); + + _max_lights = 16; // assume for now. This is totally arbitrary + _available_light_ids = PTA(Light*)(_max_lights); + _light_enabled = new bool[_max_lights]; + _cur_light_enabled = new bool[_max_lights]; + int i; + for (i = 0; i < _max_lights; i++ ) { + _available_light_ids[i] = NULL; + _light_enabled[i] = false; + } + + // Set up the clip plane id map + _max_clip_planes = D3DMAXUSERCLIPPLANES; + _available_clip_plane_ids = PTA(PlaneNode*)(_max_clip_planes); + _clip_plane_enabled = new bool[_max_clip_planes]; + _cur_clip_plane_enabled = new bool[_max_clip_planes]; + for (i = 0; i < _max_clip_planes; i++) { + _available_clip_plane_ids[i] = NULL; + _clip_plane_enabled[i] = false; + } + + // initial clip rect + SetRect(&clip_rect, 0,0,0,0); // no clip rect set + + // Make sure the GL state matches all of our initial attribute + // states. + PT(DepthTestAttribute) dta = new DepthTestAttribute; + PT(DepthWriteAttribute) dwa = new DepthWriteAttribute; + PT(CullFaceAttribute) cfa = new CullFaceAttribute; + PT(LightAttribute) la = new LightAttribute; + PT(TextureAttribute) ta = new TextureAttribute; + + dta->issue(this); + dwa->issue(this); + cfa->issue(this); + la->issue(this); + ta->issue(this); + +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::clear +// Access: Public, Virtual +// Description: Clears all of the indicated buffers to their assigned +// colors. +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +clear(const RenderBuffer &buffer) { + + activate(); + + nassertv(buffer._gsg == this); + int buffer_type = buffer._buffer_type; + + int flags = 0; + + if (buffer_type & RenderBuffer::T_depth) + flags |= D3DCLEAR_ZBUFFER; + if (buffer_type & RenderBuffer::T_back) //set appropriate flags + flags |= D3DCLEAR_TARGET; + if (buffer_type & RenderBuffer::T_stencil) + flags |= D3DCLEAR_STENCIL; + + D3DCOLOR clear_colr = D3DRGBA(_color_clear_value[0],_color_clear_value[1], + _color_clear_value[2], /*1.0*/_color_clear_value[3]); + + HRESULT hr = _d3dDevice->Clear(0, NULL, flags, clear_colr, + (D3DVALUE) _depth_clear_value, (DWORD)_stencil_clear_value); + if (hr != DD_OK) + dxgsg_cat.error() + << "dxGSG::clear_buffer failed: Clear returned " << ConvD3DErrorToString(hr) << endl; + /* The following line will cause the background to always clear to a medium red + _color_clear_value[0] = .5; + /* The following lines will cause the background color to cycle from black to red. + _color_clear_value[0] += .001; + if (_color_clear_value[0] > 1.0) _color_clear_value[0] = 0.0; + */ +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::clear +// Access: Public, Virtual +// Description: Clears all of the indicated buffers to their assigned +// colors. +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +clear(const RenderBuffer &buffer, const DisplayRegion *region) { + DisplayRegionStack old_dr = push_display_region(region); + prepare_display_region(); + clear(buffer); + pop_display_region(old_dr); +} + + + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::enable_light +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +bool DXGraphicsStateGuardian:: +enable_light(int light, bool val) +{ + if ( _light_enabled[light] != val ) + { + _light_enabled[light] = val; + HRESULT res = _d3dDevice->LightEnable( light, val ); + +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "LightEnable(" << light << "=" << val << ")" << endl; +#endif + + return (res == DD_OK); + } + return TRUE; +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::prepare_display_region +// Access: Public, Virtual +// Description: Prepare a display region for rendering (set up +// scissor region and viewport) +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +prepare_display_region() { + if (_current_display_region == (DisplayRegion*)0L) { + dxgsg_cat.error() + << "Invalid NULL display region in prepare_display_region()\n"; + + } else if (_current_display_region != _actual_display_region) { + _actual_display_region = _current_display_region; +/* + int l, b, w, h; + _actual_display_region->get_region_pixels(l, b, w, h); + GLint x = GLint(l); + GLint y = GLint(b); + GLsizei width = GLsizei(w); + GLsizei height = GLsizei(h); +#ifdef WBD_GL_MODE + call_glScissor( x, y, width, height ); + call_glViewport( x, y, width, height ); +#else + if ( _scissor_x != x || _scissor_y != y || + _scissor_width != width || _scissor_height != height ) + { + _scissor_x = x; _scissor_y = y; + _scissor_width = width; _scissor_height = height; + RECT cliprect; + SetRect(&cliprect, x, y, x+width, y+height ); + set_clipper(cliprect); + } +#endif //WBD_GL_MODE +*/ + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: set_clipper +// Access: +// Description: Useless in DX at the present time +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian::set_clipper(RECT cliprect) { + LPDIRECTDRAWCLIPPER Clipper; + HRESULT result; + + // For windowed mode, the clip region is associated with the window, + // and DirectX does not allow you to create clip regions. + if (dx_full_screen) return; + + /* The cliprect we receive is normalized so that (0,0) means the upper left of + the client portion of the window. + At least, I think that's true, and the following code assumes that. + So we must adjust the clip region by offsetting it to the origin of the + view rectangle. + */ + clip_rect = cliprect; // store the normalized clip rect + cliprect.left += _view_rect.left; + cliprect.right += _view_rect.left; + cliprect.top += _view_rect.top; + cliprect.bottom += _view_rect.top; + RGNDATA *rgn_data = (RGNDATA *)malloc(sizeof(RGNDATAHEADER) + sizeof(RECT)); + HRGN hrgn = CreateRectRgn(cliprect.left, cliprect.top, cliprect.right, cliprect.bottom); + GetRegionData(hrgn, sizeof(RGNDATAHEADER) + sizeof(RECT), rgn_data); + + if (_pri->GetClipper(&Clipper) != DD_OK) + { + result = _pDD->CreateClipper(0, &Clipper, NULL); + result = Clipper->SetClipList(rgn_data, 0); + result = _pri->SetClipper(Clipper); + } + else { + result = Clipper->SetClipList(rgn_data, 0 ); + if (result == DDERR_CLIPPERISUSINGHWND) + { + result = _pri->SetClipper(NULL); + result = _pDD->CreateClipper(0, &Clipper, NULL); + result = Clipper->SetClipList(rgn_data, 0 ) ; + result = _pri->SetClipper(Clipper); + } + } + +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::render_frame +// Access: Public, Virtual +// Description: Renders an entire frame, including all display +// regions within the frame, and includes any necessary +// pre- and post-processing like swapping buffers. +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +render_frame(const AllAttributesWrapper &initial_state) { + if (!_dx_ready) return; + + _win->begin_frame(); + _d3dDevice->BeginScene(); + +/* D3DMATRIX ident; + ident._11 = ident._32 = ident._44 = 1.0f; + ident._23 = -1.0f; + ident._12 = ident._13 = ident._14 = ident._41 = 0.0f; + ident._21 = ident._22 = ident._24 = ident._42 = 0.0f; + ident._31 = ident._33 = ident._34 = ident._43 = 0.0f; + + _d3dDevice->SetTransform(D3DTRANSFORMSTATE_VIEW, &ident); + */ +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "begin frame --------------------------------------------" << endl; +#endif + + _decal_level = 0; + + // First, clear the entire window. + PT(DisplayRegion) win_dr = + _win->make_scratch_display_region(_win->get_width(), _win->get_height()); + clear(get_render_buffer(RenderBuffer::T_back | RenderBuffer::T_depth), win_dr); + + // Now render each of our layers in order. + int max_channel_index = _win->get_max_channel_index(); + for (int c = 0; c < max_channel_index; c++) + { + if (_win->is_channel_defined(c)) + { + GraphicsChannel *chan = _win->get_channel(c); + if (chan->is_active()) + { + int num_layers = chan->get_num_layers(); + for (int l = 0; l < num_layers; l++) + { + GraphicsLayer *layer = chan->get_layer(l); + if (layer->is_active()) + { + int num_drs = layer->get_num_drs(); + for (int d = 0; d < num_drs; d++) + { + DisplayRegion *dr = layer->get_dr(d); + Camera *cam = dr->get_camera(); + + // For each display region, render from the camera's view. + if (dr->is_active() && cam != (Camera *)NULL && + cam->is_active() && cam->get_scene() != (Node *)NULL) + { + DisplayRegionStack old_dr = push_display_region(dr); + prepare_display_region(); + render_scene(cam->get_scene(), cam, initial_state); + pop_display_region(old_dr); + } + } + } // if (layer->is_active()) + } + } // if (chan->is_active()) + } + } // for (int c = 0; c < max_channel_index; c++) + + // Now we're done with the frame processing. Clean up. + + // Let's turn off all the lights we had on, and clear the light + // cache--to force the lights to be reissued next frame, in case + // their parameters or positions have changed between frames. + + for (int i = 0; i < _max_lights; i++) + { + enable_light(i, false); + _available_light_ids[i] = NULL; + } + + // Also force the lighting state to unlit, so that issue_light() + // will be guaranteed to be called next frame even if we have the + // same set of light pointers we had this frame. + NodeAttributes state; + state.set_attribute(LightTransition::get_class_type(), new LightAttribute); + state.set_attribute(TextureTransition::get_class_type(), new TextureAttribute); + set_state(state, false); + + // All this work to undo the lighting state each frame doesn't seem + // ideal--there may be a better way. Maybe if the lights were just + // more aware of whether their parameters or positions have changed + // at all? + + _d3dDevice->EndScene(); + + _win->end_frame(); + show_frame(); + +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "end frame ----------------------------------------------" << endl; +#endif +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::render_scene +// Access: Public, Virtual +// Description: Renders an entire scene, from the root node of the +// scene graph, as seen from a particular ProjectionNode +// and with a given initial state. This initial state +// may be modified during rendering. +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +render_scene(Node *root, const ProjectionNode *projnode, + const AllAttributesWrapper &initial_state) { +#ifdef GSG_VERBOSE + _pass_number = 0; + dxgsg_cat.debug() + << "begin scene - - - - - - - - - - - - - - - - - - - - - - - - -" + << endl; +#endif + _current_root_node = root; + + render_subgraph(_render_traverser, root, projnode, initial_state, + AllTransitionsWrapper()); + +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "done scene - - - - - - - - - - - - - - - - - - - - - - - - -" + << endl; +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::render_subgraph +// Access: Public, Virtual +// Description: Renders a subgraph of the scene graph as seen from a +// given projection node, and with a particular initial +// state. This state may be modified by the render +// process. +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +render_subgraph(RenderTraverser *traverser, + Node *subgraph, const ProjectionNode *projnode, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans) { + activate(); + const ProjectionNode *old_projection_node = _current_projection_node; + _current_projection_node = projnode; + LMatrix4f old_projection_mat = _current_projection_mat; + + // The projection matrix must always be right-handed Y-up, even if + // our coordinate system of choice is otherwise, because certain GL + // calls (specifically glTexGen(GL_SPHERE_MAP)) assume this kind of + // a coordinate system. Sigh. In order to implement a Z-up + // coordinate system, we'll store the Z-up conversion in the + // modelview matrix. + LMatrix4f projection_mat = + projnode->get_projection()->get_projection_mat(CS_yup_left); + + _current_projection_mat = projection_mat; + _projection_mat_stack_count++; + + // We load the projection matrix directly. + HRESULT res = + _d3dDevice->SetTransform(D3DTRANSFORMSTATE_PROJECTION, + (LPD3DMATRIX) _current_projection_mat.get_data()); + + // We infer the modelview matrix by doing a wrt on the projection + // node. + LMatrix4f modelview_mat; + get_rel_mat(subgraph, _current_projection_node, modelview_mat); +// get_rel_mat(_current_projection_node, subgraph, modelview_mat); + + + if (_coordinate_system != CS_yup_left) { + // Now we build the coordinate system conversion into the + // modelview matrix (as described in the paragraph above). + modelview_mat = modelview_mat * + LMatrix4f::convert_mat(_coordinate_system, CS_yup_left); + } + + // The modelview matrix will be loaded as each geometry is + // encountered. So we set the supplied modelview matrix as an + // initial value instead of loading it now. + AllTransitionsWrapper sub_trans = net_trans; + sub_trans.set_transition(new TransformTransition(modelview_mat)); + + render_subgraph(traverser, subgraph, initial_state, sub_trans); + + _current_projection_node = old_projection_node; + _current_projection_mat = old_projection_mat; + _projection_mat_stack_count--; + + + // We must now restore the projection matrix from before. We could + // do a push/pop matrix, but OpenGL doesn't promise more than 2 + // levels in the projection matrix stack, so we'd better do it in + // the CPU. + if (_projection_mat_stack_count > 0) + _d3dDevice->SetTransform(D3DTRANSFORMSTATE_PROJECTION, + (LPD3DMATRIX) _current_projection_mat.get_data()); + +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::render_subgraph +// Access: Public, Virtual +// Description: Renders a subgraph of the scene graph as seen from the +// current projection node, and with a particular +// initial state. This state may be modified during the +// render process. +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +render_subgraph(RenderTraverser *traverser, Node *subgraph, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans) { +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "begin subgraph (pass " << ++_pass_number + << ") - - - - - - - - - - - - - - - - - - - - - - - - -" << endl; +#endif + activate(); + + nassertv(traverser != (RenderTraverser *)NULL); + traverser->traverse(subgraph, initial_state, net_trans); + +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "end subgraph (pass " << _pass_number + << ") - - - - - - - - - - - - - - - - - - - - - - - - -" + << endl; +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_point +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +draw_point(const GeomPoint *geom) { + activate(); + +#ifdef GSG_VERBOSE + dxgsg_cat.debug() << "draw_point()" << endl; +#endif + +#ifdef WBD_GL_MODE + call_glPointSize(geom->get_size()); + + int nprims = geom->get_num_prims(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::NormalIterator ni = geom->make_normal_iterator(); + Geom::TexCoordIterator ti = geom->make_texcoord_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color_gl); + + // Points don't make a distinction between flat and smooth shading. + // We'll leave the shade model alone. + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + issuer.issue_normal(G_OVERALL, ni); + + glBegin(GL_POINTS); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + issuer.issue_normal(G_PER_PRIM, ni); + + // Draw per vertex, same thing. + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_normal(G_PER_VERTEX, ni); + + issuer.issue_vertex(G_PER_VERTEX, vi); + } + + glEnd(); +#else // The DX Way + int nPrims = geom->get_num_prims(); + + perVertex = 0; + if (geom->get_binding(G_COORD) == G_PER_VERTEX) perVertex |= PerCoord; + if (geom->get_binding(G_NORMAL) == G_PER_VERTEX) perVertex |= PerNormal; + if (geom->get_binding(G_COLOR) == G_PER_VERTEX) perVertex |= PerColor; + + perPrim = 0; + if (geom->get_binding(G_NORMAL) == G_PER_PRIM) perPrim |= PerNormal; + if (geom->get_binding(G_COLOR) == G_PER_PRIM) perPrim |= PerColor; + + size_t vertex_size = draw_prim_setup(geom); + + nassertv(_fvf_buf == NULL); // make sure the storage pointer is clean. + nassertv(nPrims * vertex_size < VERT_BUFFER_SIZE); + _fvf_buf = _sav_fvf; // _fvf_buf changes, sav_fvf doesn't + + // iterate through the point + draw_prim_inner_loop2(nPrims, geom, perPrim); + + _d3dDevice->DrawPrimitive(D3DPT_POINTLIST, p_flags, _sav_fvf, nPrims, NULL); + + _fvf_buf = NULL; + +#endif // WBD_GL_MODE +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_line +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +draw_line(const GeomLine* geom) { + activate(); + +#ifdef GSG_VERBOSE + dxgsg_cat.debug() << "draw_line()" << endl; +#endif + +#ifdef WBD_GL_MODE + call_glLineWidth(geom->get_width()); + + int nprims = geom->get_num_prims(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color_gl); + + if (geom->get_binding(G_COLOR) == G_PER_VERTEX) { + call_glShadeModel(GL_SMOOTH); + } else { + call_glShadeModel(GL_FLAT); + } + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + + glBegin(GL_LINES); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + + for (int j = 0; j < 2; j++) { + // Draw per vertex + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + } + + glEnd(); +#else // the DX way + + int nPrims = geom->get_num_prims(); + + perVertex = 0; + if (geom->get_binding(G_COORD) == G_PER_VERTEX) perVertex |= PerCoord; + if (geom->get_binding(G_NORMAL) == G_PER_VERTEX) perVertex |= PerNormal; + if (geom->get_binding(G_COLOR) == G_PER_VERTEX) perVertex |= PerColor; + + perPrim = 0; + if (geom->get_binding(G_NORMAL) == G_PER_PRIM) perPrim |= PerNormal; + if (geom->get_binding(G_COLOR) == G_PER_PRIM) perPrim |= PerColor; + + size_t vertex_size = draw_prim_setup(geom); + + void *_tmp_fvf = NULL; + nassertv(_fvf_buf == NULL); // make sure the storage pointer is clean. +// nassertv(nPrims * 2 * vertex_size < VERT_BUFFER_SIZE); + + if (nPrims * 2 * vertex_size > VERT_BUFFER_SIZE) + { + _fvf_buf = _tmp_fvf = new char[nPrims * 2 * vertex_size]; + } + else _fvf_buf = _sav_fvf; // _fvf_buf changes, sav_fvf doesn't + + for (int i = 0; i < nPrims; i++) + { + if (perPrim & PerColor) + { + p_color = geom->get_next_color(ci); // set primitive color if there is one. + p_colr = D3DRGBA(p_color[0], p_color[1], p_color[2], p_color[3]); + } + if (perPrim & PerNormal) + p_normal = geom->get_next_normal(ni); // set primitive normal if there is one. + draw_prim_inner_loop(2, geom); + } + + if (_tmp_fvf == NULL) + _d3dDevice->DrawPrimitive(D3DPT_LINELIST, p_flags, _sav_fvf, nPrims*2, NULL); + else { + _d3dDevice->DrawPrimitive(D3DPT_LINELIST, p_flags, _tmp_fvf, nPrims*2, NULL); + delete [] _tmp_fvf; + } + + _fvf_buf = NULL; + +#endif // WBD_GL_MODE +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_linestrip +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +draw_linestrip(const GeomLinestrip* geom) { + activate(); + +#ifdef GSG_VERBOSE + dxgsg_cat.debug() << "draw_linestrip()" << endl; +#endif + +#ifdef WBD_GL_MODE + call_glLineWidth(geom->get_width()); + + int nprims = geom->get_num_prims(); + const int *plen = geom->get_lengths(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color_gl); + + if (geom->get_binding(G_COLOR) == G_PER_VERTEX) { + call_glShadeModel(GL_SMOOTH); + } else { + call_glShadeModel(GL_FLAT); + } + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + + int num_verts = *(plen++); + nassertv(num_verts >= 2); + + glBegin(GL_LINE_STRIP); + + // Per-component attributes for the first line segment? + issuer.issue_color(G_PER_COMPONENT, ci); + + // Draw the first 2 vertices + int v; + for (v = 0; v < 2; v++) { + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + + // Now draw each of the remaining vertices. Each vertex from + // this point on defines a new line segment. + for (v = 2; v < num_verts; v++) { + // Per-component attributes? + issuer.issue_color(G_PER_COMPONENT, ci); + + // Per-vertex attributes + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + glEnd(); + } +#else + int nPrims = geom->get_num_prims(); + const int *plen = geom->get_lengths(); + + perVertex = 0; + if (geom->get_binding(G_COORD) == G_PER_VERTEX) perVertex |= PerCoord; + if (geom->get_binding(G_NORMAL) == G_PER_VERTEX) perVertex |= PerNormal; + if (geom->get_binding(G_COLOR) == G_PER_VERTEX) perVertex |= PerColor; + + perComp = 0; + if (geom->get_binding(G_NORMAL) == G_PER_COMPONENT) perComp |= PerNormal; + if (geom->get_binding(G_COLOR) == G_PER_COMPONENT) perComp |= PerColor; + + perPrim = 0; + if (geom->get_binding(G_NORMAL) == G_PER_PRIM) perPrim |= PerNormal; + if (geom->get_binding(G_COLOR) == G_PER_PRIM) perPrim |= PerColor; + + size_t vertex_size = draw_prim_setup(geom); + + for (int i = 0; i < nPrims; i++) + { + if (perPrim & PerColor) + { + p_color = geom->get_next_color(ci); // set primitive color if there is one. + p_colr = D3DRGBA(p_color[0], p_color[1], p_color[2], p_color[3]); + } + + size_t vertex_size = draw_prim_setup(geom); + + int nVerts = *(plen++); + nassertv(nVerts >= 2); + + nassertv(_fvf_buf == NULL); // make sure the storage pointer is clean. + nassertv(nVerts * vertex_size < VERT_BUFFER_SIZE); +// void *sav_fvf = new char[nVerts * vertex_size]; // allocate storage for vertex info. + _fvf_buf = _sav_fvf; // _fvf_buf changes, sav_fvf doesn't + + draw_prim_inner_loop2(nVerts, geom, perComp); + + _d3dDevice->DrawPrimitive(D3DPT_LINESTRIP, p_flags, _sav_fvf, nVerts, NULL); + + _fvf_buf = NULL; + } + + +#endif // WBD_GL_MODE +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_sprite +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +draw_sprite(const GeomSprite *geom) { +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_polygon +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +draw_polygon(const GeomPolygon *geom) { + activate(); + +#ifdef GSG_VERBOSE + dxgsg_cat.debug() << "draw_polygon()" << endl; +#endif + +#ifdef WBD_GL_MODE + int nprims = geom->get_num_prims(); + const int *plen = geom->get_lengths(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::NormalIterator ni = geom->make_normal_iterator(); + Geom::TexCoordIterator ti = geom->make_texcoord_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color_gl); + + // If we have per-vertex colors or normals, we need smooth shading. + // Otherwise we want flat shading for performance reasons. + if (geom->get_binding(G_COLOR) == G_PER_VERTEX || + geom->get_binding(G_NORMAL) == G_PER_VERTEX) { + call_glShadeModel(GL_SMOOTH); + } else { + call_glShadeModel(GL_FLAT); + } + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + issuer.issue_normal(G_OVERALL, ni); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + issuer.issue_normal(G_PER_PRIM, ni); + + int num_verts = *(plen++); + nassertv(num_verts >= 3); + + glBegin(GL_POLYGON); + + // Draw the vertices. + int v; + for (v = 0; v < num_verts; v++) { + // Per-vertex attributes. + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_normal(G_PER_VERTEX, ni); + issuer.issue_texcoord(G_PER_VERTEX, ti); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + glEnd(); + } +#endif // WBD_GL_MODE +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_prim_setup +// Access: Private +// Description: This adds data to the flexible vertex format +//////////////////////////////////////////////////////////////////// +size_t DXGraphicsStateGuardian:: +draw_prim_setup(const Geom *geom) +{ + + // set up iterators + vi = geom->make_vertex_iterator(); + ni = geom->make_normal_iterator(); + ti = geom->make_texcoord_iterator(); + ci = geom->make_color_iterator(); + + // Set the flags for the flexible vertex format and compute the bytes + // required to store a single vertex. + p_flags = 0; + size_t vertex_size = 0; + if (geom->get_binding(G_COLOR) != G_OFF || _issued_color_enabled) + { p_flags |= D3DFVF_DIFFUSE; vertex_size += sizeof(D3DCOLOR); } + if (geom->get_binding(G_COORD) != G_OFF) + { p_flags |= D3DFVF_XYZ; vertex_size += sizeof(D3DVALUE) * 3; } + if (geom->get_binding(G_NORMAL) != G_OFF) + { p_flags |= D3DFVF_NORMAL; vertex_size += sizeof(D3DVALUE) * 3; } + if (geom->get_binding(G_TEXCOORD) != G_OFF) + { p_flags |= D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0); + vertex_size += sizeof(float) * 2; + } + + + if (geom->get_binding(G_COLOR) == G_OVERALL) + { + p_color = geom->get_next_color(ci); // set overall color if there is one + p_colr = D3DRGBA(p_color[0], p_color[1], p_color[2], p_color[3]); + } + + if (geom->get_binding(G_NORMAL) == G_OVERALL) + p_normal = geom->get_next_normal(ni); // set overall normal if there is one + + if (_issued_color_enabled) + { + p_colr = _issued_color; // set primitive color if there is one. + perVertex &= ~PerColor; + perPrim &= ~PerColor; + perComp &= ~PerColor; + } + + // If we have per-vertex colors or normals, we need smooth shading. + // Otherwise we want flat shading for performance reasons. + if (perVertex & (PerColor | PerNormal)) + _d3dDevice->SetRenderState(D3DRENDERSTATE_SHADEMODE, D3DSHADE_GOURAUD); + else + _d3dDevice->SetRenderState(D3DRENDERSTATE_SHADEMODE, D3DSHADE_FLAT); + + return vertex_size; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_prim_inner_loop +// Access: Private +// Description: This adds data to the flexible vertex format +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +draw_prim_inner_loop(int loops, const Geom *geom) +{ + while (--loops >= 0) + { + switch(perVertex) + { + case 3: + p_normal = geom->get_next_normal(ni); + case 1: + p_vertex = geom->get_next_vertex(vi); + break; + case 5: + p_vertex = geom->get_next_vertex(vi); + case 4: + p_color = geom->get_next_color(ci); + p_colr = D3DRGBA(p_color[0], p_color[1], p_color[2], p_color[3]); + break; + case 7: + p_vertex = geom->get_next_vertex(vi); + case 6: + p_color = geom->get_next_color(ci); + p_colr = D3DRGBA(p_color[0], p_color[1], p_color[2], p_color[3]); + case 2: + p_normal = geom->get_next_normal(ni); + break; + case 9: + p_vertex = geom->get_next_vertex(vi); + case 8: + p_texcoord = geom->get_next_texcoord(ti); + break; + case 11: + p_vertex = geom->get_next_vertex(vi); + case 10: + p_normal = geom->get_next_normal(ni); + p_texcoord = geom->get_next_texcoord(ti); + break; + case 13: + p_vertex = geom->get_next_vertex(vi); + case 12: + p_color = geom->get_next_color(ci); + p_colr = D3DRGBA(p_color[0], p_color[1], p_color[2], p_color[3]); + p_texcoord = geom->get_next_texcoord(ti); + break; + case 15: + p_vertex = geom->get_next_vertex(vi); + case 14: + p_normal = geom->get_next_normal(ni); + p_color = geom->get_next_color(ci); + p_colr = D3DRGBA(p_color[0], p_color[1], p_color[2], p_color[3]); + p_texcoord = geom->get_next_texcoord(ti); + break; + } + if (p_flags & D3DFVF_XYZ) + add_to_FVF((void *)&p_vertex, sizeof(D3DVECTOR)); + if (p_flags & D3DFVF_NORMAL) + add_to_FVF((void *)&p_normal, sizeof(D3DVECTOR)); + if (p_flags & D3DFVF_DIFFUSE) + add_to_FVF((void *)&p_colr, sizeof(D3DCOLOR)); + if (p_flags & D3DFVF_TEXCOUNT_MASK) + add_to_FVF((void *)&p_texcoord, sizeof(TexCoordf)); + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_prim_inner_loop2 +// Access: Private +// Description: This adds data to the flexible vertex format with a check +// for component normals and color +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +draw_prim_inner_loop2(int loops, const Geom *geom, short& per ) +{ + while (--loops >= 0) + { + if (per & PerColor) + { + p_color = geom->get_next_color(ci); // set overall color if there is one + p_colr = D3DRGBA(p_color[0], p_color[1], p_color[2], p_color[3]); + } + if (per & PerNormal) + p_normal = geom->get_next_normal(ni); // set primitive normal if there is one. + + switch(perVertex) + { + case 3: + p_normal = geom->get_next_normal(ni); + case 1: + p_vertex = geom->get_next_vertex(vi); + break; + case 5: + p_vertex = geom->get_next_vertex(vi); + case 4: + p_color = geom->get_next_color(ci); + p_colr = D3DRGBA(p_color[0], p_color[1], p_color[2], p_color[3]); + break; + case 7: + p_vertex = geom->get_next_vertex(vi); + case 6: + p_color = geom->get_next_color(ci); + p_colr = D3DRGBA(p_color[0], p_color[1], p_color[2], p_color[3]); + case 2: + p_normal = geom->get_next_normal(ni); + break; + case 9: + p_vertex = geom->get_next_vertex(vi); + case 8: + p_texcoord = geom->get_next_texcoord(ti); + break; + case 11: + p_vertex = geom->get_next_vertex(vi); + case 10: + p_normal = geom->get_next_normal(ni); + p_texcoord = geom->get_next_texcoord(ti); + break; + case 13: + p_vertex = geom->get_next_vertex(vi); + case 12: + p_color = geom->get_next_color(ci); + p_colr = D3DRGBA(p_color[0], p_color[1], p_color[2], p_color[3]); + p_texcoord = geom->get_next_texcoord(ti); + break; + case 15: + p_vertex = geom->get_next_vertex(vi); + case 14: + p_normal = geom->get_next_normal(ni); + p_color = geom->get_next_color(ci); + p_colr = D3DRGBA(p_color[0], p_color[1], p_color[2], p_color[3]); + p_texcoord = geom->get_next_texcoord(ti); + break; + } + if (p_flags & D3DFVF_XYZ) + add_to_FVF((void *)&p_vertex, sizeof(D3DVECTOR)); + if (p_flags & D3DFVF_NORMAL) + add_to_FVF((void *)&p_normal, sizeof(D3DVECTOR)); + if (p_flags & D3DFVF_DIFFUSE) + add_to_FVF((void *)&p_colr, sizeof(D3DCOLOR)); + if (p_flags & D3DFVF_TEXCOUNT_MASK) + add_to_FVF((void *)&p_texcoord, sizeof(TexCoordf)); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::add_to_FVF +// Access: Private +// Description: This adds data to the flexible vertex format +//////////////////////////////////////////////////////////////////// +INLINE void DXGraphicsStateGuardian:: +add_to_FVF(void *data, size_t bytes) +{ + memcpy(_fvf_buf, data, bytes); + _fvf_buf = (char *)_fvf_buf + bytes; + +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_tri +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +draw_tri(const GeomTri *geom) { + // activate(); + +#ifdef GSG_VERBOSE + dxgsg_cat.debug() << "draw_tri()" << endl; +#endif + +#ifdef _DEBUG + if(_pCurTexContext!=NULL) { + dxgsg_cat.spam() << "Cur active DX texture: " << _pCurTexContext->_tex->get_name() << "\n"; + } +#endif + +#ifdef WBD_GL_MODE + int nprims = geom->get_num_prims(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::NormalIterator ni = geom->make_normal_iterator(); + Geom::TexCoordIterator ti = geom->make_texcoord_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + GeomIssuer::IssueColor *issue_color; + + if (!_color_transform_enabled && !_alpha_transform_enabled) { + issue_color = issue_color_gl; + } + else { + issue_color = issue_transformed_color_gl; + } + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color); + + // If we have per-vertex colors or normals, we need smooth shading. + // Otherwise we want flat shading for performance reasons. + if (geom->get_binding(G_COLOR) == G_PER_VERTEX || + geom->get_binding(G_NORMAL) == G_PER_VERTEX) { + + call_glShadeModel(GL_SMOOTH); + } else { + call_glShadeModel(GL_FLAT); + } + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + issuer.issue_normal(G_OVERALL, ni); + + glBegin(GL_TRIANGLES); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + issuer.issue_normal(G_PER_PRIM, ni); + + for (int j = 0; j < 3; j++) { + // Draw per vertex + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_normal(G_PER_VERTEX, ni); + issuer.issue_texcoord(G_PER_VERTEX, ti); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + } + + glEnd(); +#else // the DX way + int nPrims = geom->get_num_prims(); + + perVertex = 0; + if (geom->get_binding(G_COORD) == G_PER_VERTEX) perVertex |= PerCoord; + if (geom->get_binding(G_NORMAL) == G_PER_VERTEX) perVertex |= PerNormal; + if (geom->get_binding(G_COLOR) == G_PER_VERTEX) perVertex |= PerColor; + if (geom->get_binding(G_TEXCOORD) == G_PER_VERTEX) perVertex |= PerTexcoord; + + perPrim = 0; + if (geom->get_binding(G_NORMAL) == G_PER_PRIM) perPrim |= PerNormal; + if (geom->get_binding(G_COLOR) == G_PER_PRIM) perPrim |= PerColor; + + size_t vertex_size = draw_prim_setup(geom); + + nassertv(_fvf_buf == NULL); // make sure the storage pointer is clean. + nassertv(nPrims * 3 * vertex_size < VERT_BUFFER_SIZE); + _fvf_buf = _sav_fvf; // _fvf_buf changes, sav_fvf doesn't + + // iterate through the triangle primitive + + for (int i = 0; i < nPrims; i++) + { + if (perPrim & PerColor) + { + p_color = geom->get_next_color(ci); // set primitive color if there is one. + p_colr = D3DRGBA(p_color[0], p_color[1], p_color[2], p_color[3]); + } + + if (perPrim & PerNormal) + p_normal = geom->get_next_normal(ni); // set primitive normal if there is one. + + draw_prim_inner_loop(3, geom); + } + + _d3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, p_flags, _sav_fvf, nPrims*3, NULL); + + _fvf_buf = NULL; + +#endif // WBD_GL_MODE +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_quad +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +draw_quad(const GeomQuad *geom) { + activate(); + +#ifdef GSG_VERBOSE + dxgsg_cat.debug() << "draw_quad()" << endl; +#endif + +#ifdef WBD_GL_MODE + int nprims = geom->get_num_prims(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::NormalIterator ni = geom->make_normal_iterator(); + Geom::TexCoordIterator ti = geom->make_texcoord_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color_gl); + + // If we have per-vertex colors or normals, we need smooth shading. + // Otherwise we want flat shading for performance reasons. + if (geom->get_binding(G_COLOR) == G_PER_VERTEX || + geom->get_binding(G_NORMAL) == G_PER_VERTEX) { + call_glShadeModel(GL_SMOOTH); + } else { + call_glShadeModel(GL_FLAT); + } + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + issuer.issue_normal(G_OVERALL, ni); + + glBegin(GL_QUADS); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + issuer.issue_normal(G_PER_PRIM, ni); + + for (int j = 0; j < 4; j++) { + // Draw per vertex + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_normal(G_PER_VERTEX, ni); + issuer.issue_texcoord(G_PER_VERTEX, ti); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + } + + glEnd(); +#endif // WBD_GL_MODE +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_tristrip +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +draw_tristrip(const GeomTristrip *geom) { + activate(); + +#ifdef GSG_VERBOSE + dxgsg_cat.debug() << "draw_tristrip()" << endl; +#endif + +#ifdef WBD_GL_MODE + + int nPrims = geom->get_num_prims(); + const int *plen = geom->get_lengths(); + + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::NormalIterator ni = geom->make_normal_iterator(); + Geom::TexCoordIterator ti = geom->make_texcoord_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color_gl); + + // If we have per-vertex colors or normals, we need smooth shading. + // Otherwise we want flat shading for performance reasons. + if (geom->get_binding(G_COLOR) == G_PER_VERTEX || + geom->get_binding(G_NORMAL) == G_PER_VERTEX) { + call_glShadeModel(GL_SMOOTH); + } else { + call_glShadeModel(GL_FLAT); + } + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + issuer.issue_normal(G_OVERALL, ni); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + issuer.issue_normal(G_PER_PRIM, ni); + + int num_verts = *(plen++); + nassertv(num_verts >= 3); + + glBegin(GL_TRIANGLE_STRIP); + + // Per-component attributes for the first triangle? + issuer.issue_color(G_PER_COMPONENT, ci); + issuer.issue_normal(G_PER_COMPONENT, ni); + + // Draw the first three vertices. + int v; + for (v = 0; v < 3; v++) { + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_normal(G_PER_VERTEX, ni); + issuer.issue_texcoord(G_PER_VERTEX, ti); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + + // Now draw each of the remaining vertices. Each vertex from + // this point on defines a new triangle. + for (v = 3; v < num_verts; v++) { + // Per-component attributes? + issuer.issue_color(G_PER_COMPONENT, ci); + issuer.issue_normal(G_PER_COMPONENT, ni); + + // Per-vertex attributes. + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_normal(G_PER_VERTEX, ni); + issuer.issue_texcoord(G_PER_VERTEX, ti); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + glEnd(); + } +#else + + draw_multitri(geom, D3DPT_TRIANGLESTRIP); + +#endif // WBD_GL_MODE +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_trifan +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +draw_trifan(const GeomTrifan *geom) { + activate(); + +#ifdef GSG_VERBOSE + dxgsg_cat.debug() << "draw_trifan()" << endl; +#endif + +#ifdef WBD_GL_MODE + int nprims = geom->get_num_prims(); + const int *plen = geom->get_lengths(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::NormalIterator ni = geom->make_normal_iterator(); + Geom::TexCoordIterator ti = geom->make_texcoord_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color_gl); + + // If we have per-vertex colors or normals, we need smooth shading. + // Otherwise we want flat shading for performance reasons. + if (geom->get_binding(G_COLOR) == G_PER_VERTEX || + geom->get_binding(G_NORMAL) == G_PER_VERTEX) { + call_glShadeModel(GL_SMOOTH); + } else { + call_glShadeModel(GL_FLAT); + } + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + issuer.issue_normal(G_OVERALL, ni); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + issuer.issue_normal(G_PER_PRIM, ni); + + int num_verts = *(plen++); + nassertv(num_verts >= 3); + + glBegin(GL_TRIANGLE_FAN); + + // Per-component attributes for the first triangle? + issuer.issue_color(G_PER_COMPONENT, ci); + issuer.issue_normal(G_PER_COMPONENT, ni); + + // Draw the first three vertices. + int v; + for (v = 0; v < 3; v++) { + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_normal(G_PER_VERTEX, ni); + issuer.issue_texcoord(G_PER_VERTEX, ti); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + + // Now draw each of the remaining vertices. Each vertex from + // this point on defines a new triangle. + for (v = 3; v < num_verts; v++) { + // Per-component attributes? + issuer.issue_color(G_PER_COMPONENT, ci); + issuer.issue_normal(G_PER_COMPONENT, ni); + + // Per-vertex attributes. + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_normal(G_PER_VERTEX, ni); + issuer.issue_texcoord(G_PER_VERTEX, ti); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + glEnd(); + } +#else // the DX way + + draw_multitri(geom, D3DPT_TRIANGLEFAN); + +#endif // WBD_GL_MODE +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_multitri +// Access: Public, Virtual +// Description: handles trifans and tristrips +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +draw_multitri(const Geom *geom, D3DPRIMITIVETYPE tri_id) +{ + + int nPrims = geom->get_num_prims(); + const int *plen = geom->get_lengths(); + + perVertex = 0; + if (geom->get_binding(G_COORD) == G_PER_VERTEX) perVertex |= PerCoord; + if (geom->get_binding(G_NORMAL) == G_PER_VERTEX) perVertex |= PerNormal; + if (geom->get_binding(G_COLOR) == G_PER_VERTEX) perVertex |= PerColor; + if (geom->get_binding(G_TEXCOORD) == G_PER_VERTEX) perVertex |= PerTexcoord; + + perPrim = 0; + if (geom->get_binding(G_NORMAL) == G_PER_PRIM) perPrim |= PerNormal; + if (geom->get_binding(G_COLOR) == G_PER_PRIM) perPrim |= PerColor; + + perComp = 0; + if (geom->get_binding(G_NORMAL) == G_PER_COMPONENT) perComp |= PerNormal; + if (geom->get_binding(G_COLOR) == G_PER_COMPONENT) perComp |= PerColor; + + size_t vertex_size = draw_prim_setup(geom); + + // iterate through the triangle primitives + + for (int i = 0; i < nPrims; i++) + { + if (perPrim & PerColor) + { + p_color = geom->get_next_color(ci); // set primitive color if there is one. + p_colr = D3DRGBA(p_color[0], p_color[1], p_color[2], p_color[3]); + } + + if (perPrim & PerNormal) + p_normal = geom->get_next_normal(ni); // set primitive normal if there is one. + + int nVerts = *(plen++); + nassertv(nVerts >= 3); + + nassertv(_fvf_buf == NULL); // make sure the storage pointer is clean. + nassertv(nVerts * vertex_size < VERT_BUFFER_SIZE); + _fvf_buf = _sav_fvf; // _fvf_buf changes, sav_fvf doesn't + + if (perComp & PerColor) + { + p_color = geom->get_next_color(ci); // set overall color if there is one + p_colr = D3DRGBA(p_color[0], p_color[1], p_color[2], p_color[3]); + } + if (perComp & PerNormal) + p_normal = geom->get_next_normal(ni); // set primitive normal if there is one. + + // Store first triangle + draw_prim_inner_loop(3, geom); + + // Store remaining vertices + draw_prim_inner_loop2(nVerts-3, geom, perComp); + + _d3dDevice->DrawPrimitive(tri_id, p_flags, _sav_fvf, nVerts, NULL); + + _fvf_buf = NULL; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_sphere +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +draw_sphere(const GeomSphere *geom) { + activate(); + +#ifdef GSG_VERBOSE + dxgsg_cat.debug() << "draw_sphere()" << endl; +#endif + +#ifdef WBD_GL_MODE + int nprims = geom->get_num_prims(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color_gl); + + if (wants_normals()) { + call_glShadeModel(GL_SMOOTH); + } else { + call_glShadeModel(GL_FLAT); + } + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + + GLUquadricObj *sph = gluNewQuadric(); + gluQuadricNormals(sph, wants_normals() ? (GLenum)GLU_SMOOTH : (GLenum)GLU_NONE); + gluQuadricTexture(sph, wants_texcoords() ? (GLenum)GL_TRUE : (GLenum)GL_FALSE); + gluQuadricOrientation(sph, (GLenum)GLU_OUTSIDE); + gluQuadricDrawStyle(sph, (GLenum)GLU_FILL); + //gluQuadricDrawStyle(sph, (GLenum)GLU_LINE); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + + for (int j = 0; j < 2; j++) { + // Draw per vertex + issuer.issue_color(G_PER_VERTEX, ci); + } + Vertexf center = geom->get_next_vertex(vi); + Vertexf edge = geom->get_next_vertex(vi); + LVector3f v = edge - center; + float r = sqrt(dot(v, v)); + + // Since gluSphere doesn't have a center parameter, we have to use + // a matrix transform. + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glMultMatrixf(LMatrix4f::translate_mat(center).get_data()); + + // Now render the sphere using GLU calls. + gluSphere(sph, r, 16, 10); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + } + + gluDeleteQuadric(sph); +#endif // WBD_GL_MODE +} + + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_color_transform +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_color_transform(const ColorMatrixAttribute *attrib) { + _current_color_mat = attrib->get_matrix(); + + if (_current_color_mat == LMatrix4f::ident_mat()) { + _color_transform_enabled = false; + } + else { + _color_transform_enabled = true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_alpha_transform +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_alpha_transform(const AlphaTransformAttribute *attrib) { + _current_alpha_offset= attrib->get_offset(); + _current_alpha_scale = attrib->get_scale(); + + if (_current_alpha_offset == 0 && _current_alpha_scale == 1) { + _alpha_transform_enabled = false; + } + else { + _alpha_transform_enabled = true; + } +} + + + + + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::prepare_texture +// Access: Public, Virtual +// Description: Creates a new retained-mode representation of the +// given texture, and returns a newly-allocated +// TextureContext pointer to reference it. It is the +// responsibility of the calling function to later +// call release_texture() with this same pointer (which +// will also delete the pointer). +//////////////////////////////////////////////////////////////////// +TextureContext *DXGraphicsStateGuardian:: +prepare_texture(Texture *tex) { + activate(); + + DXTextureContext *gtc = new DXTextureContext(tex); +#ifdef WBD_GL_MODE + glGenTextures(1, >c->_index); + + bind_texture(gtc); + glPrioritizeTextures(1, >c->_index, >c->_priority); + specify_texture(tex); + apply_texture_immediate(tex); +#else + if(gtc->CreateTexture(_hdc, _d3dDevice,_cNumTexPixFmts,_pTexPixFmts) == NULL) { + delete gtc; + return NULL; + } +#endif // WBD_GL_MODE + + bool inserted = mark_prepared_texture(gtc); + + // If this assertion fails, the same texture was prepared twice, + // which shouldn't be possible, since the texture itself should + // detect this. + nassertr(inserted, NULL); + + return gtc; +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::apply_texture +// Access: Public, Virtual +// Description: Makes the texture the currently available texture for +// rendering. +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +apply_texture(TextureContext *tc) { + if(tc==NULL) { + return; + } + +// activate(); inactive + +#ifdef WBD_GL_MODE + bind_texture(tc); +#else + specify_texture(tc->_texture); + apply_texture_immediate(DCAST(DXTextureContext, tc)); +#endif +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::release_texture +// Access: Public, Virtual +// Description: Frees the GL resources previously allocated for the +// texture. +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +release_texture(TextureContext *tc) { + activate(); + DXTextureContext *gtc = DCAST(DXTextureContext, tc); + Texture *tex = tc->_texture; + +#ifdef WBD_GL_MODE + glDeleteTextures(1, >c->_index); + gtc->_index = 0; +#else + gtc->DeleteTexture(); +#endif // WBD_GL_MODE + bool erased = unmark_prepared_texture(gtc); + + // If this assertion fails, a texture was released that hadn't been + // prepared (or a texture was released twice). + nassertv(erased); + + tex->clear_gsg(this); + + delete gtc; +} + +static int logs[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, + 4096, 0 }; + +// This function returns the smallest power of two greater than or +// equal to x. +static int binary_log_cap(const int x) { + int i = 0; + for (; (x > logs[i]) && (logs[i] != 0); ++i); + if (logs[i] == 0) + return 4096; + return logs[i]; +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::copy_texture +// Access: Public, Virtual +// Description: Copy the pixel region indicated by the display +// region from the framebuffer into texture memory +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +copy_texture(TextureContext *tc, const DisplayRegion *dr) { + dxgsg_cat.fatal() << "DX copy_texture unimplemented!!!"; + return; + +#if 0 + nassertv(tc != NULL && dr != NULL); + activate(); + + Texture *tex = tc->_texture; + + // Determine the size of the grab from the given display region + // If the requested region is not a power of two, grab a region that is + // a power of two that contains the requested region + int xo, yo, req_w, req_h; + dr->get_region_pixels(xo, yo, req_w, req_h); + int w = binary_log_cap(req_w); + int h = binary_log_cap(req_h); + if (w != req_w || h != req_h) { + tex->_requested_w = req_w; + tex->_requested_h = req_h; + tex->_has_requested_size = true; + } + + PixelBuffer *pb = tex->_pbuffer; + + pb->set_xorg(xo); + pb->set_yorg(yo); + pb->set_xsize(w); + pb->set_ysize(h); + + +//#ifdef WBD_GL_MODE + bind_texture(tc); + glCopyTexImage2D( GL_TEXTURE_2D, tex->get_level(), + get_internal_image_format(pb->get_format()), + pb->get_xorg(), pb->get_yorg(), + pb->get_xsize(), pb->get_ysize(), pb->get_border() ); +#endif // WBD_GL_MODE +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::copy_texture +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +copy_texture(TextureContext *tc, const DisplayRegion *dr, const RenderBuffer &rb) { + dxgsg_cat.fatal() << "DX copy_texture unimplemented!!!"; + return; + + activate(); + set_read_buffer(rb); + copy_texture(tc, dr); +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_texture +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +draw_texture(TextureContext *tc, const DisplayRegion *dr) { + + dxgsg_cat.fatal() << "DXGSG draw_texture unimplemented!!!"; + return; + + nassertv(tc != NULL && dr != NULL); + activate(); + +#ifdef WBD_GL_MODE + Texture *tex = tc->_texture; + + DisplayRegionStack old_dr = push_display_region(dr); + prepare_display_region(); + + NodeAttributes state; + CullFaceAttribute *cfa = new CullFaceAttribute; + cfa->set_mode(CullFaceProperty::M_cull_none); + DepthTestAttribute *dta = new DepthTestAttribute; + dta->set_mode(DepthTestProperty::M_none); + DepthWriteAttribute *dwa = new DepthWriteAttribute; + dwa->set_off(); + TextureAttribute *ta = new TextureAttribute; + ta->set_on(tex); + TextureApplyAttribute *taa = new TextureApplyAttribute; + taa->set_mode(TextureApplyProperty::M_decal); + + state.set_attribute(LightTransition::get_class_type(), + new LightAttribute); + state.set_attribute(ColorMaskTransition::get_class_type(), + new ColorMaskAttribute); + state.set_attribute(RenderModeTransition::get_class_type(), + new RenderModeAttribute); + state.set_attribute(TexMatrixTransition::get_class_type(), + new TexMatrixAttribute); + state.set_attribute(TransformTransition::get_class_type(), + new TransformAttribute); + state.set_attribute(ColorBlendTransition::get_class_type(), + new ColorBlendAttribute); + state.set_attribute(CullFaceTransition::get_class_type(), cfa); + state.set_attribute(DepthTestTransition::get_class_type(), dta); + state.set_attribute(DepthWriteTransition::get_class_type(), dwa); + state.set_attribute(TextureTransition::get_class_type(), ta); + state.set_attribute(TextureApplyTransition::get_class_type(), taa); + set_state(state, false); + + // We set up an orthographic projection that defines our entire + // viewport to the range [0..1] in both dimensions. Then, when we + // create a unit square polygon below, it will exactly fill the + // viewport (and thus exactly fill the display region). + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + gluOrtho2D(0, 1, 0, 1); + + float txl, txr, tyt, tyb; + txl = tyb = 0.; + if (tex->_has_requested_size) { + txr = ((float)(tex->_requested_w)) / ((float)(tex->_pbuffer->get_xsize())); + tyt = ((float)(tex->_requested_h)) / ((float)(tex->_pbuffer->get_ysize())); + } else { + txr = tyt = 1.; + } + + // This two-triangle strip is actually a quad. But it's usually + // better to render quads as tristrips anyway. + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(txl, tyb); glVertex2i(0, 0); + glTexCoord2f(txr, tyb); glVertex2i(1, 0); + glTexCoord2f(txl, tyt); glVertex2i(0, 1); + glTexCoord2f(txr, tyt); glVertex2i(1, 1); + glEnd(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + pop_display_region(old_dr); +#endif // WBD_GL_MODE + +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_texture +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +draw_texture(TextureContext *tc, const DisplayRegion *dr, const RenderBuffer &rb) { + dxgsg_cat.fatal() << "DXGSG draw_texture unimplemented!!!"; + return; + + activate(); + set_draw_buffer(rb); + draw_texture(tc, dr); +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::texture_to_pixel_buffer +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb) { + nassertv(tc != NULL && pb != NULL); + activate(); + + Texture *tex = tc->_texture; + + int w = tex->_pbuffer->get_xsize(); + int h = tex->_pbuffer->get_ysize(); + + PT(DisplayRegion) dr = _win->make_scratch_display_region(w, h); + + FrameBufferStack old_fb = push_frame_buffer + (get_render_buffer(RenderBuffer::T_back | RenderBuffer::T_depth), + dr); + + texture_to_pixel_buffer(tc, pb, dr); + + pop_frame_buffer(old_fb); +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::texture_to_pixel_buffer +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb, + const DisplayRegion *dr) { + nassertv(tc != NULL && pb != NULL && dr != NULL); + activate(); + + Texture *tex = tc->_texture; + + // Do a deep copy to initialize the pixel buffer + pb->copy(tex->_pbuffer); + + // If the image was empty, we need to render the texture into the frame + // buffer and then copy the results into the pixel buffer's image + if (pb->_image.empty()) { + int w = pb->get_xsize(); + int h = pb->get_ysize(); + draw_texture(tc, dr); + pb->_image = PTA_uchar(w * h * pb->get_num_components()); + copy_pixel_buffer(pb, dr); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::copy_pixel_buffer +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr) { + + dxgsg_cat.fatal() << "DXGSG copy_pixel_buffer unimplemented!!!"; + return; + +#ifdef WBD_GL_MODE + nassertv(pb != NULL && dr != NULL); + activate(); + set_pack_alignment(1); + + NodeAttributes state; + + // Bug fix for RE, RE2, and VTX - need to disable texturing in order + // for glReadPixels() to work + // NOTE: reading the depth buffer is *much* slower than reading the + // color buffer + state.set_attribute(TextureTransition::get_class_type(), + new TextureAttribute); + set_state(state, false); + + int xo, yo, w, h; + dr->get_region_pixels(xo, yo, w, h); + +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "glReadPixels(" << pb->get_xorg() << ", " << pb->get_yorg() + << ", " << pb->get_xsize() << ", " << pb->get_ysize() + << ", "; + switch (get_external_image_format(pb->get_format())) { + case GL_DEPTH_COMPONENT: + dxgsg_cat.debug(false) << "GL_DEPTH_COMPONENT, "; + break; + case GL_RGB: + dxgsg_cat.debug(false) << "GL_RGB, "; + break; + case GL_RGBA: + dxgsg_cat.debug(false) << "GL_RGBA, "; + break; + default: + dxgsg_cat.debug(false) << "unknown, "; + break; + } + switch (get_image_type(pb->get_image_type())) { + case GL_UNSIGNED_BYTE: + dxgsg_cat.debug(false) << "GL_UNSIGNED_BYTE, "; + break; + case GL_FLOAT: + dxgsg_cat.debug(false) << "GL_FLOAT, "; + break; + default: + dxgsg_cat.debug(false) << "unknown, "; + break; + } + dxgsg_cat.debug(false) + << (void *)pb->_image.p() << ")" << endl; +#endif + + glReadPixels( pb->get_xorg() + xo, pb->get_yorg() + yo, + pb->get_xsize(), pb->get_ysize(), + get_external_image_format(pb->get_format()), + get_image_type(pb->get_image_type()), + pb->_image.p() ); + + nassertv(!pb->_image.empty()); +#endif // WBD_GL_MODE +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::copy_pixel_buffer +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const RenderBuffer &rb) { + activate(); + set_read_buffer(rb); + copy_pixel_buffer(pb, dr); +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_pixel_buffer +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const NodeAttributes& na) { + + dxgsg_cat.fatal() << "DXGSG draw_pixel_buffer unimplemented!!!"; + return; + +#ifdef WBD_GL_MODE + nassertv(pb != NULL && dr != NULL); + nassertv(!pb->_image.empty()); + activate(); + + DisplayRegionStack old_dr = push_display_region(dr); + prepare_display_region(); + + NodeAttributes state(na); + state.set_attribute(LightTransition::get_class_type(), + new LightAttribute); + state.set_attribute(TextureTransition::get_class_type(), + new TextureAttribute); + state.set_attribute(TransformTransition::get_class_type(), + new TransformAttribute); + state.set_attribute(ColorBlendTransition::get_class_type(), + new ColorBlendAttribute); + state.set_attribute(StencilTransition::get_class_type(), + new StencilAttribute); + + + switch (pb->get_format()) { + case PixelBuffer::F_depth_component: + { + ColorMaskAttribute *cma = new ColorMaskAttribute; + cma->set_mask(0); + DepthTestAttribute *dta = new DepthTestAttribute; + dta->set_mode(DepthTestProperty::M_always); + DepthWriteAttribute *dwa = new DepthWriteAttribute; + dwa->set_off(); + state.set_attribute(ColorMaskTransition::get_class_type(), cma); + state.set_attribute(DepthTestTransition::get_class_type(), dta); + state.set_attribute(DepthWriteTransition::get_class_type(), dwa); + } + break; + + case PixelBuffer::F_rgb: + case PixelBuffer::F_rgb5: + case PixelBuffer::F_rgb8: + case PixelBuffer::F_rgb12: + case PixelBuffer::F_rgba: + case PixelBuffer::F_rgba4: + case PixelBuffer::F_rgba8: + case PixelBuffer::F_rgba12: + { + ColorMaskAttribute *cma = new ColorMaskAttribute; + DepthTestAttribute *dta = new DepthTestAttribute; + dta->set_mode(DepthTestProperty::M_none); + DepthWriteAttribute *dwa = new DepthWriteAttribute; + dwa->set_off(); + state.set_attribute(ColorMaskTransition::get_class_type(), cma); + state.set_attribute(DepthTestTransition::get_class_type(), dta); + state.set_attribute(DepthWriteTransition::get_class_type(), dwa); + } + break; + default: + dxgsg_cat.error() + << "draw_pixel_buffer(): unknown buffer format" << endl; + break; + } + + set_state(state, false); + + enable_color_material(false); + set_unpack_alignment(1); + + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); + glLoadIdentity(); + gluOrtho2D(0, _win->get_width(), + 0, _win->get_height()); + +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "glDrawPixels(" << pb->get_xsize() << ", " << pb->get_ysize() + << ", "; + switch (get_external_image_format(pb->get_format())) { + case GL_DEPTH_COMPONENT: + dxgsg_cat.debug(false) << "GL_DEPTH_COMPONENT, "; + break; + case GL_RGB: + dxgsg_cat.debug(false) << "GL_RGB, "; + break; + case GL_RGBA: + dxgsg_cat.debug(false) << "GL_RGBA, "; + break; + default: + dxgsg_cat.debug(false) << "unknown, "; + break; + } + switch (get_image_type(pb->get_image_type())) { + case GL_UNSIGNED_BYTE: + dxgsg_cat.debug(false) << "GL_UNSIGNED_BYTE, "; + break; + case GL_FLOAT: + dxgsg_cat.debug(false) << "GL_FLOAT, "; + break; + default: + dxgsg_cat.debug(false) << "unknown, "; + break; + } + dxgsg_cat.debug(false) + << (void *)pb->_image.p() << ")" << endl; +#endif + + glRasterPos2i( pb->get_xorg(), pb->get_yorg() ); + glDrawPixels( pb->get_xsize(), pb->get_ysize(), + get_external_image_format(pb->get_format()), + get_image_type(pb->get_image_type()), + pb->_image.p() ); + + glMatrixMode( GL_PROJECTION ); + glPopMatrix(); + + pop_display_region(old_dr); +#endif // WBD_GL_MODE +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::draw_pixel_buffer +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const RenderBuffer &rb, const NodeAttributes& na) { + activate(); + set_read_buffer(rb); + draw_pixel_buffer(pb, dr, na); +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::apply_material +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian::apply_material( Material* material ) +{ + D3DMATERIAL7 cur_material; + cur_material.dcvDiffuse = *(D3DCOLORVALUE *)(material->get_diffuse().get_data()); + cur_material.dcvAmbient = *(D3DCOLORVALUE *)(material->get_ambient().get_data()); + cur_material.dcvSpecular = *(D3DCOLORVALUE *)(material->get_specular().get_data()); + cur_material.dcvEmissive = *(D3DCOLORVALUE *)(material->get_emission().get_data()); + cur_material.dvPower = material->get_shininess(); + _d3dDevice->SetMaterial(&cur_material); +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::apply_fog +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +apply_fog(Fog *fog) { + _d3dDevice->SetRenderState(D3DRENDERSTATE_FOGTABLEMODE, + get_fog_mode_type(fog->get_mode())); + _d3dDevice->SetRenderState(D3DRENDERSTATE_FOGVERTEXMODE, + get_fog_mode_type(fog->get_mode())); + + switch(fog->get_mode()) + { + case Fog::M_linear: + { + float fog_start = fog->get_start(); + float fog_end = fog->get_end(); + _d3dDevice->SetRenderState( D3DRENDERSTATE_FOGSTART, + *((LPDWORD) (&fog_start)) ); + _d3dDevice->SetRenderState( D3DRENDERSTATE_FOGEND, + *((LPDWORD) (&fog_end)) ); + } + break; + case Fog::M_exponential: + case Fog::M_super_exponential: + { + float fog_density = fog->get_density(); + _d3dDevice->SetRenderState( D3DRENDERSTATE_FOGDENSITY, + *((LPDWORD) (&fog_density)) ); + } + break; + case Fog::M_spline: + break; + } + + Colorf fog_colr = fog->get_color(); + _d3dDevice->SetRenderState(D3DRENDERSTATE_FOGCOLOR, + D3DRGBA(fog_colr[0], fog_colr[1], fog_colr[2], 0.0)); +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::apply_light +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian::apply_light( PointLight* light ) +{ + // The light position will be relative to the current matrix, so + // we have to know what the current matrix is. Find a better + // solution later. +#ifdef WBD_GL_MODE +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "glMatrixMode(GL_MODELVIEW)" << endl; + dxgsg_cat.debug() + << "glPushMatrix()" << endl; + dxgsg_cat.debug() + << "glLoadIdentity()" << endl; +#endif + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + glLoadMatrixf(LMatrix4f::convert_mat(_coordinate_system, CS_yup_left) + .get_data()); + + GLenum id = get_light_id( _cur_light_id ); + Colorf black(0, 0, 0, 1); + glLightfv(id, GL_AMBIENT, black.get_data()); + glLightfv(id, GL_DIFFUSE, light->get_color().get_data()); + glLightfv(id, GL_SPECULAR, light->get_specular().get_data()); + + // Position needs to specify x, y, z, and w + // w == 1 implies non-infinite position + LPoint3f pos = get_rel_pos( light, _current_projection_node ); + LPoint4f fpos( pos[0], pos[1], pos[2], 1 ); + glLightfv( id, GL_POSITION, fpos.get_data() ); + + // GL_SPOT_DIRECTION is not significant when cutoff == 180 + + // Exponent == 0 implies uniform light distribution + glLightf( id, GL_SPOT_EXPONENT, 0 ); + + // Cutoff == 180 means uniform point light source + glLightf( id, GL_SPOT_CUTOFF, 180.0 ); + + glLightf( id, GL_CONSTANT_ATTENUATION, + light->get_constant_attenuation() ); + glLightf( id, GL_LINEAR_ATTENUATION, + light->get_linear_attenuation() ); + glLightf( id, GL_QUADRATIC_ATTENUATION, + light->get_quadratic_attenuation() ); + + glPopMatrix(); + +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "glPopMatrix()" << endl; +#endif + +#else + D3DCOLORVALUE black; + black.r = black.g = black.b = black.a = 0.0f; + D3DLIGHT7 alight; + alight.dltType = D3DLIGHT_POINT; + alight.dcvDiffuse = *(D3DCOLORVALUE *)(light->get_color().get_data()); + alight.dcvAmbient = black ; + alight.dcvSpecular = *(D3DCOLORVALUE *)(light->get_specular().get_data()); + + // Position needs to specify x, y, z, and w + // w == 1 implies non-infinite position + alight.dvPosition = *(D3DVECTOR *)(get_rel_pos( light, _current_root_node ).get_data()); + + alight.dvRange = D3DLIGHT_RANGE_MAX; + alight.dvFalloff = 1.0f; + + alight.dvAttenuation0 = (D3DVALUE)light->get_constant_attenuation(); + alight.dvAttenuation1 = (D3DVALUE)light->get_linear_attenuation(); + alight.dvAttenuation2 = (D3DVALUE)light->get_quadratic_attenuation(); + + HRESULT res = _d3dDevice->SetLight(_cur_light_id, &alight); + +#endif // WBD_GL_MODE +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::apply_light +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian::apply_light( DirectionalLight* light ) +{ + // The light position will be relative to the current matrix, so + // we have to know what the current matrix is. Find a better + // solution later. +#ifdef WBD_GL_MODE +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "glMatrixMode(GL_MODELVIEW)" << endl; + dxgsg_cat.debug() + << "glPushMatrix()" << endl; + dxgsg_cat.debug() + << "glLoadIdentity()" << endl; +#endif + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixf(LMatrix4f::convert_mat(_coordinate_system, CS_yup_left) + .get_data()); + + GLenum id = get_light_id( _cur_light_id ); + Colorf black(0, 0, 0, 1); + glLightfv(id, GL_AMBIENT, black.get_data()); + glLightfv(id, GL_DIFFUSE, light->get_color().get_data()); + glLightfv(id, GL_SPECULAR, light->get_specular().get_data()); + + // Position needs to specify x, y, z, and w + // w == 0 implies light is at infinity + LPoint3f dir = get_rel_forward( light, _current_root_node, + _coordinate_system ); + LPoint4f pos( -dir[0], -dir[1], -dir[2], 0 ); + glLightfv( id, GL_POSITION, pos.get_data() ); + + // GL_SPOT_DIRECTION is not significant when cutoff == 180 + // In this case, position x, y, z specifies direction + + // Exponent == 0 implies uniform light distribution + glLightf( id, GL_SPOT_EXPONENT, 0 ); + + // Cutoff == 180 means uniform point light source + glLightf( id, GL_SPOT_CUTOFF, 180.0 ); + + // Default attenuation values (only spotlight can modify these) + glLightf( id, GL_CONSTANT_ATTENUATION, 1 ); + glLightf( id, GL_LINEAR_ATTENUATION, 0 ); + glLightf( id, GL_QUADRATIC_ATTENUATION, 0 ); + + glPopMatrix(); +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "glPopMatrix()" << endl; +#endif +#else // DX Directional light + D3DCOLORVALUE black; + black.r = black.g = black.b = black.a = 0.0f; + + D3DLIGHT7 alight; + ZeroMemory(&alight, sizeof(D3DLIGHT7)); + + alight.dltType = D3DLIGHT_DIRECTIONAL; + alight.dcvDiffuse = *(D3DCOLORVALUE *)(light->get_color().get_data()); + alight.dcvAmbient = black ; + alight.dcvSpecular = *(D3DCOLORVALUE *)(light->get_specular().get_data()); + + alight.dvDirection = *(D3DVECTOR *) + (get_rel_forward( light, _current_projection_node, CS_yup_left).get_data()); + + alight.dvRange = D3DLIGHT_RANGE_MAX; + alight.dvFalloff = 1.0f; + + alight.dvAttenuation0 = 1.0f; // constant + alight.dvAttenuation1 = 0.0f; // linear + alight.dvAttenuation2 = 0.0f; // quadratic + + HRESULT res = _d3dDevice->SetLight(_cur_light_id, &alight); +// _d3dDevice->LightEnable( _cur_light_id, TRUE ); +// _d3dDevice->SetRenderState( D3DRENDERSTATE_LIGHTING, TRUE ); + +#endif // WBD_GL_MODE +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::apply_light +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian::apply_light( Spotlight* light ) +{ + // The light position will be relative to the current matrix, so + // we have to know what the current matrix is. Find a better + // solution later. +#ifdef WBD_GL_MODE +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "glMatrixMode(GL_MODELVIEW)" << endl; + dxgsg_cat.debug() + << "glPushMatrix()" << endl; + dxgsg_cat.debug() + << "glLoadIdentity()" << endl; +#endif + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixf(LMatrix4f::convert_mat(_coordinate_system, CS_yup_left) + .get_data()); + + GLenum id = get_light_id( _cur_light_id ); + Colorf black(0, 0, 0, 1); + glLightfv(id, GL_AMBIENT, black.get_data()); + glLightfv(id, GL_DIFFUSE, light->get_color().get_data()); + glLightfv(id, GL_SPECULAR, light->get_specular().get_data()); + + // Position needs to specify x, y, z, and w + // w == 1 implies non-infinite position + LPoint3f pos = get_rel_pos( light, _current_projection_node ); + LPoint4f fpos( pos[0], pos[1], pos[2], 1 ); + glLightfv( id, GL_POSITION, fpos.get_data() ); + + glLightfv( id, GL_SPOT_DIRECTION, + get_rel_forward( light, _current_projection_node, + _coordinate_system ).get_data() ); + glLightf( id, GL_SPOT_EXPONENT, light->get_exponent() ); + glLightf( id, GL_SPOT_CUTOFF, + light->get_cutoff_angle() ); + glLightf( id, GL_CONSTANT_ATTENUATION, + light->get_constant_attenuation() ); + glLightf( id, GL_LINEAR_ATTENUATION, + light->get_linear_attenuation() ); + glLightf( id, GL_QUADRATIC_ATTENUATION, + light->get_quadratic_attenuation() ); + + glPopMatrix(); +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "glPopMatrix()" << endl; +#endif +#else // DX Spotlight + D3DCOLORVALUE black; + black.r = black.g = black.b = black.a = 0.0f; + + D3DLIGHT7 alight; + ZeroMemory(&alight, sizeof(D3DLIGHT7)); + + alight.dltType = D3DLIGHT_SPOT; + alight.dcvAmbient = black ; + alight.dcvDiffuse = *(D3DCOLORVALUE *)(light->get_color().get_data()); + alight.dcvSpecular = *(D3DCOLORVALUE *)(light->get_specular().get_data()); + + alight.dvPosition = *(D3DVECTOR *) + (get_rel_pos( light, _current_root_node ).get_data()); + + alight.dvDirection = *(D3DVECTOR *) + (get_rel_forward( light, _current_root_node, _coordinate_system).get_data()); + + alight.dvRange = D3DLIGHT_RANGE_MAX; + alight.dvFalloff = 1.0f; + alight.dvTheta = 0.0f; + alight.dvPhi = light->get_cutoff_angle(); + + alight.dvAttenuation0 = (D3DVALUE)light->get_constant_attenuation(); // constant + alight.dvAttenuation1 = (D3DVALUE)light->get_linear_attenuation(); // linear + alight.dvAttenuation2 = (D3DVALUE)light->get_quadratic_attenuation();// quadratic + + HRESULT res = _d3dDevice->SetLight(_cur_light_id, &alight); + +#endif // WBD_GL_MODE +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::apply_light +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian::apply_light( AmbientLight* light ) +{ + _cur_ambient_light = _cur_ambient_light + light->get_color(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_transform +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_transform(const TransformAttribute *attrib) { + activate(); +#ifndef NDEBUG + if (dx_show_transforms) { + + bool lighting_was_enabled = _lighting_enabled; + bool texturing_was_enabled = _texturing_enabled; + enable_lighting(false); + enable_texturing(false); + + typedef struct { + D3DVALUE x,y,z; // position + D3DVALUE nx,ny,nz; // normal + D3DCOLOR diff; // diffuse color + } VERTFORMAT; + + VERTFORMAT vert_buf[] = { + {0.0, 0.0, 0.0, 0.0, -1.0, 0.0, D3DRGBA(1.0, 0.0, 0.0, 1.0)}, // red + {3.0, 0.0, 0.0, 0.0, -1.0, 0.0, D3DRGBA(1.0, 0.0, 0.0, 1.0)}, // red + {0.0, 0.0, 0.0, 0.0, -1.0, 0.0, D3DRGBA(0.0, 1.0, 0.0, 1.0)}, // grn + {0.0, 3.0, 0.0, 0.0, -1.0, 0.0, D3DRGBA(0.0, 1.0, 0.0, 1.0)}, // grn + {0.0, 0.0, 0.0, 0.0, -1.0, 0.0, D3DRGBA(0.0, 0.0, 1.0, 1.0)}, // blu + {0.0, 0.0, 3.0, 0.0, -1.0, 0.0, D3DRGBA(0.0, 0.0, 1.0, 1.0)}, // blu + }; + + _d3dDevice->DrawPrimitive(D3DPT_LINELIST, D3DFVF_DIFFUSE | D3DFVF_XYZ | D3DFVF_NORMAL, + vert_buf, 6, NULL); + + enable_lighting(lighting_was_enabled); + enable_texturing(texturing_was_enabled); + } +#endif + + _d3dDevice->SetTransform(D3DTRANSFORMSTATE_WORLD/*VIEW*/, + (LPD3DMATRIX) attrib->get_matrix().get_data()); +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_matrix +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_tex_matrix(const TexMatrixAttribute *attrib) { + dxgsg_cat.fatal() << "DXGSG issue_tex_matrix unimplemented!!!"; + return; + + activate(); +#ifdef WBD_GL_MODE +#ifdef GSG_VERBOSE + dxgsg_cat.debug() + << "glLoadMatrix(GL_TEXTURE): " << attrib->get_matrix() << endl; +#endif + glMatrixMode(GL_TEXTURE); + glLoadMatrixf(attrib->get_matrix().get_data()); +#else + _d3dDevice->SetTransform( D3DTRANSFORMSTATE_TEXTURE0, + (LPD3DMATRIX)attrib->get_matrix().get_data()); +#endif // WBD_GL_MODE +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_color +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_color(const ColorAttribute *attrib) { + activate(); + if (attrib->is_on()&& attrib->is_real()) + { + _issued_color_enabled = true; + Colorf c = attrib->get_color(); + _issued_color = D3DRGBA(c[0], c[1], c[2], c[3]); + } + else _issued_color_enabled = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_texture +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_texture(const TextureAttribute *attrib) { + + activate(); + + if (attrib->is_on()) { + enable_texturing(true); + Texture *tex = attrib->get_texture(); + nassertv(tex != (Texture *)NULL); + tex->apply(this); + } else { + enable_texturing(false); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_tex_gen +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_tex_gen(const TexGenAttribute *attrib) { + dxgsg_cat.fatal() << "DXGSG issue_tex_gen unimplemented!!!"; + return; + activate(); +#ifdef WBD_GL_MODE + TexGenProperty::Mode mode = attrib->get_mode(); + + switch (mode) { + case TexGenProperty::M_none: + glDisable(GL_TEXTURE_GEN_S); + glDisable(GL_TEXTURE_GEN_T); + glDisable(GL_TEXTURE_GEN_Q); + glDisable(GL_TEXTURE_GEN_R); + break; + + case TexGenProperty::M_texture_projector: + { + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + glEnable(GL_TEXTURE_GEN_Q); + glEnable(GL_TEXTURE_GEN_R); + const LMatrix4f &plane = attrib->get_plane(); + glTexGenfv(GL_S, GL_OBJECT_PLANE, plane.get_row(0).get_data()); + glTexGenfv(GL_T, GL_OBJECT_PLANE, plane.get_row(1).get_data()); + glTexGenfv(GL_R, GL_OBJECT_PLANE, plane.get_row(2).get_data()); + glTexGenfv(GL_Q, GL_OBJECT_PLANE, plane.get_row(3).get_data()); + glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + } + break; + + case TexGenProperty::M_sphere_map: + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + glDisable(GL_TEXTURE_GEN_Q); + glDisable(GL_TEXTURE_GEN_R); + glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); + glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); + break; + + default: + dxgsg_cat.error() + << "Unknown texgen mode " << (int)mode << endl; + break; + } +#endif // WBD_GL_MODE +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_material +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_material(const MaterialAttribute *attrib) { + activate(); + if (attrib->is_on()) { + Material *material = attrib->get_material(); + nassertv(material != (Material *)NULL); + material->apply(this); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_fog +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_fog(const FogAttribute *attrib) { + activate(); + + if (attrib->is_on()) { + enable_fog(true); + Fog *fog = attrib->get_fog(); + nassertv(fog != (Fog *)NULL); + fog->apply(this); + } else { + enable_fog(false); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_render_mode +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_render_mode(const RenderModeAttribute *attrib) { + activate(); + + RenderModeProperty::Mode mode = attrib->get_mode(); + + + switch(mode) + { + case RenderModeProperty::M_filled: + _d3dDevice->SetRenderState(D3DRENDERSTATE_FILLMODE, D3DFILL_SOLID); + break; + + case RenderModeProperty::M_wireframe: + _d3dDevice->SetRenderState(D3DRENDERSTATE_FILLMODE, D3DFILL_WIREFRAME); +// call_glLineWidth(attrib->get_line_width()); + break; + + default: + dxgsg_cat.error() + << "Unknown render mode " << (int)mode << endl; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_light +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian::issue_light(const LightAttribute *attrib ) +{ + nassertv(attrib->get_properties_is_on()); + activate(); + + // Initialize the current ambient light total and currently enabled + // light list + _cur_ambient_light.set(0, 0, 0, 1); + int i; + for (i = 0; i < _max_lights; i++) + _cur_light_enabled[i] = false; + + int num_enabled = 0; + LightAttribute::const_iterator li; + for (li = attrib->begin(); li != attrib->end(); ++li) { + _cur_light_id = -1; + num_enabled++; + enable_lighting(true); + Light *light = (*li); + nassertv(light != (Light *)NULL); + + // Ambient lights don't require specific light ids + // Simply add in the ambient contribution to the current total + if (light->get_light_type() == AmbientLight::get_class_type()) { + light->apply(this); + // We need to indicate that no light id is necessary because + // it's an ambient light + _cur_light_id = -2; + } + + // Check to see if this light has already been bound to an id + for (i = 0; i < _max_lights; i++) { + if (_available_light_ids[i] == light) { + // Light has already been bound to an id, we only need + // to enable the light, not apply it + _cur_light_id = -2; + if (enable_light(i, true)) _cur_light_enabled[i] = true; + break; + } + } + + // See if there are any unbound light ids + if (_cur_light_id == -1) { + for (i = 0; i < _max_lights; i++) { + if (_available_light_ids[i] == NULL) { + _available_light_ids[i] = light; + _cur_light_id = i; + break; + } + } + } + + // If there were no unbound light ids, see if we can replace + // a currently unused but previously bound id + if (_cur_light_id == -1) { + for (i = 0; i < _max_lights; i++) { + if (attrib->is_off(_available_light_ids[i])) { + _available_light_ids[i] = light; + _cur_light_id = i; + break; + } + } + } + + if (_cur_light_id >= 0) { + if (enable_light(_cur_light_id, true)) _cur_light_enabled[_cur_light_id] = true; + + // We need to do something different for each type of light + light->apply(this); + } else if (_cur_light_id == -1) { + dxgsg_cat.error() + << "issue_light() - failed to bind light to id" << endl; + } + } + + // Disable all unused lights + for (i = 0; i < _max_lights; i++) { + if (_cur_light_enabled[i] == false) + enable_light(i, false); + } + + // If no lights were enabled, disable lighting + if (num_enabled == 0) { + enable_lighting(false); + enable_color_material(false); + } else { + call_dxLightModelAmbient(_cur_ambient_light); + enable_color_material(true); + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::reset_ambient +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +reset_ambient() +{ + _lmodel_ambient += 2.0f; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_color_blend // +// Access: Public, Virtual // +// Description: // +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_color_blend(const ColorBlendAttribute *attrib) { + activate(); + ColorBlendProperty::Mode mode = attrib->get_mode(); + + switch (mode) { + case ColorBlendProperty::M_none: + enable_blend(false); + break; + case ColorBlendProperty::M_multiply: + enable_blend(true); + call_dxBlendFunc(D3DBLEND_DESTCOLOR, D3DBLEND_ZERO); + break; + case ColorBlendProperty::M_add: + enable_blend(true); + call_dxBlendFunc(D3DBLEND_ONE, D3DBLEND_ONE); + break; + case ColorBlendProperty::M_multiply_add: + enable_blend(true); + call_dxBlendFunc(D3DBLEND_DESTCOLOR, D3DBLEND_ONE); + break; + case ColorBlendProperty::M_alpha: + enable_blend(true); + call_dxBlendFunc(D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA); + break; + default: + dxgsg_cat.error() + << "Unknown color blend mode " << (int)mode << endl; + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_texture_apply +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_texture_apply(const TextureApplyAttribute *attrib) { + //dxgsg_cat.spam() << "DXGSG issue_texture_apply null-up!!"; //bugbug: useless + return; + + activate(); +#ifdef WBD_GL_MODE + GLint glmode = get_texture_apply_mode_type(attrib->get_mode()); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, glmode); +#endif // WBD_GL_MODE +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_color_mask +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_color_mask(const ColorMaskAttribute *attrib) { + dxgsg_cat.fatal() << "DXGSG issue_color_mask unimplemented (not implementable on DX7)!!!"; + return; +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_depth_test +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_depth_test(const DepthTestAttribute *attrib) { + activate(); + + DepthTestProperty::Mode mode = attrib->get_mode(); + + if (mode == DepthTestProperty::M_none) + { + _depth_test_enabled = false; + _d3dDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, D3DZB_FALSE); + } + else { + _depth_test_enabled = true; + _d3dDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, D3DZB_TRUE); + _d3dDevice->SetRenderState(D3DRENDERSTATE_ZFUNC, get_depth_func_type(mode)); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_depth_write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_depth_write(const DepthWriteAttribute *attrib) { + activate(); + _d3dDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, attrib->is_on()); +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_stencil +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_stencil(const StencilAttribute *attrib) { + activate(); + + StencilProperty::Mode mode = attrib->get_mode(); + if (mode == StencilProperty::M_none) { + enable_stencil_test(false); + + } else { + enable_stencil_test(true); + _d3dDevice->SetRenderState(D3DRENDERSTATE_STENCILFUNC, get_stencil_func_type(mode)); + _d3dDevice->SetRenderState(D3DRENDERSTATE_STENCILPASS, get_stencil_action_type(attrib->get_action())); + _d3dDevice->SetRenderState(D3DRENDERSTATE_STENCILFAIL, get_stencil_action_type(attrib->get_action())); + _d3dDevice->SetRenderState(D3DRENDERSTATE_STENCILZFAIL, get_stencil_action_type(attrib->get_action())); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_cull_attribute +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_cull_face(const CullFaceAttribute *attrib) { + activate(); + + CullFaceProperty::Mode mode = attrib->get_mode(); + + switch (mode) { + case CullFaceProperty::M_cull_none: + _d3dDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_NONE); + break; + case CullFaceProperty::M_cull_clockwise: + _d3dDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_CW); + break; + case CullFaceProperty::M_cull_counter_clockwise: + _d3dDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_CCW); + break; + case CullFaceProperty::M_cull_all: + dxgsg_cat.warning() << "M_cull_all is invalid for DX GSG renderer, using CULL_CCW \n"; + _d3dDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_CCW); + break; + default: + dxgsg_cat.error() + << "invalid cull face mode " << (int)mode << endl; + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_clip_plane +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_clip_plane(const ClipPlaneAttribute *attrib) +{ + activate(); + + // Initialize the currently enabled clip plane list + int i; + for (i = 0; i < _max_clip_planes; i++) + _cur_clip_plane_enabled[i] = false; + + int num_enabled = 0; + ClipPlaneAttribute::const_iterator pi; + for (pi = attrib->begin(); pi != attrib->end(); ++pi) { + PlaneNode *plane_node; + DCAST_INTO_V(plane_node, (*pi)); + nassertv(plane_node != (PlaneNode *)NULL); + + _cur_clip_plane_id = -1; + num_enabled++; + + // Check to see if this clip plane has already been bound to an id + for (i = 0; i < _max_clip_planes; i++) { + if (_available_clip_plane_ids[i] == plane_node) { + // Clip plane has already been bound to an id, we only need + // to enable the clip plane, not apply it + _cur_clip_plane_id = -2; + enable_clip_plane(i, true); + _cur_clip_plane_enabled[i] = true; + break; + } + } + + // See if there are any unbound clip plane ids + if (_cur_clip_plane_id == -1) { + for (i = 0; i < _max_clip_planes; i++) { + if (_available_clip_plane_ids[i] == NULL) { + _available_clip_plane_ids[i] = plane_node; + _cur_clip_plane_id = i; + break; + } + } + } + + // If there were no unbound clip plane ids, see if we can replace + // a currently unused but previously bound id + if (_cur_clip_plane_id == -1) { + for (i = 0; i < _max_clip_planes; i++) { + if (attrib->is_off(_available_clip_plane_ids[i])) { + _available_clip_plane_ids[i] = plane_node; + _cur_clip_plane_id = i; + break; + } + } + } + + if (_cur_clip_plane_id >= 0) { + enable_clip_plane(_cur_clip_plane_id, true); + _cur_clip_plane_enabled[_cur_clip_plane_id] = true; + const Planef clip_plane = plane_node->get_plane(); + + D3DVALUE equation[4]; + equation[0] = clip_plane._a; + equation[1] = clip_plane._b; + equation[2] = clip_plane._c; + equation[3] = clip_plane._d; + _d3dDevice->SetClipPlane(_cur_clip_plane_id, equation); + + } else if (_cur_clip_plane_id == -1) { + dxgsg_cat.error() + << "issue_clip_plane() - failed to bind clip plane to id" << endl; + } + } + + // Disable all unused clip planes + for (i = 0; i < _max_clip_planes; i++) { + if (_cur_clip_plane_enabled[i] == false) + enable_clip_plane(i, false); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_transparency +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_transparency(const TransparencyAttribute *attrib ) +{ + activate(); + + TransparencyProperty::Mode mode = attrib->get_mode(); + + switch (mode) { + case TransparencyProperty::M_none: + enable_multisample_alpha_one(false); + enable_multisample_alpha_mask(false); + enable_blend(false); + enable_alpha_test(false); + break; + case TransparencyProperty::M_alpha: + case TransparencyProperty::M_alpha_sorted: + // Should we really have an "alpha" and an "alpha_sorted" mode, + // like Performer does? (The difference is that "alpha" is with + // the write to the depth buffer disabled.) Or should we just use + // the separate depth write transition to control this? Doing it + // implicitly requires a bit more logic here and in the state + // management; for now we require the user to explicitly turn off + // the depth write. + enable_multisample_alpha_one(false); + enable_multisample_alpha_mask(false); + enable_blend(true); + enable_alpha_test(false); + call_dxBlendFunc(D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA); + break; + case TransparencyProperty::M_multisample: + enable_multisample_alpha_one(true); + enable_multisample_alpha_mask(true); + enable_blend(false); + enable_alpha_test(false); + break; + case TransparencyProperty::M_multisample_mask: + enable_multisample_alpha_one(false); + enable_multisample_alpha_mask(true); + enable_blend(false); + enable_alpha_test(false); + break; + case TransparencyProperty::M_binary: + enable_multisample_alpha_one(false); + enable_multisample_alpha_mask(false); + enable_blend(false); + enable_alpha_test(true); + call_dxAlphaFunc(D3DCMP_EQUAL, 1); + break; + default: + dxgsg_cat.error() + << "invalid transparency mode " << (int)mode << endl; + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::issue_linesmooth +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +issue_linesmooth(const LinesmoothAttribute *attrib) { + activate(); + enable_line_smooth(attrib->is_on()); +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::wants_normals +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool DXGraphicsStateGuardian:: +wants_normals() const { + return (_lighting_enabled || _normals_enabled); +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::wants_texcoords +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool DXGraphicsStateGuardian:: +wants_texcoords() const { + return _texturing_enabled; +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::wants_colors +// Access: Public, Virtual +// Description: Returns true if the GSG should issue geometry color +// commands, false otherwise. +//////////////////////////////////////////////////////////////////// +bool DXGraphicsStateGuardian:: +wants_colors() const { + // If we have scene graph color enabled, return false to indicate we + // shouldn't bother issuing geometry color commands. + + const ColorAttribute *catt; + if (!get_attribute_into(catt, _state, ColorTransition::get_class_type())) { + // No scene graph color at all. + return true; + } + + // We should issue geometry colors only if the scene graph color is + // off. + return catt->is_off(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::begin_decal +// Access: Public, Virtual +// Description: This will be called to initiate decaling mode. It is +// passed the pointer to the GeomNode that will be the +// destination of the decals, which it is expected that +// the GSG will render normally; subsequent geometry +// rendered up until the next call of end_decal() should +// be rendered as decals of the base_geom. +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +begin_decal(GeomNode *base_geom) { + nassertv(base_geom != (GeomNode *)NULL); + + _decal_level++; + nassertv(4*_decal_level < 16); + + if (dx_decal_type == GDT_offset) + { + // Just draw the base geometry normally. + base_geom->draw(this); + //bugbug: restoring zbias properly requires a stack of values!!! this is wrong, need to save old value on stack + _d3dDevice->SetRenderState(D3DRENDERSTATE_ZBIAS, 4 * _decal_level); // _decal_level better not be higher than 8! + } else { + if (_decal_level > 1) + base_geom->draw(this); // If we're already decaling, just draw the geometry. + else { + // Turn off writing the depth buffer to render the base geometry. + _d3dDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE); + + // Now render the base geometry. + base_geom->draw(this); + + // Render all of the decal geometry, too. We'll keep the depth + // buffer write off during this. + } + } + +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::end_decal +// Access: Public, Virtual +// Description: This will be called to terminate decaling mode. It +// is passed the same base_geom that was passed to +// begin_decal(). +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +end_decal(GeomNode *base_geom) { + nassertv(base_geom != (GeomNode *)NULL); + + _decal_level--; +// nassertv(_decal_level >= 1); + + if (dx_decal_type == GDT_offset) + { + // Restore the Zbias offset. + //bugbug: restoring zbias properly requires a stack of values!!! this is wrong, need to save old value on stack + _d3dDevice->SetRenderState(D3DRENDERSTATE_ZBIAS, 4 * _decal_level); // _decal_level better not be higher than 8! + } + else + { + if (_decal_level == 0) + { + // Now we need to re-render the base geometry with the depth write + // on and the color mask off, so we update the depth buffer + // properly. + bool was_textured = _texturing_enabled; + bool was_blend = _blend_enabled; + D3DBLEND old_blend_source_func = _blend_source_func; + D3DBLEND old_blend_dest_func = _blend_dest_func; + + // Enable the writing to the depth buffer. + _d3dDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, TRUE); + + // Disable the writing to the color buffer, however we have to + // do this. (I don't think this is possible in DX without blending. +// if (dx_decal_type == GDT_blend) +// { + // Expensive. + enable_blend(true); + call_dxBlendFunc(D3DBLEND_ZERO, D3DBLEND_ONE); +// } +// else glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + + // No need to have texturing on for this. + enable_texturing(false); + + base_geom->draw(this); + + // Finally, restore the depth write and color mask states to the + // way they're supposed to be. + DepthWriteAttribute *depth_write; + if (get_attribute_into(depth_write, _state, + DepthWriteTransition::get_class_type())) + issue_depth_write(depth_write); + +// if (dx_decal_type == GDT_blend) { + enable_blend(was_blend); + if (was_blend) + call_dxBlendFunc(old_blend_source_func, old_blend_dest_func); + enable_texturing(was_textured); + } + } +/* } else { + ColorMaskAttribute *color_mask; + if (get_attribute_into(color_mask, _state, + ColorMaskTransition::get_class_type())) { + issue_color_mask(color_mask); + } else { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + } +*/ +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::compute_distance_to +// Access: Public, Virtual +// Description: This function may only be called during a render +// traversal; it will compute the distance to the +// indicated point, assumed to be in modelview +// coordinates, from the camera plane. +//////////////////////////////////////////////////////////////////// +float DXGraphicsStateGuardian:: +compute_distance_to(const LPoint3f &point) const { + // In the case of a DXGraphicsStateGuardian, we know that the + // modelview matrix already includes the relative transform from the + // camera, as well as a to-y-up conversion. Thus, the distance to + // the camera plane is simply the +z distance. (negative of gl compute_distance_to, + // since d3d uses left-hand coords) + + return point[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::set_draw_buffer +// Access: Protected +// Description: Sets up the glDrawBuffer to render into the buffer +// indicated by the RenderBuffer object. This only sets +// up the color bits; it does not affect the depth, +// stencil, accum layers. +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +set_draw_buffer(const RenderBuffer &rb) { + dxgsg_cat.fatal() << "DX set_draw_buffer unimplemented!!!"; + return; + +#ifdef WBD_GL_MODE + switch (rb._buffer_type & RenderBuffer::T_color) { + case RenderBuffer::T_front: + call_glDrawBuffer(GL_FRONT); + break; + + case RenderBuffer::T_back: + call_glDrawBuffer(GL_BACK); + break; + + case RenderBuffer::T_right: + call_glDrawBuffer(GL_RIGHT); + break; + + case RenderBuffer::T_left: + call_glDrawBuffer(GL_LEFT); + break; + + case RenderBuffer::T_front_right: + call_glDrawBuffer(GL_FRONT_RIGHT); + break; + + case RenderBuffer::T_front_left: + call_glDrawBuffer(GL_FRONT_LEFT); + break; + + case RenderBuffer::T_back_right: + call_glDrawBuffer(GL_BACK_RIGHT); + break; + + case RenderBuffer::T_back_left: + call_glDrawBuffer(GL_BACK_LEFT); + break; + + default: + call_glDrawBuffer(GL_FRONT_AND_BACK); + } +#endif // WBD_GL_MODE +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::set_read_buffer +// Access: Protected +// Description: Sets up the glReadBuffer to render into the buffer +// indicated by the RenderBuffer object. This only sets +// up the color bits; it does not affect the depth, +// stencil, accum layers. +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +set_read_buffer(const RenderBuffer &rb) { + dxgsg_cat.fatal() << "DX set_read_buffer unimplemented!!!"; + return; + +#ifdef WBD_GL_MODE + switch (rb._buffer_type & RenderBuffer::T_color) { + case RenderBuffer::T_front: + call_glReadBuffer(GL_FRONT); + break; + + case RenderBuffer::T_back: + call_glReadBuffer(GL_BACK); + break; + + case RenderBuffer::T_right: + call_glReadBuffer(GL_RIGHT); + break; + + case RenderBuffer::T_left: + call_glReadBuffer(GL_LEFT); + break; + + case RenderBuffer::T_front_right: + call_glReadBuffer(GL_FRONT_RIGHT); + break; + + case RenderBuffer::T_front_left: + call_glReadBuffer(GL_FRONT_LEFT); + break; + + case RenderBuffer::T_back_right: + call_glReadBuffer(GL_BACK_RIGHT); + break; + + case RenderBuffer::T_back_left: + call_glReadBuffer(GL_BACK_LEFT); + break; + + default: + call_glReadBuffer(GL_FRONT_AND_BACK); + } +#endif // WBD_GL_MODE +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::specify_texture +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +specify_texture(Texture *tex) { + + _d3dDevice->SetTextureStageState(0,D3DTSS_ADDRESSU, + get_texture_wrap_mode(tex->get_wrapu())); + _d3dDevice->SetTextureStageState(0,D3DTSS_ADDRESSV, + get_texture_wrap_mode(tex->get_wrapv())); + _d3dDevice->SetTextureStageState(0,D3DTSS_MINFILTER, + get_texture_filter_type(tex->get_minfilter())); + _d3dDevice->SetTextureStageState(0,D3DTSS_MAGFILTER, + get_texture_filter_type(tex->get_magfilter())); +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::apply_texture_immediate +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +apply_texture_immediate(DXTextureContext *tc) { + + // bugbug: does this handle the case of untextured geometry? + // we dont see this bug cause we never mix textured/untextured + + _d3dDevice->SetTexture(0, tc->_surface ); +#ifdef _DEBUG + if(tc!=NULL) { + dxgsg_cat.spam() << "Setting active DX texture: " << tc->_tex->get_name() << "\n"; + } +#endif + + _pCurTexContext = tc; // enable_texturing needs this + + /* FOR REFERENCE>>> PixelBuffer::Type and PixelBuffer::Format + use pb->get_format() and pb->get_image_type() + enum Type { + T_unsigned_byte, + T_unsigned_short, + T_unsigned_byte_332, + T_float, + }; + + enum Format { + F_color_index, + F_stencil_index, + F_depth_component, + F_red, + F_green, + F_blue, + F_alpha, + F_rgb, + F_rgb5, // specifically, 5 bits per R,G,B channel + F_rgb8, // 8 bits per R,G,B channel + F_rgb12, // 12 bits per R,G,B channel + F_rgb332, // 3 bits per R & G, 2 bits for B + F_rgba, + F_rgba4, // 4 bits per R,G,B,A channel + F_rgba8, // 8 bits per R,G,B,A channel + F_rgba12, // 12 bits per R,G,B,A channel + F_luminance, + F_luminance_alpha, + }; + + */ +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::get_texture_wrap_mode +// Access: Protected +// Description: Maps from the Texture's internal wrap mode symbols to +// GL's. +//////////////////////////////////////////////////////////////////// +D3DTEXTUREADDRESS DXGraphicsStateGuardian:: +get_texture_wrap_mode(Texture::WrapMode wm) { + switch (wm) { + case Texture::WM_clamp: + return D3DTADDRESS_CLAMP; + case Texture::WM_repeat: + return D3DTADDRESS_WRAP; + } + dxgsg_cat.error() << "Invalid Texture::WrapMode value!\n"; + return D3DTADDRESS_WRAP; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::get_texture_filter_type +// Access: Protected +// Description: Maps from the Texture's internal filter type symbols +// to GL's. +//////////////////////////////////////////////////////////////////// +D3DTEXTUREMAGFILTER DXGraphicsStateGuardian:: +get_texture_filter_type(Texture::FilterType ft) { + switch (ft) { + case Texture::FT_nearest: + return D3DTFG_POINT; + case Texture::FT_linear: + return D3DTFG_LINEAR; + case Texture::FT_nearest_mipmap_nearest: + return D3DTFG_POINT; + case Texture::FT_linear_mipmap_nearest: + return D3DTFG_LINEAR; + case Texture::FT_nearest_mipmap_linear: + return D3DTFG_POINT; + case Texture::FT_linear_mipmap_linear: + return D3DTFG_LINEAR; + } + dxgsg_cat.error() << "Invalid Texture::FilterType value!\n"; + return D3DTFG_POINT; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::get_depth_func_type +// Access: Protected +// Description: Maps from the depth func modes to gl version +//////////////////////////////////////////////////////////////////// +D3DCMPFUNC DXGraphicsStateGuardian:: +get_depth_func_type(DepthTestProperty::Mode m) const +{ + switch(m) + { + case DepthTestProperty::M_never: return D3DCMP_NEVER; + case DepthTestProperty::M_less: return D3DCMP_LESS; + case DepthTestProperty::M_equal: return D3DCMP_EQUAL; + case DepthTestProperty::M_less_equal: return D3DCMP_LESSEQUAL; + case DepthTestProperty::M_greater: return D3DCMP_GREATER; + case DepthTestProperty::M_not_equal: return D3DCMP_NOTEQUAL; + case DepthTestProperty::M_greater_equal: return D3DCMP_GREATEREQUAL; + case DepthTestProperty::M_always: return D3DCMP_ALWAYS; + } + dxgsg_cat.error() + << "Invalid DepthTestProperty::Mode value" << endl; + return D3DCMP_LESS; +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::get_stencil_func_type +// Access: Protected +// Description: Maps from the stencil func modes to dx version +//////////////////////////////////////////////////////////////////// +D3DCMPFUNC DXGraphicsStateGuardian:: +get_stencil_func_type(StencilProperty::Mode m) const +{ + switch(m) { + case StencilProperty::M_never: return D3DCMP_NEVER; + case StencilProperty::M_less: return D3DCMP_LESS; + case StencilProperty::M_equal: return D3DCMP_EQUAL; + case StencilProperty::M_less_equal: return D3DCMP_LESSEQUAL; + case StencilProperty::M_greater: return D3DCMP_GREATER; + case StencilProperty::M_not_equal: return D3DCMP_NOTEQUAL; + case StencilProperty::M_greater_equal: return D3DCMP_GREATEREQUAL; + case StencilProperty::M_always: return D3DCMP_ALWAYS; + } + dxgsg_cat.error() + << "Invalid StencilProperty::Mode value" << endl; + return D3DCMP_LESS; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::get_stencil_action_type +// Access: Protected +// Description: Maps from the stencil action modes to dx version +//////////////////////////////////////////////////////////////////// +D3DSTENCILOP DXGraphicsStateGuardian:: +get_stencil_action_type(StencilProperty::Action a) const +{ + switch(a) { + case StencilProperty::A_keep: return D3DSTENCILOP_KEEP; + case StencilProperty::A_zero: return D3DSTENCILOP_ZERO; + case StencilProperty::A_replace: return D3DSTENCILOP_REPLACE; + case StencilProperty::A_increment: return D3DSTENCILOP_INCR; + case StencilProperty::A_decrement: return D3DSTENCILOP_DECR; + case StencilProperty::A_invert: return D3DSTENCILOP_INVERT; + } + dxgsg_cat.error() + << "Invalid StencilProperty::Action value" << endl; + return D3DSTENCILOP_KEEP; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::get_fog_mode_type +// Access: Protected +// Description: Maps from the fog types to gl version +//////////////////////////////////////////////////////////////////// +D3DFOGMODE DXGraphicsStateGuardian:: +get_fog_mode_type(Fog::Mode m) const { + switch(m) { + case Fog::M_linear: return D3DFOG_LINEAR; + case Fog::M_exponential: return D3DFOG_EXP; + case Fog::M_super_exponential: return D3DFOG_EXP2; + } + dxgsg_cat.error() << "Invalid Fog::Mode value" << endl; + return D3DFOG_EXP; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::free_pointers +// Access: Public +// Description: Frees some memory that was explicitly allocated +// within the dxgsg. +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +free_pointers() { + if (_light_enabled != (bool *)NULL) { + delete[] _light_enabled; + _light_enabled = (bool *)NULL; + } + if (_cur_light_enabled != (bool *)NULL) { + delete[] _cur_light_enabled; + _cur_light_enabled = (bool *)NULL; + } + if (_clip_plane_enabled != (bool *)NULL) { + delete[] _clip_plane_enabled; + _clip_plane_enabled = (bool *)NULL; + } + if (_cur_clip_plane_enabled != (bool *)NULL) { + delete[] _cur_clip_plane_enabled; + _cur_clip_plane_enabled = (bool *)NULL; + } + + if( _pTexPixFmts != NULL) { + delete _pTexPixFmts; + _pTexPixFmts = NULL; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::save_frame_buffer +// Access: Public +// Description: Saves the indicated planes of the frame buffer +// (within the indicated display region) and returns it +// in some meaningful form that can be restored later +// via restore_frame_buffer(). This is a helper +// function for push_frame_buffer() and +// pop_frame_buffer(). +//////////////////////////////////////////////////////////////////// +PT(SavedFrameBuffer) DXGraphicsStateGuardian:: +save_frame_buffer(const RenderBuffer &buffer, + CPT(DisplayRegion) dr) { + DXSavedFrameBuffer *sfb = new DXSavedFrameBuffer(buffer, dr); + + if (buffer._buffer_type & RenderBuffer::T_depth) { + // Save the depth buffer. + sfb->_depth = + new PixelBuffer(PixelBuffer::depth_buffer(dr->get_pixel_width(), + dr->get_pixel_height())); + copy_pixel_buffer(sfb->_depth, dr, buffer); + } + + if (buffer._buffer_type & RenderBuffer::T_back) { + // Save the color buffer. + sfb->_back_rgba = new Texture; + copy_texture(sfb->_back_rgba->prepare(this), dr, buffer); + } + + return sfb; +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::restore_frame_buffer +// Access: Public +// Description: Restores the frame buffer that was previously saved. +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +restore_frame_buffer(SavedFrameBuffer *frame_buffer) { + DXSavedFrameBuffer *sfb = DCAST(DXSavedFrameBuffer, frame_buffer); + + if (sfb->_back_rgba != (Texture *)NULL && + (sfb->_buffer._buffer_type & RenderBuffer::T_back) != 0) { + // Restore the color buffer. + draw_texture(sfb->_back_rgba->prepare(this), + sfb->_display_region, sfb->_buffer); + } + + if (sfb->_depth != (PixelBuffer *)NULL && + (sfb->_buffer._buffer_type & RenderBuffer::T_depth) != 0) { + // Restore the depth buffer. + draw_pixel_buffer(sfb->_depth, sfb->_display_region, sfb->_buffer); + } +} + +// factory and type stuff + +GraphicsStateGuardian *DXGraphicsStateGuardian:: +make_DXGraphicsStateGuardian(const FactoryParams ¶ms) { + GraphicsStateGuardian::GsgWindow *win_param; + if (!get_param_into(win_param, params)) { + dxgsg_cat.error() + << "No window specified for gsg creation!" << endl; + return NULL; + } + + GraphicsWindow *win = win_param->get_window(); + return new DXGraphicsStateGuardian(win); +} + +TypeHandle DXGraphicsStateGuardian::get_type(void) const { + return get_class_type(); +} + +TypeHandle DXGraphicsStateGuardian::get_class_type(void) { + return _type_handle; +} + +void DXGraphicsStateGuardian::init_type(void) { + GraphicsStateGuardian::init_type(); + register_type(_type_handle, "DXGraphicsStateGuardian", + GraphicsStateGuardian::get_class_type()); +} + + + +#ifdef WBD_GL_MODE + + +/* All of the following routines return GL-specific constants based + on Panda defined constants. For DX, just use + the Panda defined constants to decide what to do. + + get_image_type(PixelBuffer::Type type) { + get_external_image_format(PixelBuffer::Format format) { + get_internal_image_format(PixelBuffer::Format format) { + get_texture_apply_mode_type( TextureApplyProperty::Mode am ) const +*/ + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::get_image_type +// Access: Protected +// Description: Maps from the PixelBuffer's internal Type symbols +// to GL's. +//////////////////////////////////////////////////////////////////// +GLenum DXGraphicsStateGuardian:: +get_image_type(PixelBuffer::Type type) { + switch (type) { + case PixelBuffer::T_unsigned_byte: + return GL_UNSIGNED_BYTE; + case PixelBuffer::T_unsigned_short: + return GL_UNSIGNED_SHORT; +#ifdef GL_UNSIGNED_BYTE_3_3_2_EXT + case PixelBuffer::T_unsigned_byte_332: + return GL_UNSIGNED_BYTE_3_3_2_EXT; +#endif + case PixelBuffer::T_float: + return GL_FLOAT; + } + dxgsg_cat.error() << "Invalid PixelBuffer::Type value!\n"; + return GL_UNSIGNED_BYTE; +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::get_external_image_format +// Access: Protected +// Description: Maps from the PixelBuffer's Format symbols +// to GL's. +//////////////////////////////////////////////////////////////////// +GLenum DXGraphicsStateGuardian:: +get_external_image_format(PixelBuffer::Format format) { + switch (format) { + case PixelBuffer::F_color_index: + return GL_COLOR_INDEX; + case PixelBuffer::F_stencil_index: + return GL_STENCIL_INDEX; + case PixelBuffer::F_depth_component: + return GL_DEPTH_COMPONENT; + case PixelBuffer::F_red: + return GL_RED; + case PixelBuffer::F_green: + return GL_GREEN; + case PixelBuffer::F_blue: + return GL_BLUE; + case PixelBuffer::F_alpha: + return GL_ALPHA; + case PixelBuffer::F_rgb: + case PixelBuffer::F_rgb5: + case PixelBuffer::F_rgb8: + case PixelBuffer::F_rgb12: + case PixelBuffer::F_rgb332: + return GL_RGB; + case PixelBuffer::F_rgba: + case PixelBuffer::F_rgba4: + case PixelBuffer::F_rgba8: + case PixelBuffer::F_rgba12: + return GL_RGBA; + case PixelBuffer::F_luminance: + return GL_LUMINANCE; + case PixelBuffer::F_luminance_alpha: + return GL_LUMINANCE_ALPHA; + } + dxgsg_cat.error() + << "Invalid PixelBuffer::Format value in get_external_image_format(): " + << (int)format << "\n"; + return GL_RGB; +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::get_internal_image_format +// Access: Protected +// Description: Maps from the PixelBuffer's Format symbols to a +// suitable internal format for GL textures. +//////////////////////////////////////////////////////////////////// +GLenum DXGraphicsStateGuardian:: +get_internal_image_format(PixelBuffer::Format format) { + switch (format) { + case PixelBuffer::F_rgba: + return GL_RGBA; + case PixelBuffer::F_rgba4: + return GL_RGBA4; + case PixelBuffer::F_rgba8: + return GL_RGBA8; + case PixelBuffer::F_rgba12: + return GL_RGBA12; + + case PixelBuffer::F_rgb: + return GL_RGB; + case PixelBuffer::F_rgb5: + case PixelBuffer::F_rgb8: + return GL_RGB8; + case PixelBuffer::F_rgb12: + return GL_RGB12; + case PixelBuffer::F_rgb332: + return GL_R3_G3_B2; + + case PixelBuffer::F_luminance_alpha: + return GL_LUMINANCE_ALPHA; + + case PixelBuffer::F_alpha: + return GL_ALPHA; + + case PixelBuffer::F_red: + case PixelBuffer::F_green: + case PixelBuffer::F_blue: + case PixelBuffer::F_luminance: + return GL_LUMINANCE; + } + + dxgsg_cat.error() + << "Invalid image format in get_internal_image_format(): " + << (int)format << "\n"; + return GL_RGB; +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian::get_texture_apply_mode_type +// Access: Protected +// Description: Maps from the texture environment's mode types +// to the corresponding OpenGL ids +//////////////////////////////////////////////////////////////////// +GLint DXGraphicsStateGuardian:: +get_texture_apply_mode_type( TextureApplyProperty::Mode am ) const +{ + switch( am ) + { + case TextureApplyProperty::M_modulate: return GL_MODULATE; + case TextureApplyProperty::M_decal: return GL_DECAL; + case TextureApplyProperty::M_blend: return GL_BLEND; + case TextureApplyProperty::M_replace: return GL_REPLACE; + case TextureApplyProperty::M_add: return GL_ADD; + } + dxgsg_cat.error() + << "Invalid TextureApplyProperty::Mode value" << endl; + return GL_MODULATE; +} +#endif // WBD_GL_MODE + + +//////////////////////////////////////////////////////////////////// +// Function: dx_cleanup +// Description: Clean up the DirectX environment. +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +dx_cleanup() { + + release_all_textures(); + + // Release the DDraw and D3D objects used by the app + RELEASE(_zbuf); + RELEASE(_back); + RELEASE(_pri); + + // Do a safe check for releasing the D3DDEVICE. RefCount should be zero. + if( _d3dDevice!=NULL ) { + if( 0 < _d3dDevice->Release() ) { + dxgsg_cat.error() << "DXGraphicsStateGuardian::destructor - d3dDevice reference count > 0" << endl; + } + _d3dDevice = NULL; // clear the pointer in the Gsg + } + + RELEASE(_d3d); + + // Do a safe check for releasing DDRAW. RefCount should be zero. + if( _pDD!=NULL ) { + int val; + if( 0 < (val = _pDD->Release()) ) { + dxgsg_cat.error() + << "DXGraphicsStateGuardian::destructor - context reference count = " << val << endl; + } + _pDD = NULL; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: dx_setup_after_resize +// Description: Recreate the back buffer and zbuffers at the new size +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian:: +dx_setup_after_resize(RECT viewrect, HWND mwindow) +{ + if(_back == NULL) // nothing created yet + return; + + // for safety, need some better error-cleanup here + assert((_pri!=NULL) && (_back!=NULL) && (_zbuf!=NULL)); + + DX_DECLARE_CLEAN(DDSURFACEDESC2, ddsd_back); + DX_DECLARE_CLEAN(DDSURFACEDESC2, ddsd_zbuf); + + _back->GetSurfaceDesc(&ddsd_back); + _zbuf->GetSurfaceDesc(&ddsd_zbuf); + +#if 0 + DWORD refcnt; + refcnt = _zbuf->Release(); _zbuf = NULL; + dxgsg_cat.error() << "zbuf refcnt= "<Release(); _back = NULL; + dxgsg_cat.error() << "back refcnt= "<Release(); _pri = NULL; + dxgsg_cat.error() << "pri refcnt= "<CreateSurface( &ddsd, &_pri, NULL ))) { + dxgsg_cat.fatal() << "DXGraphicsStateGuardian::resize() - CreateSurface failed for primary : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + // Create a clipper object which handles all our clipping for cases when + // our window is partially obscured by other windows. + LPDIRECTDRAWCLIPPER Clipper; + + if(FAILED(hr = _pDD->CreateClipper( 0, &Clipper, NULL ))) { + dxgsg_cat.fatal() + << "dxgsg - CreateClipper after resize failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + // Associate the clipper with our window. Note that, afterwards, the + // clipper is internally referenced by the primary surface, so it is safe + // to release our local reference to it. + Clipper->SetHWnd( 0, mwindow ); + _pri->SetClipper( Clipper ); + Clipper->Release(); + + // Recreate the backbuffer. (might want to handle failure due to running out of video memory) + + ddsd_back.dwFlags |= DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; // just to make sure + ddsd_back.ddsCaps.dwCaps |= DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE; + + PRINTVIDMEM(_pDD,&ddsd_back.ddsCaps,"resize backbuffer surf"); + + if(FAILED(hr = _pDD->CreateSurface( &ddsd_back, &_back, NULL ))) { + dxgsg_cat.fatal() << "DXGraphicsStateGuardian::resize() - CreateSurface failed for backbuffer : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + PRINTVIDMEM(_pDD,&ddsd_back.ddsCaps,"resize zbuffer surf"); + + // Recreate and attach a z-buffer. + if(FAILED(hr = _pDD->CreateSurface( &ddsd_zbuf, &_zbuf, NULL ))) { + dxgsg_cat.fatal() << "DXGraphicsStateGuardian::resize() - CreateSurface failed for Z buffer: result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + // Attach the z-buffer to the back buffer. + if( (hr = _back->AddAttachedSurface( _zbuf ) ) != DD_OK) { + dxgsg_cat.fatal() + << "DXGraphicsStateGuardian::resize() - AddAttachedSurface failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + if( (hr = _d3dDevice->SetRenderTarget(_back,0x0) ) != DD_OK) { + dxgsg_cat.fatal() + << "DXGraphicsStateGuardian::resize() - SetRenderTarget failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + // Create the viewport + D3DVIEWPORT7 vp = { 0, 0, renderWid, renderHt, 0.0f, 1.0f }; + hr = _d3dDevice->SetViewport( &vp ); + if (hr != DD_OK) { + dxgsg_cat.fatal() + << "DXGraphicsStateGuardian::config() - SetViewport failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: show_frame +// Access: +// Description: Repaint primary buffer from back buffer (windowed mode only) +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian::show_frame(void) { + + PStatTimer timer(_win->_swap_pcollector); // this times just the flip, so it must go here in dxgsg, instead of wdxdisplay, which would time the whole frame + + if (_pri!=NULL) { + if( dx_full_screen ) { + + HRESULT hr = _pri->Flip( NULL, DDFLIP_WAIT ); // bugbug: dont we want triple buffering instead of wasting time waiting for vsync? + if (hr == DDERR_SURFACELOST) { + // Check/restore the primary surface + if(( _pri!=NULL ) && _pri->IsLost()) + _pri->Restore(); + // Check/restore the back buffer + if(( _back!=NULL ) && _back->IsLost()) + _back->Restore(); + // Check/restore the z-buffer + if(( _zbuf!=NULL ) && _zbuf->IsLost()) + _zbuf->Restore(); + return; + } + + if (hr != DD_OK) { + dxgsg_cat.error() << "DXGraphicsStateGuardian::show_frame() - Flip failed : " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + } else { + HRESULT hr = _pri->Blt( &_view_rect, _back, NULL, DDBLT_WAIT, NULL ); + if(hr!=DD_OK) { + if (hr == DDERR_SURFACELOST) { + // Check/restore the primary surface + if(( _pri!=NULL ) && _pri->IsLost()) + _pri->Restore(); + // Check/restore the back buffer + if(( _back!=NULL ) && _back->IsLost()) + _back->Restore(); + // Check/restore the z-buffer + if(( _zbuf!=NULL ) && _zbuf->IsLost()) + _zbuf->Restore(); + return; + } else { + dxgsg_cat.error() << "DXGraphicsStateGuardian::show_frame() - Blt failed : " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_window_move +// Access: +// Description: we receive the new x and y position of the client +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian::adjust_view_rect(int x, int y) +{ + if (_view_rect.left != x || _view_rect.top != y) + { + _view_rect.right = x + _view_rect.right - _view_rect.left; + _view_rect.left = x; + _view_rect.bottom = y + _view_rect.bottom - _view_rect.top; + _view_rect.top = y; + +// set_clipper(clip_rect); + } +} + + diff --git a/panda/src/dxgsg/dxGraphicsStateGuardian.h b/panda/src/dxgsg/dxGraphicsStateGuardian.h new file mode 100644 index 0000000000..0ca581ebff --- /dev/null +++ b/panda/src/dxgsg/dxGraphicsStateGuardian.h @@ -0,0 +1,442 @@ +// Filename: dxGraphicsStateGuardian.h +// Created by: mike (02Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DXGRAPHICSSTATEGUARDIAN_H +#define DXGRAPHICSSTATEGUARDIAN_H + +//#define GSG_VERBOSE + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dxTextureContext.h" + +extern char * ConvD3DErrorToString(const HRESULT &error); // defined in wdxGraphicsPipe.cxx + +#ifdef PENV_WIN32 +// Must include windows.h before gl.h on NT +#define WIN32_LEAN_AND_MEAN +#include +#undef WIN32_LEAN_AND_MEAN +#endif + +#include +#include + +#include + +class PlaneNode; +class Light; + +#ifdef GSG_VERBOSE +ostream &output_gl_enum(ostream &out, GLenum v); +INLINE ostream &operator << (ostream &out, GLenum v) { + return output_gl_enum(out, v); +} +#endif + +#define DX_DECLARE_CLEAN(type, var) \ + type var; \ + ZeroMemory(&var, sizeof(type)); \ + var.dwSize = sizeof(type); + +#define RELEASE(object) if (object!=NULL) {object->Release(); object = NULL;} + +#ifdef _DEBUG +extern void dbgPrintVidMem(LPDIRECTDRAW7 pDD, LPDDSCAPS2 lpddsCaps,const char *pMsg); +#define PRINTVIDMEM(pDD,pCaps,pMsg) dbgPrintVidMem(pDD,pCaps,pMsg) +#else +#define PRINTVIDMEM(pDD,pCaps,pMsg) +#endif + +const int VERT_BUFFER_SIZE = (8*1024L); + +//////////////////////////////////////////////////////////////////// +// Class : DXGraphicsStateGuardian +// Description : A GraphicsStateGuardian specialized for rendering +// into DX. There should be no DX calls +// outside of this object. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDADX DXGraphicsStateGuardian : public GraphicsStateGuardian { +public: + DXGraphicsStateGuardian(GraphicsWindow *win); + ~DXGraphicsStateGuardian(); + + virtual void reset(); + + virtual void clear(const RenderBuffer &buffer); + virtual void clear(const RenderBuffer &buffer, const DisplayRegion* region); + + virtual void prepare_display_region(); + + virtual void render_frame(const AllAttributesWrapper &initial_state); + virtual void render_scene(Node *root, const ProjectionNode *projnode, + const AllAttributesWrapper &initial_state); + virtual void render_subgraph(RenderTraverser *traverser, + Node *subgraph, const ProjectionNode *projnode, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans); + virtual void render_subgraph(RenderTraverser *traverser, + Node *subgraph, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans); + + virtual void draw_point(const GeomPoint *geom); + virtual void draw_line(const GeomLine *geom); + virtual void draw_linestrip(const GeomLinestrip *geom); + virtual void draw_sprite(const GeomSprite *geom); + virtual void draw_polygon(const GeomPolygon *geom); + virtual void draw_quad(const GeomQuad *geom); + virtual void draw_tri(const GeomTri *geom); + virtual void draw_tristrip(const GeomTristrip *geom); + virtual void draw_trifan(const GeomTrifan *geom); + virtual void draw_sphere(const GeomSphere *geom); + + virtual TextureContext *prepare_texture(Texture *tex); + virtual void apply_texture(TextureContext *tc); + virtual void release_texture(TextureContext *tc); + + virtual void copy_texture(TextureContext *tc, const DisplayRegion *dr); + virtual void copy_texture(TextureContext *tc, const DisplayRegion *dr, + const RenderBuffer &rb); + virtual void draw_texture(TextureContext *tc, const DisplayRegion *dr); + virtual void draw_texture(TextureContext *tc, const DisplayRegion *dr, + const RenderBuffer &rb); + + virtual void texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb); + virtual void texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb, + const DisplayRegion *dr); + + virtual void copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr); + virtual void copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const RenderBuffer &rb); + virtual void draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const NodeAttributes& na=NodeAttributes()); + virtual void draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const RenderBuffer &rb, + const NodeAttributes& na=NodeAttributes()); + + virtual void apply_material(Material* material); + virtual void apply_fog(Fog *fog); + + virtual void apply_light(PointLight* light); + virtual void apply_light(DirectionalLight* light); + virtual void apply_light(Spotlight* light); + virtual void apply_light(AmbientLight* light); + + virtual void issue_transform(const TransformAttribute *attrib); + virtual void issue_tex_matrix(const TexMatrixAttribute *attrib); + virtual void issue_color(const ColorAttribute *attrib); + virtual void issue_color_transform(const ColorMatrixAttribute *); + virtual void issue_alpha_transform(const AlphaTransformAttribute *); + virtual void issue_texture(const TextureAttribute *attrib); + virtual void issue_light(const LightAttribute *attrib); + virtual void issue_material(const MaterialAttribute *attrib); + virtual void issue_render_mode(const RenderModeAttribute *attrib); + virtual void issue_color_blend(const ColorBlendAttribute *attrib); + virtual void issue_texture_apply(const TextureApplyAttribute *attrib); + virtual void issue_color_mask(const ColorMaskAttribute *attrib); + virtual void issue_depth_test(const DepthTestAttribute *attrib); + virtual void issue_depth_write(const DepthWriteAttribute *attrib); + virtual void issue_tex_gen(const TexGenAttribute *attrib); + virtual void issue_cull_face(const CullFaceAttribute *attrib); + virtual void issue_stencil(const StencilAttribute *attrib); + virtual void issue_clip_plane(const ClipPlaneAttribute *attrib); + virtual void issue_transparency(const TransparencyAttribute *attrib); + virtual void issue_fog(const FogAttribute *attrib); + virtual void issue_linesmooth(const LinesmoothAttribute *attrib); + + virtual bool wants_normals(void) const; + virtual bool wants_texcoords(void) const; + virtual bool wants_colors(void) const; + + virtual void begin_decal(GeomNode *base_geom); + virtual void end_decal(GeomNode *base_geom); + + virtual float compute_distance_to(const LPoint3f &point) const; + + void reset_ambient(); + +protected: + void free_pointers(); + virtual PT(SavedFrameBuffer) save_frame_buffer(const RenderBuffer &buffer, + CPT(DisplayRegion) dr); + virtual void restore_frame_buffer(SavedFrameBuffer *frame_buffer); + + INLINE void activate(); + + void set_draw_buffer(const RenderBuffer &rb); + void set_read_buffer(const RenderBuffer &rb); + + void bind_texture(TextureContext *tc); + void specify_texture(Texture *tex); + void apply_texture_immediate(DXTextureContext *tc); + + // for storage of the flexible vertex format + void *_fvf_buf; + void *_sav_fvf; + INLINE void add_to_FVF(void *data, size_t bytes) ; + + bool _dx_ready; + LPDIRECTDRAWSURFACE7 _back; + LPDIRECTDRAWSURFACE7 _zbuf; + LPDIRECT3D7 _d3d; + LPDIRECT3DDEVICE7 _d3dDevice; + LPDIRECTDRAWSURFACE7 _pri; + LPDIRECTDRAW7 _pDD; + RECT _view_rect; + RECT clip_rect; + HDC _hdc; + int _cNumTexPixFmts; + LPDDPIXELFORMAT _pTexPixFmts; + DXTextureContext *_pCurTexContext; + + bool _color_transform_enabled; + bool _alpha_transform_enabled; + + void set_clipper(RECT cliprect); + + INLINE void set_pack_alignment(int alignment); + INLINE void set_unpack_alignment(int alignment); + + INLINE void enable_multisample_alpha_one(bool val); + INLINE void enable_multisample_alpha_mask(bool val); + INLINE void enable_multisample(bool val); + INLINE void enable_color_material(bool val); + INLINE void enable_clip_plane(int clip_plane, bool val); + INLINE void enable_fog(bool val); + +#ifdef WBD_GL_MODE + void print_gfx_visual(); + INLINE void call_glClearColor(GLclampf red, GLclampf green, GLclampf blue, + GLclampf alpha); + INLINE void call_glClearDepth(GLclampd depth); + INLINE void call_glClearStencil(GLint s); + INLINE void call_glClearAccum(GLclampf red, GLclampf green, GLclampf blue, + GLclampf alpha); + INLINE void call_glDrawBuffer(GLenum mode); + INLINE void call_glReadBuffer(GLenum mode); + INLINE void call_glShadeModel(GLenum mode); + INLINE void call_glCullFace(GLenum mode); + INLINE void call_glViewport(GLint x, GLint y, GLsizei width, GLsizei height); + INLINE void call_glLightModelAmbient(const Colorf& color); + INLINE void call_glLightModelLocal(GLboolean local); + INLINE void call_glLightModelTwoSide(GLboolean twoside); + INLINE void call_glMaterialAmbient(bool twoside, const Colorf& color); + INLINE void call_glMaterialDiffuse(bool twoside, const Colorf& color); + INLINE void call_glMaterialAmbientDiffuse(bool twoside, const Colorf& color); + INLINE void call_glMaterialSpecular(bool twoside, const Colorf& color); + INLINE void call_glMaterialShininess(bool twoside, float shininess); + INLINE void call_glMaterialEmission(bool twoside, const Colorf& color); + INLINE void call_glStencilFunc(GLenum func); + INLINE void call_glStencilOp(GLenum op); + INLINE void call_glClipPlane(GLenum plane, const double equation[4]); + INLINE void call_glLineWidth(GLfloat width); + INLINE void call_glPointSize(GLfloat size); + INLINE void call_glDepthMask(GLboolean mask); + INLINE void call_glFogMode(GLint mode); + INLINE void call_glFogStart(GLfloat start); + INLINE void call_glFogEnd(GLfloat end); + INLINE void call_glFogDensity(GLfloat density); + INLINE void call_glFogColor(const Colorf &color); + INLINE void call_glPolygonMode(GLenum mode); + + INLINE GLenum get_light_id(int index) const; + INLINE GLenum get_clip_plane_id(int index) const; + + GLenum get_image_type(PixelBuffer::Type type); + GLenum get_external_image_format(PixelBuffer::Format format); + GLenum get_internal_image_format(PixelBuffer::Format format); + GLint get_texture_apply_mode_type( TextureApplyProperty::Mode am ) const; + + GLenum _draw_buffer_mode; + GLenum _read_buffer_mode; + GLenum _shade_model_mode; + GLboolean _lmodel_local; + GLboolean _lmodel_twoside; + GLenum _polygon_mode; + GLenum _stencil_func; + GLenum _stencil_op; + + +#else +/* INLINE void enable_multisample_alpha_one(bool val); + INLINE void enable_multisample_alpha_mask(bool val); + INLINE void enable_multisample(bool val, LPDIRECT3DDEVICE7 d3dDevice); +*/ + INLINE void enable_alpha_test(bool val); + INLINE void enable_line_smooth(bool val); + INLINE void enable_blend(bool val); + INLINE void enable_point_smooth(bool val); + INLINE void enable_texturing(bool val); + INLINE void call_dxLightModelAmbient(const Colorf& color); + INLINE void call_dxAlphaFunc(D3DCMPFUNC func, DWORD ref); + INLINE void call_dxBlendFunc(D3DBLEND sfunc, D3DBLEND dfunc); + INLINE void enable_lighting(bool val); + INLINE void enable_dither(bool val); + INLINE void enable_stencil_test(bool val); + bool enable_light(int light, bool val); + + D3DTEXTUREADDRESS get_texture_wrap_mode(Texture::WrapMode wm); + D3DTEXTUREMAGFILTER get_texture_filter_type(Texture::FilterType ft); + D3DCMPFUNC get_depth_func_type(DepthTestProperty::Mode m) const; + D3DCMPFUNC get_stencil_func_type(StencilProperty::Mode m) const; + D3DSTENCILOP get_stencil_action_type(StencilProperty::Action a) const; + D3DFOGMODE get_fog_mode_type(Fog::Mode m) const; + + void draw_prim_inner_loop(int loops, const Geom *geom); + void draw_prim_inner_loop2(int loops, const Geom *geom, short& per); + size_t draw_prim_setup(const Geom *geom) ; + void draw_multitri(const Geom *geom, D3DPRIMITIVETYPE tri_id); + + // for drawing primitives + Colorf p_color; + Normalf p_normal; + Vertexf p_vertex; + TexCoordf p_texcoord; + D3DCOLOR p_colr; + + int p_flags; + short perPrim; + short perVertex; + short perComp; + + // iterators for primitives + Geom::VertexIterator vi; + Geom::NormalIterator ni; + Geom::TexCoordIterator ti; + Geom::ColorIterator ci; + + +#endif // WBD_GL_MODE + + float _clear_color_red, _clear_color_green, _clear_color_blue,_clear_color_alpha; + double _clear_depth; + int _clear_stencil; + float _clear_accum_red, _clear_accum_green, _clear_accum_blue,_clear_accum_alpha; + int _scissor_x; + int _scissor_y; + int _scissor_width; + int _scissor_height; + int _viewport_x; + int _viewport_y; + int _viewport_width; + int _viewport_height; + Colorf _lmodel_ambient; + float _material_ambient; + float _material_diffuse; + float _material_specular; + float _material_shininess; + float _material_emission; + float _line_width; + float _point_size; + bool _depth_mask; + int _fog_mode; + float _fog_start; + float _fog_end; + float _fog_density; + float _fog_color; + float _alpha_func_ref; + D3DCMPFUNC _alpha_func; + D3DBLEND _blend_source_func; + D3DBLEND _blend_dest_func; + + int _pack_alignment; + int _unpack_alignment; + + bool _issued_color_enabled; // WBD ADDED + D3DCOLOR _issued_color; // WBD ADDED + + bool _multisample_enabled; + bool _line_smooth_enabled; + bool _point_smooth_enabled; + bool* _light_enabled; // bool[_max_lights] + bool _color_material_enabled; + bool _lighting_enabled; + bool _texturing_enabled; + bool _dither_enabled; + bool _stencil_test_enabled; + bool* _clip_plane_enabled; // bool[_max_clip_planes] + bool _multisample_alpha_one_enabled; + bool _multisample_alpha_mask_enabled; + bool _blend_enabled; + bool _depth_test_enabled; + bool _fog_enabled; + bool _alpha_test_enabled; + int _decal_level; + + PTA(Light*) _available_light_ids; + int _max_lights; + bool* _cur_light_enabled; + int _cur_light_id; + Colorf _cur_ambient_light; + LMatrix4f _current_projection_mat; + int _projection_mat_stack_count; + + PTA(PlaneNode*) _available_clip_plane_ids; + int _max_clip_planes; + bool* _cur_clip_plane_enabled; + int _cur_clip_plane_id; + + CPT(DisplayRegion) _actual_display_region; + + LMatrix4f _current_color_mat; + float _current_alpha_offset; + float _current_alpha_scale; + + int _pass_number; + +public: + static GraphicsStateGuardian* + make_DXGraphicsStateGuardian(const FactoryParams ¶ms); + + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + + LPDIRECT3DDEVICE7 GetD3DDevice() { return _d3dDevice; } + LPDIRECTDRAW7 GetDDInterface() { return _pDD; } + LPDIRECTDRAWSURFACE7 GetBackBuffer() { return _back; } + LPDIRECTDRAWSURFACE7 GetZBuffer() { return _zbuf; } + INLINE void Set_HDC(HDC hdc) { _hdc = hdc; } + void adjust_view_rect(int x, int y); + INLINE void SetDXStatus(bool stat) { _dx_ready = stat; } + + void dx_cleanup(); + void dx_setup_after_resize(RECT viewrect,HWND mwindow) ; + void show_frame(); + void init_dx( LPDIRECTDRAW7 context, + LPDIRECTDRAWSURFACE7 pri, + LPDIRECTDRAWSURFACE7 back, + LPDIRECTDRAWSURFACE7 zbuf, + LPDIRECT3D7 d3d, + LPDIRECT3DDEVICE7 d3dDevice, + RECT viewrect); + friend HRESULT CALLBACK DXGraphicsStateGuardian::EnumTexFmtsCallback( LPDDPIXELFORMAT pddpf, VOID* param ); + +private: + static TypeHandle _type_handle; +}; + +#include "dxGraphicsStateGuardian.I" + +#endif + diff --git a/panda/src/dxgsg/dxSavedFrameBuffer.I b/panda/src/dxgsg/dxSavedFrameBuffer.I new file mode 100644 index 0000000000..631eba5ec4 --- /dev/null +++ b/panda/src/dxgsg/dxSavedFrameBuffer.I @@ -0,0 +1,26 @@ +// Filename: dxSavedFrameBuffer.I +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: DXSavedFrameBuffer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DXSavedFrameBuffer:: +DXSavedFrameBuffer(const RenderBuffer &buffer, CPT(DisplayRegion) dr) : + SavedFrameBuffer(buffer, dr) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DXSavedFrameBuffer::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DXSavedFrameBuffer:: +~DXSavedFrameBuffer() { +} + diff --git a/panda/src/dxgsg/dxSavedFrameBuffer.cxx b/panda/src/dxgsg/dxSavedFrameBuffer.cxx new file mode 100644 index 0000000000..ee9bcafba4 --- /dev/null +++ b/panda/src/dxgsg/dxSavedFrameBuffer.cxx @@ -0,0 +1,8 @@ +// Filename: dxSavedFrameBuffer.cxx +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "dxSavedFrameBuffer.h" + +TypeHandle DXSavedFrameBuffer::_type_handle; diff --git a/panda/src/dxgsg/dxSavedFrameBuffer.h b/panda/src/dxgsg/dxSavedFrameBuffer.h new file mode 100644 index 0000000000..b69ab29f63 --- /dev/null +++ b/panda/src/dxgsg/dxSavedFrameBuffer.h @@ -0,0 +1,51 @@ +// Filename: dxSavedFrameBuffer.h +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DXSAVEDFRAMEBUFFER_H +#define DXSAVEDFRAMEBUFFER_H + +#include + +#include +#include +#include +#include + + +//////////////////////////////////////////////////////////////////// +// Class : DXSavedFrameBuffer +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDADX DXSavedFrameBuffer : public SavedFrameBuffer { +public: + INLINE DXSavedFrameBuffer(const RenderBuffer &buffer, + CPT(DisplayRegion) dr); + INLINE ~DXSavedFrameBuffer(); + + PT(Texture) _back_rgba; + PT(PixelBuffer) _depth; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + SavedFrameBuffer::init_type(); + register_type(_type_handle, "DXSavedFrameBuffer", + SavedFrameBuffer::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "dxSavedFrameBuffer.I" + +#endif + diff --git a/panda/src/dxgsg/dxTextureContext.I b/panda/src/dxgsg/dxTextureContext.I new file mode 100644 index 0000000000..f7715ba736 --- /dev/null +++ b/panda/src/dxgsg/dxTextureContext.I @@ -0,0 +1,18 @@ +// Filename: dxTextureContext.I +// Created by: drose (07Oct99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: DXTextureContext::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DXTextureContext:: +DXTextureContext(Texture *tex) : + TextureContext(tex) +{ + _index = 0; + _priority = 0.5; // For keeping resident in texture memory +} diff --git a/panda/src/dxgsg/dxTextureContext.cxx b/panda/src/dxgsg/dxTextureContext.cxx new file mode 100644 index 0000000000..8f71b870ac --- /dev/null +++ b/panda/src/dxgsg/dxTextureContext.cxx @@ -0,0 +1,868 @@ +// Filename: dxTextureContext.cxx +// Created by: drose (07Oct99) +// +//////////////////////////////////////////////////////////////////// +// Copyright (C) 1999-2000 Walt Disney Imagineering, Inc. +// +// These coded instructions, statements, data structures and +// computer programs contain unpublished proprietary information of +// Walt Disney Imagineering and are protected by Federal copyright +// law. They may not be disclosed to third parties or copied or +// duplicated in any form, in whole or in part, without the prior +// written consent of Walt Disney Imagineering Inc. +//////////////////////////////////////////////////////////////////// + +#include "dxTextureContext.h" +#include "config_dxgsg.h" +#include "dxGraphicsStateGuardian.h" +#include + +//#define FORCE_USE_OF_16BIT_TEXFMTS + +TypeHandle DXTextureContext::_type_handle; + +#ifdef _DEBUG +static void DebugPrintPixFmt(DDPIXELFORMAT* pddpf) { + static int iddpfnum=0; + ostream *dbgout = &dxgsg_cat.debug(); + + *dbgout << "DDPF[" << iddpfnum << "]: RGBBitCount:" << pddpf->dwRGBBitCount + << " Flags:" << (void *)pddpf->dwFlags ; + + if(pddpf->dwFlags & DDPF_RGB) { + *dbgout << " RGBmask:" << (void *) (pddpf->dwRBitMask | pddpf->dwGBitMask | pddpf->dwBBitMask); + } + + if(pddpf->dwFlags & DDPF_ALPHAPIXELS) { + *dbgout << " Amask:" << (void *) pddpf->dwRGBAlphaBitMask; + } + + if(pddpf->dwFlags & DDPF_LUMINANCE) { + *dbgout << " Lummask:" << (void *) pddpf->dwLuminanceBitMask; + } + + *dbgout << endl; + + iddpfnum++; +} +#endif + +/* for reference +enum Format { + F_color_index, + F_stencil_index, + F_depth_component, + F_red, + F_green, + F_blue, + F_alpha, + F_rgb, + F_rgb5, // specifically, 5 bits per R,G,B channel + F_rgb8, // 8 bits per R,G,B channel + F_rgb12, // 12 bits per R,G,B channel + F_rgb332, // 3 bits per R & G, 2 bits for B + F_rgba, + F_rgba4, // 4 bits per R,G,B,A channel + F_rgba8, // 8 bits per R,G,B,A channel + F_rgba12, // 12 bits per R,G,B,A channel + F_luminance, + F_luminance_alpha, + }; + enum Type { + T_unsigned_byte, // 1 byte per channel + T_unsigned_short, // 2 byte per channel + T_unsigned_byte_332, // RGB in 1 byte + T_float, // 1 channel stored as float + }; +*/ + +//////////////////////////////////////////////////////////////////// +// Function: DXTextureContext::get_bits_per_pixel +// Access: Protected +// Description: Maps from the PixelBuffer's Format symbols +// to bpp. returns # of alpha bits +// Note: PixelBuffer's format indicates REQUESTED final format, +// not the stored format, which is indicated by pixelbuffer type +//////////////////////////////////////////////////////////////////// +unsigned int DXTextureContext:: +get_bits_per_pixel(PixelBuffer::Format format, int *alphbits) { + *alphbits = 0; // assume no alpha bits + switch (format) { + case PixelBuffer::F_alpha: + *alphbits = 8; + return 8; + case PixelBuffer::F_color_index: + case PixelBuffer::F_red: + case PixelBuffer::F_green: + case PixelBuffer::F_blue: + case PixelBuffer::F_rgb332: + case PixelBuffer::F_luminance: + return 8; + case PixelBuffer::F_rgba4: + *alphbits = 4; + return 16; + case PixelBuffer::F_rgba5: + *alphbits = 1; + return 16; + case PixelBuffer::F_depth_component: + case PixelBuffer::F_rgb5: + return 16; + case PixelBuffer::F_rgb8: + case PixelBuffer::F_rgb: + return 24; + case PixelBuffer::F_luminance_alpha: // must expand into DIB buffer + case PixelBuffer::F_rgba8: + case PixelBuffer::F_rgba: + *alphbits = 8; + return 32; + case PixelBuffer::F_rgb12: + return 36; + case PixelBuffer::F_rgba12: + *alphbits = 12; + return 48; + } +return 8; +} + +//----------------------------------------------------------------------------- +// Name: CreateTextureFromBitmap() +// Desc: Use a bitmap to create a texture for the specified device. This code +// gets the attributes of the texture from the bitmap, creates the +// texture, and then copies the bitmap into the texture. +//----------------------------------------------------------------------------- +LPDIRECTDRAWSURFACE7 DXTextureContext:: +CreateTexture( HDC hdc, LPDIRECT3DDEVICE7 pd3dDevice, int cNumTexPixFmts, LPDDPIXELFORMAT pTexPixFmts) +{ + HRESULT hr; + + PixelBuffer *pbuf = _texture->_pbuffer; + BOOL bSizeExpanded = FALSE; + int cNumAlphaBits; // number of alpha bits in texture pixfmt + + DDPIXELFORMAT *pDesiredPixFmt; + LPDIRECTDRAWSURFACE7 pddsRender; + LPDIRECTDRAW7 pDD = NULL; + + DDPIXELFORMAT TexFmtsArr[MAX_DX_TEXPIXFMTS]; + + typedef enum {None,Conv32to32,Conv32to32_NoAlpha,Conv32to24,Conv32to16_0555, + Conv32to16_1555,Conv32to16_0565,Conv32to16_4444,Conv24to32,Conv24to24, + Conv24to16_0555,Conv24to16_0565 + } ConversionType; + + ConversionType ConvNeeded; + +#ifdef _DEBUG + char *ConvNameStrs[] = {"None","Conv32to32","Conv32to32_NoAlpha","Conv32to24","Conv32to16_0555", + "Conv32to16_1555","Conv32to16_0565","Conv32to16_4444","Conv24to32","Conv24to24", + "Conv24to16_0555","Conv24to16_0565"}; +#endif + + // make local copy of array so I can muck with it during searches for this texture fmt + memcpy(TexFmtsArr,pTexPixFmts,cNumTexPixFmts*sizeof(DDPIXELFORMAT)); + pTexPixFmts=TexFmtsArr; + + // bpp indicates requested fmt, not pixbuf fmt + DWORD bpp = get_bits_per_pixel(pbuf->get_format(), &cNumAlphaBits); + PixelBuffer::Type pixbuf_type = pbuf->get_image_type(); + DWORD cNumColorChannels = pbuf->get_num_components(); + + if((cNumColorChannels != 3) && (cNumColorChannels != 4)) { + dxgsg_cat.error() << "CreateTexture failed, havent handled pixbufs with < 3 channels yet! \n"; + return NULL; + } + + if((cNumColorChannels == 3) && (cNumAlphaBits>0)) { + dxgsg_cat.error() << "CreateTexture ignoring request for texformat w/alpha channel since original texture is RGB only\n"; + cNumAlphaBits=0; + } + + if((pixbuf_type != PixelBuffer::T_unsigned_byte) || (pbuf->get_component_width()!=1)) { + dxgsg_cat.error() << "CreateTexture failed, havent handled non 8-bit channel pixelbuffer types yet! \n"; + return NULL; + } + + DWORD dwOrigWidth = (DWORD)pbuf->get_xsize(); + DWORD dwOrigHeight = (DWORD)pbuf->get_ysize(); + + if (pbuf->get_format() == PixelBuffer::F_luminance_alpha) { // must expand + + dxgsg_cat.error() << "havent handled luminance in CreateTexture yet!! \n"; + return NULL; + + /* + /// old code I need to rewrite and re-test for this case + + #define DIBLINEWIDTH(x) ((((x)+3)>>2)<<2) + + DWORD lineBytes = DIBLINEWIDTH(dwWidth * (bpp >> 3)); + DWORD imageBytes = lineBytes * dwHeight; + int bytes_per_pixel = bpp >> 3; // should be 4 + DWORD padBytes = lineBytes - (dwWidth * bytes_per_pixel); // should be 0; + unsigned char *src = (unsigned char *)pbuf->_image; + unsigned char *dst = (unsigned char *)bits; + int x,y; + for (y = dwHeight; --y >= 0;) + { + for (x = dwWidth; --x >= 0;) + { + *dst++ = *src; + *dst++ = *src; + *dst++ = *src++; + *dst++ = *src++; + } + dst += padBytes; + } + */ + } + + // Get the device caps so we can check if the device has any constraints + // when using textures + D3DDEVICEDESC7 ddDesc; + if( FAILED( pd3dDevice->GetCaps( &ddDesc ) ) ) { + goto error_exit; + } + + // Setup the new surface desc for the texture. Note how we are using the + // texture manage attribute, so Direct3D does alot of dirty work for us + DDSURFACEDESC2 ddsd; + ZeroMemory( &ddsd, sizeof(DDSURFACEDESC2) ); + ddsd.dwSize = sizeof(DDSURFACEDESC2); + ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH + | DDSD_PIXELFORMAT | DDSD_TEXTURESTAGE; + + ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); + + // setup ddpf to match against avail fmts + + ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; // assumes non-luminance/bump texture + ddsd.ddpfPixelFormat.dwRGBBitCount = bpp; + + if (cNumAlphaBits) { + if (bpp == 8 && cNumAlphaBits == 8) // handle special case: Alpha only buffer + { + ddsd.dwFlags |= DDPF_ALPHA; + ddsd.dwAlphaBitDepth = 8; + ddsd.ddpfPixelFormat.dwAlphaBitDepth = 8; + ddsd.ddpfPixelFormat.dwFlags = DDPF_ALPHA; + ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff; + } + else { + ddsd.ddpfPixelFormat.dwFlags |= DDPF_ALPHAPIXELS; + if (cNumAlphaBits == 8) + ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff000000; + else if (cNumAlphaBits == 4) + ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0xf000; + } + } + + ddsd.dwWidth = dwOrigWidth; + ddsd.dwHeight = dwOrigHeight; + +#define ISPOW2(X) (((X) & ((X)-1))==0) + + if(!ISPOW2(ddsd.dwWidth) || !ISPOW2(ddsd.dwHeight)) { + dxgsg_cat.error() << "ERROR: Image size is not a power of 2 for " << _tex->get_name() << "!!!!! \n"; +#define EXPAND_TEXSIZE_TO_POW2 +#ifndef EXPAND_TEXSIZE_TO_POW2 +#ifdef _DEBUG + exit(1); // want to catch badtexsize errors +#else + goto error_exit; +#endif +#else + // Expand width and height, if the driver requires it + if( ddDesc.dpcTriCaps.dwTextureCaps & D3DPTEXTURECAPS_POW2 ) { + // round up to next pow of 2 + for( ddsd.dwWidth=1; dwOrigWidth>ddsd.dwWidth; ddsd.dwWidth<<=1 ); + for( ddsd.dwHeight=1; dwOrigHeight>ddsd.dwHeight; ddsd.dwHeight<<=1 ); + bSizeExpanded = TRUE; + dxgsg_cat.debug() << "Expanding texture to power of 2 size (extra space will be black)\n"; + } +#endif + } + + if( ddDesc.dpcTriCaps.dwTextureCaps & D3DPTEXTURECAPS_SQUAREONLY ) { + // expand smaller dimension to equal the larger + if( ddsd.dwWidth > ddsd.dwHeight ) ddsd.dwHeight = ddsd.dwWidth; + else ddsd.dwWidth = ddsd.dwHeight; + bSizeExpanded = (ddsd.dwWidth!=ddsd.dwHeight); + if(bSizeExpanded) + dxgsg_cat.debug() << "Forcing texture to square size (extra space will be black)\n"; + } + + int i; + +#ifdef _DEBUG + { static BOOL bPrinted=FALSE; + if(!bPrinted) { + for(i=0;idwFlags & (DDPF_LUMINANCE|DDPF_BUMPLUMINANCE|DDPF_BUMPDUDV) ) || + ( pCurPixFmt->dwFourCC != 0 ) || + ((cNumAlphaBits==0) && (pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS))) { + + // Make sure to skip any FourCC formats, bump/luminance formats + // they are not handled by this code yet + + // note: I'm screening out alpha if no alpha requested, so + // search will fails if 32-rgba avail, but not 32-bit rgb + // I could recode for that case too, hopefully this case will not pop up + + pCurPixFmt->dwRGBBitCount+=1; // incr so it wont be an exact match anymore + } + } + + assert((cNumColorChannels==4)==(cNumAlphaBits>0)); + + // handle each bitdepth separately + + switch(bpp) { // bpp is REQUESTED bpp + + case 32: + +#ifndef FORCE_USE_OF_16BIT_TEXFMTS + for(i=0,pCurPixFmt=pTexPixFmts;idwRGBBitCount==32) && + (((pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS)!=0)==(cNumAlphaBits!=0))) { + // we should have a match + assert(pCurPixFmt->dwRGBAlphaBitMask==0xFF000000); + ConvNeeded=((cNumColorChannels==3) ? Conv24to32 : Conv32to32); + goto found_matching_format; + break; + } + } +#endif + + if(cNumAlphaBits>0) { + // no 32-bit fmt, look for 16 bit w/alpha (1-15) + + for(i=0,pCurPixFmt=&pTexPixFmts[cNumTexPixFmts-1];i16 conversion + // this should be true on most cards. + if((pCurPixFmt->dwRGBBitCount==16) && (pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS) + && (pCurPixFmt->dwRGBAlphaBitMask==0x8000)) { + ConvNeeded=Conv32to16_1555; + goto found_matching_format; + } +#if 0 + // 32 bit RGBA was requested, but only 16 bit alpha fmts are avail + // by default, convert to 15-1, which is the most common fmt + + // 4-4-4-4 would be useful if we know pixelbuf contains non-binary alpha, + // but hard to infer that from RGBA request, and 15-1 is the better general choice + + if((pCurPixFmt->dwRGBBitCount==16) && (pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS) + && (pCurPixFmt->dwRGBAlphaBitMask==0xf000)) { + ConvNeeded=Conv32to16_4444; + goto found_matching_format; + } +#endif + } + + // at this point, bail. dont worry about converting to non-alpha formats yet, + // I think this will be a very rare case + szErrorMsg = "CreateTexture failed: couldn't find compatible Tex DDPIXELFORMAT! no available 16 or 32-bit alpha formats!\n"; + } + + break; + + case 24: + + assert(cNumAlphaBits==0); // dont know how to handle non-zero alpha for 24bit total + +#ifndef FORCE_USE_OF_16BIT_TEXFMTS + for(i=0,pCurPixFmt=pTexPixFmts;idwFlags & DDPF_RGB)&&(pCurPixFmt->dwRGBBitCount==24)) { + ConvNeeded=((cNumColorChannels==3) ? Conv24to24 : Conv32to24); + goto found_matching_format; + } + } + + // no 24-bit fmt. look for 32 bit fmt (note: this is memory-hogging choice + // instead I could look for memory-conserving 16-bit fmt). + // check mask to ensure ARGB, not RGBA (which I am not handling here) + + for(i=0,pCurPixFmt=pTexPixFmts;idwRGBBitCount==32) && (pCurPixFmt->dwFlags & DDPF_RGB) + && ((pCurPixFmt->dwRBitMask|pCurPixFmt->dwGBitMask|pCurPixFmt->dwBBitMask)==0xFFFFFF) + ) { + // I'm allowing alpha formats here. will set alpha to opaque + ConvNeeded=((cNumColorChannels==3) ? Conv24to32 : Conv32to32_NoAlpha); + goto found_matching_format; + } + } +#endif + + // no 24-bit or 32 fmt. look for 16 bit fmt + for(i=0,pCurPixFmt=&pTexPixFmts[cNumTexPixFmts-1];i16 conversion, should be true on most cards. + if((pCurPixFmt->dwFlags & DDPF_RGB) && (pCurPixFmt->dwRGBBitCount==16) + && !(pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS)) { // no alpha fmts + // if numchan==4,this means user has requested we throw away the input alpha channel + if(pCurPixFmt->dwGBitMask==0x7E0) { + // assumes GBitMask is the biggest one, if we have 16 bit 3-channel + ConvNeeded=((cNumColorChannels==3) ? Conv24to16_0565 : Conv32to16_0565); + } else { + assert((pCurPixFmt->dwRBitMask|pCurPixFmt->dwGBitMask|pCurPixFmt->dwBBitMask)==0x7FFF); + ConvNeeded=((cNumColorChannels==3) ? Conv24to16_0555 : Conv32to16_0555); + } + goto found_matching_format; + } + } + + // at this point, bail. + break; + + case 16: + // look for compatible 16bit fmts, if none then give up + // (dont worry about other bitdepths for 16 bit) + + for(i=0,pCurPixFmt=pTexPixFmts;idwRGBBitCount==16)&&(pCurPixFmt->dwFlags & DDPF_RGB)) { + switch(cNumAlphaBits) { + case 0: + if(!(pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS)) { + // if numchan==4,this means user has requested we throw away the input alpha channel + if(pCurPixFmt->dwGBitMask==0x7E0) { + // assumes GBitMask is the biggest one, if we have 16 bit 3-channel + ConvNeeded=((cNumColorChannels==3) ? Conv24to16_0565 : Conv32to16_0565); + } else { + assert((pCurPixFmt->dwRBitMask|pCurPixFmt->dwGBitMask|pCurPixFmt->dwBBitMask)==0x7FFF); + ConvNeeded=((cNumColorChannels==3) ? Conv24to16_0555 : Conv32to16_0555); + } + goto found_matching_format; + } + break; + case 1: + if((pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS)&& + (pCurPixFmt->dwRGBAlphaBitMask==0x8000)) { + ConvNeeded=Conv32to16_1555; + goto found_matching_format; + } + break; + case 4: + if((pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS)&& + (pCurPixFmt->dwRGBAlphaBitMask==0xF000)) { + ConvNeeded=Conv32to16_4444; + goto found_matching_format; + } + break; + } + } + } + + break; + + case 8: + + default: + szErrorMsg = "CreateTexture failed: unhandled pixel bitdepth in DX loader"; + } + + // if we've gotten here, haven't found a match + + dxgsg_cat.error() << szErrorMsg << "; requested tex bitdepth: " << bpp << "\n"; + goto error_exit; + + /////////////////////////////////////////////////////////// + + found_matching_format: + + ddsd.ddpfPixelFormat = *pCurPixFmt; + + // Get the device's render target, so we can then use the render target to + // get a ptr to a DDraw object. We need the DirectDraw interface for + // creating surfaces. + + pd3dDevice->GetRenderTarget( &pddsRender ); + pddsRender->GetDDInterface( (VOID**)&pDD ); + pddsRender->Release(); + + ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE; + + ddsd.ddsCaps.dwCaps2 = DDSCAPS2_TEXTUREMANAGE // Turn on texture management + | DDSCAPS2_HINTSTATIC; // BUGBUG: is this ok for ALL textures? + + PRINTVIDMEM(pDD,&ddsd.ddsCaps,"texture surf (includes AGP mem)"); + + // Create a new surface for the texture + if( FAILED( hr = pDD->CreateSurface( &ddsd, &_surface, NULL ) ) ) + { + dxgsg_cat.error() << "CreateTexture failed: pDD->CreateSurface() failed! hr = " << ConvD3DErrorToString(hr) << "\n"; + goto error_exit; + } + + if(bSizeExpanded) { + // init to black + DDBLTFX bltfx; + ZeroMemory(&bltfx,sizeof(bltfx)); + bltfx.dwSize = sizeof(bltfx); + bltfx.dwFillColor=0; + if( FAILED( hr = _surface->Blt(NULL,NULL,NULL,DDBLT_COLORFILL | DDBLT_WAIT,&bltfx))) { + dxgsg_cat.error() << "CreateTexture failed: _surface->Blt() failed! hr = " << ConvD3DErrorToString(hr) << "\n"; + } + } + + // Create a new surface for the texture + if( FAILED( hr = _surface->Lock( NULL, &ddsd, DDLOCK_WAIT, NULL ))) { + dxgsg_cat.error() << "CreateTexture failed: _surface->Lock() failed! hr = " << ConvD3DErrorToString(hr) << "\n"; + goto error_exit; + } + + { + DWORD lPitch = ddsd.lPitch; + BYTE* pDDSurfBytes = (BYTE*)ddsd.lpSurface; + + BYTE r,g,b,a; + DWORD x,y,dwPixel; + +#ifdef _DEBUG + dxgsg_cat.debug() << "CreateTexture executing conversion " << ConvNameStrs[ConvNeeded] << "\n"; +#endif + + switch(ConvNeeded) { + case Conv32to32: + case Conv32to32_NoAlpha: { + + DWORD *pSrcWord = (DWORD *) pbuf->_image.p(); + DWORD *pDstWord; + DWORD dwAlphaMaskOff = (ConvNeeded==Conv32to32_NoAlpha) ? 0x00FFFFFF : 0xFFFFFFFF; + + for( y=0; y>24)); // unsigned >>, no need to & + b = (BYTE)((dwPixel>>16)&0x000000ff); + g = (BYTE)((dwPixel>> 8)&0x000000ff); + r = (BYTE)((dwPixel )&0x000000ff); + + *pDstWord = (a << 24) | (r << 16)| (g << 8) | b; +#else + // simpler: just swap r & b + b = (BYTE)((dwPixel>>16)&0x000000ff); + r = (BYTE)((dwPixel )&0x000000ff); + *pDstWord = ((dwPixel & 0xff00ff00) | (r<<16) | b) & dwAlphaMaskOff; +#endif + } + } + break; + } + + case Conv32to16_1555: + case Conv32to16_0555: { + DWORD abit,*pSrcWord = (DWORD *) pbuf->_image.p(); + WORD *pDstWord; + DWORD dwAlphaMaskOff = (ConvNeeded==Conv32to16_0555) ? 0x7FFF : 0xFFFF; + + assert(ddsd.ddpfPixelFormat.dwRBitMask==0x7C00); + // for some reason, bits are 'in-order' when converting to 16bit + + // just handle 1/15 alpha/rgb + for( y=0; y>16) & 0x00008000); // just copy high bit + + // just look at most-signf-bit for alpha. (alternately, could + // convert any non-zero alpha to full transparent) + + b = (BYTE)((dwPixel>>16) & 0x000000ff) >> 3; + g = (BYTE)((dwPixel>> 8) & 0x000000ff) >> 3; + r = (BYTE)((dwPixel ) & 0x000000ff) >> 3; + + // code truncates 8 bit values to 5 bit (or 1 for alpha) + + *pDstWord = (abit | (r << 10)| (g << 5) | b) & dwAlphaMaskOff; + } + } + break; + } + + case Conv32to16_0565: { + DWORD *pSrcWord = (DWORD *) pbuf->_image.p(); + WORD *pDstWord; + + assert(ddsd.ddpfPixelFormat.dwRBitMask==0xF800); + // for some reason, bits are 'in-order' when converting to 16bit + + // just handle 1/15 alpha/rgb + for( y=0; y>16) & 0x000000ff) >> 3; + g = (BYTE)((dwPixel>> 8) & 0x000000ff) >> 2; + r = (BYTE)((dwPixel ) & 0x000000ff) >> 3; + + // code truncates 8 bit values to 5 bit (or 1 for alpha) + + *pDstWord = ((r << 11)| (g << 5) | b); + } + } + break; + } + + + case Conv32to24: { + + DWORD *pSrcWord = (DWORD *) pbuf->_image.p(); + BYTE *pDstWord; + + for( y=0; y>16) & 0x000000ff); + g = (BYTE)((dwPixel>> 8) & 0x000000ff); + r = (BYTE)((dwPixel ) & 0x000000ff); + + *pDstWord = r; + *(pDstWord+1) = g; + *(pDstWord+2) = b; + } + } + break; + } + + + case Conv32to16_4444: { + DWORD *pSrcWord = (DWORD *) pbuf->_image.p(); + WORD *pDstWord; + + assert(cNumAlphaBits==4); + assert(ddsd.ddpfPixelFormat.dwRGBAlphaBitMask==0xf000); // assumes ARGB + assert(ddsd.ddpfPixelFormat.dwRBitMask==0x0f00); // assumes ARGB + + for( y=0; y>24)) >> 4; + b = (BYTE)((dwPixel>>16) & 0x000000ff) >> 4; + g = (BYTE)((dwPixel>> 8) & 0x000000ff) >> 4; + r = (BYTE)((dwPixel ) & 0x000000ff) >> 4; + + *pDstWord = (a << 12) | (r << 8)| (g << 4) | b; + } + } + break; + } + + + case Conv24to24: { + + BYTE *pSrcWord = (BYTE *) pbuf->_image.p(); + BYTE *pDstWord; + + for( y=0; y_image.p(); + WORD *pDstWord; + + assert(ddsd.ddpfPixelFormat.dwRBitMask==0x7C00); + // for some reason, bits are 'in-order' when converting to 16bit + + for( y=0; y> 3; + g = *(pSrcWord+1) >> 3; + b = *(pSrcWord+2) >> 3; + + // code truncates 8 bit values to 5 bit, leaves high bit as 0 + + *pDstWord = (r << 10)| (g << 5) | b; + } + } + break; + } + + case Conv24to16_0565: { + BYTE *pSrcWord = (BYTE *) pbuf->_image.p(); + WORD *pDstWord; + + assert(ddsd.ddpfPixelFormat.dwRBitMask==0xF800); + // for some reason, bits are 'in-order' when converting to 16bit + + for( y=0; y> 3; + g = *(pSrcWord+1) >> 2; + b = *(pSrcWord+2) >> 3; + + // code truncates 8 bit values to 5 bit, leaves high bit as 0 + + *pDstWord = (r << 11)| (g << 5) | b; + } + } + break; + } + + case Conv24to32: { + + BYTE *pSrcWord = (BYTE *) pbuf->_image.p(); + DWORD *pDstWord; + + for( y=0; yUnlock(NULL); + goto error_exit; + } + } + + _surface->Unlock(NULL); + + // Done with DDraw + pDD->Release(); + + // Return the newly created texture + return _surface; + + error_exit: + + if(pDD!=NULL) + pDD->Release(); + if(_surface!=NULL) { + _surface->Release(); + _surface = NULL; + } + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Name: DeleteTexture() +// Desc: Release the surface used to store the texture +//----------------------------------------------------------------------------- +void DXTextureContext:: +DeleteTexture( ) +{ + if (_surface) _surface->Release(); + _surface = NULL; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DXTextureContext::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DXTextureContext:: +DXTextureContext(Texture *tex) : + TextureContext(tex) +{ +#ifdef _DEBUG + dxgsg_cat.debug() << "Making DX texture for " << tex->get_name() << "\n"; +#endif + _surface = NULL; + _tex = tex; +} + +DXTextureContext:: +~DXTextureContext() +{ +#ifdef _DEBUG + dxgsg_cat.debug() << "Deleting DX texture for " << _tex->get_name() << "\n"; +#endif + DeleteTexture(); + TextureContext::~TextureContext(); + _tex = NULL; +} + + diff --git a/panda/src/dxgsg/dxTextureContext.h b/panda/src/dxgsg/dxTextureContext.h new file mode 100644 index 0000000000..11858f85bd --- /dev/null +++ b/panda/src/dxgsg/dxTextureContext.h @@ -0,0 +1,62 @@ +// Filename: dxTextureContext.h +// Created by: drose (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DXTEXTURECONTEXT_H +#define DXTEXTURECONTEXT_H + +#ifdef PENV_WIN32 +// Must include windows.h before gl.h on NT +#define WIN32_LEAN_AND_MEAN +#include + +#include +#undef WIN32_LEAN_AND_MEAN +#endif +#include +#include + +#include + +#define MAX_DX_TEXPIXFMTS 20 // should be enough for any card + +//////////////////////////////////////////////////////////////////// +// Class : DXTextureContext +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDADX DXTextureContext : public TextureContext { +public: + DXTextureContext(Texture *tex); + ~DXTextureContext(); + + LPDIRECTDRAWSURFACE7 _surface; + Texture *_tex; // ptr to parent, primarily for access to namestr + + LPDIRECTDRAWSURFACE7 CreateTexture( HDC hdc, LPDIRECT3DDEVICE7 pd3dDevice, int cNumTexPixFmts, LPDDPIXELFORMAT pTexPixFmts); + void DeleteTexture(); + +protected: + unsigned int get_bits_per_pixel(PixelBuffer::Format format, int *alphbits); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TextureContext::init_type(); + register_type(_type_handle, "DXTextureContext", + TextureContext::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + + +#endif + diff --git a/panda/src/effects/Sources.pp b/panda/src/effects/Sources.pp new file mode 100644 index 0000000000..ff9ab02a2a --- /dev/null +++ b/panda/src/effects/Sources.pp @@ -0,0 +1,17 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET effects + #define LOCAL_LIBS \ + display sgraph graph sgraphutil sgattrib gobj putil gsgbase linmath \ + mathutil switchnode + + #define SOURCES \ + config_effects.cxx config_effects.h lensFlareNode.I \ + lensFlareNode.cxx lensFlareNode.h + + #define INSTALL_HEADERS \ + config_effects.h lensFlareNode.I lensFlareNode.h + +#end lib_target + diff --git a/panda/src/effects/config_effects.cxx b/panda/src/effects/config_effects.cxx new file mode 100644 index 0000000000..d5b522b238 --- /dev/null +++ b/panda/src/effects/config_effects.cxx @@ -0,0 +1,19 @@ +// Filename: config_effects.cxx +// Created by: jason (18Jul00) +// + +#include "config_effects.h" +#include "lensFlareNode.h" + +#include + +Configure(config_effects); +NotifyCategoryDef(effects, ""); + +ConfigureFn(config_effects) { + LensFlareNode::init_type(); + + //Registration of writeable object's creation + //functions with BamReader's factory + LensFlareNode::register_with_read_factory(); +} diff --git a/panda/src/effects/config_effects.h b/panda/src/effects/config_effects.h new file mode 100644 index 0000000000..0621d1638e --- /dev/null +++ b/panda/src/effects/config_effects.h @@ -0,0 +1,14 @@ +// Filename: config_effects.h +// Created by: jason (18Jul00) +// + + +#ifndef CONFIG_EFFECTS_H +#define CONFIG_EFFECTS_H + +#include +#include + +NotifyCategoryDecl(effects, EXPCL_PANDA, EXPTP_PANDA); + +#endif /* CONFIG_EFFECTS_H */ diff --git a/panda/src/effects/lensFlareNode.I b/panda/src/effects/lensFlareNode.I new file mode 100644 index 0000000000..a5e440296d --- /dev/null +++ b/panda/src/effects/lensFlareNode.I @@ -0,0 +1,71 @@ +// Filename: lensFlareNode.I +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LensFlareNode:: +LensFlareNode(void) : + _global_scale(1), _texel_scale(0.1), _blind_fall_off(45), _flare_fall_off(5) +{ + _blind = (Texture *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::set_texel_scale +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void LensFlareNode:: +set_texel_scale(float texel_to_world) +{ + _texel_scale = texel_to_world; +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::set_global_scale +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void LensFlareNode:: +set_global_scale(float scale) +{ + _global_scale = scale; +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::set_blind_falloff +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void LensFlareNode:: +set_blind_falloff(float fall_off) +{ + _blind_fall_off = cos(deg_2_rad(fall_off)) - 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::set_flare_falloff +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void LensFlareNode:: +set_flare_falloff(float fall_off) +{ + _flare_fall_off = cos(deg_2_rad(fall_off)) - 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::set_light +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void LensFlareNode:: +set_light_source(PT_Node light) +{ + _light_node = light; +} diff --git a/panda/src/effects/lensFlareNode.cxx b/panda/src/effects/lensFlareNode.cxx new file mode 100644 index 0000000000..56bb41c44c --- /dev/null +++ b/panda/src/effects/lensFlareNode.cxx @@ -0,0 +1,726 @@ +// Filename: lensFlareNode.cxx +// Created by: jason (18Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "lensFlareNode.h" +#include "config_effects.h" +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle LensFlareNode::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::add_bloom +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void LensFlareNode:: +add_flare(PT(Texture) flare, PTA_float scales, PTA_float offsets, + PTA_float angle_scales, PTA_Colorf colors) +{ + nassertv(scales.size() == offsets.size()); + nassertv(colors.size() == offsets.size()); + nassertv(angle_scales.size() == offsets.size()); + + _flare_scales.push_back(scales); + _flare_offsets.push_back(offsets); + _flare_colors.push_back(colors); + _flare_angle_scales.push_back(angle_scales); + _flares.push_back(flare); +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::add_blind +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void LensFlareNode:: +add_blind(PT(Texture) blind) +{ + _blind = blind; + GeomSprite *sprite = new GeomSprite(); + GeomNode *node = new GeomNode(); + + //We don't want to set any geometry right now, as that will be + //taken care of later (and on each subsequent render), but + //Geoms requires a certain amount of info or else they crash, + //so simply give it the minimum it needs not to crash + + //The lengths and number of prims will never change, so give + //it valid values for those, but pass it an empty array of + //vertices + PTA_Vertexf coords(0); + PTA_float tex_scales(0); + + tex_scales.push_back(_texel_scale); + + sprite->set_coords(coords, G_PER_VERTEX); + sprite->set_num_prims(1); + sprite->set_texture(_blind); + + node->add_geom(sprite); + + _blind_arc = new RenderRelation(this, node); +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::set_geometry +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void LensFlareNode:: +set_geometry(GeomSprite *sprite, const PTA_float &geom_scales, + const PTA_float &geom_offsets, const PTA_float &geom_angle_scales, + const PTA_Colorf &geom_colors, const LVector3f &delta, + const LPoint3f &light, const float &angle) +{ + PTA_Vertexf coords(0); + PTA_float tex_scales(0); + PTA_Colorf colors(0); + + //Sanity check + nassertv(geom_scales.size() == geom_offsets.size()); + nassertv(geom_colors.size() == geom_offsets.size()); + + float world_scale = _texel_scale * _global_scale; + for(int i = 0; i < geom_scales.size(); i++) + { + LVector3f position = (delta * geom_offsets[i]) + light; + float view_scale; + //If this is true then we are supposed to invert the meaning of + //the scale. I.E. As the angle between the viewing direction and + //the light direction increases we want the size of the thing to + //get smaller and as it increases we want it to get bigger. This + //will actually be the normal use of angle scales + if (geom_angle_scales[i] < 0) + { + view_scale = (1-pow(angle, 15)) * -geom_angle_scales[i]; + } + else + { + view_scale = pow(angle, 15) * geom_angle_scales[i]; + } + float offset = (angle - 1) / _flare_fall_off; + offset = (offset < 0) ? 0 : ((offset > 1) ? 1 : offset); + float r = geom_colors[i][0] - offset; + float g = geom_colors[i][1] - offset; + float b = geom_colors[i][2] - offset; + r = (r < 0) ? 0 : r; + g = (g < 0) ? 0 : g; + b = (b < 0) ? 0 : b; + + coords.push_back(position); tex_scales.push_back(geom_scales[i] * (world_scale + view_scale)); + colors.push_back(Colorf(r, g, b, 1)); + } + + sprite->set_coords(coords, G_PER_VERTEX); + sprite->set_x_texel_ratio(tex_scales, G_PER_PRIM); + sprite->set_y_texel_ratio(tex_scales, G_PER_PRIM); + sprite->set_colors(colors, G_PER_PRIM); +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::prepare_flares +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void LensFlareNode:: +prepare_flares(const LVector3f &delta, const LPoint3f &light, const float &angle) +{ + if (_flares.size() > 0) + { + if (_flares.size() > _flare_arcs.size()) + { + for(int i = _flare_arcs.size(); i < _flares.size(); i++) + { + GeomSprite *sprite = new GeomSprite(); + GeomNode *node = new GeomNode(); + + //We don't want to set any geometry right now, as that will be + //taken care of later (and on each subsequent render), but + //Geoms requires a certain amount of info or else they crash, + //so simply give it the minimum it needs not to crash + + //The lengths and number of prims will never change, so give + //it valid values for those, but pass it an empty array of + //vertices + PTA_Vertexf coords(0); + + //Sanity check + nassertv(_flare_offsets[i].size() == _flare_scales[i].size()); + + sprite->set_coords(coords, G_PER_VERTEX); + sprite->set_num_prims(_flare_offsets[i].size()); + + node->add_geom(sprite); + + RenderRelation *arc = new RenderRelation(this, node); + //arc->set_transition(new TransparencyTransition(TransparencyProperty::M_alpha)); + _flare_arcs.push_back(arc); + } + } + + for(int i = 0; i < _flares.size(); i++) + { + GeomNode *node = DCAST(GeomNode, _flare_arcs[i]->get_child()); + GeomSprite *sprite = DCAST(GeomSprite, node->get_geom(0)); + + set_geometry(sprite, _flare_scales[i], _flare_offsets[i], + _flare_angle_scales[i], _flare_colors[i], + delta, light, angle); + sprite->set_texture(_flares[i]); + + //Tell them to recompute their bounding volumes + sprite->mark_bound_stale(); + node->mark_bound_stale(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::prepare_blind +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void LensFlareNode:: +prepare_blind(const float &angle, const float &tnear) +{ + if (_blind != (Texture*) NULL) + { + GeomNode *node = DCAST(GeomNode, _blind_arc->get_child()); + GeomSprite *sprite = DCAST(GeomSprite, node->get_geom(0)); + + float offset = (angle - 1) / _blind_fall_off; + //Make sure that it always blends some + offset = (offset < 0.3) ? 0.3 : ((offset > 1) ? 1 : offset); + + PTA_Vertexf coords(0); + PTA_Colorf colors(0); + PTA_float x_tex_scales(0); + PTA_float y_tex_scales(0); + + //The height and the width are set to two as sprites are always + //drawn in a frustum of size 2. + float width = sprite->get_frustum_right() - sprite->get_frustum_left(); + float height = sprite->get_frustum_top() - sprite->get_frustum_bottom(); + float x_offset_scale = width / _blind->_pbuffer->get_xsize(); + float y_offset_scale = height / _blind->_pbuffer->get_ysize(); + + float inten = 1 - offset; + + coords.push_back(Vertexf(0, 0, -tnear )); + colors.push_back(Colorf(inten,inten,inten,1)); + x_tex_scales.push_back(x_offset_scale); y_tex_scales.push_back(y_offset_scale); + + sprite->set_x_texel_ratio(x_tex_scales, G_PER_PRIM); + sprite->set_y_texel_ratio(y_tex_scales, G_PER_PRIM); + sprite->set_coords(coords, G_PER_VERTEX); + sprite->set_colors(colors, G_PER_PRIM); + + //Tell it to recompute it's bounding volume + sprite->mark_bound_stale(); + node->mark_bound_stale(); + } +} +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::render_child +// Access: Pribate +// Description: +//////////////////////////////////////////////////////////////////// +void LensFlareNode:: +render_child(RenderRelation *arc, const AllAttributesWrapper &attrib, + AllTransitionsWrapper &trans, GraphicsStateGuardian *gsg) +{ + + AllAttributesWrapper new_attrib(attrib); + new_attrib.clear_attribute(TransformTransition::get_class_type()); + + AllTransitionsWrapper arc_trans; + arc_trans.extract_from(arc); + + AllTransitionsWrapper new_trans(trans); + new_trans.compose_in_place(arc_trans); + + // Now render everything from this node and below. + gsg->render_subgraph(gsg->get_render_traverser(), + arc->get_child(), new_attrib, new_trans); +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::render_children +// Access: Pribate +// Description: +//////////////////////////////////////////////////////////////////// +void LensFlareNode:: +render_children(const vector_relation &arcs, const AllAttributesWrapper &attrib, + AllTransitionsWrapper &trans, GraphicsStateGuardian *gsg) +{ + for(int i = 0; i < arcs.size(); i++) + { + render_child(arcs[i], attrib, trans, gsg); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::sub_render +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool LensFlareNode:: +sub_render(const AllAttributesWrapper &attrib, AllTransitionsWrapper &trans, + GraphicsStateGuardianBase *gsgbase) +{ + GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, gsgbase); + + nassertr(_light_node != (Node*) NULL, false); + + //First we need the light position + const ProjectionNode *camera_node = gsg->get_current_projection_node(); + const PerspectiveProjection *pp = DCAST(PerspectiveProjection, camera_node->get_projection()); + + LPoint3f light_pos = get_rel_pos(_light_node, camera_node); + + LMatrix4f light_mat; + get_rel_mat(_light_node, camera_node, light_mat); + + LMatrix4f modelview_mat; + + const TransformAttribute *ta; + if (!get_attribute_into(ta, attrib.get_attributes(), TransformTransition::get_class_type())) + modelview_mat = LMatrix4f::ident_mat(); + else + modelview_mat = ta->get_matrix(); + + LMatrix4f inv_light_mat = invert(light_mat); + light_pos = light_pos * inv_light_mat * modelview_mat; + + //Now figure out where the center of the screen is. Since we are + //doing everything in camera space, this should merely be the + //distance between the camera and the near clipping plane projected + //along Y into the screen + LPoint3f center = LPoint3f::origin() + LPoint3f::rfu(0,pp->get_frustum()._fnear,0); + center = center * inv_light_mat * modelview_mat; + + //Now lets get the vector from the light to the center. + LPoint3f delta = center - light_pos; + delta.set_z(light_pos.get_z()); + + //Now perform the angle caclulationss for increasing the brightness + //as we look at the light dead on + LPoint3f origin = LPoint3f::origin() * inv_light_mat * modelview_mat; + LVector3f light_dir = light_pos - origin; + light_dir.normalize(); + LVector3f view_dir = center - origin; + + float dot = view_dir.dot(light_dir); + dot = (dot < 0) ? -dot : dot; + + prepare_flares(delta, light_pos, dot); + prepare_blind(dot, pp->get_frustum()._fnear); + + render_children(_flare_arcs, attrib, trans, gsg); + render_child(_blind_arc, attrib, trans, gsg); + + //Short circuit the rendering + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::has_sub_render +// Access: Public, Virtual +// Description: Should be redefined to return true if the function +// sub_render(), above, expects to be called during +// traversal. +//////////////////////////////////////////////////////////////////// +bool LensFlareNode:: +has_sub_render() const +{ + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::write_object +// Description: Writes the contents of this object to the datagram +// for shipping out to a Bam file. +//////////////////////////////////////////////////////////////////// +void LensFlareNode:: +write_datagram(BamWriter *manager, Datagram &me) { + int i; + + Node::write_datagram(manager, me); + + me.add_uint16(_flares.size()); + for(i = 0; i < _flares.size(); i++) + { + manager->write_pointer(me, _flares[i]); + } + manager->write_pointer(me, _blind); + + me.add_uint16(_flare_arcs.size()); + for(i = 0; i < _flare_arcs.size(); i++) + { + manager->write_pointer(me, _flare_arcs[i]); + } + + me.add_uint16(_flare_scales.size()); + for(i = 0; i < _flare_scales.size(); i++) + { + WRITE_PTA(manager, me, IPD_float::write_datagram, _flare_scales[i]) + } + + me.add_uint16(_flare_angle_scales.size()); + for(i = 0; i < _flare_angle_scales.size(); i++) + { + WRITE_PTA(manager, me, IPD_float::write_datagram, _flare_angle_scales[i]) + } + + me.add_uint16(_flare_offsets.size()); + for(i = 0; i < _flare_offsets.size(); i++) + { + WRITE_PTA(manager, me, IPD_float::write_datagram, _flare_offsets[i]) + } + + me.add_uint16(_flare_colors.size()); + for(i = 0; i < _flare_colors.size(); i++) + { + WRITE_PTA(manager, me, IPD_Colorf::write_datagram, _flare_colors[i]) + } + + me.add_float64(_global_scale); + me.add_float64(_texel_scale); + me.add_float64(_blind_fall_off); + me.add_float64(_flare_fall_off); + + manager->write_pointer(me, _light_node); +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::fillin +// Access: Protected +// Description: This internal function is called by make_LensFlareNode to +// read in all of the relevant data from the BamFile for +// the new LensFlareNode. +//////////////////////////////////////////////////////////////////// +void LensFlareNode:: +fillin(DatagramIterator &scan, BamReader *manager) +{ + int i, size; + Node::fillin(scan, manager); + + Node::fillin(scan, manager); + + _num_flares = scan.get_uint16(); + for(i = 0; i < _num_flares; i++) + { + manager->read_pointer(scan, this); + } + manager->read_pointer(scan, this); + + _num_arcs = scan.get_uint16(); + for(i = 0; i < _num_arcs; i++) + { + manager->read_pointer(scan, this); + } + + size = scan.get_uint16(); + for(i = 0; i < size; i++) + { + PTA_float temp(0); + READ_PTA(manager, scan, IPD_float::read_datagram, temp) + _flare_scales.push_back(temp); + } + + size = scan.get_uint16(); + for(i = 0; i < size; i++) + { + PTA_float temp(0); + READ_PTA(manager, scan, IPD_float::read_datagram, temp) + _flare_angle_scales.push_back(temp); + } + + size = scan.get_uint16(); + for(i = 0; i < size; i++) + { + PTA_float temp(0); + READ_PTA(manager, scan, IPD_float::read_datagram, temp) + _flare_offsets.push_back(temp); + } + + size = scan.get_uint16(); + for(i = 0; i < size; i++) + { + PTA_Colorf temp(0); + READ_PTA(manager, scan, IPD_Colorf::read_datagram, temp) + _flare_colors.push_back(temp); + } + + _global_scale = scan.get_float64(); + _texel_scale = scan.get_float64(); + _blind_fall_off = scan.get_float64(); + _flare_fall_off = scan.get_float64(); + + manager->read_pointer(scan, this); +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::complete_pointers +// Access: Public +// Description: Takes in a vector of pointes to TypedWriteable +// objects that correspond to all the requests for +// pointers that this object made to BamReader. +//////////////////////////////////////////////////////////////////// +int LensFlareNode:: +complete_pointers(vector_typedWriteable &plist, BamReader *manager) +{ + int i; + int start = Node::complete_pointers(plist, manager); + int end = _num_flares + start; + + for(i = start; i < end; i++) + { + _flares.push_back(DCAST(Texture, plist[i])); + } + + _blind = DCAST(Texture, plist[end]); + + end += 1 + _num_arcs; + for(; i < end; i++) + { + _flare_arcs.push_back(DCAST(RenderRelation, plist[i])); + } + + _light_node = DCAST(Node, plist[end]); + + return end+1; +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::make_LensFlareNode +// Access: Protected +// Description: This function is called by the BamReader's factory +// when a new object of type LensFlareNode is encountered in +// the Bam file. It should create the LensFlareNode and +// extract its information from the file. +//////////////////////////////////////////////////////////////////// +TypedWriteable *LensFlareNode:: +make_LensFlareNode(const FactoryParams ¶ms) { + LensFlareNode *me = new LensFlareNode; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::register_with_read_factory +// Access: Public, Static +// Description: Tells the BamReader how to create objects of type +// LensFlareNode. +//////////////////////////////////////////////////////////////////// +void LensFlareNode:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_LensFlareNode); +} + + +/*************** + OLD SPARKLE CODE + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +LensFlareNode:: +LensFlareNode(void) : + _global_scale(1), _next_switch(-1), _sparkle_fps(0.2), + _texel_scale(0.1), _inv_sparkle_fps(5), _exp_scale(15) +{ + _global_clock = ClockObject::get_global_clock(); + _next_switch = _global_clock->get_real_time() + _sparkle_fps; +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::set_sparkle_fps +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void LensFlareNode:: +set_sparkle_fps(float fps) +{ + nassertv(fps > 0); + _next_switch = _next_switch - _sparkle_fps + fps; + _sparkle_fps = fps; + _inv_sparkle_fps = 1. / _sparkle_fps; +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::compute_current +// Access: Private +// Description: Determines the current sparkle index +//////////////////////////////////////////////////////////////////// +int LensFlareNode:: +compute_current(int ¤t_sparkle, vector_texture sparkles) +{ + double current_time = _global_clock->get_real_time(); + unsigned int increment = (unsigned int) ((current_time - _next_switch) * _inv_sparkle_fps); + + _next_switch += _sparkle_fps * increment; + current_sparkle = (current_sparkle + increment) % sparkles.size(); + + return current_sparkle; +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::add_sparkle +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void LensFlareNode:: +add_sparkle(PT_Node source, PT(Texture) sparkle) +{ + set_light(source); + _sparkles.push_back(sparkle); +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::set_sparkles_attributes +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void LensFlareNode:: +set_sparkles_attributes(PT_Node source, vector_float scales, + vector_float offsets, vector_Colorf colors) +{ + nassertv(scales.size() == offsets.size()); + + set_light(source); + + _sparkle_scales = scales; + _sparkle_offsets = offsets; + _sparkle_colors = colors; +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::set_light +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void LensFlareNode:: +set_light(PT_Node light) +{ + _lights.insert(light); + if (_current_sparkles.find(light) == _current_sparkles.end()) + { + _current_sparkles[light] = 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: LensFlareNode::prepare_sparkles +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void LensFlareNode:: +prepare_sparkles(vector_relation &arcs, const vector_texture &sparkles, + const vector_float &scales, const vector_float &offsets, + const vector_Colorf &colors, const LVector3f &delta, + const LPoint3f &light, const BoundingVolume &bound, int &old_sparkle) +{ + //Sanity check + nassertv(scales.size() == offsets.size()); + + if (scales.size() > 0) + { + if (arcs.size() == 0) + { + for(int i = 0; i < scales.size(); i++) + { + GeomSprite *sprite = new GeomSprite(); + GeomNode *node = new GeomNode(); + + //We don't want to set any geometry right now, as that will be + //taken care of later (and on each subsequent render), but + //Geoms requires a certain amount of info or else they crash, + //so simply give it the minimum it needs not to crash + + //The lengths and number of prims will never change, so give + //it valid values for those, but pass it an empty array of + //vertices + PTA_Vertexf coords(0); + + sprite->set_coords(coords, G_PER_VERTEX); + sprite->set_num_prims(1); + + node->add_geom(sprite); + + arcs.push_back(new RenderRelation(this, node)); + } + } + + //Unfortunately, we can't use set_geometry here because only a + //certain number of sparkles are active at once, and set_geometry + //knows nothing about switching between them + + for(int i = 0; i < scales.size(); i++) + { + int index = (compute_current(old_sparkle, sparkles)+i) % sparkles.size(); + LVector3f position = (delta * offsets[i]) + light; + + GeomNode *node = DCAST(GeomNode, arcs[i]->get_child()); + GeomSprite *sprite = DCAST(GeomSprite, node->get_geom(0)); + + PTA_Vertexf coords(0); + PTA_float tex_scales(0); + PTA_Colorf sprite_colors(0); + + coords.push_back(position); tex_scales.push_back(scales[i] * _texel_scale * _global_scale); + + sprite_colors.push_back(colors[i]); + + sprite->set_coords(coords, G_PER_VERTEX); + sprite->set_x_texel_ratio(tex_scales, G_PER_PRIM); + sprite->set_y_texel_ratio(tex_scales, G_PER_PRIM); + sprite->set_colors(sprite_colors, G_PER_PRIM); + sprite->set_texture(sparkles[index]); + + //Tell them to recompute their bounding volumes + sprite->set_bound(bound); + sprite->mark_bound_stale(); + node->mark_bound_stale(); + } + } +} + +****************/ diff --git a/panda/src/effects/lensFlareNode.h b/panda/src/effects/lensFlareNode.h new file mode 100644 index 0000000000..9a74d380f6 --- /dev/null +++ b/panda/src/effects/lensFlareNode.h @@ -0,0 +1,162 @@ +// Filename: lensFlareNode.h +// Created by: jason (18Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LENS_FLARE_NODE +#define LENS_FLARE_NODE + +#include + +#include +#include +#include +#include +#include +#include +#include + +class GraphicsStateGuardian; +class ClockObject; + +class EXPCL_PANDA LensFlareNode : public Node +{ +public: + INLINE LensFlareNode(void); + + void add_flare(PT(Texture) flare, PTA_float scales, PTA_float offsets, + PTA_float angle_scales, PTA_Colorf colors); + void add_blind(PT(Texture) blind); + + INLINE void set_angle_scale(float scale); + INLINE void set_texel_scale(float texel_to_world); + INLINE void set_global_scale(float scale); + + INLINE void set_blind_falloff(float fall_off); + INLINE void set_flare_falloff(float fall_off); + + INLINE void set_light_source(PT_Node source); + + virtual bool sub_render(const AllAttributesWrapper &attrib, AllTransitionsWrapper &trans, + GraphicsStateGuardianBase *gsgbase); + virtual bool has_sub_render() const; + +private: + typedef vector vector_Vfloat; + typedef vector vector_Vcolorf; + typedef vector vector_relation; + typedef vector vector_texture; + + vector_texture _flares; + PT(Texture) _blind; + + vector_relation _flare_arcs; + PT(RenderRelation) _blind_arc; + + vector_Vfloat _flare_scales; + vector_Vfloat _flare_angle_scales; + vector_Vfloat _flare_offsets; + vector_Vcolorf _flare_colors; + + float _global_scale; + float _texel_scale; + + float _blind_fall_off; + float _flare_fall_off; + + PT_Node _light_node; + + /****Internal functions*****/ + + //Sub-routines that are defined only for code cleanliness + void prepare_flares(const LVector3f &delta, const LPoint3f &light, const float &angle); + void prepare_blind(const float &angle, const float &tnear); + + //All of the geometry for halos and blooms is created the same way. + //Sparkle geometry is created differently because we cycle through + //the set of Sparkle textures to create an animation + + void set_geometry(GeomSprite *sprite, const PTA_float &geom_scales, + const PTA_float &geom_offsets, const PTA_float &geom_angle_scales, + const PTA_Colorf &geom_colors, const LVector3f &delta, + const LPoint3f &light, const float &angle); + + + void render_child(RenderRelation *arc, const AllAttributesWrapper &attrib, + AllTransitionsWrapper &trans, GraphicsStateGuardian *gsg); + void render_children(const vector_relation &arcs, const AllAttributesWrapper &attrib, + AllTransitionsWrapper &trans, GraphicsStateGuardian *gsg); + +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &me); + virtual int complete_pointers(vector_typedWriteable &plist, + BamReader *manager); + + static TypedWriteable *make_LensFlareNode(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator &scan, BamReader *manager); + +private: + int _num_flares, _num_arcs; + +public: + static TypeHandle get_class_type( void ) { + return _type_handle; + } + static void init_type( void ) { + NamedNode::init_type(); + register_type( _type_handle, "LensFlareNode", + NamedNode::get_class_type() ); + } + virtual TypeHandle get_type( void ) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "lensFlareNode.I" + +#endif + +/***********OLD SPARKLE CODE. LEFT IN CASE +public: + void add_sparkle(PT_Node source, PT(Texture) sparkle); + void set_sparkles_attributes(PT_Node source, float angle_scale, vector_float scales, + vector_float offsets, vector_Colorf colors); + void set_sparkle_fps(float fps); + +private: + //Sparkle animation variables + ClockObject* _global_clock; + float _next_switch; + float _sparkle_fps; + float _inv_sparkle_fps; + + typedef map Sparkle_Scales; + typedef map Sparkle_Offsets; + typedef map Sparkle_Colors; + + Textures _sparkles; + Relations _sparkle_arcs; + + Sparkle_Scales _sparkle_scales; + Sparkle_Offsets _sparkle_offsets; + Sparkle_Colors _sparkle_colors; + + int _num_sparkles_on; + map _current_sparkles; + + void prepare_sparkles(vector_relation &arcs, const vector_texture &sparkles, + const vector_float &scales, const vector_float &offsets, + const vector_Colorf &colors, const LVector3f &delta, + const LPoint3f &light, const BoundingVolume &bound, int &old_sparkle); + + //Timing function to control sparkle animation + int compute_current(int ¤t_sparkle, vector_texture sparkles); + +******************/ diff --git a/panda/src/egg/Sources.pp b/panda/src/egg/Sources.pp new file mode 100644 index 0000000000..53398cf045 --- /dev/null +++ b/panda/src/egg/Sources.pp @@ -0,0 +1,69 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase +#define YACC_PREFIX eggyy +#define LFLAGS -i + +#begin lib_target + #define TARGET egg + #define LOCAL_LIBS \ + linmath putil collide + + #define SOURCES \ + config_egg.cxx config_egg.h eggAlphaMode.I eggAlphaMode.cxx \ + eggAlphaMode.h eggAnimData.I eggAnimData.cxx eggAnimData.h \ + eggAttributes.I eggAttributes.cxx eggAttributes.h eggBin.cxx \ + eggBin.h eggBinMaker.cxx eggBinMaker.h eggComment.I eggComment.cxx \ + eggComment.h eggCoordinateSystem.I eggCoordinateSystem.cxx \ + eggCoordinateSystem.h eggCurve.I eggCurve.cxx eggCurve.h eggData.I \ + eggData.cxx eggData.h eggExternalReference.I \ + eggExternalReference.cxx eggExternalReference.h eggFilenameNode.I \ + eggFilenameNode.cxx eggFilenameNode.h eggGroup.I eggGroup.cxx \ + eggGroup.h eggGroupNode.I eggGroupNode.cxx eggGroupNode.h \ + eggMaterial.I eggMaterial.cxx eggMaterial.h eggMiscFuncs.I \ + eggMiscFuncs.cxx eggMiscFuncs.h eggNamedObject.I eggNamedObject.cxx \ + eggNamedObject.h eggNode.I eggNode.cxx eggNode.h eggNurbsCurve.I \ + eggNurbsCurve.cxx eggNurbsCurve.h eggNurbsSurface.I \ + eggNurbsSurface.cxx eggNurbsSurface.h eggObject.I eggObject.cxx \ + eggObject.h eggParameters.cxx eggParameters.h eggPoint.I \ + eggPoint.cxx eggPoint.h eggPolygon.I eggPolygon.cxx eggPolygon.h \ + eggPrimitive.I eggPrimitive.cxx eggPrimitive.h eggSAnimData.I \ + eggSAnimData.cxx eggSAnimData.h eggSurface.I eggSurface.cxx \ + eggSurface.h eggSwitchCondition.cxx eggSwitchCondition.h eggTable.I \ + eggTable.cxx eggTable.h eggTexture.I eggTexture.cxx eggTexture.h \ + eggTextureCollection.I eggTextureCollection.cxx \ + eggTextureCollection.h eggUtilities.I eggUtilities.cxx \ + eggUtilities.h eggVertex.I eggVertex.cxx eggVertex.h \ + eggVertexPool.I eggVertexPool.cxx eggVertexPool.h eggXfmAnimData.I \ + eggXfmAnimData.cxx eggXfmAnimData.h eggXfmSAnim.I eggXfmSAnim.cxx \ + eggXfmSAnim.h parserDefs.h parser.yxx lexerDefs.h lexer.lxx + + #define INSTALL_HEADERS \ + eggAlphaMode.I eggAlphaMode.h eggAnimData.I eggAnimData.h \ + eggAttributes.I eggAttributes.h eggBin.h eggBinMaker.h eggComment.I \ + eggComment.h eggCoordinateSystem.I eggCoordinateSystem.h eggCurve.I \ + eggCurve.h eggData.I eggData.h eggExternalReference.I \ + eggExternalReference.h eggFilenameNode.I eggFilenameNode.h \ + eggGroup.I eggGroup.h eggGroupNode.I eggGroupNode.h eggMaterial.I \ + eggMaterial.h eggMorph.I eggMorph.h eggMorphList.I eggMorphList.h \ + eggNamedObject.I eggNamedObject.h eggNode.I eggNode.h \ + eggNurbsCurve.I eggNurbsCurve.h eggNurbsSurface.I eggNurbsSurface.h \ + eggObject.I eggObject.h eggParameters.h eggPoint.I eggPoint.h \ + eggPolygon.I eggPolygon.h eggPrimitive.I eggPrimitive.h \ + eggSAnimData.I eggSAnimData.h eggSurface.I eggSurface.h \ + eggSwitchCondition.h eggTable.I eggTable.h eggTexture.I \ + eggTexture.h eggTextureCollection.I eggTextureCollection.h \ + eggUtilities.I eggUtilities.h eggVertex.I eggVertex.h \ + eggVertexPool.I eggVertexPool.h eggXfmAnimData.I eggXfmAnimData.h \ + eggXfmSAnim.I eggXfmSAnim.h + +#end lib_target + +#begin test_bin_target + #define TARGET test_egg + #define LOCAL_LIBS \ + egg putil mathutil + + #define SOURCES \ + test_egg.cxx + +#end test_bin_target + diff --git a/panda/src/egg/config_egg.cxx b/panda/src/egg/config_egg.cxx new file mode 100644 index 0000000000..1613d0787f --- /dev/null +++ b/panda/src/egg/config_egg.cxx @@ -0,0 +1,80 @@ +// Filename: config_egg.cxx +// Created by: drose (19Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_egg.h" +#include "eggAlphaMode.h" +#include "eggAnimData.h" +#include "eggAttributes.h" +#include "eggBin.h" +#include "eggBinMaker.h" +#include "eggComment.h" +#include "eggCoordinateSystem.h" +#include "eggCurve.h" +#include "eggExternalReference.h" +#include "eggFilenameNode.h" +#include "eggGroup.h" +#include "eggGroupNode.h" +#include "eggNamedObject.h" +#include "eggNode.h" +#include "eggNurbsCurve.h" +#include "eggNurbsSurface.h" +#include "eggObject.h" +#include "eggPoint.h" +#include "eggPolygon.h" +#include "eggPrimitive.h" +#include "eggSAnimData.h" +#include "eggSurface.h" +#include "eggSwitchCondition.h" +#include "eggTable.h" +#include "eggTexture.h" +#include "eggVertex.h" +#include "eggVertexPool.h" +#include "eggXfmAnimData.h" +#include "eggXfmSAnim.h" + +#include +#include + +Configure(config_egg); +NotifyCategoryDef(egg, ""); + +ConfigureFn(config_egg) { + EggAlphaMode::init_type(); + EggAnimData::init_type(); + EggAttributes::init_type(); + EggBin::init_type(); + EggBinMaker::init_type(); + EggComment::init_type(); + EggCoordinateSystem::init_type(); + EggCurve::init_type(); + EggExternalReference::init_type(); + EggFilenameNode::init_type(); + EggGroup::init_type(); + EggGroupNode::init_type(); + EggNamedObject::init_type(); + EggNode::init_type(); + EggNurbsCurve::init_type(); + EggNurbsSurface::init_type(); + EggObject::init_type(); + EggPoint::init_type(); + EggPolygon::init_type(); + EggPrimitive::init_type(); + EggSAnimData::init_type(); + EggSurface::init_type(); + EggSwitchCondition::init_type(); + EggSwitchConditionDistance::init_type(); + EggTable::init_type(); + EggTexture::init_type(); + EggVertex::init_type(); + EggVertexPool::init_type(); + EggXfmAnimData::init_type(); + EggXfmSAnim::init_type(); +} + +const DSearchPath & +get_egg_path() { + static DSearchPath *egg_path = NULL; + return get_config_path("egg-path", egg_path); +} diff --git a/panda/src/egg/config_egg.h b/panda/src/egg/config_egg.h new file mode 100644 index 0000000000..cf5543445e --- /dev/null +++ b/panda/src/egg/config_egg.h @@ -0,0 +1,18 @@ +// Filename: config_egg.h +// Created by: drose (19Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_EGG_H +#define CONFIG_EGG_H + +#include +#include + +class DSearchPath; + +NotifyCategoryDecl(egg, EXPCL_PANDAEGG, EXPTP_PANDAEGG); + +const DSearchPath &get_egg_path(); + +#endif diff --git a/panda/src/egg/eggAlphaMode.I b/panda/src/egg/eggAlphaMode.I new file mode 100644 index 0000000000..27ca75bcec --- /dev/null +++ b/panda/src/egg/eggAlphaMode.I @@ -0,0 +1,116 @@ +// Filename: eggAlphaMode.I +// Created by: drose (20Jan99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggAlphaMode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggAlphaMode:: +EggAlphaMode() { + _alpha_mode = AM_unspecified; + _draw_order = 0.0; + _has_draw_order = false; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggAlphaMode::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggAlphaMode:: +EggAlphaMode(const EggAlphaMode ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAlphaMode::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggAlphaMode &EggAlphaMode:: +operator = (const EggAlphaMode ©) { + _alpha_mode = copy._alpha_mode; + _draw_order = copy._draw_order; + _has_draw_order = copy._has_draw_order; + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggAlphaMode::set_alpha_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggAlphaMode:: +set_alpha_mode(AlphaMode mode) { + _alpha_mode = mode; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggAlphaMode::get_alpha_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggAlphaMode::AlphaMode EggAlphaMode:: +get_alpha_mode() const { + return _alpha_mode; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggAlphaMode::set_draw_order +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggAlphaMode:: +set_draw_order(double order) { + _draw_order = order; + _has_draw_order = true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggAlphaMode::get_draw_order +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE double EggAlphaMode:: +get_draw_order() const { + return _draw_order; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAlphaMode::has_draw_order +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggAlphaMode:: +has_draw_order() const { + return _has_draw_order; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAlphaMode::clear_draw_order +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggAlphaMode:: +clear_draw_order() { + _has_draw_order = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAlphaMode::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggAlphaMode:: +operator != (const EggAlphaMode &other) const { + return !(*this == other); +} diff --git a/panda/src/egg/eggAlphaMode.cxx b/panda/src/egg/eggAlphaMode.cxx new file mode 100644 index 0000000000..d5a8f4d1ce --- /dev/null +++ b/panda/src/egg/eggAlphaMode.cxx @@ -0,0 +1,129 @@ +// Filename: eggAlphaMode.cxx +// Created by: drose (20Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggAlphaMode.h" +#include +#include +#include + +TypeHandle EggAlphaMode::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: EggAlphaMode::write +// Access: Public +// Description: Writes the attributes to the indicated output stream in +// Egg format. +//////////////////////////////////////////////////////////////////// +void EggAlphaMode:: +write(ostream &out, int indent_level) const { + if (get_alpha_mode() != AM_unspecified) { + indent(out, indent_level) + << " alpha { " << get_alpha_mode() << " }\n"; + } + if (has_draw_order()) { + indent(out, indent_level) + << " draw-order { " << get_draw_order() << " }\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAlphaMode::Equality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool EggAlphaMode:: +operator == (const EggAlphaMode &other) const { + if (_alpha_mode != other._alpha_mode || + _has_draw_order != other._has_draw_order) { + return false; + } + + if (_has_draw_order) { + if (_draw_order != other._draw_order) { + return false; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAlphaMode::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool EggAlphaMode:: +operator < (const EggAlphaMode &other) const { + if (_alpha_mode != other._alpha_mode) { + return (int)_alpha_mode < (int)other._alpha_mode; + } + + if (_has_draw_order != other._has_draw_order) { + return (int)_has_draw_order < (int)other._has_draw_order; + } + + if (_has_draw_order) { + if (_draw_order != other._draw_order) { + return _draw_order < other._draw_order; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAlphaMode::string_alpha_mode +// Access: Public +// Description: Returns the AlphaMode value associated with the given +// string representation, or AM_unspecified if the string +// does not match any known AlphaMode value. +//////////////////////////////////////////////////////////////////// +EggAlphaMode::AlphaMode EggAlphaMode:: +string_alpha_mode(const string &string) { + if (cmp_nocase_uh(string, "off") == 0) { + return AM_off; + } else if (cmp_nocase_uh(string, "on") == 0) { + return AM_on; + } else if (cmp_nocase_uh(string, "blend") == 0) { + return AM_blend; + } else if (cmp_nocase_uh(string, "blend_no_occlude") == 0) { + return AM_blend_no_occlude; + } else if (cmp_nocase_uh(string, "ms") == 0) { + return AM_ms; + } else if (cmp_nocase_uh(string, "ms_mask") == 0) { + return AM_ms_mask; + } else { + return AM_unspecified; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: AlphaMode output operator +// Description: +//////////////////////////////////////////////////////////////////// +ostream &operator << (ostream &out, EggAlphaMode::AlphaMode mode) { + switch (mode) { + case EggAlphaMode::AM_unspecified: + return out << "unspecified"; + case EggAlphaMode::AM_off: + return out << "off"; + case EggAlphaMode::AM_on: + return out << "on"; + case EggAlphaMode::AM_blend: + return out << "blend"; + case EggAlphaMode::AM_blend_no_occlude: + return out << "blend_no_occlude"; + case EggAlphaMode::AM_ms: + return out << "ms"; + case EggAlphaMode::AM_ms_mask: + return out << "ms_mask"; + } + + nassertr(false, out); + return out << "(**invalid**)"; +} + + diff --git a/panda/src/egg/eggAlphaMode.h b/panda/src/egg/eggAlphaMode.h new file mode 100644 index 0000000000..ceab0131d7 --- /dev/null +++ b/panda/src/egg/eggAlphaMode.h @@ -0,0 +1,78 @@ +// Filename: eggAlphaMode.h +// Created by: drose (20Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGALPHAMODE_H +#define EGGALPHAMODE_H + +#include + +#include + +#include + + +//////////////////////////////////////////////////////////////////// +// Class : EggAlphaMode +// Description : This is a base class for things that can have the +// alpha mode and draw order set on them. This includes +// textures, primitives, and groups. +// +// This class cannot inherit from EggObject, because it +// causes problems at the EggPolygon level with multiple +// appearances of the EggObject base class. And making +// EggObject a virtual base class is just no fun. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggAlphaMode { +public: + INLINE EggAlphaMode(); + INLINE EggAlphaMode(const EggAlphaMode ©); + INLINE EggAlphaMode &operator = (const EggAlphaMode ©); + + void write(ostream &out, int indent_level) const; + + enum AlphaMode { + AM_unspecified, AM_off, AM_on, + AM_blend, AM_blend_no_occlude, AM_ms, AM_ms_mask + }; + + INLINE void set_alpha_mode(AlphaMode mode); + INLINE AlphaMode get_alpha_mode() const; + + INLINE void set_draw_order(double order); + INLINE double get_draw_order() const; + INLINE bool has_draw_order() const; + INLINE void clear_draw_order(); + + // Comparison operators are handy. + bool operator == (const EggAlphaMode &other) const; + INLINE bool operator != (const EggAlphaMode &other) const; + bool operator < (const EggAlphaMode &other) const; + + static AlphaMode string_alpha_mode(const string &string); + +private: + AlphaMode _alpha_mode; + double _draw_order; + bool _has_draw_order; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + register_type(_type_handle, "EggAlphaMode"); + } + +private: + static TypeHandle _type_handle; +}; + +ostream &operator << (ostream &out, EggAlphaMode::AlphaMode mode); + +#include "eggAlphaMode.I" + +#endif + diff --git a/panda/src/egg/eggAnimData.I b/panda/src/egg/eggAnimData.I new file mode 100644 index 0000000000..cd094d21ca --- /dev/null +++ b/panda/src/egg/eggAnimData.I @@ -0,0 +1,144 @@ +// Filename: eggAnimData.I +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////// +// Function: EggAnimData::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggAnimData:: +EggAnimData(const string &name) : EggNode(name) { + _has_fps = false; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggAnimData::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggAnimData:: +EggAnimData(const EggAnimData ©) : + EggNode(copy), _data(copy._data), + _fps(copy._fps), _has_fps(copy._has_fps) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggAnimData::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggAnimData &EggAnimData:: +operator = (const EggAnimData ©) { + EggNode::operator = (copy); + _data = copy._data; + _fps = copy._fps; + _has_fps = copy._has_fps; + + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggAnimData::set_fps +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggAnimData:: +set_fps(double fps) { + _fps = fps; + _has_fps = true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggAnimData::clear_fps +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggAnimData:: +clear_fps() { + _has_fps = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAnimData::has_fps +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggAnimData:: +has_fps() const { + return _has_fps; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAnimData::get_fps +// Access: Public +// Description: This is only valid if has_fps() returns true. +//////////////////////////////////////////////////////////////////// +INLINE double EggAnimData:: +get_fps() const { + nassertr(has_fps(), 0.0); + return _fps; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggAnimData::clear_data +// Access: Public +// Description: Removes all data and empties the table. +//////////////////////////////////////////////////////////////////// +INLINE void EggAnimData:: +clear_data() { + _data.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAnimData::add_data +// Access: Public +// Description: Adds a single element to the table. +//////////////////////////////////////////////////////////////////// +INLINE void EggAnimData:: +add_data(double value) { + _data.push_back(value); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: EggAnimData::get_size +// Access: Public +// Description: Returns the number of elements in the table. +//////////////////////////////////////////////////////////////////// +INLINE int EggAnimData:: +get_size() const { + return _data.size(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggAnimData::get_data +// Access: Public +// Description: Returns the entire table of data. +//////////////////////////////////////////////////////////////////// +INLINE PTA_double EggAnimData:: +get_data() const { + return _data; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggAnimData::set_data +// Access: Public +// Description: Replaces the entire table of data. +//////////////////////////////////////////////////////////////////// +INLINE void EggAnimData:: +set_data(const PTA_double &data) { + _data = data; +} + diff --git a/panda/src/egg/eggAnimData.cxx b/panda/src/egg/eggAnimData.cxx new file mode 100644 index 0000000000..b53fcf4cc3 --- /dev/null +++ b/panda/src/egg/eggAnimData.cxx @@ -0,0 +1,8 @@ +// Filename: eggAnimData.cxx +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggAnimData.h" + +TypeHandle EggAnimData::_type_handle; diff --git a/panda/src/egg/eggAnimData.h b/panda/src/egg/eggAnimData.h new file mode 100644 index 0000000000..6ae8a2df4f --- /dev/null +++ b/panda/src/egg/eggAnimData.h @@ -0,0 +1,69 @@ +// Filename: eggAnimData.h +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGANIMDATA_H +#define EGGANIMDATA_H + +#include + +#include "eggNode.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : EggAnimData +// Description : A base class for EggSAnimData and EggXfmAnimData, +// which contain rows and columns of numbers. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggAnimData : public EggNode { +public: + + INLINE EggAnimData(const string &name = ""); + INLINE EggAnimData(const EggAnimData ©); + INLINE EggAnimData &operator = (const EggAnimData ©); + + INLINE void set_fps(double type); + INLINE void clear_fps(); + INLINE bool has_fps() const; + INLINE double get_fps() const; + + INLINE void clear_data(); + INLINE void add_data(double value); + + INLINE int get_size() const; + INLINE PTA_double get_data() const; + INLINE void set_data(const PTA_double &data); + +protected: + PTA_double _data; + +private: + double _fps; + bool _has_fps; + + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggNode::init_type(); + register_type(_type_handle, "EggAnimData", + EggNode::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "eggAnimData.I" + +#endif diff --git a/panda/src/egg/eggAttributes.I b/panda/src/egg/eggAttributes.I new file mode 100644 index 0000000000..a63fe5bbb3 --- /dev/null +++ b/panda/src/egg/eggAttributes.I @@ -0,0 +1,153 @@ +// Filename: eggAttributes.I +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include + + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggAttributes:: +EggAttributes() { + _flags = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggAttributes:: +EggAttributes(const EggAttributes ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::has_normal +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggAttributes:: +has_normal() const { + return (_flags & F_has_normal) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::get_normal +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const Normald &EggAttributes:: +get_normal() const { + nassertr(has_normal(), _normal); + return _normal; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::set_normal +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggAttributes:: +set_normal(const Normald &normal) { + _normal = normal; + _flags |= F_has_normal; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::clear_normal +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggAttributes:: +clear_normal() { + _flags &= ~F_has_normal; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::has_uv +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggAttributes:: +has_uv() const { + return (_flags & F_has_uv) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::get_uv +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const TexCoordd &EggAttributes:: +get_uv() const { + nassertr(has_uv(), _uv); + return _uv; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::set_uv +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggAttributes:: +set_uv(const TexCoordd &uv) { + _uv = uv; + _flags |= F_has_uv; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::clear_uv +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggAttributes:: +clear_uv() { + _flags &= ~F_has_uv; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::has_color +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggAttributes:: +has_color() const { + return (_flags & F_has_color) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::get_color +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const Colorf &EggAttributes:: +get_color() const { + nassertr(has_color(), _color); + return _color; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggAttributes:: +set_color(const Colorf &color) { + _color = color; + _flags |= F_has_color; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes:: +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggAttributes:: +clear_color() { + _flags &= ~F_has_color; +} diff --git a/panda/src/egg/eggAttributes.cxx b/panda/src/egg/eggAttributes.cxx new file mode 100644 index 0000000000..bc10810b77 --- /dev/null +++ b/panda/src/egg/eggAttributes.cxx @@ -0,0 +1,149 @@ +// Filename: eggAttributes.cxx +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggAttributes.h" +#include "eggParameters.h" +#include "eggMorph.h" +#include "eggMorphList.h" + +#include +#include + +TypeHandle EggAttributes::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggAttributes &EggAttributes:: +operator = (const EggAttributes ©) { + _flags = copy._flags; + _normal = copy._normal; + _uv = copy._uv; + _color = copy._color; + _dnormals = copy._dnormals; + _duvs = copy._duvs; + _drgbas = copy._drgbas; + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::write +// Access: Public +// Description: Writes the attributes to the indicated output stream in +// Egg format. +//////////////////////////////////////////////////////////////////// +void EggAttributes:: +write(ostream &out, int indent_level) const { + if (has_normal()) { + if (_dnormals.empty()) { + indent(out, indent_level) + << " { " << get_normal() << " }\n"; + } else { + indent(out, indent_level) << " {\n"; + indent(out, indent_level+2) << get_normal() << "\n"; + _dnormals.write(out, indent_level+2); + indent(out, indent_level) << "}\n"; + } + } + if (has_uv()) { + if (_duvs.empty()) { + indent(out, indent_level) + << " { " << get_uv() << " }\n"; + } else { + indent(out, indent_level) << " {\n"; + indent(out, indent_level+2) << get_uv() << "\n"; + _duvs.write(out, indent_level+2); + indent(out, indent_level) << "}\n"; + } + } + if (has_color()) { + if (_drgbas.empty()) { + indent(out, indent_level) + << " { " << get_color() << " }\n"; + } else { + indent(out, indent_level) << " {\n"; + indent(out, indent_level+2) << get_color() << "\n"; + _drgbas.write(out, indent_level+2); + indent(out, indent_level) << "}\n"; + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::sorts_less_than +// Access: Public +// Description: An ordering operator to compare two vertices for +// sorting order. This imposes an arbitrary ordering +// useful to identify unique vertices. +//////////////////////////////////////////////////////////////////// +bool EggAttributes:: +sorts_less_than(const EggAttributes &other) const { + if (_flags != other._flags) { + return _flags < other._flags; + } + + if (has_normal()) { + int compare = + _normal.compare_to(other._normal, egg_parameters->_normal_threshold); + if (compare != 0) { + return compare < 0; + } + if (_dnormals != other._dnormals) { + return _dnormals < other._dnormals; + } + } + + if (has_uv()) { + int compare = + _uv.compare_to(other._uv, egg_parameters->_uv_threshold); + if (compare != 0) { + return compare < 0; + } + if (_duvs != other._duvs) { + return _duvs < other._duvs; + } + } + + if (has_color()) { + int compare = + _color.compare_to(other._color, egg_parameters->_color_threshold); + if (compare != 0) { + return compare < 0; + } + if (_drgbas != other._drgbas) { + return _drgbas < other._drgbas; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::transform +// Access: Public, Virtual +// Description: Applies the indicated transformation matrix to the +// attributes. +//////////////////////////////////////////////////////////////////// +void EggAttributes:: +transform(const LMatrix4d &mat) { + if (has_normal()) { + _normal = _normal * mat; + + EggMorphNormalList::iterator mi; + for (mi = _dnormals.begin(); mi != _dnormals.end(); ++mi) { + // We can safely cast the morph object to a non-const, because + // we're not changing its name, which is the only thing the set + // cares about preserving. + EggMorphNormal &morph = (EggMorphNormal &)(*mi); + + morph.set_offset((*mi).get_offset() * mat); + } + } +} diff --git a/panda/src/egg/eggAttributes.h b/panda/src/egg/eggAttributes.h new file mode 100644 index 0000000000..498ac684e2 --- /dev/null +++ b/panda/src/egg/eggAttributes.h @@ -0,0 +1,85 @@ +// Filename: eggAttributes.h +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGATTRIBUTES_H +#define EGGATTRIBUTES_H + +#include + +#include "eggMorphList.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : EggAttributes +// Description : The set of attributes that may be applied to vertices +// as well as polygons, such as surface normal and +// color. +// +// This class cannot inherit from EggObject, because it +// causes problems at the EggPolygon level with multiple +// appearances of the EggObject base class. And making +// EggObject a virtual base class is just no fun. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggAttributes { +public: + INLINE EggAttributes(); + INLINE EggAttributes(const EggAttributes ©); + EggAttributes &operator = (const EggAttributes ©); + + INLINE bool has_normal() const; + INLINE const Normald &get_normal() const; + INLINE void set_normal(const Normald &normal); + INLINE void clear_normal(); + + INLINE bool has_uv() const; + INLINE const TexCoordd &get_uv() const; + INLINE void set_uv(const TexCoordd &texCoord); + INLINE void clear_uv(); + + INLINE bool has_color() const; + INLINE const Colorf &get_color() const; + INLINE void set_color(const Colorf &Color); + INLINE void clear_color(); + + void write(ostream &out, int indent_level) const; + bool sorts_less_than(const EggAttributes &other) const; + + void transform(const LMatrix4d &mat); + + EggMorphNormalList _dnormals; + EggMorphTexCoordList _duvs; + EggMorphColorList _drgbas; + +private: + enum Flags { + F_has_normal = 0x001, + F_has_uv = 0x002, + F_has_color = 0x004, + }; + + int _flags; + Normald _normal; + TexCoordd _uv; + Colorf _color; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + register_type(_type_handle, "EggAttributes"); + } + +private: + static TypeHandle _type_handle; +}; + +#include "eggAttributes.I" + +#endif + diff --git a/panda/src/egg/eggBin.cxx b/panda/src/egg/eggBin.cxx new file mode 100644 index 0000000000..ef8c5b82ab --- /dev/null +++ b/panda/src/egg/eggBin.cxx @@ -0,0 +1,63 @@ +// Filename: eggBin.cxx +// Created by: drose (21Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggBin.h" + + +TypeHandle EggBin::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: EggBin::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggBin:: +EggBin(const string &name) : EggGroup(name) { + _bin_number = 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggBin::EggGroup copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggBin:: +EggBin(const EggGroup ©) : EggGroup(copy) { + _bin_number = 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggBin::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggBin:: +EggBin(const EggBin ©) : EggGroup(copy), _bin_number(copy._bin_number) { +} + + + +//////////////////////////////////////////////////////////////////// +// Function: EggBin::set_bin_number +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void EggBin:: +set_bin_number(int bin_number) { + _bin_number = bin_number; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggBin::get_bin_number +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int EggBin:: +get_bin_number() const { + return _bin_number; +} diff --git a/panda/src/egg/eggBin.h b/panda/src/egg/eggBin.h new file mode 100644 index 0000000000..d432854174 --- /dev/null +++ b/panda/src/egg/eggBin.h @@ -0,0 +1,52 @@ +// Filename: eggBin.h +// Created by: drose (21Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGBIN_H +#define EGGBIN_H + +#include + +#include "eggGroup.h" + +//////////////////////////////////////////////////////////////////// +// Class : EggBin +// Description : A type of group node that holds related subnodes. +// This is a special kind of node that will never be +// read in from an egg file, but can only exist in the +// egg scene graph if it is created via the use of an +// EggBinMaker. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggBin : public EggGroup { +public: + EggBin(const string &name = ""); + EggBin(const EggGroup ©); + EggBin(const EggBin ©); + + void set_bin_number(int bin_number); + int get_bin_number() const; + +private: + int _bin_number; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggGroup::init_type(); + register_type(_type_handle, "EggBin", + EggGroup::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/egg/eggBinMaker.cxx b/panda/src/egg/eggBinMaker.cxx new file mode 100644 index 0000000000..67b896b574 --- /dev/null +++ b/panda/src/egg/eggBinMaker.cxx @@ -0,0 +1,267 @@ +// Filename: eggBinMaker.cxx +// Created by: drose (21Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggBinMaker.h" +#include "eggGroupNode.h" +#include "eggGroup.h" +#include "eggBin.h" + +#include + +TypeHandle EggBinMaker::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: EggBinMakerCompareNodes::Function operator +// Access: Public +// Description: Called by the SortedNodes set to put nodes into bin +// order. Returns true if the first node falls into an +// earlier bin than the second node, false otherwise. +//////////////////////////////////////////////////////////////////// +bool EggBinMakerCompareNodes:: +operator ()(const EggNode *a, const EggNode *b) const { + int bin_number_a = _ebm->get_bin_number(a); + int bin_number_b = _ebm->get_bin_number(b); + + if (bin_number_a != bin_number_b) { + // If the two nodes return different bin numbers, then they + // sort based on those numbers. + return bin_number_a < bin_number_b; + } + + // The two nodes fell into the same bin number, so fall back on the + // comparison function to see if they should be differentiated. + return _ebm->sorts_less(bin_number_a, a, b); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggBinMaker::make_bins +// Access: Public +// Description: The main entry point to EggBinMaker. Walks the egg +// scene graph beginning at the indicated root node, and +// moves all binnable nodes into EggBin objects. +// Returns the number of EggBins created. +//////////////////////////////////////////////////////////////////// +int EggBinMaker:: +make_bins(EggGroupNode *root_group) { + _group_nodes.clear(); + + collect_nodes(root_group); + + int num_bins = 0; + GroupNodes::const_iterator gi; + for (gi = _group_nodes.begin(); gi != _group_nodes.end(); ++gi) { + num_bins += get_bins_for_group(gi); + } + + return num_bins; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggBinMaker::sorts_less +// Access: Public, Virtual +// Description: May be overridden in derived classes to create +// additional bins within a particular bin number, based +// on some arbitrary property of nodes. This function +// establishes an arbitrary but fixed ordering between +// nodes; if two nodes do not sort to the same position, +// different bins are created for each one (with the +// same bin number on each bin). +//////////////////////////////////////////////////////////////////// +bool EggBinMaker:: +sorts_less(int, const EggNode *, const EggNode *) { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggBinMaker::collapse_group +// Access: Public, Virtual +// Description: May be overridden in derived classes to specify +// whether a particular group node, apparently +// redundant, may be safely collapsed out. +//////////////////////////////////////////////////////////////////// +bool EggBinMaker:: +collapse_group(const EggGroup *, int) { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggBinMaker::get_bin_name +// Access: Public, Virtual +// Description: May be overridden in derived classes to define a name +// for each new bin, based on its bin number. +//////////////////////////////////////////////////////////////////// +string EggBinMaker:: +get_bin_name(int) { + return string(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggBinMaker::collect_nodes +// Access: Private +// Description: Walks the egg scene graph, identifying nodes to be +// binned and moving them from the scene graph into the +// internal bin structure. +//////////////////////////////////////////////////////////////////// +void EggBinMaker:: +collect_nodes(EggGroupNode *group) { + // We have to play games with this next iterator, because we might + // be destructively operation on the child list as we traverse it. + EggGroupNode::iterator i, next; + + bool first_in_group = true; + GroupNodes::iterator gni = _group_nodes.end(); + + i = group->begin(); + next = i; + while (i != group->end()) { + EggNode *node = (*i); + ++next; + + if (get_bin_number(node) != 0) { + // Ok, here's a node to be binned. Add it to the appropriate + // bin. + if (first_in_group) { + // If this is the first time this group has been encountered, + // we need to create a new entry in _group_nodes for it. + + pair result; + result = _group_nodes.insert + (GroupNodes::value_type + (group, SortedNodes(EggBinMakerCompareNodes(this)))); + + nassertv(result.second); + gni = result.first; + first_in_group = false; + } + + // Add this node to the set of all nodes being binned for the + // group. This also puts the nodes into bin order. + nassertv(gni != _group_nodes.end()); + (*gni).second.insert(node); + + // And remove it from the scene graph. + group->erase(i); + + } else if (node->is_of_type(EggGroupNode::get_class_type())) { + // Here's a normal group node, not to be binned. Traverse. + collect_nodes(DCAST(EggGroupNode, node)); + } + + i = next; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggBinMaker::get_bins_for_group +// Access: Private +// Description: Breaks the set of nodes for a given group up into +// individual bins. +//////////////////////////////////////////////////////////////////// +int EggBinMaker:: +get_bins_for_group(GroupNodes::const_iterator gi) { + EggGroupNode *group = (*gi).first; + const SortedNodes &nodes = (*gi).second; + + // It shouldn't be possible for this to be empty. + nassertr(!nodes.empty(), 0); + + Bins bins; + EggBinMakerCompareNodes cn(this); + SortedNodes::const_iterator sni, last; + sni = nodes.begin(); + last = sni; + + bins.push_back(Nodes()); + bins.back().push_back(*sni); + ++sni; + while (sni != nodes.end()) { + if (cn(*last, *sni)) { + // Begin a new bin. + bins.push_back(Nodes()); + } + bins.back().push_back(*sni); + + last = sni; + ++sni; + } + + make_bins_for_group(group, bins); + return bins.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggBinMaker::make_bins_for_group +// Access: Private +// Description: Creates the EggBin nodes indicated by the internal +// bin structure for each group. +//////////////////////////////////////////////////////////////////// +void EggBinMaker:: +make_bins_for_group(EggGroupNode *group, const Bins &bins) { + // We shouldn't be able to get here if we have no bins! + nassertv(!bins.empty()); + + // If the group will have only one bin, and no other children, and + // the group is not the root node (and it is not some funny + // group-like node like a
), maybe we should collapse the + // group and its bin together. + + bool collapse = false; + + if (group->empty() && + bins.size() == 1 && + group->get_parent() != NULL && + group->is_of_type(EggGroup::get_class_type())) { + const Nodes &nodes = bins.front(); + nassertv(!nodes.empty()); + int bin_number = get_bin_number(nodes.front()); + collapse = collapse_group(DCAST(EggGroup, group), bin_number); + } + + if (collapse) { + EggBin *bin = new EggBin(*DCAST(EggGroup, group)); + setup_bin(bin, bins.front()); + + EggGroupNode *parent = group->get_parent(); + parent->remove_child(group); + parent->add_child(bin); + + } else { + Bins::const_iterator bi; + for (bi = bins.begin(); bi != bins.end(); ++bi) { + EggBin *bin = new EggBin; + setup_bin(bin, *bi); + + group->add_child(bin); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggBinMaker::setup_bin +// Access: Private +// Description: Sets up a recently-created EggBin structure with all +// of its children. +//////////////////////////////////////////////////////////////////// +void EggBinMaker:: +setup_bin(EggBin *bin, const Nodes &nodes) { + nassertv(!nodes.empty()); + int bin_number = get_bin_number(nodes.front()); + bin->set_bin_number(bin_number); + + string bin_name = get_bin_name(bin_number); + if (!bin_name.empty()) { + bin->set_name(bin_name); + } + + Nodes::const_iterator ni; + for (ni = nodes.begin(); ni != nodes.end(); ++ni) { + bin->add_child(*ni); + } +} + diff --git a/panda/src/egg/eggBinMaker.h b/panda/src/egg/eggBinMaker.h new file mode 100644 index 0000000000..b2272f2fb6 --- /dev/null +++ b/panda/src/egg/eggBinMaker.h @@ -0,0 +1,299 @@ +// Filename: eggBinMaker.h +// Created by: drose (21Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGBINMAKER_H +#define EGGBINMAKER_H + +/////////////////////////////////////////////////////////////////// +// +// EggBinMaker +// +// This is a handy class for collecting related nodes together. Its +// purpose is to make it easier to process egg files for converting to +// another scene graph format. Egg is very general and allows nodes +// to be parented willy-nilly anywhere you like, while many other +// scene graph formats have requirements that certain kinds of nodes +// be grouped together. +// +// Although EggBinMaker can be used to group any kinds of nodes +// together, one of the most common examples is grouping polygons into +// polysets. Egg allows individual polygons to be parented directly +// to any group node, while most scene graph formats prefer to have +// polygons with similar attributes grouped into some kind of a +// polyset node. Therefore, the following usage discussion will use +// grouping polygons into polysets as an example. +// +// EggBinMaker is actually an abstract class; it cannot be used +// directly. To use it, you must create a subclass and redefine some +// or all of its virtual functions to specify the precise behavior you +// require. +// +// You must define at least the following function: +// +// virtual int get_bin_number(const EggNode *node); +// +// This function identifies the kinds of nodes in the graph, for +// instance EggPolygons, that are to be put into bins. It will be +// called once for each node encountered, and it should return +// nonzero if the node is to be binned, and zero otherwise. To +// group polygons into polysets, this function might look like: +// +// virtual int get_bin_number(const EggNode *node) { +// if (node->is_of_type(EggPolygon::get_class_type())) { +// return 1; +// } else { +// return 0; +// } +// } +// + +// This function may also return the bin number that a given node +// should be dropped into. The bin number is completely arbitrary, +// and it just serves to differentiate different bins. +// +// By default, all sibling nodes will be dropped into the same bin; +// you can redefine this to sort nodes further into categories. +// For instance, if you wanted to put textured polygons into a +// different polyset than untextured polygons, you might define +// this function as follows: +// +// virtual int get_bin_number(const EggNode *node) { +// if (node->is_of_type(EggPolygon::get_class_type())) { +// EggPolygon *poly = DCAST(EggPolygon, node); +// return (poly->has_texture()) ? 1 : 2; +// } else { +// return 0; +// } +// } +// +// Of course, unrelated nodes--nodes that belong to different +// parents--will never be placed into the same bin together, +// regardless of the bin number. +// +// It is important to note that it is not necessarily true that +// there is only one bin for each bin number. If you redefine +// sorts_less(), below, you provide a finer-grained control that +// may create multiple bins for a given bin number. +// +// This function may be called several times for a given node, and +// it should return the same number each time. +// +// +// You may also redefine any or all of the following functions: +// +// virtual bool sorts_less(int bin_number, const EggNode *a, const EggNode *b); +// +// Sometimes a simple bin number alone is not enough. For +// instance, suppose you needed to group together not just all +// textured polygons, but all polygons that shared a particular +// texture map. Two polygons that are each textured with a +// different texture map should go into different polysets. To do +// this with bin numbers, you'd have to know ahead of time all the +// texture maps that are in use, and assign a unique number to each +// one. +// +// sorts_less() can make this unnecessary. It's a finer-grained +// sorting than by bin numbers. Once two nodes have been grouped +// together into the same bin number, sorts_less is called on them. +// If it returns true, then node a should be placed into an earlier +// bin than node b, even though they share the same bin number. If +// sorts_less(a, b) and sorts_less(b, a) both return false, then +// nodes a and b are placed into the same bin. +// +// To continue the example, and sort polygons into different bins +// based on the texture map: +// +// virtual bool sorts_less(int bin_number, +// const EggNode *a, const EggNode *b) { +// if (bin_number == 2) { +// // bin 2, textured geometry +// return (a->get_texture() < b->get_texture()); +// } else { +// // bin 1, untextured geometry +// return false; +// } +// } +// +// The actual comparison can be arbitrary, as long as it is +// consistent. Its only purpose is to assign some ordering among +// bins. In the example, for instance, the comparison is based on +// the pointer to the texture maps--it doesn't matter which comes +// before the other, as long as it's consistent. +// +// In particular, it should never be true that sorts_less(a, b) and +// sorts_less(b, a) both return true--that is a clear +// contradiction. +// +// Of course, if you're using sorts_less() anyway, you could put +// *all* of the logic for binning into this function; there's no +// need to use both get_bin_number() and sorts_less(), necessarily. +// In the current example, here's another version of sorts_less() +// that accomplishes the same thing as the combined effects of the +// above get_bin_number() and sorts_less() working together: +// +// virtual bool sorts_less(int bin_number, +// const EggNode *a, const EggNode *b) { +// if (a->has_texture() != b->has_texture()) { +// return ((int)a->has_texture() < (int)b->has_texture()); +// } +// if (a->has_texture()) { +// return (a->get_texture() < b->get_texture()); +// } +// return false; +// } +// +// +// virtual bool collapse_group(const EggGroup *group, int bin_number); +// +// After all the nodes have been assigned to bins and the +// individual bins (polysets) have been created, it might turn out +// that some groups have had all their children placed into the +// same bin. In this case, the group node is now redundant, since +// it contains just the one child, the new EggBin (polyset) node. +// It might be advantageous to remove the group and collapse its +// properties into the new node. +// +// In this case (and this case only), collapse_group() will be +// called, given the node and the bin number. If it returns true, +// the node will indeed be collapsed into its bin; otherwise, they +// will be left separate. +// +// The point is that there might be some attributes in the group +// node (for instance, a matrix transform) that cannot be +// represented in a polyset node in the new scene graph format, so +// there may be some cases in which the group cannot be safely +// collapsed. Since the egg library cannot know about which such +// cases cause problems, it leaves it up to you. The default +// behavior is never to collapse nodes. +// +// +// virtual string get_bin_name(int bin_number); +// +// This function is called as each new bin is created, to +// optionally define a name for the new node. If it returns the +// empty string, the node name will be empty, unless it was +// collapsed with its parent group, in which case it will inherit +// its former parent's name. +// +// +// +// Once you have subclassed EggBinMaker and defined the functions as +// you require, you use it by simply calling make_bins() one or more +// times, passing it the pointer to the root of the scene graph or of +// some subgraph. It will traverse the subgraph and create a series +// of EggBin objects, as required, moving all the binned geometry +// under the EggBin objects. The return value is the number of +// EggBins created. Each EggBin stores its bin number, which may be +// retrieved via get_bin_number(). +// +/////////////////////////////////////////////////////////////////// + + +#include + +#include "eggObject.h" + +#include +#include + +#include +#include + +class EggNode; +class EggGroup; +class EggGroupNode; +class EggBin; +class EggBinMaker; + +/////////////////////////////////////////////////////////////////// +// Class : EggBinMakerCompareNodes +// Description : This is just an STL function object, used to sort +// nodes within EggBinMaker. It's part of the private +// interface; ignore it. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggBinMakerCompareNodes { +public: + EggBinMakerCompareNodes() { + // We need to have a default constructor to compile, but it should + // never be called. + nassertv(false); + } + EggBinMakerCompareNodes(EggBinMaker *ebm) : _ebm(ebm) { } + bool operator ()(const EggNode *a, const EggNode *b) const; + + EggBinMaker *_ebm; +}; + + +/////////////////////////////////////////////////////////////////// +// Class : EggBinMaker +// Description : This is a handy class for collecting related nodes +// together. It is an abstract class; to use it you +// must subclass off of it. See the somewhat lengthy +// comment above. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggBinMaker : public EggObject { +public: + EggBinMaker() { } + + int make_bins(EggGroupNode *root_group); + + virtual int + get_bin_number(const EggNode *node)=0; + + virtual bool + sorts_less(int bin_number, const EggNode *a, const EggNode *b); + + virtual bool + collapse_group(const EggGroup *group, int bin_number); + + virtual string + get_bin_name(int bin_number); + +private: + // The logic is two-pass. First, we make a scene graph traversal + // and store all the pointers into the GroupNodes/SortedNodes + // structure, which groups nodes by their parent group, and then + // sorted into bin order. + typedef multiset SortedNodes; + typedef map GroupNodes; + + // Then we walk through that list and create a Bins/Nodes structure + // for each group, which separates out the nodes into the individual + // bins. + typedef vector Nodes; + typedef vector Bins; + + void collect_nodes(EggGroupNode *group); + int get_bins_for_group(GroupNodes::const_iterator gi); + void make_bins_for_group(EggGroupNode *group, const Bins &bins); + void setup_bin(EggBin *bin, const Nodes &nodes); + + GroupNodes _group_nodes; + + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggObject::init_type(); + register_type(_type_handle, "EggBinMaker", + EggObject::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + +}; + +#endif + + diff --git a/panda/src/egg/eggComment.I b/panda/src/egg/eggComment.I new file mode 100644 index 0000000000..96deb9e88f --- /dev/null +++ b/panda/src/egg/eggComment.I @@ -0,0 +1,82 @@ +// Filename: eggComment.I +// Created by: drose (20Jan99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggComment::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggComment:: +EggComment(const string &node_name, const string &comment) + : EggNode(node_name), _comment(comment) { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggComment::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggComment:: +EggComment(const EggComment ©) : EggNode(copy), _comment(copy._comment) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggComment::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggComment &EggComment:: +operator = (const string &comment) { + _comment = comment; + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggComment::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggComment &EggComment:: +operator = (const EggComment ©) { + _comment = copy._comment; + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggComment::String typecast operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggComment:: +operator const string & () const { + return _comment; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggComment::set_comment +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggComment:: +set_comment(const string &comment) { + _comment = comment; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggComment::set_comment +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE string EggComment:: +get_comment() const { + return _comment; +} + diff --git a/panda/src/egg/eggComment.cxx b/panda/src/egg/eggComment.cxx new file mode 100644 index 0000000000..ba8b2a477c --- /dev/null +++ b/panda/src/egg/eggComment.cxx @@ -0,0 +1,26 @@ +// Filename: eggComment.cxx +// Created by: drose (20Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggComment.h" +#include "eggMiscFuncs.h" + +#include +#include + +TypeHandle EggComment::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: EggComment::write +// Access: Public, Virtual +// Description: Writes the comment definition to the indicated output +// stream in Egg format. +//////////////////////////////////////////////////////////////////// +void EggComment:: +write(ostream &out, int indent_level) const { + write_header(out, indent_level, ""); + enquote_string(out, get_comment(), indent_level + 2) << "\n"; + indent(out, indent_level) << "}\n"; +} diff --git a/panda/src/egg/eggComment.h b/panda/src/egg/eggComment.h new file mode 100644 index 0000000000..f93b8b4e62 --- /dev/null +++ b/panda/src/egg/eggComment.h @@ -0,0 +1,65 @@ +// Filename: eggComment.h +// Created by: drose (20Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGCOMMENT_H +#define EGGCOMMENT_H + +#include + +#include "eggNode.h" + +#include + +/////////////////////////////////////////////////////////////////// +// Class : EggComment +// Description : A comment that appears in an egg file within a +// entry. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggComment : public EggNode { +public: + INLINE EggComment(const string &node_name, const string &comment); + INLINE EggComment(const EggComment ©); + + // You can use the string operators to directly set and manipulate + // the comment. + + INLINE EggComment &operator = (const string &comment); + INLINE EggComment &operator = (const EggComment ©); + + INLINE operator const string & () const; + + // Or, you can set and get it explicitly. + + INLINE void set_comment(const string &comment); + INLINE string get_comment() const; + + virtual void write(ostream &out, int indent_level) const; + +private: + string _comment; + + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggNode::init_type(); + register_type(_type_handle, "EggComment", + EggNode::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "eggComment.I" + +#endif diff --git a/panda/src/egg/eggCoordinateSystem.I b/panda/src/egg/eggCoordinateSystem.I new file mode 100644 index 0000000000..a69674fac7 --- /dev/null +++ b/panda/src/egg/eggCoordinateSystem.I @@ -0,0 +1,49 @@ +// Filename: eggCoordinateSystem.I +// Created by: drose (20Jan99) +// +//////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////// +// Function: EggCoordinateSystem::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggCoordinateSystem:: +EggCoordinateSystem(CoordinateSystem value) { + _value = value; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggCoordinateSystem::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggCoordinateSystem:: +EggCoordinateSystem(const EggCoordinateSystem ©) + : EggNode(copy), _value(copy._value) { } + + +//////////////////////////////////////////////////////////////////// +// Function: EggCoordinateSystem::set_value +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggCoordinateSystem:: +set_value(CoordinateSystem value) { + _value = value; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggCoordinateSystem::set_value +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CoordinateSystem EggCoordinateSystem:: +get_value() const { + return _value; +} + diff --git a/panda/src/egg/eggCoordinateSystem.cxx b/panda/src/egg/eggCoordinateSystem.cxx new file mode 100644 index 0000000000..c1613b07ef --- /dev/null +++ b/panda/src/egg/eggCoordinateSystem.cxx @@ -0,0 +1,44 @@ +// Filename: eggCoordinateSystem.cxx +// Created by: drose (20Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggCoordinateSystem.h" + +#include + +TypeHandle EggCoordinateSystem::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: EggCoordinateSystem::write +// Access: Public, Virtual +// Description: Writes the coordinate system definition to the +// indicated output stream in Egg format. +//////////////////////////////////////////////////////////////////// +void EggCoordinateSystem:: +write(ostream &out, int indent_level) const { + if (get_value() != CS_default && + get_value() != CS_invalid) { + indent(out, indent_level) + << " { "; + switch (get_value()) { + case CS_zup_right: + out << "Z-Up"; + break; + + case CS_yup_right: + out << "Y-Up"; + break; + + case CS_zup_left: + out << "Z-Up-Left"; + break; + + case CS_yup_left: + out << "Y-Up-Left"; + break; + } + out << " }\n\n"; + } +} diff --git a/panda/src/egg/eggCoordinateSystem.h b/panda/src/egg/eggCoordinateSystem.h new file mode 100644 index 0000000000..77f84d0788 --- /dev/null +++ b/panda/src/egg/eggCoordinateSystem.h @@ -0,0 +1,58 @@ +// Filename: eggCoordinateSystem.h +// Created by: drose (20Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGCOORDINATESYSTEM_H +#define EGGCOORDINATESYSTEM_H + +#include + +#include "eggNode.h" +#include "eggData.h" +#include + + +/////////////////////////////////////////////////////////////////// +// Class : EggCoordinateSystem +// Description : The entry at the top of an egg +// file. Don't confuse this with the enum +// EggData::CoordinateSystem, which is the value +// contained by this entry. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggCoordinateSystem : public EggNode { +public: + INLINE EggCoordinateSystem(CoordinateSystem value = CS_default); + INLINE EggCoordinateSystem(const EggCoordinateSystem ©); + + INLINE void set_value(CoordinateSystem value); + INLINE CoordinateSystem get_value() const; + + virtual void write(ostream &out, int indent_level) const; + +private: + CoordinateSystem _value; + + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggNode::init_type(); + register_type(_type_handle, "EggCoordinateSystem", + EggNode::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "eggCoordinateSystem.I" + +#endif diff --git a/panda/src/egg/eggCurve.I b/panda/src/egg/eggCurve.I new file mode 100644 index 0000000000..1ea693fa97 --- /dev/null +++ b/panda/src/egg/eggCurve.I @@ -0,0 +1,92 @@ +// Filename: eggCurve.I +// Created by: drose (15Feb00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggCurve::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggCurve:: +EggCurve(const string &name) : EggPrimitive(name) { + _subdiv = 0; + _type = CT_none; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggCurve::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggCurve:: +EggCurve(const EggCurve ©) : + EggPrimitive(copy), + _subdiv(copy._subdiv), + _type(copy._type) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: EggCurve::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggCurve &EggCurve:: +operator = (const EggCurve ©) { + EggPrimitive::operator = (copy); + _subdiv = copy._subdiv; + _type = copy._type; + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggCurve::set_subdiv +// Access: Public +// Description: Sets the number of subdivisions that will be +// requested across the curve. (This doesn't necessary +// guarantee that this number of subdivisions will be +// made; it's just a hint to any curve renderer or quick +// tesselator.) Set the number to 0 to disable the +// hint. +//////////////////////////////////////////////////////////////////// +INLINE void EggCurve:: +set_subdiv(int subdiv) { + _subdiv = subdiv; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggCurve::get_subdiv +// Access: Public +// Description: Returns the requested number of subdivisions, or 0 if +// no particular subdivisions have been requested. +//////////////////////////////////////////////////////////////////// +INLINE int EggCurve:: +get_subdiv() const { + return _subdiv; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggCurve::set_curve_type +// Access: Public +// Description: Sets the type of the curve. This is primarily used +// as a hint to any code that may need to deal with this +// curve. +//////////////////////////////////////////////////////////////////// +INLINE void EggCurve:: +set_curve_type(EggCurve::CurveType type) { + _type = type; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggCurve::get_curve_type +// Access: Public +// Description: Returns the indicated type of the curve. +//////////////////////////////////////////////////////////////////// +INLINE EggCurve::CurveType EggCurve:: +get_curve_type() const { + return _type; +} diff --git a/panda/src/egg/eggCurve.cxx b/panda/src/egg/eggCurve.cxx new file mode 100644 index 0000000000..84656a8865 --- /dev/null +++ b/panda/src/egg/eggCurve.cxx @@ -0,0 +1,53 @@ +// Filename: eggCurve.cxx +// Created by: drose (15Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "eggCurve.h" + +#include +#include + +TypeHandle EggCurve::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: EggCurve::string_curve_type +// Access: Public, Static +// Description: Returns the CurveType value associated with the given +// string representation, or CT_invalid if the string +// does not match any known CurveType value. +//////////////////////////////////////////////////////////////////// +EggCurve::CurveType EggCurve:: +string_curve_type(const string &string) { + if (cmp_nocase_uh(string, "xyz") == 0) { + return CT_xyz; + } else if (cmp_nocase_uh(string, "hpr") == 0) { + return CT_hpr; + } else if (cmp_nocase_uh(string, "t") == 0) { + return CT_t; + } else { + return CT_none; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CurveType output operator +// Description: +//////////////////////////////////////////////////////////////////// +ostream &operator << (ostream &out, EggCurve::CurveType t) { + switch (t) { + case EggCurve::CT_none: + return out << "none"; + case EggCurve::CT_xyz: + return out << "XYZ"; + case EggCurve::CT_hpr: + return out << "HPR"; + case EggCurve::CT_t: + return out << "T"; + } + + nassertr(false, out); + return out << "(**invalid**)"; +} + diff --git a/panda/src/egg/eggCurve.h b/panda/src/egg/eggCurve.h new file mode 100644 index 0000000000..3e929e0823 --- /dev/null +++ b/panda/src/egg/eggCurve.h @@ -0,0 +1,66 @@ +// Filename: eggCurve.h +// Created by: drose (15Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGCURVE_H +#define EGGCURVE_H + +#include + +#include "eggPrimitive.h" + +//////////////////////////////////////////////////////////////////// +// Class : EggCurve +// Description : A parametric curve of some kind. See +// EggNurbsCurve. +//////////////////////////////////////////////////////////////////// +class EggCurve : public EggPrimitive { +public: + INLINE EggCurve(const string &name = ""); + INLINE EggCurve(const EggCurve ©); + INLINE EggCurve &operator = (const EggCurve ©); + + enum CurveType { + CT_none, + CT_xyz, + CT_hpr, + CT_t + }; + + INLINE void set_subdiv(int subdiv); + INLINE int get_subdiv() const; + + INLINE void set_curve_type(CurveType type); + INLINE CurveType get_curve_type() const; + + static CurveType string_curve_type(const string &string); + +private: + int _subdiv; + CurveType _type; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggPrimitive::init_type(); + register_type(_type_handle, "EggCurve", + EggPrimitive::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +ostream &operator << (ostream &out, EggCurve::CurveType t); + +#include "eggCurve.I" + +#endif diff --git a/panda/src/egg/eggData.I b/panda/src/egg/eggData.I new file mode 100644 index 0000000000..4d4247c129 --- /dev/null +++ b/panda/src/egg/eggData.I @@ -0,0 +1,78 @@ +// Filename: eggData.I +// Created by: drose (11Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggData::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggData:: +EggData() { + _coordsys = CS_default; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggData::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggData:: +EggData(const EggData ©) : + EggGroupNode(copy), + _coordsys(copy._coordsys), + _egg_filename(copy._egg_filename) { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggData::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggData &EggData:: +operator = (const EggData ©) { + EggGroupNode::operator = (copy); + _coordsys = copy._coordsys; + _egg_filename = copy._egg_filename; + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggData::get_coordinate_system +// Access: Public +// Description: Returns the coordinate system in which the egg file +// is defined. +//////////////////////////////////////////////////////////////////// +INLINE CoordinateSystem EggData:: +get_coordinate_system() const { + return _coordsys; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggData::set_egg_filename +// Access: Public +// Description: Sets the directory in which the egg file is +// considered to reside. This is also implicitly set by +// read(). +//////////////////////////////////////////////////////////////////// +INLINE void EggData:: +set_egg_filename(const Filename &directory) { + _egg_filename = directory; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggData::get_egg_filename +// Access: Public +// Description: Returns the directory in which the egg file is +// considered to reside. +//////////////////////////////////////////////////////////////////// +INLINE const Filename &EggData:: +get_egg_filename() const { + return _egg_filename; +} + diff --git a/panda/src/egg/eggData.cxx b/panda/src/egg/eggData.cxx new file mode 100644 index 0000000000..8e9085fe6a --- /dev/null +++ b/panda/src/egg/eggData.cxx @@ -0,0 +1,267 @@ +// Filename: eggData.cxx +// Created by: drose (20Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggData.h" +#include "eggCoordinateSystem.h" +#include "eggTextureCollection.h" +#include "eggComment.h" +#include "config_egg.h" + +#include +#include +#include + +extern int eggyyparse(void); +#include "parserDefs.h" +#include "lexerDefs.h" + +//////////////////////////////////////////////////////////////////// +// Function: EggData::resolve_egg_filename +// Access: Public, Static +// Description: Looks for the indicated filename, first along the +// indicated searchpath, and then along the egg_path and +// finally along the model_path. If found, updates the +// filename to the full path and returns true; +// otherwise, returns false. +//////////////////////////////////////////////////////////////////// +bool EggData:: +resolve_egg_filename(Filename &egg_filename, const DSearchPath &searchpath) { + egg_filename.resolve_filename(searchpath, "egg"); + egg_filename.resolve_filename(get_egg_path(), "egg"); + egg_filename.resolve_filename(get_model_path(), "egg"); + return egg_filename.exists(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggData::read +// Access: Public +// Description: Opens the indicated filename and reads the egg data +// contents from it. Returns true if the file was +// successfully opened and read, false if there were +// some errors, in which case the data may be partially +// read. +// +// error is the output stream to which to write error +// messages. +//////////////////////////////////////////////////////////////////// +bool EggData:: +read(Filename filename) { + if (!resolve_egg_filename(filename)) { + egg_cat.error() + << "Could not find " << filename << "\n"; + return false; + } + + filename.set_text(); + set_egg_filename(filename); + + ifstream file; + if (!filename.open_read(file)) { + egg_cat.error() << "Unable to open " << filename << "\n"; + return false; + } + + egg_cat.info() + << "Reading " << filename << "\n"; + + return read(file); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggData::read +// Access: Public +// Description: Parses the egg syntax contained in the indicated +// input stream. Returns true if the stream was a +// completely valid egg file, false if there were some +// errors, in which case the data may be partially read. +// +// Before you call this routine, you should probably +// call set_egg_filename() to set the name of the egg +// file we're processing, if at all possible. If there +// is no such filename, you may set it to the empty +// string. +//////////////////////////////////////////////////////////////////// +bool EggData:: +read(istream &in) { + // First, dispense with any children we had previously. We will + // replace them with the new data. + clear(); + + // Create a temporary EggData structure to read into. We initialize + // it with a copy of ourselves, so that it will get our _coordsys + // value, if the user set it. + PT(EggData) data = new EggData(*this); + egg_init_parser(in, get_egg_filename(), data, data); + eggyyparse(); + egg_cleanup_parser(); + + data->post_read(); + + steal_children(*data); + (*this) = *data; + + return (egg_error_count() == 0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggData::resolve_externals +// Access: Public +// Description: Loads up all the egg files referenced by +// entries within the egg structure, and inserts their +// contents in place of the entries. Searches +// for files in the searchpath, if not found directly, +// and writes error messages to the indicated output +// stream. Returns true if all externals were loaded +// successfully, false otherwise. +//////////////////////////////////////////////////////////////////// +bool EggData:: +resolve_externals(const DSearchPath &searchpath) { + return + r_resolve_externals(searchpath, get_coordinate_system()); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggData::write_egg +// Access: Public +// Description: The main interface for writing complete egg files. +//////////////////////////////////////////////////////////////////// +bool EggData:: +write_egg(Filename filename) { + filename.set_text(); + filename.unlink(); + + ofstream file; + if (!filename.open_write(file)) { + egg_cat.error() << "Unable to open " << filename << " for writing.\n"; + return false; + } + + return write_egg(file); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggData::write_egg +// Access: Public +// Description: The main interface for writing complete egg files. +//////////////////////////////////////////////////////////////////// +bool EggData:: +write_egg(ostream &out) { + pre_write(); + write(out, 0); + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggData::set_coordinate_system +// Access: Public +// Description: Changes the coordinate system of the EggData. If the +// coordinate system was previously different, this may +// result in a conversion of the data. +//////////////////////////////////////////////////////////////////// +void EggData:: +set_coordinate_system(CoordinateSystem new_coordsys) { + if (new_coordsys == CS_default) { + new_coordsys = default_coordinate_system; + } + if (new_coordsys != _coordsys && + (_coordsys != CS_default && _coordsys != CS_invalid)) { + // Time to convert the data. + r_transform(LMatrix4d::convert_mat(_coordsys, new_coordsys), + LMatrix4d::convert_mat(new_coordsys, _coordsys), + new_coordsys); + } + + _coordsys = new_coordsys; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggData::post_read +// Access: Private +// Description: Does whatever processing is appropriate after reading +// the data in from an egg file. +//////////////////////////////////////////////////////////////////// +void EggData:: +post_read() { + CoordinateSystem old_coordsys = _coordsys; + _coordsys = find_coordsys_entry(); + + if (_coordsys == CS_default) { + // If the egg file didn't contain a entry, + // assume it's Y-up, by convention. + _coordsys = CS_yup_right; + + } else if (_coordsys == CS_invalid) { + egg_cat.warning() + << "Contradictory entries encountered.\n"; + _coordsys = CS_yup_right; + } + + r_mark_coordsys(_coordsys); + + if (old_coordsys != CS_default) { + // Now if we had a previous definition, enforce it. This might + // convert the data to the given coordinate system. + set_coordinate_system(old_coordsys); + } + + // Resolve filenames that are relative to the egg file. + DSearchPath dir; + dir.append_directory(get_egg_filename().get_dirname()); + resolve_filenames(dir); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggData::pre_write +// Access: Private +// Description: Does whatever processing is appropriate just before +// writing the data out to an egg file. This includes +// verifying that vertex pool names are unique, etc. +//////////////////////////////////////////////////////////////////// +void EggData:: +pre_write() { + // Pull out all of the texture definitions in the file and massage + // them a bit. + EggTextureCollection textures; + textures.extract_textures(this); + + // Remove any textures that aren't being used. + textures.remove_unused_textures(this); + + // Collapse out any textures that are equivalent except for the TRef + // name. + textures.collapse_equivalent_textures(~EggTexture::E_tref_name, this); + + // Make sure all of the textures have unique TRef names. + textures.uniquify_trefs(); + textures.sort_by_tref(); + + // Now put them all back at the head of the file, after any initial + // comment records. + iterator ci = begin(); + while (ci != end() && (*ci)->is_of_type(EggComment::get_class_type())) { + ++ci; + } + + textures.insert_textures(this, ci); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggData::write +// Access: Public, Virtual +// Description: Writes the egg data out to the indicated output +// stream. +//////////////////////////////////////////////////////////////////// +void EggData:: +write(ostream &out, int indent_level) const { + EggCoordinateSystem ecs(_coordsys); + ecs.write(out, indent_level); + EggGroupNode::write(out, indent_level); + out << flush; +} + diff --git a/panda/src/egg/eggData.h b/panda/src/egg/eggData.h new file mode 100644 index 0000000000..0fbaa8e7f6 --- /dev/null +++ b/panda/src/egg/eggData.h @@ -0,0 +1,69 @@ +// Filename: eggData.h +// Created by: drose (20Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGDATA_H +#define EGGDATA_H + +#include + +#include "eggGroupNode.h" +#include +#include +#include +#include + +#include + + +/////////////////////////////////////////////////////////////////// +// Class : EggData +// Description : This is the primary interface into all the egg data, +// and the root of the egg file structure. An EggData +// structure corresponds exactly with an egg file on the +// disk. +// +// The EggData class inherits from EggGroupNode its +// collection of children, which are accessed by using +// the EggData itself as an STL container with begin() +// and end() calls. The children of the EggData class +// are the toplevel nodes in the egg file. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggData : public EggGroupNode { +public: + INLINE EggData(); + INLINE EggData(const EggData ©); + INLINE EggData &operator = (const EggData ©); + + static bool resolve_egg_filename(Filename &egg_filename, + const DSearchPath &searchpath = DSearchPath()); + + bool read(Filename filename); + bool read(istream &in); + + bool resolve_externals(const DSearchPath &searchpath = DSearchPath()); + + bool write_egg(Filename filename); + bool write_egg(ostream &out); + + void set_coordinate_system(CoordinateSystem coordsys); + INLINE CoordinateSystem get_coordinate_system() const; + + INLINE void set_egg_filename(const Filename &directory); + INLINE const Filename &get_egg_filename() const; + +protected: + virtual void write(ostream &out, int indent_level = 0) const; + +private: + void post_read(); + void pre_write(); + + CoordinateSystem _coordsys; + Filename _egg_filename; +}; + +#include "eggData.I" + +#endif diff --git a/panda/src/egg/eggExternalReference.I b/panda/src/egg/eggExternalReference.I new file mode 100644 index 0000000000..9e5ff50492 --- /dev/null +++ b/panda/src/egg/eggExternalReference.I @@ -0,0 +1,38 @@ +// Filename: eggExternalReference.I +// Created by: drose (11Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggExternalReference::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggExternalReference &EggExternalReference:: +operator = (const string &filename) { + EggFilenameNode::operator = (filename); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggExternalReference::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggExternalReference &EggExternalReference:: +operator = (const char *filename) { + EggFilenameNode::operator = (filename); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggExternalReference::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggExternalReference &EggExternalReference:: +operator = (const Filename &filename) { + EggFilenameNode::operator = (filename); + return *this; +} diff --git a/panda/src/egg/eggExternalReference.cxx b/panda/src/egg/eggExternalReference.cxx new file mode 100644 index 0000000000..adfaa91610 --- /dev/null +++ b/panda/src/egg/eggExternalReference.cxx @@ -0,0 +1,67 @@ +// Filename: eggExternalReference.cxx +// Created by: drose (11Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggExternalReference.h" +#include "eggMiscFuncs.h" + +#include +#include + +TypeHandle EggExternalReference::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: EggExternalReference::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggExternalReference:: +EggExternalReference(const string &node_name, const string &filename) + : EggFilenameNode(node_name, filename) { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggExternalReference::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggExternalReference:: +EggExternalReference(const EggExternalReference ©) + : EggFilenameNode(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggExternalReference::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggExternalReference &EggExternalReference:: +operator = (const EggExternalReference ©) { + EggFilenameNode::operator = (copy); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggExternalReference::write +// Access: Public, Virtual +// Description: Writes the reference to the indicated output +// stream in Egg format. +//////////////////////////////////////////////////////////////////// +void EggExternalReference:: +write(ostream &out, int indent_level) const { + write_header(out, indent_level, ""); + enquote_string(out, get_fullpath(), indent_level + 2) << "\n"; + indent(out, indent_level) << "}\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggExternalReference::get_default_extension +// Access: Public, Virtual +// Description: Returns the default extension for this filename type. +//////////////////////////////////////////////////////////////////// +string EggExternalReference:: +get_default_extension() const { + return string("egg"); +} diff --git a/panda/src/egg/eggExternalReference.h b/panda/src/egg/eggExternalReference.h new file mode 100644 index 0000000000..5a6556db36 --- /dev/null +++ b/panda/src/egg/eggExternalReference.h @@ -0,0 +1,53 @@ +// Filename: eggExternalReference.h +// Created by: drose (11Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGEXTERNALREFERENCE_H +#define EGGEXTERNALREFERENCE_H + +#include + +#include "eggFilenameNode.h" + +//////////////////////////////////////////////////////////////////// +// Class : EggExternalReference +// Description : Defines a reference to another egg file which should +// be inserted at this point. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggExternalReference : public EggFilenameNode { +public: + EggExternalReference(const string &node_name, const string &filename); + EggExternalReference(const EggExternalReference ©); + EggExternalReference &operator = (const EggExternalReference ©); + + INLINE EggExternalReference &operator = (const string &filename); + INLINE EggExternalReference &operator = (const char *filename); + INLINE EggExternalReference &operator = (const Filename ©); + + virtual void write(ostream &out, int indent_level) const; + + virtual string get_default_extension() const; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggFilenameNode::init_type(); + register_type(_type_handle, "EggExternalReference", + EggFilenameNode::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "eggExternalReference.I" + +#endif diff --git a/panda/src/egg/eggFilenameNode.I b/panda/src/egg/eggFilenameNode.I new file mode 100644 index 0000000000..6e71ca90b8 --- /dev/null +++ b/panda/src/egg/eggFilenameNode.I @@ -0,0 +1,80 @@ +// Filename: eggFilenameNode.I +// Created by: drose (11Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggFilenameNode::Default constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggFilenameNode:: +EggFilenameNode() { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggFilenameNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggFilenameNode:: +EggFilenameNode(const string &node_name, const string &filename) : + EggNode(node_name), Filename(filename) { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggFilenameNode::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggFilenameNode:: +EggFilenameNode(const EggFilenameNode ©) : + EggNode(copy), Filename(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggFilenameNode::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggFilenameNode &EggFilenameNode:: +operator = (const EggFilenameNode ©) { + EggNode::operator = (copy); + Filename::operator = (copy); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggFilenameNode::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggFilenameNode &EggFilenameNode:: +operator = (const string &filename) { + Filename::operator = (filename); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggFilenameNode::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggFilenameNode &EggFilenameNode:: +operator = (const char *filename) { + Filename::operator = (filename); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggFilenameNode::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggFilenameNode &EggFilenameNode:: +operator = (const Filename &filename) { + Filename::operator = (filename); + return *this; +} + diff --git a/panda/src/egg/eggFilenameNode.cxx b/panda/src/egg/eggFilenameNode.cxx new file mode 100644 index 0000000000..cafc63c7ec --- /dev/null +++ b/panda/src/egg/eggFilenameNode.cxx @@ -0,0 +1,18 @@ +// Filename: eggFilenameNode.cxx +// Created by: drose (11Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggFilenameNode.h" + +TypeHandle EggFilenameNode::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: EggFilenameNode::get_default_extension +// Access: Public, Virtual +// Description: Returns the default extension for this filename type. +//////////////////////////////////////////////////////////////////// +string EggFilenameNode:: +get_default_extension() const { + return string(); +} diff --git a/panda/src/egg/eggFilenameNode.h b/panda/src/egg/eggFilenameNode.h new file mode 100644 index 0000000000..1c1bb12760 --- /dev/null +++ b/panda/src/egg/eggFilenameNode.h @@ -0,0 +1,54 @@ +// FilenameNode: eggFilenameNode.h +// Created by: drose (11Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGFILENAMENODE_H +#define EGGFILENAMENODE_H + +#include + +#include "eggNode.h" +#include + +//////////////////////////////////////////////////////////////////// +// Class : EggFilenameNode +// Description : This is an egg node that contains a filename. It +// references a physical file relative to the directory +// the egg file was loaded in. It is a base class for +// EggTexture and EggExternalReference. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggFilenameNode : public EggNode, public Filename { +public: + INLINE EggFilenameNode(); + INLINE EggFilenameNode(const string &node_name, const string &filename); + INLINE EggFilenameNode(const EggFilenameNode ©); + INLINE EggFilenameNode &operator = (const EggFilenameNode ©); + + INLINE EggFilenameNode &operator = (const string &filename); + INLINE EggFilenameNode &operator = (const char *filename); + INLINE EggFilenameNode &operator = (const Filename ©); + + virtual string get_default_extension() const; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggNode::init_type(); + register_type(_type_handle, "EggFilenameNode", + EggNode::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "eggFilenameNode.I" + +#endif diff --git a/panda/src/egg/eggGroup.I b/panda/src/egg/eggGroup.I new file mode 100644 index 0000000000..c105059260 --- /dev/null +++ b/panda/src/egg/eggGroup.I @@ -0,0 +1,407 @@ +// Filename: eggGroup.I +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +INLINE EggGroup::GroupType EggGroup:: +get_group_type() const { + return (GroupType)(_flags & F_group_type); +} + +INLINE void EggGroup:: +set_billboard_type(BillboardType type) { + // Make sure the user didn't give us any stray bits. + nassertv((type & ~F_billboard_type)==0); + _flags = (_flags & ~F_billboard_type) | type; +} +INLINE EggGroup::BillboardType EggGroup:: +get_billboard_type() const { + return (BillboardType)(_flags & F_billboard_type); +} + +INLINE void EggGroup:: +set_cs_type(CollisionSolidType type) { + // Make sure the user didn't give us any stray bits. + nassertv((type & ~F_cs_type)==0); + _flags = (_flags & ~F_cs_type) | type; +} +INLINE EggGroup::CollisionSolidType EggGroup:: +get_cs_type() const { + return (CollisionSolidType)(_flags & F_cs_type); +} + +INLINE void EggGroup:: +set_collision_name(const string &collision_name) { + _collision_name = collision_name; +} +INLINE void EggGroup:: +clear_collision_name() { + _collision_name = ""; +} +INLINE bool EggGroup:: +has_collision_name() const { + return !_collision_name.empty(); +} +INLINE const string &EggGroup:: +get_collision_name() const { + return _collision_name; +} + +INLINE void EggGroup:: +set_collide_flags(int flags) { + // Make sure the user didn't give us any stray bits. + nassertv((flags & ~F_collide_flags)==0); + _flags = (_flags & ~F_collide_flags) | flags; +} +INLINE EggGroup::CollideFlags EggGroup:: +get_collide_flags() const { + return (EggGroup::CollideFlags)(_flags & F_collide_flags); +} + +INLINE void EggGroup:: +set_dcs_flag(bool flag) { + if (flag) { + _flags |= F_dcs_flag; + } else { + _flags &= ~F_dcs_flag; + } +} +INLINE bool EggGroup:: +get_dcs_flag() const { + return ((_flags & F_dcs_flag) != 0); +} + +INLINE void EggGroup:: +set_dart_type(DartType type) { + // Make sure the user didn't give us any stray bits. + nassertv((type & ~F_dart_type)==0); + _flags = (_flags & ~F_dart_type) | type; +} +INLINE EggGroup::DartType EggGroup:: +get_dart_type() const { + return (DartType)(_flags & F_dart_type); +} + +INLINE void EggGroup:: +set_switch_flag(bool flag) { + if (flag) { + _flags |= F_switch_flag; + } else { + _flags &= ~F_switch_flag; + } +} +INLINE bool EggGroup:: +get_switch_flag() const { + return ((_flags & F_switch_flag) != 0); +} + +INLINE void EggGroup:: +set_switch_fps(double fps) { + _fps = fps; +} +INLINE double EggGroup:: +get_switch_fps() const { + return _fps; +} + +INLINE bool EggGroup:: +has_transform() const { + return (_flags & F_has_transform) != 0; +} +INLINE LMatrix4d EggGroup:: +get_transform() const { + nassertr(_flags & F_has_transform, _transform); + return _transform; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::transform_is_identity() +// Access: Public +// Description: Returns true if no transform matrix has been +// specified, or if the one specified is the identity +// transform. Returns false only if a nonidentity +// transform has been applied. +//////////////////////////////////////////////////////////////////// +INLINE bool EggGroup:: +transform_is_identity() const { + return (!has_transform() || + _transform.almost_equal(LMatrix4d::ident_mat(), 0.0001)); +} + +INLINE void EggGroup:: +set_objecttype(const string &objecttype) { + _objecttype = objecttype; +} +INLINE void EggGroup:: +clear_objecttype() { + _objecttype = ""; +} +INLINE bool EggGroup:: +has_objecttype() const { + return !_objecttype.empty(); +} +INLINE const string &EggGroup:: +get_objecttype() const { + return _objecttype; +} + +INLINE void EggGroup:: +set_model_flag(bool flag) { + if (flag) { + _flags |= F_model_flag; + } else { + _flags &= ~F_model_flag; + } +} +INLINE bool EggGroup:: +get_model_flag() const { + return ((_flags & F_model_flag) != 0); +} + +INLINE void EggGroup:: +set_texlist_flag(bool flag) { + if (flag) { + _flags |= F_texlist_flag; + } else { + _flags &= ~F_texlist_flag; + } +} +INLINE bool EggGroup:: +get_texlist_flag() const { + return ((_flags & F_texlist_flag) != 0); +} + +INLINE void EggGroup:: +set_nofog_flag(bool flag) { + if (flag) { + _flags |= F_nofog_flag; + } else { + _flags &= ~F_nofog_flag; + } +} +INLINE bool EggGroup:: +get_nofog_flag() const { + return ((_flags & F_nofog_flag) != 0); +} + +INLINE void EggGroup:: +set_decal_flag(bool flag) { + if (flag) { + _flags |= F_decal_flag; + } else { + _flags &= ~F_decal_flag; + } +} +INLINE bool EggGroup:: +get_decal_flag() const { + return ((_flags & F_decal_flag) != 0); +} + +INLINE void EggGroup:: +set_direct_flag(bool flag) { + if (flag) { + _flags |= F_direct_flag; + } else { + _flags &= ~F_direct_flag; + } +} +INLINE bool EggGroup:: +get_direct_flag() const { + return ((_flags & F_direct_flag) != 0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::set_collide_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggGroup:: +set_collide_mask(CollideMask mask) { + _collide_mask = mask; + _flags2 |= F2_collide_mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::clear_collide_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggGroup:: +clear_collide_mask() { + _flags2 &= ~F2_collide_mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::has_collide_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggGroup:: +has_collide_mask() const { + return (_flags2 & F2_collide_mask) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::get_collide_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollideMask EggGroup:: +get_collide_mask() const { + nassertr(has_collide_mask(), CollideMask()); + return _collide_mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::set_from_collide_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggGroup:: +set_from_collide_mask(CollideMask mask) { + _from_collide_mask = mask; + _flags2 |= F2_from_collide_mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::clear_from_collide_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggGroup:: +clear_from_collide_mask() { + _flags2 &= ~F2_from_collide_mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::has_from_collide_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggGroup:: +has_from_collide_mask() const { + return (_flags2 & F2_from_collide_mask) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::get_from_collide_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollideMask EggGroup:: +get_from_collide_mask() const { + nassertr(has_from_collide_mask(), CollideMask()); + return _from_collide_mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::set_into_collide_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggGroup:: +set_into_collide_mask(CollideMask mask) { + _into_collide_mask = mask; + _flags2 |= F2_into_collide_mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::clear_into_collide_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggGroup:: +clear_into_collide_mask() { + _flags2 &= ~F2_into_collide_mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::has_into_collide_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggGroup:: +has_into_collide_mask() const { + return (_flags2 & F2_into_collide_mask) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::get_into_collide_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CollideMask EggGroup:: +get_into_collide_mask() const { + nassertr(has_into_collide_mask(), CollideMask()); + return _into_collide_mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::set_lod +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggGroup:: +set_lod(const EggSwitchCondition &lod) { + _lod = lod.make_copy(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::has_lod +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggGroup:: +has_lod() const { + return (!(_lod == NULL)); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::get_lod +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const EggSwitchCondition &EggGroup:: +get_lod() const { + return *_lod; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::vref_begin +// Access: Public +// Description: Returns an iterator that can, in conjunction with +// vref_end(), be used to traverse the entire set of +// referenced vertices. Each iterator returns a +// pair. +//////////////////////////////////////////////////////////////////// +INLINE EggGroup::VertexRef::const_iterator EggGroup:: +vref_begin() const { + return _vref.begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::vref_end +// Access: Public +// Description: Returns an iterator that can, in conjunction with +// vref_begin(), be used to traverse the entire set of +// referenced vertices. Each iterator returns a +// pair. +//////////////////////////////////////////////////////////////////// +INLINE EggGroup::VertexRef::const_iterator EggGroup:: +vref_end() const { + return _vref.end(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGrop::vref_size +// Access: Public +// Description: Returns the number of elements between vref_begin() +// and vref_end(). +//////////////////////////////////////////////////////////////////// +INLINE EggGroup::VertexRef::size_type EggGroup:: +vref_size() const { + return _vref.size(); +} + + diff --git a/panda/src/egg/eggGroup.cxx b/panda/src/egg/eggGroup.cxx new file mode 100644 index 0000000000..b1e0a1aa5c --- /dev/null +++ b/panda/src/egg/eggGroup.cxx @@ -0,0 +1,817 @@ +// Filename: eggGroup.cxx +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggGroup.h" +#include "eggMiscFuncs.h" +#include "eggVertexPool.h" + +#include +#include +#include + +extern int eggyyparse(void); +#include "parserDefs.h" +#include "lexerDefs.h" + +TypeHandle EggGroup::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggGroup:: +EggGroup(const string &name) : EggGroupNode(name) { + _flags = 0; + _flags2 = 0; + _transform = LMatrix4d::ident_mat(); + _fps = 0.0; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggGroup:: +EggGroup(const EggGroup ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggGroup &EggGroup:: +operator = (const EggGroup ©) { + _flags = copy._flags; + _flags2 = copy._flags2; + _transform = copy._transform; + _objecttype = copy._objecttype; + _collision_name = copy._collision_name; + _fps = copy._fps; + + unref_all_vertices(); + _vref = copy._vref; + + // We must walk through the vertex ref list, and flag each vertex as + // now reffed by this group. + VertexRef::iterator vri; + for (vri = _vref.begin(); vri != _vref.end(); ++vri) { + EggVertex *vert = (*vri).first; + + bool inserted = vert->_gref.insert(this).second; + // Did the group not exist previously in the vertex's gref list? + // If it was there already, we must be out of sync between + // vertices and groups. + nassertr(inserted, *this); + } + + // These must be down here, because the EggNode assignment operator + // will force an update_under(). Therefore, we can't call it until + // all the attributes that affect adjust_under() are in place. + EggGroupNode::operator = (copy); + EggAlphaMode::operator = (copy); + + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggGroup:: +~EggGroup() { + unref_all_vertices(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::set_group_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void EggGroup:: +set_group_type(GroupType type) { + if (type != get_group_type()) { + // Make sure the user didn't give us any stray bits. + nassertv((type & ~F_group_type)==0); + _flags = (_flags & ~F_group_type) | type; + + // Now we might have changed the type to or from an instance node, + // so we have to recompute the under_flags. + update_under(0); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::set_transform +// Access: Public +// Description: Sets the indicated transformation matrix on the node. +//////////////////////////////////////////////////////////////////// +void EggGroup:: +set_transform(const LMatrix4d &transform) { + _transform = transform; + if (!has_transform()) { + _flags |= F_has_transform; + + // Now we have to update the under_flags. + update_under(0); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::clear_transform +// Access: Public +// Description: Removes any transformation matrix from the node. +//////////////////////////////////////////////////////////////////// +void EggGroup:: +clear_transform() { + _transform = LMatrix4d::ident_mat(); + if (has_transform()) { + _flags &= ~F_has_transform; + + // Now we have to update the under_flags. + update_under(0); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::write +// Access: Public, Virtual +// Description: Writes the group and all of its children to the +// indicated output stream in Egg format. +//////////////////////////////////////////////////////////////////// +void EggGroup:: +write(ostream &out, int indent_level) const { + test_under_integrity(); + + switch (get_group_type()) { + case GT_group: + write_header(out, indent_level, ""); + break; + + case GT_instance: + write_header(out, indent_level, ""); + break; + + case GT_joint: + write_header(out, indent_level, ""); + break; + + default: + // invalid group type + nassertv(false); + } + + if (has_lod()) { + get_lod().write(out, indent_level + 2); + } + + if (get_billboard_type() != BT_none) { + indent(out, indent_level + 2) + << " { " << get_billboard_type() << " }\n"; + } + + if (get_cs_type() != CST_none) { + indent(out, indent_level + 2) << " "; + if (has_collision_name()) { + enquote_string(out, get_collision_name()) << " "; + } + out << "{ " << get_cs_type(); + if (get_collide_flags() != CF_none) { + out << " " << get_collide_flags(); + } + out << " }\n"; + } + + if (has_collide_mask()) { + indent(out, indent_level + 2) + << " collide-mask { 0x"; + get_collide_mask().output_hex(out, 0); + out << " }\n"; + } + + if (has_from_collide_mask()) { + indent(out, indent_level + 2) + << " from-collide-mask { 0x"; + get_from_collide_mask().output_hex(out, 0); + out << " }\n"; + } + + if (has_into_collide_mask()) { + indent(out, indent_level + 2) + << " into-collide-mask { 0x"; + get_into_collide_mask().output_hex(out, 0); + out << " }\n"; + } + + if (get_dcs_flag()) { + indent(out, indent_level + 2) << " { 1 }\n"; + } + + if (get_dart_type() != DT_none) { + indent(out, indent_level + 2) + << " { " << get_dart_type() << " }\n"; + } + + if (get_switch_flag()) { + indent(out, indent_level + 2) << " { 1 }\n"; + if (get_switch_fps() != 0.0) { + indent(out, indent_level + 2) + << " fps { " << get_switch_fps() << " }\n"; + } + } + + if (has_transform()) { + write_transform(out, _transform, indent_level + 2); + } + + if (has_objecttype()) { + indent(out, indent_level + 2) + << " { "; + enquote_string(out, get_objecttype()) << " }\n"; + } + + if (get_model_flag()) { + indent(out, indent_level + 2) << " { 1 }\n"; + } + + if (get_texlist_flag()) { + indent(out, indent_level + 2) << " { 1 }\n"; + } + + if (get_nofog_flag()) { + indent(out, indent_level + 2) << " no-fog { 1 }\n"; + } + + if (get_decal_flag()) { + indent(out, indent_level + 2) << " decal { 1 }\n"; + } + + if (get_direct_flag()) { + indent(out, indent_level + 2) << " direct { 1 }\n"; + } + + EggAlphaMode::write(out, indent_level + 2); + + write_vertex_ref(out, indent_level + 2); + + EggGroupNode::write(out, indent_level + 2); + indent(out, indent_level) << "}\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::parse_egg +// Access: Public +// Description: Parses the egg syntax given in the indicate string as +// if it had been read from the egg file within the +// { ... } definition. Updates the EggGroup +// accordingly. Returns true if successful, false if +// there was some parse error. +//////////////////////////////////////////////////////////////////// +bool EggGroup:: +parse_egg(const string &egg_syntax) { + istringstream in(egg_syntax); + egg_init_parser(in, "", this, this); + egg_start_group_body(); + eggyyparse(); + egg_cleanup_parser(); + + return (egg_error_count() == 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::ref_vertex +// Access: Public +// Description: Adds the vertex to the set of those referenced by the +// group, at the indicated membership level. If the +// vertex is already being referenced, increases the +// membership amount by the indicated amount. +//////////////////////////////////////////////////////////////////// +void EggGroup:: +ref_vertex(EggVertex *vert, double membership) { + VertexRef::iterator vri = _vref.find(vert); + + if (vri != _vref.end()) { + // The vertex was already being reffed; increment its membership + // amount. + (*vri).second += membership; + + // If that takes us down to zero, go ahead and unref the vertex. + if ((*vri).second == 0.0) { + unref_vertex(vert); + } + + } else { + // The vertex was not already reffed; ref it. + if (membership != 0.0) { + _vref[vert] = membership; + + bool inserted = vert->_gref.insert(this).second; + // Did the group not exist previously in the vertex's gref list? + // If it was there already, we must be out of sync between + // vertices and groups. + nassertv(inserted); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::unref_vertex +// Access: Public +// Description: Removes the vertex from the set of those referenced +// by the group. Does nothing if the vertex is not +// already reffed. +//////////////////////////////////////////////////////////////////// +void EggGroup:: +unref_vertex(EggVertex *vert) { + VertexRef::iterator vri = _vref.find(vert); + + if (vri != _vref.end()) { + _vref.erase(vri); + int count = vert->_gref.erase(this); + // Did the group exist in the vertex's gref list? If it didn't, + // we must be out of sync between vertices and groups. + nassertv(count == 1); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::unref_all_vertices +// Access: Public +// Description: Removes all vertices from the reference list. +//////////////////////////////////////////////////////////////////// +void EggGroup:: +unref_all_vertices() { + // We must walk through the vertex ref list, and flag each vertex as + // unreffed in its own structure. + VertexRef::iterator vri; + for (vri = _vref.begin(); vri != _vref.end(); ++vri) { + EggVertex *vert = (*vri).first; + int count = vert->_gref.erase(this); + // Did the group exist in the vertex's gref list? If it didn't, + // we must be out of sync between vertices and groups. + nassertv(count == 1); + } + + _vref.clear(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::get_vertex_membership +// Access: Public +// Description: Returns the amount of membership of the indicated +// vertex in this group. If the vertex is not reffed by +// the group, returns 0. +//////////////////////////////////////////////////////////////////// +double EggGroup:: +get_vertex_membership(const EggVertex *vert) const { + VertexRef::const_iterator vri = _vref.find((EggVertex *)vert); + + if (vri != _vref.end()) { + return (*vri).second; + } else { + return 0.0; + } +} + + +#ifndef NDEBUG + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::test_vref_integrity +// Access: Public +// Description: Verifies that each vertex in the group exists and +// that it knows it is referenced by the group. +//////////////////////////////////////////////////////////////////// +void EggGroup:: +test_vref_integrity() const { + test_ref_count_integrity(); + + VertexRef::const_iterator vri; + for (vri = vref_begin(); vri != vref_end(); ++vri) { + const EggVertex *vert = (*vri).first; + vert->test_ref_count_integrity(); + + nassertv(vert->has_gref(this)); + } +} + +#endif // NDEBUG + + + + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::string_group_type +// Access: Public, Static +// Description: Returns the GroupType value associated with the given +// string representation, or GT_invalid if the string +// does not match any known GroupType value. +//////////////////////////////////////////////////////////////////// +EggGroup::GroupType EggGroup:: +string_group_type(const string &string) { + if (cmp_nocase_uh(string, "group") == 0) { + return GT_group; + } else if (cmp_nocase_uh(string, "instance") == 0) { + return GT_instance; + } else if (cmp_nocase_uh(string, "joint") == 0) { + return GT_joint; + } else { + return GT_invalid; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::string_dart_type +// Access: Public, Static +// Description: Returns the DartType value associated with the given +// string representation, or DT_none if the string +// does not match any known DartType value. +//////////////////////////////////////////////////////////////////// +EggGroup::DartType EggGroup:: +string_dart_type(const string &string) { + if (cmp_nocase_uh(string, "sync") == 0) { + return DT_sync; + } else if (cmp_nocase_uh(string, "nosync") == 0) { + return DT_nosync; + } else if (cmp_nocase_uh(string, "default") == 0) { + return DT_default; + } else { + return DT_none; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::string_billboard_type +// Access: Public, Static +// Description: Returns the BillboardType value associated with the +// given string representation, or BT_none if the string +// does not match any known BillboardType value. +//////////////////////////////////////////////////////////////////// +EggGroup::BillboardType EggGroup:: +string_billboard_type(const string &string) { + if (cmp_nocase_uh(string, "axis") == 0) { + return BT_axis; + } else if (cmp_nocase_uh(string, "point_eye") == 0) { + return BT_point_camera_relative; + } else if (cmp_nocase_uh(string, "point_world") == 0) { + return BT_point_world_relative; + } else if (cmp_nocase_uh(string, "point") == 0) { + return BT_point_world_relative; + } else { + return BT_none; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::string_cs_type +// Access: Public, Static +// Description: Returns the CollisionSolidType value associated with the +// given string representation, or CST_none if the string +// does not match any known CollisionSolidType value. +//////////////////////////////////////////////////////////////////// +EggGroup::CollisionSolidType EggGroup:: +string_cs_type(const string &string) { + if (cmp_nocase_uh(string, "plane") == 0) { + return CST_plane; + } else if (cmp_nocase_uh(string, "polygon") == 0) { + return CST_polygon; + } else if (cmp_nocase_uh(string, "polyset") == 0) { + return CST_polyset; + } else if (cmp_nocase_uh(string, "sphere") == 0) { + return CST_sphere; + } else if (cmp_nocase_uh(string, "inversesphere") == 0) { + return CST_inverse_sphere; + } else if (cmp_nocase_uh(string, "geode") == 0) { + return CST_geode; + } else { + return CST_none; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::string_collide_flags +// Access: Public, Static +// Description: Returns the CollideFlags value associated with the +// given string representation, or CF_none if the string +// does not match any known CollideFlags value. This +// only recognizes a single keyword; it does not attempt +// to parse a string of keywords. +//////////////////////////////////////////////////////////////////// +EggGroup::CollideFlags EggGroup:: +string_collide_flags(const string &string) { + if (cmp_nocase_uh(string, "intangible") == 0) { + return CF_intangible; + } else if (cmp_nocase_uh(string, "event") == 0) { + return CF_event; + } else if (cmp_nocase_uh(string, "descend") == 0) { + return CF_descend; + } else if (cmp_nocase_uh(string, "keep") == 0) { + return CF_keep; + } else if (cmp_nocase_uh(string, "solid") == 0) { + return CF_solid; + } else if (cmp_nocase_uh(string, "center") == 0) { + return CF_center; + } else if (cmp_nocase_uh(string, "turnstile") == 0) { + return CF_turnstile; + } else { + return CF_none; + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::write_vertex_ref +// Access: Protected +// Description: Writes out the vertex ref component of the group body +// only. This may consist of a number of +// entries, each with its own membership value. +//////////////////////////////////////////////////////////////////// +void EggGroup:: +write_vertex_ref(ostream &out, int indent_level) const { + // We want to put the vertices together into groups first by vertex + // pool, then by membership value. Each of these groups becomes a + // separate VertexRef entry. Within each group, we'll sort the + // vertices by index number. + + typedef set Indices; + typedef map Memberships; + typedef map Pools; + + Pools _entries; + bool all_membership_one = true; + + VertexRef::const_iterator vri; + for (vri = _vref.begin(); vri != _vref.end(); ++vri) { + EggVertex *vert = (*vri).first; + double membership = (*vri).second; + + if (membership != 1.0) { + all_membership_one = false; + } + + _entries[vert->get_pool()][membership].insert(vert->get_index()); + } + + // Now that we've reordered them, we can simply traverse the entries + // and write them out. + Pools::const_iterator pi; + for (pi = _entries.begin(); pi != _entries.end(); ++pi) { + EggVertexPool *pool = (*pi).first; + const Memberships &memberships = (*pi).second; + Memberships::const_iterator mi; + for (mi = memberships.begin(); mi != memberships.end(); ++mi) { + double membership = (*mi).first; + const Indices &indices = (*mi).second; + + indent(out, indent_level) + << " {\n"; + write_long_list(out, indent_level+2, indices.begin(), indices.end(), + "", "", 72); + + // If all vrefs in this group have membership of 1, don't bother + // to write out the membership scalar. + if (!all_membership_one) { + indent(out, indent_level + 2) + << " membership { " << membership << " }\n"; + } + indent(out, indent_level + 2) + << " { " << pool->get_name() << " }\n"; + indent(out, indent_level) + << "}\n"; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::adjust_under +// Access: Protected, Virtual +// Description: This is called within update_under() after all the +// various under settings have been inherited directly +// from the parent node. It is responsible for +// adjusting these settings to reflect states local to +// the current node; for instance, an node +// will force the UF_under_instance bit on. +//////////////////////////////////////////////////////////////////// +void EggGroup:: +adjust_under() { + // Billboards are an implicit instance. + if (get_group_type() == GT_instance || + get_billboard_type() != BT_none) { + _under_flags |= UF_under_instance; + if (_under_flags & UF_under_transform) { + // If we've reached an instance node and we're under a + // transform, that means we've just defined a local coordinate + // system. + _under_flags |= UF_local_coord; + } + + // An instance node means that from this point and below, vertices + // are defined relative to this node. Thus, the node frame + // becomes the vertex frame. + _vertex_frame = _node_frame; + _vertex_frame_inv = _node_frame_inv; + _vertex_to_node = NULL; + } + + // If we have our own transform, it carries forward, but it doesn't + // have any effect on the local_coord flag, above. + if (has_transform()) { + _under_flags |= UF_under_transform; + + // Our own transform also affects our node frame. + _node_frame = + new MatrixFrame(get_transform() * get_node_frame()); + _node_frame_inv = + new MatrixFrame(invert(get_node_frame())); + _vertex_to_node = + new MatrixFrame(get_vertex_frame() * get_node_frame_inv()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroup::r_transform +// Access: Protected, Virtual +// Description: This is called from within the egg code by +// transform(). It applies a transformation matrix +// to the current node in some sensible way, then +// continues down the tree. +// +// The first matrix is the transformation to apply; the +// second is its inverse. The third parameter is the +// coordinate system we are changing to, or CS_default +// if we are not changing coordinate systems. +//////////////////////////////////////////////////////////////////// +void EggGroup:: +r_transform(const LMatrix4d &mat, const LMatrix4d &inv, + CoordinateSystem to_cs) { + if (has_transform()) { + // Since we want to apply this transform to all matrices, + // including nested matrices, we can't simply premult it in and + // leave it, because that would leave the rotational component in + // the scene graph's matrix, and all nested matrices would inherit + // the same rotational component. So we have to premult and then + // postmult by the inverse to undo the rotational component each + // time. + + LMatrix4d mat1 = mat; + LMatrix4d inv1 = inv; + + // If we have a translation component, we should only apply + // it to the top matrix. All subsequent matrices get just the + // rotational component. + mat1.set_row(3, LVector3d(0.0, 0.0, 0.0)); + inv1.set_row(3, LVector3d(0.0, 0.0, 0.0)); + + _transform = inv1 * _transform * mat; + + EggGroupNode::r_transform(mat1, inv1, to_cs); + } else { + EggGroupNode::r_transform(mat, inv, to_cs); + } + + // Convert the LOD description too. + if (has_lod()) { + _lod->transform(mat); + } +} + + + + +//////////////////////////////////////////////////////////////////// +// Function: GroupType output operator +// Description: +//////////////////////////////////////////////////////////////////// +ostream &operator << (ostream &out, EggGroup::GroupType t) { + switch (t) { + case EggGroup::GT_invalid: + return out << "invalid group"; + case EggGroup::GT_group: + return out << "group"; + case EggGroup::GT_instance: + return out << "instance"; + case EggGroup::GT_joint: + return out << "joint"; + } + + nassertr(false, out); + return out << "(**invalid**)"; +} + +//////////////////////////////////////////////////////////////////// +// Function: DartType output operator +// Description: +//////////////////////////////////////////////////////////////////// +ostream &operator << (ostream &out, EggGroup::DartType t) { + switch (t) { + case EggGroup::DT_none: + return out << "none"; + case EggGroup::DT_sync: + return out << "sync"; + case EggGroup::DT_nosync: + return out << "nosync"; + case EggGroup::DT_default: + return out << "1"; + } + + nassertr(false, out); + return out << "(**invalid**)"; +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardType output operator +// Description: +//////////////////////////////////////////////////////////////////// +ostream &operator << (ostream &out, EggGroup::BillboardType t) { + switch (t) { + case EggGroup::BT_none: + return out << "none"; + case EggGroup::BT_axis: + return out << "axis"; + case EggGroup::BT_point_camera_relative: + return out << "point_eye"; + case EggGroup::BT_point_world_relative: + return out << "point_world"; + } + + nassertr(false, out); + return out << "(**invalid**)"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollisionSolidType output operator +// Description: +//////////////////////////////////////////////////////////////////// +ostream &operator << (ostream &out, EggGroup::CollisionSolidType t) { + switch (t) { + case EggGroup::CST_none: + return out << "None"; + case EggGroup::CST_plane: + return out << "Plane"; + case EggGroup::CST_polygon: + return out << "Polygon"; + case EggGroup::CST_polyset: + return out << "Polyset"; + case EggGroup::CST_sphere: + return out << "Sphere"; + case EggGroup::CST_inverse_sphere: + return out << "InverseSphere"; + case EggGroup::CST_geode: + return out << "Geode"; + } + + nassertr(false, out); + return out << "(**invalid**)"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CollideFlags output operator +// Description: +//////////////////////////////////////////////////////////////////// +ostream &operator << (ostream &out, EggGroup::CollideFlags t) { + if (t == EggGroup::CF_none) { + return out << "none"; + } + int bits = (int)t; + const char *space = ""; + + if (bits & EggGroup::CF_intangible) { + out << space << "intangible"; + space = " "; + } + if (bits & EggGroup::CF_event) { + out << space << "event"; + space = " "; + } + if (bits & EggGroup::CF_descend) { + out << space << "descend"; + space = " "; + } + if (bits & EggGroup::CF_keep) { + out << space << "keep"; + space = " "; + } + if (bits & EggGroup::CF_solid) { + out << space << "solid"; + space = " "; + } + if (bits & EggGroup::CF_center) { + out << space << "center"; + space = " "; + } + if (bits & EggGroup::CF_turnstile) { + out << space << "turnstile"; + space = " "; + } + return out; +} diff --git a/panda/src/egg/eggGroup.h b/panda/src/egg/eggGroup.h new file mode 100644 index 0000000000..59b0dd5e59 --- /dev/null +++ b/panda/src/egg/eggGroup.h @@ -0,0 +1,247 @@ +// Filename: eggGroup.h +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGGROUP_H +#define EGGGROUP_H + +#include + +#include "eggGroupNode.h" +#include "eggAlphaMode.h" +#include "eggVertex.h" +#include "eggSwitchCondition.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : EggGroup +// Description : The main glue of the egg hierarchy, this corresponds +// to the , , and type nodes. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggGroup : public EggGroupNode, public EggAlphaMode { +public: + typedef map VertexRef; + + // These bits are all stored somewhere in _flags. + enum GroupType { + // The bits here must correspond to those in Flags, below. + GT_invalid = -1, + GT_group = 0x00000000, + GT_instance = 0x00000001, + GT_joint = 0x00000002, + }; + enum DartType { + // The bits here must correspond to those in Flags, below. + DT_none = 0x00000000, + DT_sync = 0x00000004, + DT_nosync = 0x00000008, + DT_default = 0x0000000c, + }; + enum BillboardType { + // The bits here must correspond to those in Flags, below. + BT_none = 0x00000000, + BT_axis = 0x00000020, + BT_point_camera_relative = 0x00000040, + BT_point_world_relative = 0x00000080, + }; + enum CollisionSolidType { + // The bits here must correspond to those in Flags, below. + CST_none = 0x00000000, + CST_plane = 0x00010000, + CST_polygon = 0x00020000, + CST_polyset = 0x00030000, + CST_sphere = 0x00040000, + CST_inverse_sphere = 0x00050000, + CST_geode = 0x00060000, + }; + enum CollideFlags { + // The bits here must correspond to those in Flags, below. + CF_none = 0x00000000, + CF_intangible = 0x00080000, + CF_descend = 0x00100000, + CF_event = 0x00200000, + CF_keep = 0x00400000, + CF_solid = 0x00800000, + CF_center = 0x01000000, + CF_turnstile = 0x02000000, + }; + + EggGroup(const string &name = ""); + EggGroup(const EggGroup ©); + EggGroup &operator = (const EggGroup ©); + ~EggGroup(); + + virtual void write(ostream &out, int indent_level) const; + bool parse_egg(const string &egg_syntax); + + void set_group_type(GroupType type); + INLINE GroupType get_group_type() const; + + INLINE void set_billboard_type(BillboardType type); + INLINE BillboardType get_billboard_type() const; + + INLINE void set_cs_type(CollisionSolidType type); + INLINE CollisionSolidType get_cs_type() const; + + INLINE void set_collide_flags(int flags); + INLINE CollideFlags get_collide_flags() const; + + INLINE void set_collision_name(const string &collision_name); + INLINE void clear_collision_name(); + INLINE bool has_collision_name() const; + INLINE const string &get_collision_name() const; + + INLINE void set_dcs_flag(bool flag); + INLINE bool get_dcs_flag() const; + + INLINE void set_dart_type(DartType type); + INLINE DartType get_dart_type() const; + + INLINE void set_switch_flag(bool flag); + INLINE bool get_switch_flag() const; + + INLINE void set_switch_fps(double fps); + INLINE double get_switch_fps() const; + + void set_transform(const LMatrix4d &transform); + void clear_transform(); + INLINE bool has_transform() const; + INLINE LMatrix4d get_transform() const; + INLINE bool transform_is_identity() const; + + INLINE void set_objecttype(const string &objecttype); + INLINE void clear_objecttype(); + INLINE bool has_objecttype() const; + INLINE const string &get_objecttype() const; + + INLINE void set_model_flag(bool flag); + INLINE bool get_model_flag() const; + + INLINE void set_texlist_flag(bool flag); + INLINE bool get_texlist_flag() const; + + INLINE void set_nofog_flag(bool flag); + INLINE bool get_nofog_flag() const; + + INLINE void set_decal_flag(bool flag); + INLINE bool get_decal_flag() const; + + INLINE void set_direct_flag(bool flag); + INLINE bool get_direct_flag() const; + + INLINE void set_collide_mask(CollideMask mask); + INLINE void clear_collide_mask(); + INLINE bool has_collide_mask() const; + INLINE CollideMask get_collide_mask() const; + + INLINE void set_from_collide_mask(CollideMask mask); + INLINE void clear_from_collide_mask(); + INLINE bool has_from_collide_mask() const; + INLINE CollideMask get_from_collide_mask() const; + + INLINE void set_into_collide_mask(CollideMask mask); + INLINE void clear_into_collide_mask(); + INLINE bool has_into_collide_mask() const; + INLINE CollideMask get_into_collide_mask() const; + + INLINE void set_lod(const EggSwitchCondition &lod); + INLINE bool has_lod() const; + INLINE const EggSwitchCondition &get_lod() const; + + void ref_vertex(EggVertex *vert, double membership = 1.0); + void unref_vertex(EggVertex *vert); + void unref_all_vertices(); + double get_vertex_membership(const EggVertex *vert) const; + + INLINE VertexRef::const_iterator vref_begin() const; + INLINE VertexRef::const_iterator vref_end() const; + INLINE VertexRef::size_type vref_size() const; + +#ifndef NDEBUG + void test_vref_integrity() const; +#else + void test_vref_integrity() const { } +#endif // NDEBUG + + static GroupType string_group_type(const string &string); + static DartType string_dart_type(const string &string); + static BillboardType string_billboard_type(const string &string); + static CollisionSolidType string_cs_type(const string &string); + static CollideFlags string_collide_flags(const string &string); + +protected: + void write_vertex_ref(ostream &out, int indent_level) const; + virtual void adjust_under(); + + virtual void r_transform(const LMatrix4d &mat, const LMatrix4d &inv, + CoordinateSystem to_cs); + +private: + + enum Flags { + F_group_type = 0x00000003, + F_dart_type = 0x0000000c, + F_dcs_flag = 0x00000010, + F_billboard_type = 0x000000e0, + F_switch_flag = 0x00000100, + F_has_transform = 0x00000200, + F_model_flag = 0x00000400, + F_texlist_flag = 0x00000800, + F_nofog_flag = 0x00001000, + F_decal_flag = 0x00002000, + F_direct_flag = 0x00004000, + F_cs_type = 0x00070000, + F_collide_flags = 0x03f80000, + }; + enum Flags2 { + F2_collide_mask = 0x00000001, + F2_from_collide_mask = 0x00000002, + F2_into_collide_mask = 0x00000004, + }; + + int _flags; + int _flags2; + LMatrix4d _transform; + CollideMask _collide_mask, _from_collide_mask, _into_collide_mask; + string _objecttype; + string _collision_name; + double _fps; + PT(EggSwitchCondition) _lod; + VertexRef _vref; + + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggGroupNode::init_type(); + EggAlphaMode::init_type(); + register_type(_type_handle, "EggGroup", + EggGroupNode::get_class_type(), + EggAlphaMode::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +ostream &operator << (ostream &out, EggGroup::GroupType t); +ostream &operator << (ostream &out, EggGroup::DartType t); +ostream &operator << (ostream &out, EggGroup::BillboardType t); +ostream &operator << (ostream &out, EggGroup::CollisionSolidType t); +ostream &operator << (ostream &out, EggGroup::CollideFlags t); + + +#include "eggGroup.I" + +#endif + diff --git a/panda/src/egg/eggGroupNode.I b/panda/src/egg/eggGroupNode.I new file mode 100644 index 0000000000..50327436c1 --- /dev/null +++ b/panda/src/egg/eggGroupNode.I @@ -0,0 +1,113 @@ +// Filename: eggGroupNode.I +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::begin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggGroupNode::iterator EggGroupNode:: +begin() const { + return _children.begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::end +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggGroupNode::iterator EggGroupNode:: +end() const { + return _children.end(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::rbegin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggGroupNode::reverse_iterator EggGroupNode:: +rbegin() const { + return _children.rbegin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::rend +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggGroupNode::reverse_iterator EggGroupNode:: +rend() const { + return _children.rend(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::empty +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggGroupNode:: +empty() const { + return _children.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggGroupNode::size_type EggGroupNode:: +size() const { + return _children.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::insert +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggGroupNode::iterator EggGroupNode:: +insert(iterator position, PT(EggNode) x) { + prepare_add_child(x); + return _children.insert((Children::iterator &)position, x); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::erase +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggGroupNode::iterator EggGroupNode:: +erase(iterator position) { + prepare_remove_child(*position); + return _children.erase((Children::iterator &)position); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::replace +// Access: Public +// Description: Replaces the node at the indicated position with +// the indicated node. It is an error to call this +// with an invalid position iterator (e.g. end()). +//////////////////////////////////////////////////////////////////// +INLINE void EggGroupNode:: +replace(iterator position, PT(EggNode) x) { + nassertv(position != end()); + + prepare_remove_child(*position); + prepare_add_child(x); + *(Children::iterator &)position = x; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::clear +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggGroupNode:: +clear() { + erase(begin(), end()); +} diff --git a/panda/src/egg/eggGroupNode.cxx b/panda/src/egg/eggGroupNode.cxx new file mode 100644 index 0000000000..2359440e66 --- /dev/null +++ b/panda/src/egg/eggGroupNode.cxx @@ -0,0 +1,466 @@ +// Filename: eggGroupNode.cxx +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggGroupNode.h" +#include "eggCoordinateSystem.h" +#include "eggData.h" +#include "eggFilenameNode.h" +#include "eggExternalReference.h" +#include "eggPrimitive.h" +#include "eggTextureCollection.h" +#include "config_egg.h" + +#include + +#include + +TypeHandle EggGroupNode::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggGroupNode:: +EggGroupNode(const EggGroupNode ©) : EggNode(copy) { + if (!copy.empty()) { + egg_cat.warning() + << "The EggGroupNode copy constructor does not copy children!\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggGroupNode &EggGroupNode:: +operator =(const EggGroupNode ©) { + if (!copy.empty()) { + egg_cat.warning() + << "The EggGroupNode copy assignment does not copy children!\n"; + } + EggNode::operator =(copy); + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::write +// Access: Public, Virtual +// Description: Writes the group and all of its children to the +// indicated output stream in Egg format. +//////////////////////////////////////////////////////////////////// +void EggGroupNode:: +write(ostream &out, int indent_level) const { + iterator i; + for (i = begin(); i != end(); ++i) { + (*i)->write(out, indent_level); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::erase +// Access: Public +// Description: Part of the implementaion of the EggGroupNode as an +// STL container. Most of the rest of these functions +// are inline and declared in eggGroupNode.I. +//////////////////////////////////////////////////////////////////// +EggGroupNode::iterator EggGroupNode:: +erase(iterator first, iterator last) { + iterator i; + for (i = first; i != last; ++i) { + prepare_remove_child(*i); + } + return _children.erase((Children::iterator &)first, + (Children::iterator &)last); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::add_child +// Access: Public +// Description: Adds the indicated child to the group and returns it. +// If the child node is already a child of some other +// node, removes it first. +//////////////////////////////////////////////////////////////////// +PT(EggNode) EggGroupNode:: +add_child(PT(EggNode) node) { + if (node->_parent != NULL) { + node->_parent->remove_child(node); + } + prepare_add_child(node); + _children.push_back(node); + return node; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::remove_child +// Access: Public +// Description: Removes the indicated child node from the group and +// returns it. If the child was not already in the +// group, does nothing and returns NULL. +//////////////////////////////////////////////////////////////////// +PT(EggNode) EggGroupNode:: +remove_child(PT(EggNode) node) { + iterator i = find(begin(), end(), node); + if (i == end()) { + return PT(EggNode)(); + } else { + // erase() calls prepare_remove_child(). + erase(i); + return node; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::steal_children +// Access: Public +// Description: Moves all the children from the other node to this +// one. This is especially useful because the group +// node copy assignment operator does not copy children. +//////////////////////////////////////////////////////////////////// +void EggGroupNode:: +steal_children(EggGroupNode &other) { + Children::iterator ci; + for (ci = other._children.begin(); + ci != other._children.end(); + ++ci) { + other.prepare_remove_child(*ci); + prepare_add_child(*ci); + } + + _children.splice(_children.end(), other._children); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::resolve_filenames +// Access: Public +// Description: Walks the tree and attempts to resolve any filenames +// encountered. This looks up filenames in the search +// path, etc. It does not automatically search the +// egg_path for missing files. +//////////////////////////////////////////////////////////////////// +void EggGroupNode:: +resolve_filenames(const DSearchPath &searchpath) { + Children::iterator ci; + for (ci = _children.begin(); + ci != _children.end(); + ++ci) { + EggNode *child = *ci; + if (child->is_of_type(EggFilenameNode::get_class_type())) { + EggFilenameNode *filename = DCAST(EggFilenameNode, child); + + filename->resolve_filename(searchpath, + filename->get_default_extension()); + + } else if (child->is_of_type(EggGroupNode::get_class_type())) { + DCAST(EggGroupNode, child)->resolve_filenames(searchpath); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::reverse_vertex_ordering +// Access: Public +// Description: Reverses the vertex ordering of all polygons defined +// at this node and below. Does not change the surface +// normals, if any. +//////////////////////////////////////////////////////////////////// +void EggGroupNode:: +reverse_vertex_ordering() { + Children::iterator ci; + for (ci = _children.begin(); + ci != _children.end(); + ++ci) { + EggNode *child = *ci; + if (child->is_of_type(EggPrimitive::get_class_type())) { + EggPrimitive *prim = DCAST(EggPrimitive, child); + prim->reverse_vertex_ordering(); + + } else if (child->is_of_type(EggGroupNode::get_class_type())) { + DCAST(EggGroupNode, child)->reverse_vertex_ordering(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::update_under +// Access: Protected, Virtual +// Description: This function is called from within EggGroupNode +// whenever the parentage of the node has changed. It +// should update the depth and under_instance flags +// accordingly. +// +// Offset is the difference between the old depth value +// and the new value. It should be consistent with the +// supplied depth value. If it is not, we have some +// error. +//////////////////////////////////////////////////////////////////// +void EggGroupNode:: +update_under(int depth_offset) { + EggNode::update_under(depth_offset); + + Children::iterator ci; + for (ci = _children.begin(); + ci != _children.end(); + ++ci) { + nassertv((*ci)->get_parent() == this); + (*ci)->update_under(depth_offset); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::r_transform +// Access: Protected, Virtual +// Description: This is called from within the egg code by +// transform(). It applies a transformation matrix +// to the current node in some sensible way, then +// continues down the tree. +// +// The first matrix is the transformation to apply; the +// second is its inverse. The third parameter is the +// coordinate system we are changing to, or CS_default +// if we are not changing coordinate systems. +//////////////////////////////////////////////////////////////////// +void EggGroupNode:: +r_transform(const LMatrix4d &mat, const LMatrix4d &inv, + CoordinateSystem to_cs) { + Children::iterator ci; + for (ci = _children.begin(); + ci != _children.end(); + ++ci) { + (*ci)->r_transform(mat, inv, to_cs); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::r_mark_coordsys +// Access: Protected, Virtual +// Description: This is only called immediately after loading an egg +// file from disk, to propagate the value found in the +// CoordinateSystem entry (or the default Y-up +// coordinate system) to all nodes that care about what +// the coordinate system is. +//////////////////////////////////////////////////////////////////// +void EggGroupNode:: +r_mark_coordsys(CoordinateSystem cs) { + Children::iterator ci; + for (ci = _children.begin(); + ci != _children.end(); + ++ci) { + (*ci)->r_mark_coordsys(cs); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::find_coordsys_entry() +// Access: Protected +// Description: Walks the tree, looking for an EggCoordinateSystem +// entry. If one is found, extracts it and returns its +// value. If multiple entries are found, extracts all +// of them and returns CS_invalid if they disagree. +//////////////////////////////////////////////////////////////////// +CoordinateSystem EggGroupNode:: +find_coordsys_entry() { + CoordinateSystem coordsys = CS_default; + + // We can do this ci/cnext iteration through the list as we modify + // it, only because we know this works with an STL list type + // container. If this were a vector or a set, this wouldn't + // necessarily work. + + Children::iterator ci, cnext; + ci = _children.begin(); + while (ci != _children.end()) { + cnext = ci; + ++cnext; + EggNode *child = *ci; + + if (child->is_of_type(EggCoordinateSystem::get_class_type())) { + CoordinateSystem new_cs = + DCAST(EggCoordinateSystem, child)->get_value(); + + // Now remove the CoordinateSystem entry from our child list. + prepare_remove_child(child); + _children.erase(ci); + + if (new_cs != CS_default) { + if (coordsys != CS_default && coordsys != new_cs) { + coordsys = CS_invalid; + } else { + coordsys = new_cs; + } + } + + } else if (child->is_of_type(EggGroupNode::get_class_type())) { + CoordinateSystem new_cs = + DCAST(EggGroupNode, child)->find_coordsys_entry(); + if (new_cs != CS_default) { + if (coordsys != CS_default && coordsys != new_cs) { + coordsys = CS_invalid; + } else { + coordsys = new_cs; + } + } + } + + ci = cnext; + } + + return coordsys; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::find_textures() +// Access: Protected +// Description: Walks the tree, looking for EggTextures. Each +// EggTexture that is found is removed from the +// hierarchy and added to the EggTextureCollection. +// Returns the number of EggTextures found. +//////////////////////////////////////////////////////////////////// +int EggGroupNode:: +find_textures(EggTextureCollection *collection) { + int num_found = 0; + + // We can do this ci/cnext iteration through the list as we modify + // it, only because we know this works with an STL list type + // container. If this were a vector or a set, this wouldn't + // necessarily work. + + Children::iterator ci, cnext; + ci = _children.begin(); + while (ci != _children.end()) { + cnext = ci; + ++cnext; + EggNode *child = *ci; + + if (child->is_of_type(EggTexture::get_class_type())) { + PT(EggTexture) tex = DCAST(EggTexture, child); + + // Now remove the EggTexture entry from our child list. + prepare_remove_child(tex); + _children.erase(ci); + + // And add it to the collection. + collection->add_texture(tex); + num_found++; + + } else if (child->is_of_type(EggGroupNode::get_class_type())) { + num_found += + DCAST(EggGroupNode, child)->find_textures(collection); + } + + ci = cnext; + } + + return num_found; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::r_resolve_externals +// Access: Protected +// Description: Walks the tree and locates unloaded external +// reference nodes, which it attempts to locate and load +// in. The reference node is replaced with the entire +// subtree loaded. This is intended to be called from +// EggData::resolve_externals(). +//////////////////////////////////////////////////////////////////// +bool EggGroupNode:: +r_resolve_externals(const DSearchPath &searchpath, + CoordinateSystem coordsys) { + bool success = true; + + Children::iterator ci; + for (ci = _children.begin(); + ci != _children.end(); + ++ci) { + EggNode *child = *ci; + if (child->is_of_type(EggExternalReference::get_class_type())) { + PT(EggExternalReference) ref = DCAST(EggExternalReference, child); + + // Replace the reference with an empty group node. When we load + // the external file successfully, we'll put its contents here. + EggGroupNode *new_node = + new EggGroupNode(ref->get_basename_wo_extension()); + replace(ci, new_node); + + if (!EggData::resolve_egg_filename(*ref, searchpath)) { + egg_cat.error() + << "Could not locate " << ref->get_fullpath() << " in " + << searchpath << "\n"; + } else { + // Now define a new EggData structure to hold the external + // reference, and load it. + EggData ext_data; + ext_data.set_coordinate_system(coordsys); + if (ext_data.read(*ref)) { + // The external file was read correctly. Add its contents + // into the tree at this point. + success = + ext_data.resolve_externals(searchpath) + && success; + new_node->steal_children(ext_data); + } + } + + } else if (child->is_of_type(EggGroupNode::get_class_type())) { + EggGroupNode *group_child = DCAST(EggGroupNode, child); + success = + group_child->r_resolve_externals(searchpath, coordsys) + && success; + } + } + return success; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::prepare_add_child +// Access: Private +// Description: Marks the node as a child of the group. This is an +// internal function called by the STL-like functions +// push_back() and insert(), in preparation for actually +// adding the child. +// +// It is an error to add a node that is already a child +// of this group or some other group. +//////////////////////////////////////////////////////////////////// +void EggGroupNode:: +prepare_add_child(EggNode *node) { + // Make sure the node is not already a child of some other group. + nassertv(node->get_parent() == NULL); + nassertv(node->get_depth() == 0); + node->_parent = this; + + node->update_under(get_depth() + 1); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::prepare_remove_child +// Access: Private +// Description: Marks the node as removed from the group. This is an +// internal function called by the STL-like functions +// pop_back() and erase(), in preparation for actually +// doing the removal. +// +// It is an error to attempt to remove a node that is +// not already a child of this group. +//////////////////////////////////////////////////////////////////// +void EggGroupNode:: +prepare_remove_child(EggNode *node) { + // Make sure the node is in fact a child of this group. + nassertv(node->get_parent() == this); + nassertv(node->get_depth() == get_depth() + 1); + node->_parent = NULL; + + node->update_under(-(get_depth() + 1)); +} + + diff --git a/panda/src/egg/eggGroupNode.h b/panda/src/egg/eggGroupNode.h new file mode 100644 index 0000000000..2ae0312367 --- /dev/null +++ b/panda/src/egg/eggGroupNode.h @@ -0,0 +1,142 @@ +// Filename: eggGroupNode.h +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGGROUPNODE_H +#define EGGGROUPNODE_H + +#include + +#include "eggNode.h" + +#include +#include +#include +#include + +class EggTextureCollection; +class DSearchPath; + +//////////////////////////////////////////////////////////////////// +// Class : EggGroupNode +// Description : A base class for nodes in the hierarchy that are not +// leaf nodes. (See also EggGroup, which is +// specifically the "" node in egg.) +// +// An EggGroupNode is an STL-style container of pointers +// to EggNodes, like a vector. Functions +// push_back()/pop_back() and insert()/erase() are +// provided to manipulate the list. The list may also +// be operated on (read-only) via iterators and +// begin()/end(). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggGroupNode : public EggNode { + + // This is a bit of private interface stuff that must be here as a + // forward reference. This allows us to define the EggGroupNode as + // an STL container. + +private: + // We define the list of children as a list and not a vector, so we + // can avoid the bad iterator-invalidating properties of vectors as + // we insert/delete elements. + typedef list Children; + + // Here begins the actual public interface to EggGroupNode. + +public: + EggGroupNode(const string &name = "") : EggNode(name) { } + EggGroupNode(const EggGroupNode ©); + EggGroupNode &operator = (const EggGroupNode ©); + + virtual void write(ostream &out, int indent_level) const; + + // The EggGroupNode itself appears to be an STL container of + // pointers to EggNodes. The set of children is read-only, however, + // except through the limited add_child/remove_child or insert/erase + // interface. The following implements this. + +#ifdef WIN32_VC + typedef const PT(EggNode) *pointer; + typedef const PT(EggNode) *const_pointer; +#else + typedef Children::const_pointer pointer; + typedef Children::const_pointer const_pointer; +#endif + typedef Children::const_reference reference; + typedef Children::const_reference const_reference; + typedef Children::const_iterator iterator; + typedef Children::const_iterator const_iterator; + typedef Children::const_reverse_iterator reverse_iterator; + typedef Children::const_reverse_iterator const_reverse_iterator; + typedef Children::size_type size_type; + typedef Children::difference_type difference_type; + + INLINE iterator begin() const; + INLINE iterator end() const; + INLINE reverse_iterator rbegin() const; + INLINE reverse_iterator rend() const; + INLINE bool empty() const; + INLINE size_type size() const; + + INLINE iterator insert(iterator position, PT(EggNode) x); + INLINE iterator erase(iterator position); + iterator erase(iterator first, iterator last); + void replace(iterator position, PT(EggNode) x); + INLINE void clear(); + + PT(EggNode) add_child(PT(EggNode) node); + PT(EggNode) remove_child(PT(EggNode) node); + void steal_children(EggGroupNode &other); + + void resolve_filenames(const DSearchPath &searchpath); + void reverse_vertex_ordering(); + +protected: + virtual void update_under(int depth_offset); + + virtual void r_transform(const LMatrix4d &mat, const LMatrix4d &inv, + CoordinateSystem to_cs); + virtual void r_mark_coordsys(CoordinateSystem cs); + + CoordinateSystem find_coordsys_entry(); + int find_textures(EggTextureCollection *collection); + bool r_resolve_externals(const DSearchPath &searchpath, + CoordinateSystem coordsys); + +private: + Children _children; + + // Don't try to use these private functions. User code should add + // and remove children via add_child()/remove_child(), or via the + // STL-like push_back()/pop_back() or insert()/erase(), above. + void prepare_add_child(EggNode *node); + void prepare_remove_child(EggNode *node); + + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggNode::init_type(); + register_type(_type_handle, "EggGroupNode", + EggNode::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend class EggTextureCollection; +}; + +#include "eggGroupNode.I" + +#endif + diff --git a/panda/src/egg/eggMaterial.I b/panda/src/egg/eggMaterial.I new file mode 100644 index 0000000000..72c199606f --- /dev/null +++ b/panda/src/egg/eggMaterial.I @@ -0,0 +1,52 @@ +// Filename: eggMaterial.I +// Created by: drose (29Jan99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggMaterial::set_diff +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggMaterial:: +set_diff(const Colorf &diff) { + _diff = diff; + _has_diff = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMaterial::clear_diff +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggMaterial:: +clear_diff() { + _has_diff = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMaterial::has_diff +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggMaterial:: +has_diff() const { + return _has_diff; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMaterial::get_diff +// Access: Public +// Description: It is legal to call this even if has_diff() returns +// false. If so, it simply returns the default diff +// color. +//////////////////////////////////////////////////////////////////// +INLINE Colorf EggMaterial:: +get_diff() const { + if (has_diff()) { + return _diff; + } else { + return Colorf(1.0, 1.0, 1.0, 1.0); + } +} diff --git a/panda/src/egg/eggMaterial.cxx b/panda/src/egg/eggMaterial.cxx new file mode 100644 index 0000000000..6ec35a2788 --- /dev/null +++ b/panda/src/egg/eggMaterial.cxx @@ -0,0 +1,57 @@ +// Filename: eggMaterial.cxx +// Created by: drose (29Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggMaterial.h" + +#include + + +//////////////////////////////////////////////////////////////////// +// Function: EggMaterial::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggMaterial:: +EggMaterial(const string &mref_name) + : EggNode(mref_name) +{ + _diff.set(1.0, 1.0, 1.0, 1.0); + _has_diff = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMaterial::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggMaterial:: +EggMaterial(const EggMaterial ©) + : EggNode(copy), + _has_diff(copy._has_diff), + _diff(copy._diff) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggMaterial::write +// Access: Public, Virtual +// Description: Writes the material definition to the indicated output +// stream in Egg format. +//////////////////////////////////////////////////////////////////// +void EggMaterial:: +write(ostream &out, int indent_level) const { + write_header(out, indent_level, ""); + + if (has_diff()) { + indent(out, indent_level + 2) + << " diffr { " << get_diff()[0] << " }\n"; + indent(out, indent_level + 2) + << " diffg { " << get_diff()[1] << " }\n"; + indent(out, indent_level + 2) + << " diffb { " << get_diff()[2] << " }\n"; + } + + indent(out, indent_level) << "}\n"; +} diff --git a/panda/src/egg/eggMaterial.h b/panda/src/egg/eggMaterial.h new file mode 100644 index 0000000000..b16ef24b30 --- /dev/null +++ b/panda/src/egg/eggMaterial.h @@ -0,0 +1,38 @@ +// Filename: eggMaterial.h +// Created by: drose (29Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGMATERIAL_H +#define EGGMATERIAL_H + +#include + +#include "eggNode.h" + +#include + +/////////////////////////////////////////////////////////////////// +// Class : EggMaterial +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggMaterial : public EggNode { +public: + EggMaterial(const string &mref_name); + EggMaterial(const EggMaterial ©); + + virtual void write(ostream &out, int indent_level) const; + + INLINE void set_diff(const Colorf &diff); + INLINE void clear_diff(); + INLINE bool has_diff() const; + INLINE Colorf get_diff() const; + +private: + bool _has_diff; + Colorf _diff; +}; + +#include "eggMaterial.I" + +#endif diff --git a/panda/src/egg/eggMiscFuncs.I b/panda/src/egg/eggMiscFuncs.I new file mode 100644 index 0000000000..add13b04cc --- /dev/null +++ b/panda/src/egg/eggMiscFuncs.I @@ -0,0 +1,5 @@ +// Filename: eggMiscFuncs.I +// Created by: drose (29Jan99) +// +//////////////////////////////////////////////////////////////////// + diff --git a/panda/src/egg/eggMiscFuncs.cxx b/panda/src/egg/eggMiscFuncs.cxx new file mode 100644 index 0000000000..d14bf8bd10 --- /dev/null +++ b/panda/src/egg/eggMiscFuncs.cxx @@ -0,0 +1,140 @@ +// Filename: eggMiscFuncs.cxx +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include +#include "eggMiscFuncs.h" + +#include + +#include + + +//////////////////////////////////////////////////////////////////// +// Function: enquote_string +// Description: Writes the string to the indicated output stream. If +// the string contains any characters special to egg, +// writes quotation marks around it. If always_quote is +// true, writes quotation marks regardless. +//////////////////////////////////////////////////////////////////// +ostream & +enquote_string(ostream &out, const string &str, int indent_level, + bool always_quote) { + indent(out, indent_level); + + // First, see if we need to enquote it. + bool legal = !always_quote; + string::const_iterator p; + for (p = str.begin(); p != str.end() && legal; ++p) { + legal = (isalnum(*p) || *p=='-' || *p=='_' || *p=='#' || *p=='.'); + } + + if (legal) { + out << str; + + } else { + out << '"'; + + for (p = str.begin(); p != str.end(); ++p) { + switch (*p) { + case '"': + // Can't output nested quote marks at all. + out << "'"; + break; + + case '\n': + // A newline necessitates ending the quotes, newlining, and + // beginning again. + out << "\"\n"; + indent(out, indent_level) << '"'; + break; + + default: + out << *p; + } + } + out << '"'; + } + + return out; +} + + +//////////////////////////////////////////////////////////////////// +// Function: write_transform +// Description: A helper function to write out a 3x3 transform +// matrix. +//////////////////////////////////////////////////////////////////// +void +write_transform(ostream &out, const LMatrix3d &mat, int indent_level) { + indent(out, indent_level) << " {\n"; + + indent(out, indent_level+2) << " {\n"; + + for (int r = 0; r < 3; r++) { + indent(out, indent_level+3); + for (int c = 0; c < 3; c++) { + out << " " << mat(r, c); + } + out << "\n"; + } + indent(out, indent_level+2) << "}\n"; + indent(out, indent_level) << "}\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: write_transform +// Description: A helper function to write out a 4x4 transform +// matrix. +//////////////////////////////////////////////////////////////////// +void +write_transform(ostream &out, const LMatrix4d &mat, int indent_level) { + indent(out, indent_level) << " {\n"; + + bool written = false; + + /* + int mat_type = mat.getMatType(); + if ((mat_type & + ~(PFMAT_TRANS | PFMAT_ROT | PFMAT_SCALE | PFMAT_NONORTHO))==0) { + // Write out the matrix componentwise if possible. + pfVec3 s, r, t; + if (ExtractMatrix(mat, s, r, t)) { + if (!s.almostEqual(pfVec3(1.0, 1.0, 1.0), 0.0001)) { + Indent(out, indent+2) << " { " << s << " }\n"; + } + if (fabs(r[0]) > 0.0001) { + Indent(out, indent+2) << " { " << r[0] << " }\n"; + } + if (fabs(r[1]) > 0.0001) { + Indent(out, indent+2) << " { " << r[1] << " }\n"; + } + if (fabs(r[2]) > 0.0001) { + Indent(out, indent+2) << " { " << r[2] << " }\n"; + } + if (!t.almostEqual(pfVec3(0.0, 0.0, 0.0), 0.0001)) { + Indent(out, indent+2) << " { " << t << " }\n"; + } + written = true; + } + } + */ + + if (!written) { + // It's some non-affine matrix, or it has a shear; write out the + // general 4x4. + indent(out, indent_level+2) << " {\n"; + + for (int r = 0; r < 4; r++) { + indent(out, indent_level+3); + for (int c = 0; c < 4; c++) { + out << " " << mat(r, c); + } + out << "\n"; + } + indent(out, indent_level+2) << "}\n"; + } + + indent(out, indent_level) << "}\n"; +} diff --git a/panda/src/egg/eggMiscFuncs.h b/panda/src/egg/eggMiscFuncs.h new file mode 100644 index 0000000000..56a7aef8ed --- /dev/null +++ b/panda/src/egg/eggMiscFuncs.h @@ -0,0 +1,65 @@ +// Filename: eggMiscFuncs.h +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGMISCFUNCS_H +#define EGGMISCFUNCS_H + +//////////////////////////////////////////////////////////////////// +// +// eggMiscFuncs.h +// +// This contains the prototypes for functions that are useful to +// internal egg code. Also see eggUtilities.h, which contains +// functions that may be useful to the rest of the world. +// +//////////////////////////////////////////////////////////////////// + +#include + +#include +#include + +#include + +#ifndef WIN32_VC +class ostream; +#endif + + +//////////////////////////////////////////////////////////////////// +// Function: enquote_string +// Description: Writes the string to the indicated output stream. If +// the string contains any characters special to egg, +// writes quotation marks around it. If always_quote is +// true, writes quotation marks regardless. +//////////////////////////////////////////////////////////////////// +ostream & +enquote_string(ostream &out, const string &str, + int indent_level = 0, + bool always_quote = false); + + + +//////////////////////////////////////////////////////////////////// +// Function: write_transform +// Description: A helper function to write out a 3x3 transform +// matrix. +//////////////////////////////////////////////////////////////////// +void +write_transform(ostream &out, const LMatrix3d &mat, int indent_level); + + +//////////////////////////////////////////////////////////////////// +// Function: write_transform +// Description: A helper function to write out a 4x4 transform +// matrix. +//////////////////////////////////////////////////////////////////// +void +write_transform(ostream &out, const LMatrix4d &mat, int indent_level); + +#include "eggMiscFuncs.I" + +#endif + diff --git a/panda/src/egg/eggMorph.I b/panda/src/egg/eggMorph.I new file mode 100644 index 0000000000..d158c1c044 --- /dev/null +++ b/panda/src/egg/eggMorph.I @@ -0,0 +1,103 @@ +// Filename: eggMorph.I +// Created by: drose (29Jan99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggMorph::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE EggMorph:: +EggMorph(const string &name, const Parameter &offset) + : Namable(name), _offset(offset) { +} + + + +//////////////////////////////////////////////////////////////////// +// Function: EggMorph::set_offset +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void EggMorph:: +set_offset(const Parameter &offset) { + _offset = offset; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggMorph::get_offset +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE const Parameter &EggMorph:: +get_offset() const { + return _offset; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggMorph::Ordering operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool EggMorph:: +operator < (const EggMorph &other) const { + return get_name() < other.get_name(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggMorph::Equality operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool EggMorph:: +operator == (const EggMorph &other) const { + return get_name() == other.get_name(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggMorphVertex output operator +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ostream &operator << (ostream &out, const EggMorphVertex &m) { + return out << " " << m.get_name() << " { " << m.get_offset() << " }"; +} + + +/* +//////////////////////////////////////////////////////////////////// +// Function: EggMorphNormal output operator +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ostream &operator << (ostream &out, const EggMorphNormal &m) { + return out << " " << m.get_name() << " { " << m.get_offset() << " }"; +} +*/ + + +//////////////////////////////////////////////////////////////////// +// Function: EggMorphTexCoord output operator +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ostream &operator << (ostream &out, const EggMorphTexCoord &m) { + return out << " " << m.get_name() << " { " << m.get_offset() << " }"; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggMorphColor output operator +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ostream &operator << (ostream &out, const EggMorphColor &m) { + return out << " " << m.get_name() << " { " << m.get_offset() << " }"; +} diff --git a/panda/src/egg/eggMorph.h b/panda/src/egg/eggMorph.h new file mode 100644 index 0000000000..33ae065b73 --- /dev/null +++ b/panda/src/egg/eggMorph.h @@ -0,0 +1,58 @@ +// Filename: eggMorph.h +// Created by: drose (29Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGMORPH_H +#define EGGMORPH_H + +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : EggMorph +// Description : A single or or some such entry. This +// simply contains the morph name and the offset value. +// The class EggMorph is actually a template class on +// the type of value that is being offset; the specific +// kinds of morphs are instantiated from this below. +//////////////////////////////////////////////////////////////////// +template +class EXPCL_PANDAEGG EggMorph : public Namable { +public: + INLINE EggMorph(const string &name, const Parameter &offset); + INLINE void set_offset(const Parameter &offset); + INLINE const Parameter &get_offset() const; + + INLINE bool operator < (const EggMorph &other) const; + INLINE bool operator == (const EggMorph &other) const; + +private: + Parameter _offset; +}; + +typedef EggMorph EggMorphVertex; +typedef EggMorph EggMorphNormal; +typedef EggMorph EggMorphTexCoord; +typedef EggMorph EggMorphColor; + +INLINE ostream &operator << (ostream &out, const EggMorphVertex &m); +INLINE ostream &operator << (ostream &out, const EggMorphTexCoord &m); +INLINE ostream &operator << (ostream &out, const EggMorphColor &m); + +// EggMorphNormal is, by virtue of equivalent typedefs, another name +// for EggMorphVertex. Therefore we shouldn't define its output +// operator again. +//INLINE ostream &operator << (ostream &out, const EggMorphNormal &m); + +typedef set EggMorphVertices; +typedef set EggMorphNormals; +typedef set EggMorphTexCoords; +typedef set EggMorphColors; + +#include "eggMorph.I" + +#endif diff --git a/panda/src/egg/eggMorphList.I b/panda/src/egg/eggMorphList.I new file mode 100644 index 0000000000..1559a10db7 --- /dev/null +++ b/panda/src/egg/eggMorphList.I @@ -0,0 +1,23 @@ +// Filename: eggMorphList.I +// Created by: drose (29Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include + + +//////////////////////////////////////////////////////////////////// +// Function: EggMorphList::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void EggMorphList:: +write(ostream &out, int indent_level) const { + const_iterator i; + + for (i = begin(); i != end(); ++i) { + indent(out, indent_level) << *i << "\n"; + } +} + diff --git a/panda/src/egg/eggMorphList.h b/panda/src/egg/eggMorphList.h new file mode 100644 index 0000000000..0857a1ab9c --- /dev/null +++ b/panda/src/egg/eggMorphList.h @@ -0,0 +1,36 @@ +// Filename: eggMorphList.h +// Created by: drose (29Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGMORPHLIST_H +#define EGGMORPHLIST_H + +#include + +#include "eggMorph.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : EggMorphList +// Description : A collection of 's or 's or some such. +//////////////////////////////////////////////////////////////////// +template +class EXPCL_PANDAEGG EggMorphList : public set { +public: + + void write(ostream &out, int indent_level) const; + +private: + typedef set Morphs; +}; + +typedef EggMorphList EggMorphVertexList; +typedef EggMorphList EggMorphNormalList; +typedef EggMorphList EggMorphTexCoordList; +typedef EggMorphList EggMorphColorList; + +#include "eggMorphList.I" + +#endif diff --git a/panda/src/egg/eggNamedObject.I b/panda/src/egg/eggNamedObject.I new file mode 100644 index 0000000000..4c3d966512 --- /dev/null +++ b/panda/src/egg/eggNamedObject.I @@ -0,0 +1,37 @@ +// Filename: eggNamedObject.I +// Created by: drose (10Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggNamedObject::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggNamedObject:: +EggNamedObject(const string &name) : Namable(name) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggNamedObject::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggNamedObject:: +EggNamedObject(const EggNamedObject ©) : EggObject(copy), Namable(copy) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggNamedObject::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggNamedObject &EggNamedObject:: +operator = (const EggNamedObject ©) { + EggObject::operator = (copy); + Namable::operator = (copy); + return *this; +} diff --git a/panda/src/egg/eggNamedObject.cxx b/panda/src/egg/eggNamedObject.cxx new file mode 100644 index 0000000000..e3a23fd580 --- /dev/null +++ b/panda/src/egg/eggNamedObject.cxx @@ -0,0 +1,31 @@ +// Filename: eggNamedObject.cxx +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggNamedObject.h" +#include "eggMiscFuncs.h" + +#include + +TypeHandle EggNamedObject::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: EggNamedObject::write_header +// Access: Public +// Description: Writes the first line of the egg object, +// e.g. " group_name {" or some such. It +// automatically enquotes the name if it contains any +// special characters. egg_keyword is the keyword that +// begins the line, e.g. "". +//////////////////////////////////////////////////////////////////// +void EggNamedObject:: +write_header(ostream &out, int indent_level, const char *egg_keyword) const { + indent(out, indent_level) << egg_keyword << " "; + + if (has_name()) { + enquote_string(out, get_name()) << " {\n"; + } else { + out << "{\n"; + } +} diff --git a/panda/src/egg/eggNamedObject.h b/panda/src/egg/eggNamedObject.h new file mode 100644 index 0000000000..0368d00858 --- /dev/null +++ b/panda/src/egg/eggNamedObject.h @@ -0,0 +1,55 @@ +// Filename: eggNamedObject.h +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGNAMEDOBJECT_H +#define EGGNAMEDOBJECT_H + +#include + +#include "eggObject.h" +#include +#include + +#ifndef WIN32_VC +class ostream; +#endif + +//////////////////////////////////////////////////////////////////// +// Class : EggNamedObject +// Description : This is a fairly high-level base class--any egg +// object that has a name. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggNamedObject : public EggObject, public Namable { +public: + INLINE EggNamedObject(const string &name = ""); + INLINE EggNamedObject(const EggNamedObject ©); + INLINE EggNamedObject &operator = (const EggNamedObject ©); + + void write_header(ostream &out, int indent_level, + const char *egg_keyword) const; + + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggObject::init_type(); + Namable::init_type(); + register_type(_type_handle, "EggNamedObject", + EggObject::get_class_type(), + Namable::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "eggNamedObject.I" + +#endif diff --git a/panda/src/egg/eggNode.I b/panda/src/egg/eggNode.I new file mode 100644 index 0000000000..a8081eb0c8 --- /dev/null +++ b/panda/src/egg/eggNode.I @@ -0,0 +1,214 @@ +// Filename: eggNode.I +// Created by: drose (10Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggNode:: +EggNode(const string &name) : EggNamedObject(name) { + _parent = NULL; + _depth = 0; + _under_flags = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggNode:: +EggNode(const EggNode ©) : EggNamedObject(copy) { + _parent = NULL; + _depth = 0; + _under_flags = 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggNode &EggNode:: +operator = (const EggNode ©) { + EggNamedObject::operator = (copy); + update_under(0); + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::get_parent +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggGroupNode *EggNode:: +get_parent() const { + return _parent; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::get_depth +// Access: Public +// Description: Returns the number of nodes above this node in the +// egg hierarchy. +//////////////////////////////////////////////////////////////////// +INLINE int EggNode:: +get_depth() const { + return _depth; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::is_under_instance +// Access: Public +// Description: Returns true if there is an node somewhere +// in the egg tree at or above this node, false +// otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool EggNode:: +is_under_instance() const { + return (_under_flags & UF_under_instance) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::is_under_transform +// Access: Public +// Description: Returns true if there is a entry somewhere +// in the egg tree at or above this node, false +// otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool EggNode:: +is_under_transform() const { + return (_under_flags & UF_under_transform) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::is_local_coord +// Access: Public +// Description: Returns true if this node's vertices are not in the +// global coordinate space. This will be the case if +// there was an node under a transform at or +// above this node. +//////////////////////////////////////////////////////////////////// +INLINE bool EggNode:: +is_local_coord() const { + return (_under_flags & UF_local_coord) != 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::get_vertex_frame +// Access: Public +// Description: Returns the coordinate frame of the vertices +// referenced by primitives at or under this node. This +// is not the same as get_node_frame(). +// +// Generally, vertices in an egg file are stored in the +// global coordinate space, regardless of the transforms +// defined at each node. Thus, get_vertex_frame() will +// usually return the identity transform (global +// coordinate space). However, primitives under an +// entry reference their vertices in the +// coordinate system under effect at the time of the +// . Thus, nodes under an entry +// may return this non-identity matrix. +// +// Specifically, this may return a non-identity matrix +// only if is_local_coord() is true. +//////////////////////////////////////////////////////////////////// +INLINE const LMatrix4d &EggNode:: +get_vertex_frame() const { + if (_vertex_frame == NULL) { + return LMatrix4d::ident_mat(); + } else { + return *_vertex_frame; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::get_node_frame +// Access: Public +// Description: Returns the coordinate frame of the node itself. +// This is simply the net product of all transformations +// up to the root. +//////////////////////////////////////////////////////////////////// +INLINE const LMatrix4d &EggNode:: +get_node_frame() const { + if (_node_frame == NULL) { + return LMatrix4d::ident_mat(); + } else { + return *_node_frame; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::get_vertex_frame_inv +// Access: Public +// Description: Returns the inverse of the matrix returned by +// get_vertex_frame(). See get_vertex_frame(). +//////////////////////////////////////////////////////////////////// +INLINE const LMatrix4d &EggNode:: +get_vertex_frame_inv() const { + if (_vertex_frame_inv == NULL) { + return LMatrix4d::ident_mat(); + } else { + return *_vertex_frame_inv; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::get_node_frame_inv +// Access: Public +// Description: Returns the inverse of the matrix returned by +// get_vertex_frame(). See get_vertex_frame(). +//////////////////////////////////////////////////////////////////// +INLINE const LMatrix4d &EggNode:: +get_node_frame_inv() const { + if (_node_frame_inv == NULL) { + return LMatrix4d::ident_mat(); + } else { + return *_node_frame_inv; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::get_vertex_to_node +// Access: Public +// Description: Returns the transformation matrix suitable for +// converting the vertices as read from the egg file +// into the coordinate space of the node. This is the +// same thing as: +// +// get_vertex_frame() * get_node_frame_inv() +// +//////////////////////////////////////////////////////////////////// +INLINE const LMatrix4d &EggNode:: +get_vertex_to_node() const { + if (_vertex_to_node == NULL) { + return LMatrix4d::ident_mat(); + } else { + return *_vertex_to_node; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::transform +// Access: Public +// Description: Applies the indicated transformation to the node and +// all of its descendants. +//////////////////////////////////////////////////////////////////// +INLINE void EggNode:: +transform(const LMatrix4d &mat) { + LMatrix4d inv = invert(mat); + + r_transform(mat, inv, CS_default); +} diff --git a/panda/src/egg/eggNode.cxx b/panda/src/egg/eggNode.cxx new file mode 100644 index 0000000000..5caa985131 --- /dev/null +++ b/panda/src/egg/eggNode.cxx @@ -0,0 +1,141 @@ +// Filename: eggNode.cxx +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggNode.h" +#include "eggGroupNode.h" +#include "config_egg.h" + +#include + +TypeHandle EggNode::_type_handle; + + +#ifndef NDEBUG + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::test_under_integrity +// Access: Public +// Description: Recursively checks the integrity of the _under_flags, +// _parent, and _depth members of this node and all of +// its ancestors. +//////////////////////////////////////////////////////////////////// +void EggNode:: +test_under_integrity() const { + if (_parent == NULL) { + // If we have no parent, everything should be zero. + nassertv(_depth == 0); + nassertv(_under_flags == 0); + } else { + // Otherwise, make sure we're consistent with our parent. + _parent->test_ref_count_integrity(); + + nassertv(_depth == _parent->_depth + 1); + + // We can't perform too much checking on the under_flags, since we + // don't know which bits should have been added for this node. + // We'll verify that at least we didn't accidentally take some + // bits away. + nassertv((_under_flags & _parent->_under_flags) == _parent->_under_flags); + + // Make sure we're mentioned in our parent's children list. + EggGroupNode::iterator ci; + ci = find(_parent->begin(), _parent->end(), this); + nassertv(ci != _parent->end()); + + // Now recurse up our parent. + _parent->test_under_integrity(); + } +} + +#endif // NDEBUG + + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::update_under +// Access: Protected, Virtual +// Description: This function is called from within EggGroupNode +// whenever the parentage of the node has changed. It +// should update the depth and under_instance flags +// accordingly. +// +// depth_offset is the difference between the old depth +// value and the new value. It should be consistent +// with the supplied depth value. If it is not, we have +// some error. +//////////////////////////////////////////////////////////////////// +void EggNode:: +update_under(int depth_offset) { + int depth; + if (_parent == NULL) { + depth = 0; + _under_flags = 0; + _vertex_frame = NULL; + _node_frame = NULL; + _vertex_frame_inv = NULL; + _node_frame_inv = NULL; + _vertex_to_node = NULL; + } else { + depth = _parent->_depth + 1; + _under_flags = _parent->_under_flags; + _vertex_frame = _parent->_vertex_frame; + _node_frame = _parent->_node_frame; + _vertex_frame_inv = _parent->_vertex_frame_inv; + _node_frame_inv = _parent->_node_frame_inv; + _vertex_to_node = _parent->_vertex_to_node; + } + + if (depth - _depth != depth_offset) { + egg_cat.error() << "Cycle in egg graph or invalid egg pointer!\n"; + return; + } + _depth = depth; + + adjust_under(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::adjust_under +// Access: Protected, Virtual +// Description: This is called within update_under() after all the +// various under settings have been inherited directly +// from the parent node. It is responsible for +// adjusting these settings to reflect states local to +// the current node; for instance, an node +// will force the UF_under_instance bit on. +//////////////////////////////////////////////////////////////////// +void EggNode:: +adjust_under() { +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::r_transform +// Access: Protected, Virtual +// Description: This is called from within the egg code by +// transform(). It applies a transformation matrix +// to the current node in some sensible way, then +// continues down the tree. +// +// The first matrix is the transformation to apply; the +// second is its inverse. The third parameter is the +// coordinate system we are changing to, or CS_default +// if we are not changing coordinate systems. +//////////////////////////////////////////////////////////////////// +void EggNode:: +r_transform(const LMatrix4d &, const LMatrix4d &, CoordinateSystem) { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNode::r_mark_coordsys +// Access: Protected, Virtual +// Description: This is only called immediately after loading an egg +// file from disk, to propagate the value found in the +// CoordinateSystem entry (or the default Y-up +// coordinate system) to all nodes that care about what +// the coordinate system is. +//////////////////////////////////////////////////////////////////// +void EggNode:: +r_mark_coordsys(CoordinateSystem) { +} diff --git a/panda/src/egg/eggNode.h b/panda/src/egg/eggNode.h new file mode 100644 index 0000000000..08ad308515 --- /dev/null +++ b/panda/src/egg/eggNode.h @@ -0,0 +1,110 @@ +// Filename: eggNode.h +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGNODE_H +#define EGGNODE_H + +#include + +#include "eggNamedObject.h" + +#include +#include +#include +#include + +class EggGroupNode; + +//////////////////////////////////////////////////////////////////// +// Class : EggNode +// Description : A base class for things that may be directly added +// into the egg hierarchy. This includes groups, +// joints, polygons, vertex pools, etc., but does not +// include things like vertices. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggNode : public EggNamedObject { +public: + INLINE EggNode(const string &name = ""); + INLINE EggNode(const EggNode ©); + INLINE EggNode &operator = (const EggNode ©); + + INLINE EggGroupNode *get_parent() const; + INLINE int get_depth() const; + INLINE bool is_under_instance() const; + INLINE bool is_under_transform() const; + INLINE bool is_local_coord() const; + + INLINE const LMatrix4d &get_vertex_frame() const; + INLINE const LMatrix4d &get_node_frame() const; + INLINE const LMatrix4d &get_vertex_frame_inv() const; + INLINE const LMatrix4d &get_node_frame_inv() const; + INLINE const LMatrix4d &get_vertex_to_node() const; + + INLINE void transform(const LMatrix4d &mat); + + virtual void write(ostream &out, int indent_level) const=0; + +#ifndef NDEBUG + void test_under_integrity() const; +#else + void test_under_integrity() const { } +#endif // NDEBUG + + +protected: + enum UnderFlags { + UF_under_instance = 0x001, + UF_under_transform = 0x002, + UF_local_coord = 0x004, + }; + + virtual void update_under(int depth_offset); + virtual void adjust_under(); + + virtual void r_transform(const LMatrix4d &mat, const LMatrix4d &inv, + CoordinateSystem to_cs); + virtual void r_mark_coordsys(CoordinateSystem cs); + + // These members are updated automatically by prepare_add_child(), + // prepare_remove_child(), and update_under(). Other functions + // shouldn't be fiddling with them. + + EggGroupNode *_parent; + int _depth; + int _under_flags; + + typedef RefCountObj MatrixFrame; + + PT(MatrixFrame) _vertex_frame; + PT(MatrixFrame) _node_frame; + PT(MatrixFrame) _vertex_frame_inv; + PT(MatrixFrame) _node_frame_inv; + PT(MatrixFrame) _vertex_to_node; + + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggNamedObject::init_type(); + register_type(_type_handle, "EggNode", + EggNamedObject::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + +friend class EggGroupNode; +}; + +#include "eggNode.I" + +#endif diff --git a/panda/src/egg/eggNurbsCurve.I b/panda/src/egg/eggNurbsCurve.I new file mode 100644 index 0000000000..8b41720257 --- /dev/null +++ b/panda/src/egg/eggNurbsCurve.I @@ -0,0 +1,131 @@ +// Filename: eggNurbsCurve.I +// Created by: drose (15Feb00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsCurve::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggNurbsCurve:: +EggNurbsCurve(const string &name) : EggCurve(name) { + _order = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsCurve::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggNurbsCurve:: +EggNurbsCurve(const EggNurbsCurve ©) : + EggCurve(copy), + _knots(copy._knots), + _order(copy._order) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsCurve::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggNurbsCurve &EggNurbsCurve:: +operator = (const EggNurbsCurve ©) { + EggCurve::operator = (copy); + _knots = copy._knots; + _order = copy._order; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsCurve::set_order +// Access: Public +// Description: Directly changes the order to the indicated value +// (which must be an integer in the range 1 <= order <= +// 4). If possible, it is preferable to use the setup() +// method instead of this method, since changing the +// order directly may result in an invalid curve. +//////////////////////////////////////////////////////////////////// +INLINE void EggNurbsCurve:: +set_order(int order) { + nassertv(order >= 1 && order <= 4); + _order = order; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsCurve::set_knot +// Access: Public +// Description: Resets the value of the indicated knot as indicated. +// k must be in the range 0 <= k < get_num_knots(), +// and the value must be in the range get_knot(k - 1) +// <= value <= get_knot(k + 1). +//////////////////////////////////////////////////////////////////// +INLINE void EggNurbsCurve:: +set_knot(int k, double value) { + nassertv(k >= 0 && k < (int)_knots.size()); + _knots[k] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsCurve::get_order +// Access: Public +// Description: Returns the order of the curve. The order is the +// degree of the NURBS equation plus 1; for a typical +// NURBS, the order is 4. With this implementation of +// NURBS, the order must be in the range [1, 4]. +//////////////////////////////////////////////////////////////////// +INLINE int EggNurbsCurve:: +get_order() const { + return _order; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsCurve::get_degree +// Access: Public +// Description: Returns the degree of the curve. For a typical +// NURBS, the degree is 3. +//////////////////////////////////////////////////////////////////// +INLINE int EggNurbsCurve:: +get_degree() const { + return _order - 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsCurve::get_num_knots +// Access: Public +// Description: Returns the number of knots. +//////////////////////////////////////////////////////////////////// +INLINE int EggNurbsCurve:: +get_num_knots() const { + return _knots.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsCurve::get_num_cvs +// Access: Public +// Description: Returns the total number of control vertices that +// *should* be defined for the curve. This is +// determined by the number of knots and the order, in +// each direction; it does not necessarily reflect the +// number of vertices that have actually been added to +// the curve. (However, if the number of vertices in +// the curve are wrong, the curve is invalid.) +//////////////////////////////////////////////////////////////////// +INLINE int EggNurbsCurve:: +get_num_cvs() const { + return get_num_knots() - get_order(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsCurve::get_knot +// Access: Public +// Description: Returns the nth knot value defined. +//////////////////////////////////////////////////////////////////// +INLINE double EggNurbsCurve:: +get_knot(int k) const { + nassertr(k >= 0 && k < (int)_knots.size(), 0.0); + return _knots[k]; +} + diff --git a/panda/src/egg/eggNurbsCurve.cxx b/panda/src/egg/eggNurbsCurve.cxx new file mode 100644 index 0000000000..3a43c7936e --- /dev/null +++ b/panda/src/egg/eggNurbsCurve.cxx @@ -0,0 +1,145 @@ +// Filename: eggNurbsCurve.cxx +// Created by: drose (15Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "eggNurbsCurve.h" + +#include + +TypeHandle EggNurbsCurve::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsCurve::setup +// Access: Public +// Description: Prepares a new curve definition with the indicated +// order and number of knots. This also implies a +// particular number of vertices as well (the number of +// knots minus the order), but it is up to the user to +// add the correct number of vertices to the curve by +// repeatedly calling push_back(). +//////////////////////////////////////////////////////////////////// +void EggNurbsCurve:: +setup(int order, int num_knots) { + _order = order; + _knots.clear(); + + int i; + _knots.reserve(num_knots); + for (i = 0; i < num_knots; i++) { + _knots.push_back((double)i); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsCurve::set_num_knots +// Access: Public +// Description: Directly changes the number of knots. This will +// either add zero-valued knots onto the end, or +// truncate knot values from the end, depending on +// whether the list is being increased or decreased. If +// possible, it is preferable to use the setup() method +// instead of directly setting the number of knots, as +// this may result in an invalid curve. +//////////////////////////////////////////////////////////////////// +void EggNurbsCurve:: +set_num_knots(int num) { + if ((int)_knots.size() >= num) { + // Truncate knot values at the end. + _knots.erase(_knots.begin() + num, _knots.end()); + } else { + // Append knot values to the end. + _knots.reserve(num); + for (int i = _knots.size(); i < num; i++) { + _knots.push_back(0.0); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsCurve::is_valid +// Access: Public +// Description: Returns true if the NURBS parameters are all +// internally consistent (e.g. it has the right number +// of vertices to match its number of knots and order in +// each dimension), or false otherwise. +//////////////////////////////////////////////////////////////////// +bool EggNurbsCurve:: +is_valid() const { + if (_order < 1 || _order > 4) { + // Invalid order. + return false; + } + + if (get_num_cvs() != (int)size()) { + // Wrong number of CV's. + return false; + } + + // Do all the knot values monotonically increase? + int i; + for (i = 1; i < get_num_knots(); i++) { + if (get_knot(i) < get_knot(i - 1)) { + return false; + } + } + + // Everything's looking good! + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsCurve::is_closed +// Access: Public +// Description: Returns true if the curve appears to be closed. +// Since the Egg syntax does not provide a means for +// explicit indication of closure, this has to be +// guessed at by examining the curve itself. +//////////////////////////////////////////////////////////////////// +bool EggNurbsCurve:: +is_closed() const { + // Technically, the curve is closed if the CV's at the end are + // repeated from the beginning. We'll do a cheesy test for + // expediency's sake: the curve is closed if the first n knots are + // not repeated. I think this will catch all the normal curves + // we're likely to see. + + int i; + for (i = 1; i < get_order(); i++) { + if (get_knot(i) != get_knot(i-1)) { + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsCurve::write +// Access: Public, Virtual +// Description: Writes the nurbsCurve to the indicated output stream in +// Egg format. +//////////////////////////////////////////////////////////////////// +void EggNurbsCurve:: +write(ostream &out, int indent_level) const { + write_header(out, indent_level, ""); + + if (get_curve_type() != CT_none) { + indent(out, indent_level + 2) + << " type { " << get_curve_type() << " }\n"; + } + if (get_subdiv() != 0) { + indent(out, indent_level + 2) + << " subdiv { " << get_subdiv() << " }\n"; + } + indent(out, indent_level + 2) + << " { " << get_order() << " }\n"; + indent(out, indent_level + 2) + << " {\n"; + write_long_list(out, indent_level+4, _knots.begin(), _knots.end(), "", + "", 72); + indent(out, indent_level + 2) + << "}\n"; + + write_body(out, indent_level+2); + indent(out, indent_level) << "}\n"; +} diff --git a/panda/src/egg/eggNurbsCurve.h b/panda/src/egg/eggNurbsCurve.h new file mode 100644 index 0000000000..afcadf0732 --- /dev/null +++ b/panda/src/egg/eggNurbsCurve.h @@ -0,0 +1,72 @@ +// Filename: eggNurbsCurve.h +// Created by: drose (15Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGNURBSCURVE_H +#define EGGNURBSCURVE_H + +#include + +#include "eggCurve.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : EggNurbsCurve +// Description : A parametric NURBS curve. +//////////////////////////////////////////////////////////////////// +class EggNurbsCurve : public EggCurve { +public: + INLINE EggNurbsCurve(const string &name = ""); + INLINE EggNurbsCurve(const EggNurbsCurve ©); + INLINE EggNurbsCurve &operator = (const EggNurbsCurve ©); + + void setup(int order, int num_knots); + + INLINE void set_order(int order); + void set_num_knots(int num); + + INLINE void set_knot(int k, double value); + + bool is_valid() const; + + INLINE int get_order() const; + INLINE int get_degree() const; + INLINE int get_num_knots() const; + INLINE int get_num_cvs() const; + + bool is_closed() const; + + INLINE double get_knot(int k) const; + + virtual void write(ostream &out, int indent_level) const; + +private: + typedef vector_double Knots; + Knots _knots; + int _order; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggPrimitive::init_type(); + register_type(_type_handle, "EggNurbsCurve", + EggPrimitive::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + +}; + +#include "eggNurbsCurve.I" + +#endif diff --git a/panda/src/egg/eggNurbsSurface.I b/panda/src/egg/eggNurbsSurface.I new file mode 100644 index 0000000000..8b6fd0faf9 --- /dev/null +++ b/panda/src/egg/eggNurbsSurface.I @@ -0,0 +1,316 @@ +// Filename: eggNurbsSurface.I +// Created by: drose (15Feb00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggNurbsSurface:: +EggNurbsSurface(const string &name) : EggSurface(name) { + _u_order = 0; + _v_order = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggNurbsSurface:: +EggNurbsSurface(const EggNurbsSurface ©) : + EggSurface(copy), + _u_knots(copy._u_knots), + _v_knots(copy._v_knots), + _u_order(copy._u_order), + _v_order(copy._v_order) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggNurbsSurface &EggNurbsSurface:: +operator = (const EggNurbsSurface ©) { + EggSurface::operator = (copy); + _u_knots = copy._u_knots; + _v_knots = copy._v_knots; + _u_order = copy._u_order; + _v_order = copy._v_order; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::set_u_order +// Access: Public +// Description: Directly changes the order in the U direction to the +// indicated value (which must be an integer in the +// range 1 <= u_order <= 4). If possible, it is +// preferable to use the setup() method instead of this +// method, since changing the order directly may result +// in an invalid surface. +//////////////////////////////////////////////////////////////////// +INLINE void EggNurbsSurface:: +set_u_order(int u_order) { + nassertv(u_order >= 1 && u_order <= 4); + _u_order = u_order; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::set_v_order +// Access: Public +// Description: Directly changes the order in the V direction to the +// indicated value (which must be an integer in the +// range 1 <= v_order <= 4). If possible, it is +// preferable to use the setup() method instead of this +// method, since changing the order directly may result +// in an invalid surface. +//////////////////////////////////////////////////////////////////// +INLINE void EggNurbsSurface:: +set_v_order(int v_order) { + nassertv(v_order >= 1 && v_order <= 4); + _v_order = v_order; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::set_u_knot +// Access: Public +// Description: Resets the value of the indicated knot as indicated. +// k must be in the range 0 <= k < get_num_u_knots(), +// and the value must be in the range get_u_knot(k - 1) +// <= value <= get_u_knot(k + 1). +//////////////////////////////////////////////////////////////////// +INLINE void EggNurbsSurface:: +set_u_knot(int k, double value) { + nassertv(k >= 0 && k < (int)_u_knots.size()); + _u_knots[k] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::set_v_knot +// Access: Public +// Description: Resets the value of the indicated knot as indicated. +// k must be in the range 0 <= k < get_num_v_knots(), +// and the value must be in the range get_v_knot(k - 1) +// <= value <= get_v_knot(k + 1). +//////////////////////////////////////////////////////////////////// +INLINE void EggNurbsSurface:: +set_v_knot(int k, double value) { + nassertv(k >= 0 && k < (int)_v_knots.size()); + _v_knots[k] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::set_cv +// Access: Public +// Description: Redefines the control vertex associated with a +// particular u, v coordinate pair. This is just a +// shorthand to access the EggPrimitive's normal vertex +// assignment for a 2-d control vertex. +//////////////////////////////////////////////////////////////////// +INLINE void EggNurbsSurface:: +set_cv(int ui, int vi, PT(EggVertex) vertex) { + int vertex_index = get_vertex_index(ui, vi); + set_vertex(vertex_index, vertex); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::get_u_order +// Access: Public +// Description: Returns the order of the surface in the U direction. +// The order is the degree of the NURBS equation plus 1; +// for a typical NURBS, the order is 4. With this +// implementation of NURBS, the order must be in the +// range [1, 4]. +//////////////////////////////////////////////////////////////////// +INLINE int EggNurbsSurface:: +get_u_order() const { + return _u_order; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::get_v_order +// Access: Public +// Description: Returns the order of the surface in the V direction. +// The order is the degree of the NURBS equation plus 1; +// for a typical NURBS, the order is 4. With this +// implementation of NURBS, the order must be in the +// range [1, 4]. +//////////////////////////////////////////////////////////////////// +INLINE int EggNurbsSurface:: +get_v_order() const { + return _v_order; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::get_u_degree +// Access: Public +// Description: Returns the degree of the surface in the U direction. +// For a typical NURBS, the degree is 3. +//////////////////////////////////////////////////////////////////// +INLINE int EggNurbsSurface:: +get_u_degree() const { + return _u_order - 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::get_v_degree +// Access: Public +// Description: Returns the degree of the surface in the V direction. +// for a typical NURBS, the degree is 3. +//////////////////////////////////////////////////////////////////// +INLINE int EggNurbsSurface:: +get_v_degree() const { + return _v_order - 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::get_num_u_knots +// Access: Public +// Description: Returns the number of knots in the U direction. +//////////////////////////////////////////////////////////////////// +INLINE int EggNurbsSurface:: +get_num_u_knots() const { + return _u_knots.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::get_num_v_knots +// Access: Public +// Description: Returns the number of knots in the V direction. +//////////////////////////////////////////////////////////////////// +INLINE int EggNurbsSurface:: +get_num_v_knots() const { + return _v_knots.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::get_num_u_cvs +// Access: Public +// Description: Returns the number of control vertices that should be +// present in the U direction. This is determined by +// the number of knots and the order; it does not +// necessarily reflect the number of vertices that have +// actually been added to the surface. (However, if the +// number of vertices in the surface are wrong, the +// surface is invalid.) +//////////////////////////////////////////////////////////////////// +INLINE int EggNurbsSurface:: +get_num_u_cvs() const { + return get_num_u_knots() - get_u_order(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::get_num_v_cvs +// Access: Public +// Description: Returns the number of control vertices that should be +// present in the V direction. This is determined by +// the number of knots and the order; it does not +// necessarily reflect the number of vertices that have +// actually been added to the surface. (However, if the +// number of vertices in the surface are wrong, the +// surface is invalid.) +//////////////////////////////////////////////////////////////////// +INLINE int EggNurbsSurface:: +get_num_v_cvs() const { + return get_num_v_knots() - get_v_order(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::get_num_cvs +// Access: Public +// Description: Returns the total number of control vertices that +// *should* be defined for the surface. This is +// determined by the number of knots and the order, in +// each direction; it does not necessarily reflect the +// number of vertices that have actually been added to +// the surface. (However, if the number of vertices in +// the surface are wrong, the surface is invalid.) +//////////////////////////////////////////////////////////////////// +INLINE int EggNurbsSurface:: +get_num_cvs() const { + return get_num_u_cvs() * get_num_v_cvs(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::get_u_index +// Access: Public +// Description: Returns the U index number of the given vertex within +// the EggPrimitive's linear list of vertices. An +// EggNurbsSurface maps a linear list of vertices to its +// 2-d mesh; this returns the U index number that +// corresponds to the nth vertex in the list. +//////////////////////////////////////////////////////////////////// +INLINE int EggNurbsSurface:: +get_u_index(int vertex_index) const { + nassertr(vertex_index >= 0 && vertex_index < get_num_cvs(), 0); + return vertex_index % get_num_u_cvs(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::get_v_index +// Access: Public +// Description: Returns the V index number of the given vertex within +// the EggPrimitive's linear list of vertices. An +// EggNurbsSurface maps a linear list of vertices to its +// 2-d mesh; this returns the V index number that +// corresponds to the nth vertex in the list. +//////////////////////////////////////////////////////////////////// +INLINE int EggNurbsSurface:: +get_v_index(int vertex_index) const { + nassertr(vertex_index >= 0 && vertex_index < get_num_cvs(), 0); + return vertex_index / get_num_u_cvs(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::get_vertex_index +// Access: Public +// Description: Returns the index number within the EggPrimitive's +// list of the control vertex at position ui, vi. +//////////////////////////////////////////////////////////////////// +INLINE int EggNurbsSurface:: +get_vertex_index(int ui, int vi) const { + nassertr(ui >= 0 && ui < get_num_u_cvs(), 0); + nassertr(vi >= 0 && vi < get_num_v_cvs(), 0); + return vi * get_num_u_cvs() + ui; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::get_u_knot +// Access: Public +// Description: Returns the nth knot value defined in the U +// direction. +//////////////////////////////////////////////////////////////////// +INLINE double EggNurbsSurface:: +get_u_knot(int k) const { + nassertr(k >= 0 && k < (int)_u_knots.size(), 0.0); + return _u_knots[k]; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::get_v_knot +// Access: Public +// Description: Returns the nth knot value defined in the V +// direction. +//////////////////////////////////////////////////////////////////// +INLINE double EggNurbsSurface:: +get_v_knot(int k) const { + nassertr(k >= 0 && k < (int)_v_knots.size(), 0.0); + return _v_knots[k]; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::get_cv +// Access: Public +// Description: Returns the control vertex at the indicate U, V +// position. +//////////////////////////////////////////////////////////////////// +INLINE PT(EggVertex) EggNurbsSurface:: +get_cv(int ui, int vi) const { + int vertex_index = get_vertex_index(ui, vi); + return get_vertex(vertex_index); +} diff --git a/panda/src/egg/eggNurbsSurface.cxx b/panda/src/egg/eggNurbsSurface.cxx new file mode 100644 index 0000000000..753a1dc76c --- /dev/null +++ b/panda/src/egg/eggNurbsSurface.cxx @@ -0,0 +1,231 @@ +// Filename: eggNurbsSurface.cxx +// Created by: drose (15Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "eggNurbsSurface.h" + +#include + +TypeHandle EggNurbsSurface::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::setup +// Access: Public +// Description: Prepares a new surface definition with the indicated +// order and number of knots in each dimension. This +// also implies a particular number of vertices in each +// dimension as well (the number of knots minus the +// order), but it is up to the user to add the correct +// number of vertices to the surface by repeatedly +// calling push_back(). +//////////////////////////////////////////////////////////////////// +void EggNurbsSurface:: +setup(int u_order, int v_order, + int num_u_knots, int num_v_knots) { + _u_order = u_order; + _v_order = v_order; + _u_knots.clear(); + _v_knots.clear(); + + int i; + _u_knots.reserve(num_u_knots); + for (i = 0; i < num_u_knots; i++) { + _u_knots.push_back((double)i); + } + _v_knots.reserve(num_v_knots); + for (i = 0; i < num_v_knots; i++) { + _v_knots.push_back((double)i); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::set_num_u_knots +// Access: Public +// Description: Directly changes the number of knots in the U +// direction. This will either add zero-valued knots +// onto the end, or truncate knot values from the end, +// depending on whether the list is being increased or +// decreased. If possible, it is preferable to use the +// setup() method instead of directly setting the number +// of knots, as this may result in an invalid surface. +//////////////////////////////////////////////////////////////////// +void EggNurbsSurface:: +set_num_u_knots(int num) { + if ((int)_u_knots.size() >= num) { + // Truncate knot values at the end. + _u_knots.erase(_u_knots.begin() + num, _u_knots.end()); + } else { + // Append knot values to the end. + _u_knots.reserve(num); + for (int i = _u_knots.size(); i < num; i++) { + _u_knots.push_back(0.0); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::set_num_v_knots +// Access: Public +// Description: Directly changes the number of knots in the V +// direction. This will either add zero-valued knots +// onto the end, or truncate knot values from the end, +// depending on whether the list is being increased or +// decreased. If possible, it is preferable to use the +// setup() method instead of directly setting the number +// of knots, as this may result in an invalid surface. +//////////////////////////////////////////////////////////////////// +void EggNurbsSurface:: +set_num_v_knots(int num) { + if ((int)_v_knots.size() >= num) { + // Truncate knot values at the end. + _v_knots.erase(_v_knots.begin() + num, _v_knots.end()); + } else { + // Append knot values to the end. + _v_knots.reserve(num); + for (int i = _v_knots.size(); i < num; i++) { + _v_knots.push_back(0.0); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::is_valid +// Access: Public +// Description: Returns true if the NURBS parameters are all +// internally consistent (e.g. it has the right number +// of vertices to match its number of knots and order in +// each dimension), or false otherwise. +//////////////////////////////////////////////////////////////////// +bool EggNurbsSurface:: +is_valid() const { + if (_u_order < 1 || _u_order > 4 || _v_order < 1 || _v_order > 4) { + // Invalid order. + return false; + } + + if (get_num_cvs() != (int)size()) { + // Wrong number of CV's. + return false; + } + + // Do all the knot values monotonically increase? + int i; + for (i = 1; i < get_num_u_knots(); i++) { + if (get_u_knot(i) < get_u_knot(i - 1)) { + return false; + } + } + for (i = 1; i < get_num_v_knots(); i++) { + if (get_v_knot(i) < get_v_knot(i - 1)) { + return false; + } + } + + // Everything's looking good! + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::is_closed_u +// Access: Public +// Description: Returns true if the surface appears to be closed in +// the U direction. Since the Egg syntax does not +// provide a means for explicit indication of closure, +// this has to be guessed at by examining the surface +// itself. +//////////////////////////////////////////////////////////////////// +bool EggNurbsSurface:: +is_closed_u() const { + // Technically, the surface is closed if the CV's at the end are + // repeated from the beginning. We'll do a cheesy test for + // expediency's sake: the surface is closed if the first n knots are + // not repeated. I think this will catch all the normal surfaces + // we're likely to see. + + int i; + for (i = 1; i < get_u_order(); i++) { + if (get_u_knot(i) != get_u_knot(i-1)) { + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::is_closed_v +// Access: Public +// Description: Returns true if the surface appears to be closed in +// the V direction. Since the Egg syntax does not +// provide a means for explicit indication of closure, +// this has to be guessed at by examining the surface +// itself. +//////////////////////////////////////////////////////////////////// +bool EggNurbsSurface:: +is_closed_v() const { + int i; + for (i = 1; i < get_v_order(); i++) { + if (get_v_knot(i) != get_v_knot(i-1)) { + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggNurbsSurface::write +// Access: Public, Virtual +// Description: Writes the nurbsSurface to the indicated output stream in +// Egg format. +//////////////////////////////////////////////////////////////////// +void EggNurbsSurface:: +write(ostream &out, int indent_level) const { + write_header(out, indent_level, ""); + + Trims::const_iterator ti; + for (ti = _trims.begin(); ti != _trims.end(); ++ti) { + indent(out, indent_level + 2) << " {\n"; + Loops::const_iterator li; + for (li = (*ti).begin(); li != (*ti).end(); ++li) { + indent(out, indent_level + 4) << " {\n"; + Curves::const_iterator ci; + for (ci = (*li).begin(); ci != (*li).end(); ++ci) { + (*ci)->write(out, indent_level + 6); + } + indent(out, indent_level + 4) << "}\n"; + } + indent(out, indent_level + 2) << "}\n"; + } + + if (get_u_subdiv() != 0) { + indent(out, indent_level + 2) + << " U-subdiv { " << get_u_subdiv() << " }\n"; + } + if (get_v_subdiv() != 0) { + indent(out, indent_level + 2) + << " V-subdiv { " << get_v_subdiv() << " }\n"; + } + indent(out, indent_level + 2) + << " { " << get_u_order() << " " << get_v_order() << " }\n"; + indent(out, indent_level + 2) + << " {\n"; + write_long_list(out, indent_level+4, _u_knots.begin(), _u_knots.end(), + "", "", 72); + indent(out, indent_level + 2) + << "}\n"; + indent(out, indent_level + 2) + << " {\n"; + write_long_list(out, indent_level+4, _v_knots.begin(), _v_knots.end(), + "", "", 72); + indent(out, indent_level + 2) + << "}\n"; + + write_body(out, indent_level+2); + + Curves::const_iterator ci; + for (ci = _curves_on_surface.begin(); ci != _curves_on_surface.end(); ++ci) { + (*ci)->write(out, indent_level + 2); + } + + indent(out, indent_level) << "}\n"; +} diff --git a/panda/src/egg/eggNurbsSurface.h b/panda/src/egg/eggNurbsSurface.h new file mode 100644 index 0000000000..48cf1ffcd3 --- /dev/null +++ b/panda/src/egg/eggNurbsSurface.h @@ -0,0 +1,103 @@ +// Filename: eggNurbsSurface.h +// Created by: drose (15Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGNURBSSURFACE_H +#define EGGNURBSSURFACE_H + +#include + +#include "eggSurface.h" +#include "eggNurbsCurve.h" + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : EggNurbsSurface +// Description : A parametric NURBS surface. +//////////////////////////////////////////////////////////////////// +class EggNurbsSurface : public EggSurface { +public: + typedef list Curves; + typedef Curves Loop; + typedef list Loops; + typedef Loops Trim; + typedef list Trims; + + INLINE EggNurbsSurface(const string &name = ""); + INLINE EggNurbsSurface(const EggNurbsSurface ©); + INLINE EggNurbsSurface &operator = (const EggNurbsSurface ©); + + void setup(int u_order, int v_order, + int num_u_knots, int num_v_knots); + + INLINE void set_u_order(int u_order); + INLINE void set_v_order(int v_order); + void set_num_u_knots(int num); + void set_num_v_knots(int num); + + INLINE void set_u_knot(int k, double value); + INLINE void set_v_knot(int k, double value); + INLINE void set_cv(int ui, int vi, PT(EggVertex) vertex); + + bool is_valid() const; + + INLINE int get_u_order() const; + INLINE int get_v_order() const; + INLINE int get_u_degree() const; + INLINE int get_v_degree() const; + INLINE int get_num_u_knots() const; + INLINE int get_num_v_knots() const; + INLINE int get_num_u_cvs() const; + INLINE int get_num_v_cvs() const; + INLINE int get_num_cvs() const; + + INLINE int get_u_index(int vertex_index) const; + INLINE int get_v_index(int vertex_index) const; + INLINE int get_vertex_index(int ui, int vi) const; + + bool is_closed_u() const; + bool is_closed_v() const; + + INLINE double get_u_knot(int k) const; + INLINE double get_v_knot(int k) const; + INLINE PT(EggVertex) get_cv(int ui, int vi) const; + + virtual void write(ostream &out, int indent_level) const; + + Curves _curves_on_surface; + Trims _trims; + +private: + typedef vector_double Knots; + Knots _u_knots; + Knots _v_knots; + int _u_order; + int _v_order; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggPrimitive::init_type(); + register_type(_type_handle, "EggNurbsSurface", + EggPrimitive::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + +}; + +#include "eggNurbsSurface.I" + +#endif diff --git a/panda/src/egg/eggObject.I b/panda/src/egg/eggObject.I new file mode 100644 index 0000000000..94b11e3595 --- /dev/null +++ b/panda/src/egg/eggObject.I @@ -0,0 +1,36 @@ +// Filename: eggObject.I +// Created by: drose (10Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggObject::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggObject:: +EggObject() { +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggObject::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggObject:: +EggObject(const EggObject ©) : TypedReferenceCount(copy) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggObject::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggObject &EggObject:: +operator = (const EggObject ©) { + TypedReferenceCount::operator = (copy); + return *this; +} diff --git a/panda/src/egg/eggObject.cxx b/panda/src/egg/eggObject.cxx new file mode 100644 index 0000000000..44adff43b3 --- /dev/null +++ b/panda/src/egg/eggObject.cxx @@ -0,0 +1,9 @@ +// Filename: eggObject.cxx +// Created by: drose (17Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggObject.h" + +TypeHandle EggObject::_type_handle; + diff --git a/panda/src/egg/eggObject.h b/panda/src/egg/eggObject.h new file mode 100644 index 0000000000..f88d7bb5af --- /dev/null +++ b/panda/src/egg/eggObject.h @@ -0,0 +1,49 @@ +// Filename: eggObject.h +// Created by: drose (17Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGOBJECT_H +#define EGGOBJECT_H + +#include + +#include + +#ifndef WIN32_VC +class ostream; +#endif + +//////////////////////////////////////////////////////////////////// +// Class : EggObject +// Description : The highest-level base class in the egg directory. +// (Almost) all things egg inherit from this. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggObject : public TypedReferenceCount { +public: + INLINE EggObject(); + INLINE EggObject(const EggObject ©); + INLINE EggObject &operator = (const EggObject ©); + + virtual ~EggObject() { } + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "EggObject", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "eggObject.I" + +#endif diff --git a/panda/src/egg/eggParameters.cxx b/panda/src/egg/eggParameters.cxx new file mode 100644 index 0000000000..bf9942ed56 --- /dev/null +++ b/panda/src/egg/eggParameters.cxx @@ -0,0 +1,38 @@ +// Filename: eggParameters.cxx +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggParameters.h" + +#include + +static EggParameters default_egg_parameters; +EggParameters *egg_parameters = &default_egg_parameters; + + +//////////////////////////////////////////////////////////////////// +// Function: EggParameters::Constructor +// Access: Public +// Description: Initializes all the parameters with default values. +//////////////////////////////////////////////////////////////////// +EggParameters:: +EggParameters() { + _pos_threshold = 0.0001; + _normal_threshold = 0.0001; + _uv_threshold = 0.0001; + _color_threshold = 1.0/256.0; + + _table_threshold = 0.0001; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggParameters::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggParameters:: +EggParameters(const EggParameters &other) { + memcpy(this, &other, sizeof(EggParameters)); +} diff --git a/panda/src/egg/eggParameters.h b/panda/src/egg/eggParameters.h new file mode 100644 index 0000000000..56bcf8f303 --- /dev/null +++ b/panda/src/egg/eggParameters.h @@ -0,0 +1,54 @@ +// Filename: eggParameters.h +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGPARAMETERS_H +#define EGGPARAMETERS_H + +#include + +/////////////////////////////////////////////////////////////////// +// Class : EggParameters +// Description : The values stored in this structure are global +// parameters that control some aspects of the egg +// library. User code may adjust these parameters by +// meddling with the values in structure directly, or by +// fiddling with the pointer to completely replace the +// structure. +// +// However, these parameters should not be changed at +// any time during the processing of any egg structure: +// set the parameters, load an egg file, process it, and +// write the egg file out again before resetting the +// parameters again. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggParameters { +public: + EggParameters(); + EggParameters(const EggParameters ©); + + // The per-component difference below which two vertices are deemed + // to be at the same position. + double _pos_threshold; + + // The per-component difference below which two vertices are deemed + // to have the same normal. + double _normal_threshold; + + // The per-component difference below which two vertices are deemed + // to have the same texture coordinates. + double _uv_threshold; + + // The per-component difference below which two vertices are deemed + // to have the same color. + float _color_threshold; + + // The per-component difference below which two anim table values + // are deemed to be equivalent. + double _table_threshold; +}; + +extern EggParameters *egg_parameters; + +#endif diff --git a/panda/src/egg/eggPoint.I b/panda/src/egg/eggPoint.I new file mode 100644 index 0000000000..6cdb293d19 --- /dev/null +++ b/panda/src/egg/eggPoint.I @@ -0,0 +1,35 @@ +// Filename: eggPoint.I +// Created by: drose (15Dec99) +// +//////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////// +// Function: EggPoint::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggPoint:: +EggPoint(const string &name) : EggPrimitive(name) { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPoint::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggPoint:: +EggPoint(const EggPoint ©) : EggPrimitive(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPoint::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggPoint &EggPoint:: +operator = (const EggPoint ©) { + EggPrimitive::operator = (copy); + return *this; +} diff --git a/panda/src/egg/eggPoint.cxx b/panda/src/egg/eggPoint.cxx new file mode 100644 index 0000000000..4b174af91d --- /dev/null +++ b/panda/src/egg/eggPoint.cxx @@ -0,0 +1,39 @@ +// Filename: eggPoint.cxx +// Created by: drose (15Dec99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggPoint.h" + +#include + +#include + +TypeHandle EggPoint::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: EggPoint::cleanup +// Access: Public, Virtual +// Description: Cleans up modeling errors in whatever context this +// makes sense. For instance, for a polygon, this calls +// remove_doubled_verts(true). For a point, it calls +// remove_nonunique_verts(). +//////////////////////////////////////////////////////////////////// +void EggPoint:: +cleanup() { + remove_nonunique_verts(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPoint::write +// Access: Public, Virtual +// Description: Writes the point to the indicated output stream in +// Egg format. +//////////////////////////////////////////////////////////////////// +void EggPoint:: +write(ostream &out, int indent_level) const { + write_header(out, indent_level, ""); + write_body(out, indent_level+2); + indent(out, indent_level) << "}\n"; +} diff --git a/panda/src/egg/eggPoint.h b/panda/src/egg/eggPoint.h new file mode 100644 index 0000000000..7429dc3ec8 --- /dev/null +++ b/panda/src/egg/eggPoint.h @@ -0,0 +1,50 @@ +// Filename: eggPoint.h +// Created by: drose (15Dec99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGPOINT_H +#define EGGPOINT_H + +#include + +#include "eggPrimitive.h" + +//////////////////////////////////////////////////////////////////// +// Class : EggPoint +// Description : A single point, or a collection of points as defined +// by a single entry. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggPoint : public EggPrimitive { +public: + INLINE EggPoint(const string &name = ""); + INLINE EggPoint(const EggPoint ©); + INLINE EggPoint &operator = (const EggPoint ©); + + virtual void cleanup(); + + virtual void write(ostream &out, int indent_level) const; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggPrimitive::init_type(); + register_type(_type_handle, "EggPoint", + EggPrimitive::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + +}; + +#include "eggPoint.I" + +#endif diff --git a/panda/src/egg/eggPolygon.I b/panda/src/egg/eggPolygon.I new file mode 100644 index 0000000000..c27d22829e --- /dev/null +++ b/panda/src/egg/eggPolygon.I @@ -0,0 +1,35 @@ +// Filename: eggPolygon.I +// Created by: drose (10Feb99) +// +//////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////// +// Function: EggPolygon::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggPolygon:: +EggPolygon(const string &name) : EggPrimitive(name) { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPolygon::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggPolygon:: +EggPolygon(const EggPolygon ©) : EggPrimitive(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPolygon::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggPolygon &EggPolygon:: +operator = (const EggPolygon ©) { + EggPrimitive::operator = (copy); + return *this; +} diff --git a/panda/src/egg/eggPolygon.cxx b/panda/src/egg/eggPolygon.cxx new file mode 100644 index 0000000000..7bced5bda1 --- /dev/null +++ b/panda/src/egg/eggPolygon.cxx @@ -0,0 +1,39 @@ +// Filename: eggPolygon.cxx +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggPolygon.h" + +#include + +#include + +TypeHandle EggPolygon::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: EggPolygon::cleanup +// Access: Public, Virtual +// Description: Cleans up modeling errors in whatever context this +// makes sense. For instance, for a polygon, this calls +// remove_doubled_verts(true). For a point, it calls +// remove_nonunique_verts(). +//////////////////////////////////////////////////////////////////// +void EggPolygon:: +cleanup() { + remove_doubled_verts(true); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPolygon::write +// Access: Public, Virtual +// Description: Writes the polygon to the indicated output stream in +// Egg format. +//////////////////////////////////////////////////////////////////// +void EggPolygon:: +write(ostream &out, int indent_level) const { + write_header(out, indent_level, ""); + write_body(out, indent_level+2); + indent(out, indent_level) << "}\n"; +} diff --git a/panda/src/egg/eggPolygon.h b/panda/src/egg/eggPolygon.h new file mode 100644 index 0000000000..f597651901 --- /dev/null +++ b/panda/src/egg/eggPolygon.h @@ -0,0 +1,49 @@ +// Filename: eggPolygon.h +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGPOLYGON_H +#define EGGPOLYGON_H + +#include + +#include "eggPrimitive.h" + +//////////////////////////////////////////////////////////////////// +// Class : EggPolygon +// Description : A single polygon. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggPolygon : public EggPrimitive { +public: + INLINE EggPolygon(const string &name = ""); + INLINE EggPolygon(const EggPolygon ©); + INLINE EggPolygon &operator = (const EggPolygon ©); + + virtual void cleanup(); + + virtual void write(ostream &out, int indent_level) const; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggPrimitive::init_type(); + register_type(_type_handle, "EggPolygon", + EggPrimitive::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + +}; + +#include "eggPolygon.I" + +#endif diff --git a/panda/src/egg/eggPrimitive.I b/panda/src/egg/eggPrimitive.I new file mode 100644 index 0000000000..5e6690af2a --- /dev/null +++ b/panda/src/egg/eggPrimitive.I @@ -0,0 +1,334 @@ +// Filename: eggPrimitive.I +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggPrimitive:: +EggPrimitive(const string &name): EggNode(name) { + _bface = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggPrimitive:: +EggPrimitive(const EggPrimitive ©) + : EggNode(copy), + EggAttributes(copy), + _bface(copy._bface) { + copy_vertices(copy); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggPrimitive &EggPrimitive:: +operator = (const EggPrimitive ©) { + EggNode::operator = (copy); + EggAttributes::operator = (copy); + copy_vertices(copy); + _bface = copy._bface; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggPrimitive:: +~EggPrimitive() { + clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::set_texture +// Access: Public +// Description: Applies the indicated texture to the primitive. +//////////////////////////////////////////////////////////////////// +INLINE void EggPrimitive:: +set_texture(PT(EggTexture) texture) { + _texture = texture; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::clear_texture +// Access: Public +// Description: Removes any texturing from the primitive. +//////////////////////////////////////////////////////////////////// +INLINE void EggPrimitive:: +clear_texture() { + _texture = (EggTexture *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::get_texture +// Access: Public +// Description: Returns a pointer to the applied texture, or NULL if +// there is no texture applied. +//////////////////////////////////////////////////////////////////// +INLINE PT(EggTexture) EggPrimitive:: +get_texture() const { + return _texture; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::has_texture +// Access: Public +// Description: Returns true if the primitive is textured (and +// get_texture() will return a real pointer), false +// otherwise (and get_texture() will return NULL). +//////////////////////////////////////////////////////////////////// +INLINE bool EggPrimitive:: +has_texture() const { + return _texture != (EggTexture*)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::set_material +// Access: Public +// Description: Applies the indicated material to the primitive. +//////////////////////////////////////////////////////////////////// +INLINE void EggPrimitive:: +set_material(PT(EggMaterial) material) { + _material = material; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::clear_material +// Access: Public +// Description: Removes any material from the primitive. +//////////////////////////////////////////////////////////////////// +INLINE void EggPrimitive:: +clear_material() { + _material = (EggMaterial *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::get_material +// Access: Public +// Description: Returns a pointer to the applied material, or NULL if +// there is no material applied. +//////////////////////////////////////////////////////////////////// +INLINE PT(EggMaterial) EggPrimitive:: +get_material() const { + return _material; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::has_material +// Access: Public +// Description: Returns true if the primitive is materiald (and +// get_material() will return a real pointer), false +// otherwise (and get_material() will return NULL). +//////////////////////////////////////////////////////////////////// +INLINE bool EggPrimitive:: +has_material() const { + return _material != (EggMaterial *)NULL; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::set_bface_flag +// Access: Public +// Description: Sets the backfacing flag of the polygon. If this is +// true, the polygon will be rendered so that both faces +// are visible; if it is false, only the front face of +// the polygon will be visible. +//////////////////////////////////////////////////////////////////// +INLINE void EggPrimitive:: +set_bface_flag(bool flag) { + _bface = flag; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::get_bface_flag +// Access: Public +// Description: Retrieves the backfacing flag of the polygon. See +// set_bface_flag(). +//////////////////////////////////////////////////////////////////// +INLINE bool EggPrimitive:: +get_bface_flag() const { + return _bface; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::begin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggPrimitive::iterator EggPrimitive:: +begin() const { + return _vertices.begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::end +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggPrimitive::iterator EggPrimitive:: +end() const { + return _vertices.end(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::rbegin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggPrimitive::reverse_iterator EggPrimitive:: +rbegin() const { + return _vertices.rbegin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::rend +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggPrimitive::reverse_iterator EggPrimitive:: +rend() const { + return _vertices.rend(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::empty +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggPrimitive:: +empty() const { + return _vertices.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggPrimitive::size_type EggPrimitive:: +size() const { + return _vertices.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::Indexing operator +// Access: Public +// Description: This is read-only: you can't assign directly to an +// indexed vertex. See set_vertex() instead. +//////////////////////////////////////////////////////////////////// +INLINE PT(EggVertex) EggPrimitive:: +operator [] (int index) const { + nassertr(index >= 0 && index < (int)size(), NULL); + return *(begin() + index); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::insert +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggPrimitive::iterator EggPrimitive:: +insert(iterator position, PT(EggVertex) x) { + prepare_add_vertex(x); + iterator i = _vertices.insert((Vertices::iterator &)position, x); + x->test_pref_integrity(); + test_vref_integrity(); + return i; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::erase +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggPrimitive::iterator EggPrimitive:: +erase(iterator position) { + prepare_remove_vertex(*position); + iterator i = _vertices.erase((Vertices::iterator &)position); + test_vref_integrity(); + return i; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::replace +// Access: Public +// Description: Replaces the vertex at the indicated position with +// the indicated vertex. It is an error to call this +// with an invalid position iterator (e.g. end()). +//////////////////////////////////////////////////////////////////// +INLINE void EggPrimitive:: +replace(iterator position, PT(EggVertex) x) { + nassertv(position != end()); + + prepare_remove_vertex(*position); + prepare_add_vertex(x); + *(Vertices::iterator &)position = x; + + x->test_pref_integrity(); + test_vref_integrity(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::clear +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggPrimitive:: +clear() { + erase(begin(), end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::set_vertex +// Access: Public +// Description: Replaces a particular vertex based on its index +// number in the list of vertices. This is just a +// convenience function for people who don't want to +// mess with the iterators. +//////////////////////////////////////////////////////////////////// +INLINE void EggPrimitive:: +set_vertex(int index, PT(EggVertex) vertex) { + nassertv(index >= 0 && index < (int)size()); + replace(begin() + index, vertex); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::get_vertex +// Access: Public +// Description: Returns a particular index based on its index number. +//////////////////////////////////////////////////////////////////// +INLINE PT(EggVertex) EggPrimitive:: +get_vertex(int index) const { + nassertr(index >= 0 && index < (int)size(), NULL); + return *(begin() + index); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::get_pool +// Access: Public +// Description: Returns the vertex pool associated with the vertices +// of the primitive, or NULL if the primitive has no +// vertices. +//////////////////////////////////////////////////////////////////// +INLINE EggVertexPool *EggPrimitive:: +get_pool() const { + return empty() ? (EggVertexPool *)0L : _vertices.front()->get_pool(); +} diff --git a/panda/src/egg/eggPrimitive.cxx b/panda/src/egg/eggPrimitive.cxx new file mode 100644 index 0000000000..05c6f013e1 --- /dev/null +++ b/panda/src/egg/eggPrimitive.cxx @@ -0,0 +1,400 @@ +// Filename: eggPrimitive.cxx +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggPrimitive.h" +#include "eggVertexPool.h" +#include "eggMiscFuncs.h" + +#include +#include + +TypeHandle EggPrimitive::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::reverse_vertex_ordering +// Access: Public, Virtual +// Description: Reverses the ordering of the vertices in this +// primitive, if appropriate, in order to change the +// direction the polygon appears to be facing. Does not +// adjust the surface normal, if any. +//////////////////////////////////////////////////////////////////// +void EggPrimitive:: +reverse_vertex_ordering() { + // This really only makes sense for polygons. Lights don't care + // about vertex ordering, and NURBS surfaces have to do a bit more + // work in addition to this. + reverse(_vertices.begin(), _vertices.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::cleanup +// Access: Public, Virtual +// Description: Cleans up modeling errors in whatever context this +// makes sense. For instance, for a polygon, this calls +// remove_doubled_verts(true). For a point, it calls +// remove_nonunique_verts(). +//////////////////////////////////////////////////////////////////// +void EggPrimitive:: +cleanup() { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::remove_doubled_verts +// Access: Public +// Description: Certain kinds of primitives, particularly polygons, +// don't like to have the same vertex repeated +// consecutively. Unfortunately, some modeling programs +// (like MultiGen) make this an easy mistake to make. +// +// It's handy to have a function to remove these +// redundant vertices. If closed is true, it also +// checks that the first and last vertices are not the +// same. +// +// This function identifies repeated vertices by pointer +// only; it does not remove consecutive equivalent but +// different vertices. +//////////////////////////////////////////////////////////////////// +void EggPrimitive:: +remove_doubled_verts(bool closed) { + if (!_vertices.empty()) { + Vertices new_vertices; + Vertices::iterator vi, vlast; + vi = _vertices.begin(); + new_vertices.push_back(*vi); + + vlast = vi; + ++vi; + while (vi != _vertices.end()) { + if ((*vi) != (*vlast)) { + new_vertices.push_back(*vi); + } else { + prepare_remove_vertex(*vi); + } + vlast = vi; + ++vi; + } + _vertices.swap(new_vertices); + } + + if (closed) { + // Then, if this is a polygon (which will be closed anyway), + // remove the vertex from the end if it's a repeat of the + // beginning. + while (_vertices.size() > 1 && _vertices.back() == _vertices.front()) { + prepare_remove_vertex(_vertices.back()); + _vertices.pop_back(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::remove_nonunique_verts +// Access: Public +// Description: Removes any multiple appearances of the same vertex +// from the primitive. This primarily makes sense for a +// point primitive, which is really a collection of +// points and which doesn't make sense to include the +// same point twice, in any order. +//////////////////////////////////////////////////////////////////// +void EggPrimitive:: +remove_nonunique_verts() { + Vertices::iterator vi, vj; + Vertices new_vertices; + + for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) { + bool okflag = true; + for (vj = _vertices.begin(); vj != vi && okflag; ++vj) { + okflag = ((*vi) != (*vj)); + } + if (okflag) { + new_vertices.push_back(*vi); + } else { + prepare_remove_vertex(*vi); + } + } + + _vertices.swap(new_vertices); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::erase +// Access: Public +// Description: Part of the implementaion of the EggPrimitive as an +// STL container. Most of the rest of these functions +// are inline and declared in EggPrimitive.I. +//////////////////////////////////////////////////////////////////// +EggPrimitive::iterator EggPrimitive:: +erase(iterator first, iterator last) { + iterator i; + for (i = first; i != last; ++i) { + prepare_remove_vertex(*i); + } + iterator result = _vertices.erase((Vertices::iterator &)first, + (Vertices::iterator &)last); + test_vref_integrity(); + return result; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::add_vertex +// Access: Public +// Description: Adds the indicated vertex to the end of the +// primitive's list of vertices, and returns it. +//////////////////////////////////////////////////////////////////// +PT(EggVertex) EggPrimitive:: +add_vertex(PT(EggVertex) vertex) { + prepare_add_vertex(vertex); + _vertices.push_back(vertex); + + vertex->test_pref_integrity(); + test_vref_integrity(); + + return vertex; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::remove_vertex +// Access: Public +// Description: Removes the indicated vertex vertex from the +// primitive and returns it. If the vertex was not +// already in the primitive, does nothing and returns +// NULL. +//////////////////////////////////////////////////////////////////// +PT(EggVertex) EggPrimitive:: +remove_vertex(PT(EggVertex) vertex) { + iterator i = find(begin(), end(), vertex); + if (i == end()) { + return PT(EggVertex)(); + } else { + // erase() calls prepare_remove_vertex(). + erase(i); + + vertex->test_pref_integrity(); + test_vref_integrity(); + + return vertex; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::copy_vertices +// Access: Public +// Description: Replaces the current primitive's list of vertices +// with a copy of the list of vertices on the other +// primitive. +//////////////////////////////////////////////////////////////////// +void EggPrimitive:: +copy_vertices(const EggPrimitive &other) { + clear(); + _vertices.reserve(other.size()); + + iterator vi; + for (vi = other.begin(); vi != other.end(); ++vi) { + add_vertex(*vi); + } + + test_vref_integrity(); + other.test_vref_integrity(); +} + +#ifndef NDEBUG + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::test_vref_integrity +// Access: Public +// Description: Verifies that each vertex in the primitive exists and +// that it knows it is referenced by the primitive. +//////////////////////////////////////////////////////////////////// +void EggPrimitive:: +test_vref_integrity() const { + test_ref_count_integrity(); + + // First, we need to know how many times each vertex appears. + // Usually, this will be only one, but it's possible for a vertex to + // appear more than once. + typedef map VertexCount; + VertexCount _count; + + // Now count up the vertices. + iterator vi; + for (vi = begin(); vi != end(); ++vi) { + const EggVertex *vert = *vi; + vert->test_ref_count_integrity(); + + VertexCount::iterator vci = _count.find(vert); + if (vci == _count.end()) { + _count[vert] = 1; + } else { + (*vci).second++; + } + } + + // Ok, now walk through the vertices found and make sure the vertex + // has the proper number of entries of this primitive in its pref. + VertexCount::iterator vci; + for (vci = _count.begin(); vci != _count.end(); ++vci) { + const EggVertex *vert = (*vci).first; + + int count = (*vci).second; + int vert_count = vert->has_pref(this); + + nassertv(count == vert_count); + } +} + +#endif // NDEBUG + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::prepare_add_vertex +// Access: Private +// Description: Marks the vertex as belonging to the primitive. This +// is an internal function called by the STL-like +// functions push_back() and insert(), in preparation +// for actually adding the vertex. +//////////////////////////////////////////////////////////////////// +void EggPrimitive:: +prepare_add_vertex(EggVertex *vertex) { + // We can't test integrity within this function, because it might be + // called when the primitive is in an incomplete state. + + // The vertex must have the same vertex pool as the vertices already + // added. + nassertv(empty() || vertex->get_pool() == get_pool()); + + // Since a given vertex might appear more than once in a particular + // primitive, we can't conclude anything about data integrity by + // inspecting the return value of insert(). (In fact, the vertex's + // pref is a multiset, so the insert() will always succeed.) + + vertex->_pref.insert(this); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::prepare_remove_vertex +// Access: Private +// Description: Marks the vertex as removed from the primitive. This +// is an internal function called by the STL-like +// functions pop_back() and erase(), in preparation for +// actually doing the removal. +// +// It is an error to attempt to remove a vertex that is +// not already a vertex of this primitive. +//////////////////////////////////////////////////////////////////// +void EggPrimitive:: +prepare_remove_vertex(EggVertex *vertex) { + // We can't test integrity within this function, because it might be + // called when the primitive is in an incomplete state. + + // Now we must remove the primitive from the vertex's pref. We + // can't just use the simple erase() function, since that will + // remove all instances of this primitive from the pref; instead, we + // must find one instance and remove that. + + EggVertex::PrimitiveRef::iterator pri = vertex->_pref.find(this); + + // We should have found the primitive in the vertex's pref. If we + // did not, something's out of sync internally. + nassertv(pri != vertex->_pref.end()); + + vertex->_pref.erase(pri); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::write_body +// Access: Protected +// Description: Writes the attributes and the vertices referenced by +// the primitive to the indicated output stream in Egg +// format. +//////////////////////////////////////////////////////////////////// +void EggPrimitive:: +write_body(ostream &out, int indent_level) const { + test_vref_integrity(); + + EggAttributes::write(out, indent_level); + EggAlphaMode::write(out, indent_level); + + if (has_texture()) { + EggTexture *texture = get_texture(); + + // Make sure the texture is named. + nassertv(texture->has_name()); + + indent(out, indent_level) << " { "; + enquote_string(out, texture->get_name()) + << " }\n"; + } + + if (has_material()) { + EggMaterial *material = get_material(); + + // Make sure the material is named. + nassertv(material->has_name()); + + indent(out, indent_level) << " { "; + enquote_string(out, material->get_name()) + << " }\n"; + } + + if (get_bface_flag()) { + indent(out, indent_level) << " { 1 }\n"; + } + + if (!empty()) { + EggVertexPool *pool = get_pool(); + + // Make sure the vertices belong to some vertex pool. + nassertv(pool != NULL); + + // Make sure the vertex pool is named. + nassertv(pool->has_name()); + + if ((int)size() < 10) { + // A simple primitive gets all its vertex indices written on one + // line. + indent(out, indent_level) << " {"; + const_iterator i; + for (i = begin(); i != end(); ++i) { + EggVertex *vert = *i; + vert->test_pref_integrity(); + + // Make sure each vertex belongs to the same pool. + nassertv(vert->get_pool() == pool); + + out << " " << vert->get_index(); + } + out << " { "; + enquote_string(out, pool->get_name()) << " } }\n"; + + } else { + + // A larger primitive gets its vertex indices written as + // multiple lines. + vector_int indices; + const_iterator i; + for (i = begin(); i != end(); ++i) { + EggVertex *vert = *i; + vert->test_pref_integrity(); + + // Make sure each vertex belongs to the same pool. + nassertv(vert->get_pool() == pool); + + indices.push_back(vert->get_index()); + } + + indent(out, indent_level) << " {\n"; + write_long_list(out, indent_level+2, indices.begin(), indices.end(), + "", "", 72); + indent(out, indent_level+2) << " { "; + enquote_string(out, pool->get_name()) << " }\n"; + indent(out, indent_level) << "}\n"; + } + } +} diff --git a/panda/src/egg/eggPrimitive.h b/panda/src/egg/eggPrimitive.h new file mode 100644 index 0000000000..ae018a94b4 --- /dev/null +++ b/panda/src/egg/eggPrimitive.h @@ -0,0 +1,170 @@ +// Filename: eggPrimitive.h +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGPRIMITIVE_H +#define EGGPRIMITIVE_H + +#include + +#include "eggNode.h" +#include "eggAttributes.h" +#include "eggVertex.h" +#include "eggTexture.h" +#include "eggMaterial.h" +#include "eggAlphaMode.h" +#include +#include + +class EggVertexPool; + +//////////////////////////////////////////////////////////////////// +// Class : EggPrimitive +// Description : A base class for any of a number of kinds of geometry +// primitives: polygons, point lights, nurbs patches, +// parametrics curves, etc. Things with a set of +// vertices and some rendering properties like color. +// +// An EggPrimitive is an STL-style container of pointers +// to EggVertex's. In fact, it IS a vector, and can be +// manipulated in all the ways that vectors can. +// However, it is necessary that all vertices belong to +// the same vertex pool. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggPrimitive : public EggNode, public EggAttributes, + public EggAlphaMode +{ + + // This is a bit of private interface stuff that must be here as a + // forward reference. This allows us to define the EggPrimitive as + // an STL container. + +private: + typedef vector Vertices; + + // Here begins the actual public interface to EggPrimitive. + +public: + INLINE EggPrimitive(const string &name = ""); + INLINE EggPrimitive(const EggPrimitive ©); + INLINE EggPrimitive &operator = (const EggPrimitive ©); + INLINE ~EggPrimitive(); + + INLINE void set_texture(PT(EggTexture) texture); + INLINE void clear_texture(); + INLINE PT(EggTexture) get_texture() const; + INLINE bool has_texture() const; + + INLINE void set_material(PT(EggMaterial) material); + INLINE void clear_material(); + INLINE PT(EggMaterial) get_material() const; + INLINE bool has_material() const; + + INLINE void set_bface_flag(bool flag); + INLINE bool get_bface_flag() const; + + virtual void reverse_vertex_ordering(); + virtual void cleanup(); + + void remove_doubled_verts(bool closed); + void remove_nonunique_verts(); + + + // The EggPrimitive itself appears to be an STL container of + // pointers to EggVertex objects. The set of vertices is read-only, + // however, except through the limited add_vertex/remove_vertex or + // insert/erase interface. The following implements this. + +#ifdef WIN32_VC + typedef PT(EggVertex) *pointer; + typedef PT(EggVertex) *const_pointer; +#else + typedef Vertices::const_pointer pointer; + typedef Vertices::const_pointer const_pointer; +#endif + typedef Vertices::const_reference reference; + typedef Vertices::const_reference const_reference; + typedef Vertices::const_iterator iterator; + typedef Vertices::const_iterator const_iterator; + typedef Vertices::const_reverse_iterator reverse_iterator; + typedef Vertices::const_reverse_iterator const_reverse_iterator; + typedef Vertices::size_type size_type; + typedef Vertices::difference_type difference_type; + + INLINE iterator begin() const; + INLINE iterator end() const; + INLINE reverse_iterator rbegin() const; + INLINE reverse_iterator rend() const; + INLINE bool empty() const; + INLINE size_type size() const; + + INLINE PT(EggVertex) operator [] (int index) const; + + INLINE iterator insert(iterator position, PT(EggVertex) x); + INLINE iterator erase(iterator position); + iterator erase(iterator first, iterator last); + INLINE void replace(iterator position, PT(EggVertex) vertex); + INLINE void clear(); + + PT(EggVertex) add_vertex(PT(EggVertex) vertex); + PT(EggVertex) remove_vertex(PT(EggVertex) vertex); + void copy_vertices(const EggPrimitive &other); + + // These are shorthands if you don't want to use the iterators. + INLINE void set_vertex(int index, PT(EggVertex) vertex); + INLINE PT(EggVertex) get_vertex(int index) const; + + INLINE EggVertexPool *get_pool() const; + +#ifndef NDEBUG + void test_vref_integrity() const; +#else + void test_vref_integrity() const { } +#endif // NDEBUG + +private: + Vertices _vertices; + + // Don't try to use these private functions. User code should add + // and remove vertices via add_vertex()/remove_vertex(), or via the + // STL-like push_back()/pop_back() or insert()/erase(), above. + void prepare_add_vertex(EggVertex *vertex); + void prepare_remove_vertex(EggVertex *vertex); + + +protected: + void write_body(ostream &out, int indent_level) const; + + +private: + PT(EggTexture) _texture; + PT(EggMaterial) _material; + bool _bface; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggNode::init_type(); + EggAttributes::init_type(); + EggAlphaMode::get_class_type(); + register_type(_type_handle, "EggPrimitive", + EggNode::get_class_type(), + EggAttributes::get_class_type(), + EggAlphaMode::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "eggPrimitive.I" + +#endif diff --git a/panda/src/egg/eggSAnimData.I b/panda/src/egg/eggSAnimData.I new file mode 100644 index 0000000000..cc596759f5 --- /dev/null +++ b/panda/src/egg/eggSAnimData.I @@ -0,0 +1,64 @@ +// Filename: eggSAnimData.I +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggSAnimData::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggSAnimData:: +EggSAnimData(const string &name) : EggAnimData(name) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggSAnimData::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggSAnimData:: +EggSAnimData(const EggSAnimData ©) : EggAnimData(copy) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggSAnimData::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggSAnimData &EggSAnimData:: +operator = (const EggSAnimData ©) { + EggAnimData::operator = (copy); + + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggSAnimData::get_num_rows +// Access: Public +// Description: Returns the number of rows in the table. For an +// SAnim table, each row has one column. +//////////////////////////////////////////////////////////////////// +INLINE int EggSAnimData:: +get_num_rows() const { + return get_size(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggSAnimData::get_value +// Access: Public +// Description: Returns the value at the indicated row. Row must be +// in the range 0 <= row < get_num_rows(). +//////////////////////////////////////////////////////////////////// +INLINE double EggSAnimData:: +get_value(int row) const { + nassertr(row >= 0 && row < get_num_rows(), 0.0); + return _data[row]; +} + + diff --git a/panda/src/egg/eggSAnimData.cxx b/panda/src/egg/eggSAnimData.cxx new file mode 100644 index 0000000000..1ea16de0d9 --- /dev/null +++ b/panda/src/egg/eggSAnimData.cxx @@ -0,0 +1,86 @@ +// Filename: eggSAnimData.cxx +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggSAnimData.h" +#include "eggMiscFuncs.h" +#include "eggParameters.h" + +#include + +#include + +TypeHandle EggSAnimData::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: EggSAnimData::optimize +// Access: Public +// Description: Optimizes the data by collapsing a long table of +// duplicate values into a single value. +//////////////////////////////////////////////////////////////////// +void EggSAnimData:: +optimize() { + if (get_num_rows() > 1) { + double value = get_value(0); + for (int row = 1; row < get_num_rows(); row++) { + if (fabs(get_value(row) - value) > egg_parameters->_table_threshold) { + return; + } + } + + // Ok, all the rows had the same value. Collapse them. + _data.erase(_data.begin() + 1, _data.end()); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggSAnimData::write +// Access: Public, Virtual +// Description: Writes the data to the indicated output stream in Egg +// format. +//////////////////////////////////////////////////////////////////// +void EggSAnimData:: +write(ostream &out, int indent_level) const { + if (get_num_rows() <= 1) { + // We get a lot of these little tiny tables. For brevity, we'll + // write these all on one line, because we can. This just makes + // it easier for a human to scan the egg file. + + indent(out, indent_level) << " "; + if (has_name()) { + enquote_string(out, get_name()) << " {"; + } else { + out << "{"; + } + + if (has_fps()) { + out << " fps { " << get_fps() << " }"; + } + + if (get_num_rows() == 1) { + out << " { " << get_value(0) << " }"; + } else { + out << " { }"; + } + + out << " }\n"; + + } else { + // If there are at least two values in the table, we'll write it + // out over multiple lines. + + write_header(out, indent_level, ""); + + if (has_fps()) { + indent(out, indent_level + 2) + << " fps { " << get_fps() << " }\n"; + } + indent(out, indent_level + 2) << " {\n"; + write_long_list(out, indent_level + 4, _data.begin(), _data.end(), + "", "", 72); + indent(out, indent_level + 2) << "}\n"; + indent(out, indent_level) << "}\n"; + } +} diff --git a/panda/src/egg/eggSAnimData.h b/panda/src/egg/eggSAnimData.h new file mode 100644 index 0000000000..bdf501d9a7 --- /dev/null +++ b/panda/src/egg/eggSAnimData.h @@ -0,0 +1,54 @@ +// Filename: eggSAnimData.h +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGSANIMDATA_H +#define EGGSANIMDATA_H + +#include + +#include "eggAnimData.h" + +//////////////////////////////////////////////////////////////////// +// Class : EggSAnimData +// Description : Corresponding to an entry, this stores a +// single column of numbers, for instance for a morph +// target, or as one column in an EggXfmSAnim. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggSAnimData : public EggAnimData { +public: + INLINE EggSAnimData(const string &name = ""); + INLINE EggSAnimData(const EggSAnimData ©); + INLINE EggSAnimData &operator = (const EggSAnimData ©); + + INLINE int get_num_rows() const; + INLINE double get_value(int row) const; + + void optimize(); + + virtual void write(ostream &out, int indent_level) const; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggAnimData::init_type(); + register_type(_type_handle, "EggSAnimData", + EggAnimData::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "eggSAnimData.I" + +#endif + diff --git a/panda/src/egg/eggSurface.I b/panda/src/egg/eggSurface.I new file mode 100644 index 0000000000..e1a506544a --- /dev/null +++ b/panda/src/egg/eggSurface.I @@ -0,0 +1,97 @@ +// Filename: eggSurface.I +// Created by: drose (15Feb00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggSurface::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggSurface:: +EggSurface(const string &name) : EggPrimitive(name) { + _u_subdiv = 0; + _v_subdiv = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSurface::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggSurface:: +EggSurface(const EggSurface ©) : + EggPrimitive(copy), + _u_subdiv(copy._u_subdiv), + _v_subdiv(copy._v_subdiv) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSurface::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggSurface &EggSurface:: +operator = (const EggSurface ©) { + EggPrimitive::operator = (copy); + _u_subdiv = copy._u_subdiv; + _v_subdiv = copy._v_subdiv; + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggSurface::set_u_subdiv +// Access: Public +// Description: Sets the number of subdivisions in the U direction +// that will be requested across the surface. (This +// doesn't necessary guarantee that this number of +// subdivisions will be made; it's just a hint to any +// surface renderer or quick tesselator.) Set the +// number to 0 to disable the hint. +//////////////////////////////////////////////////////////////////// +INLINE void EggSurface:: +set_u_subdiv(int subdiv) { + _u_subdiv = subdiv; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSurface::get_u_subdiv +// Access: Public +// Description: Returns the requested number of subdivisions in the U +// direction, or 0 if no particular subdivisions have +// been requested. +//////////////////////////////////////////////////////////////////// +INLINE int EggSurface:: +get_u_subdiv() const { + return _u_subdiv; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSurface::set_v_subdiv +// Access: Public +// Description: Sets the number of subdivisions in the U direction +// that will be requested across the surface. (This +// doesn't necessary guarantee that this number of +// subdivisions will be made; it's just a hint to any +// surface renderer or quick tesselator.) Set the +// number to 0 to disable the hint. +//////////////////////////////////////////////////////////////////// +INLINE void EggSurface:: +set_v_subdiv(int subdiv) { + _v_subdiv = subdiv; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSurface::get_v_subdiv +// Access: Public +// Description: Returns the requested number of subdivisions in the U +// direction, or 0 if no particular subdivisions have +// been requested. +//////////////////////////////////////////////////////////////////// +INLINE int EggSurface:: +get_v_subdiv() const { + return _v_subdiv; +} diff --git a/panda/src/egg/eggSurface.cxx b/panda/src/egg/eggSurface.cxx new file mode 100644 index 0000000000..4cac69c576 --- /dev/null +++ b/panda/src/egg/eggSurface.cxx @@ -0,0 +1,9 @@ +// Filename: eggSurface.cxx +// Created by: drose (15Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "eggSurface.h" + +TypeHandle EggSurface::_type_handle; + diff --git a/panda/src/egg/eggSurface.h b/panda/src/egg/eggSurface.h new file mode 100644 index 0000000000..125b58ed58 --- /dev/null +++ b/panda/src/egg/eggSurface.h @@ -0,0 +1,55 @@ +// Filename: eggSurface.h +// Created by: drose (15Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGSURFACE_H +#define EGGSURFACE_H + +#include + +#include "eggPrimitive.h" + +//////////////////////////////////////////////////////////////////// +// Class : EggSurface +// Description : A parametric surface of some kind. See +// EggNurbsSurface. +//////////////////////////////////////////////////////////////////// +class EggSurface : public EggPrimitive { +public: + INLINE EggSurface(const string &name = ""); + INLINE EggSurface(const EggSurface ©); + INLINE EggSurface &operator = (const EggSurface ©); + + INLINE void set_u_subdiv(int subdiv); + INLINE int get_u_subdiv() const; + INLINE void set_v_subdiv(int subdiv); + INLINE int get_v_subdiv() const; + +private: + int _u_subdiv; + int _v_subdiv; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggPrimitive::init_type(); + register_type(_type_handle, "EggSurface", + EggPrimitive::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + +}; + +#include "eggSurface.I" + +#endif diff --git a/panda/src/egg/eggSwitchCondition.cxx b/panda/src/egg/eggSwitchCondition.cxx new file mode 100644 index 0000000000..687767929c --- /dev/null +++ b/panda/src/egg/eggSwitchCondition.cxx @@ -0,0 +1,69 @@ +// Filename: eggSwitchCondition.cxx +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggSwitchCondition.h" + +#include + +TypeHandle EggSwitchCondition::_type_handle; +TypeHandle EggSwitchConditionDistance::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: EggSwitchConditionDistance::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggSwitchConditionDistance:: +EggSwitchConditionDistance(double switch_in, double switch_out, + const LPoint3d ¢er, double fade) { + _switch_in = switch_in; + _switch_out = switch_out; + _center = center; + _fade = fade; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggSwitchConditionDistance::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +EggSwitchCondition *EggSwitchConditionDistance:: +make_copy() const { + return new EggSwitchConditionDistance(*this); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggSwitchConditionDistance::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void EggSwitchConditionDistance:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << " {\n"; + indent(out, indent_level+2) + << " { " << _switch_in << " " << _switch_out; + + if (_fade != 0.0) { + out << " " << _fade; + } + + out << " { " << _center << " } }\n"; + indent(out, indent_level) << "}\n"; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggSwitchConditionDistance::transform +// Access: Public, Virtual +// Description: Applies the indicated transformation matrix to the +// switch condition parameters. +//////////////////////////////////////////////////////////////////// +void EggSwitchConditionDistance:: +transform(const LMatrix4d &mat) { + _center = _center * mat; +} diff --git a/panda/src/egg/eggSwitchCondition.h b/panda/src/egg/eggSwitchCondition.h new file mode 100644 index 0000000000..60b894a134 --- /dev/null +++ b/panda/src/egg/eggSwitchCondition.h @@ -0,0 +1,90 @@ +// Filename: eggSwitchCondition.h +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGSWITCHCONDITION +#define EGGSWITCHCONDITION + +#include + +#include "eggObject.h" +#include + +//////////////////////////////////////////////////////////////////// +// Class : EggSwitchCondition +// Description : This corresponds to a entry within +// a group. It indicates the condition at which a +// level-of-detail is switched in or out. This is +// actually an abstract base class for potentially any +// number of specific different kinds of switching +// conditions; presently, only a type is +// actually supported. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggSwitchCondition : public EggObject { +public: + virtual EggSwitchCondition *make_copy() const=0; + virtual void write(ostream &out, int indent_level) const=0; + + virtual void transform(const LMatrix4d &mat)=0; + + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggObject::init_type(); + register_type(_type_handle, "EggSwitchCondition", + EggObject::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + + +//////////////////////////////////////////////////////////////////// +// Class : EggSwitchConditionDistance +// Description : A SwitchCondition that switches the levels-of-detail +// based on distance from the camera's eyepoint. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggSwitchConditionDistance : public EggSwitchCondition { +public: + EggSwitchConditionDistance(double switch_in, double switch_out, + const LPoint3d ¢er, double fade = 0.0); + + virtual EggSwitchCondition *make_copy() const; + virtual void write(ostream &out, int indent_level) const; + + virtual void transform(const LMatrix4d &mat); + + double _switch_in, _switch_out, _fade; + LPoint3d _center; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggSwitchCondition::init_type(); + register_type(_type_handle, "EggSwitchConditionDistance", + EggSwitchCondition::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + +private: + static TypeHandle _type_handle; +}; + + +#endif + diff --git a/panda/src/egg/eggTable.I b/panda/src/egg/eggTable.I new file mode 100644 index 0000000000..f89e6f8f98 --- /dev/null +++ b/panda/src/egg/eggTable.I @@ -0,0 +1,61 @@ +// Filename: eggTable.I +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggTable::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTable:: +EggTable(const string &name) : EggGroupNode(name) { + _type = TT_table; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggTable::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTable:: +EggTable(const EggTable ©) : EggGroupNode(copy), _type(copy._type) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggTable::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTable &EggTable:: +operator = (const EggTable ©) { + EggGroupNode::operator = (copy); + _type = copy._type; + + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggTable::set_table_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggTable:: +set_table_type(TableType type) { + _type = type; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggTable::get_table_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTable::TableType EggTable:: +get_table_type() const { + return _type; +} diff --git a/panda/src/egg/eggTable.cxx b/panda/src/egg/eggTable.cxx new file mode 100644 index 0000000000..f17e541a3c --- /dev/null +++ b/panda/src/egg/eggTable.cxx @@ -0,0 +1,77 @@ +// Filename: eggTable.cxx +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggTable.h" + +#include +#include + +TypeHandle EggTable::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: EggTable::write +// Access: Public, Virtual +// Description: Writes the table and all of its children to the +// indicated output stream in Egg format. +//////////////////////////////////////////////////////////////////// +void EggTable:: +write(ostream &out, int indent_level) const { + test_under_integrity(); + + switch (get_table_type()) { + case TT_table: + write_header(out, indent_level, "
"); + break; + + case TT_bundle: + write_header(out, indent_level, ""); + break; + + default: + // invalid table type + nassertv(false); + } + + EggGroupNode::write(out, indent_level + 2); + indent(out, indent_level) << "}\n"; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggTable::string_table_type +// Access: Public, Static +// Description: Returns the TableType value associated with the given +// string representation, or TT_invalid if the string +// does not match any known TableType value. +//////////////////////////////////////////////////////////////////// +EggTable::TableType EggTable:: +string_table_type(const string &string) { + if (cmp_nocase_uh(string, "table") == 0) { + return TT_table; + } else if (cmp_nocase_uh(string, "bundle") == 0) { + return TT_bundle; + } else { + return TT_invalid; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: TableType output operator +// Description: +//////////////////////////////////////////////////////////////////// +ostream &operator << (ostream &out, EggTable::TableType t) { + switch (t) { + case EggTable::TT_invalid: + return out << "invalid table"; + case EggTable::TT_table: + return out << "table"; + case EggTable::TT_bundle: + return out << "bundle"; + } + + nassertr(false, out); + return out << "(**invalid**)"; +} diff --git a/panda/src/egg/eggTable.h b/panda/src/egg/eggTable.h new file mode 100644 index 0000000000..69d6b186ef --- /dev/null +++ b/panda/src/egg/eggTable.h @@ -0,0 +1,69 @@ +// Filename: eggTable.h +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGTABLE_H +#define EGGTABLE_H + +#include + +#include "eggGroupNode.h" + +//////////////////////////////////////////////////////////////////// +// Class : EggTable +// Description : This corresponds to a
or a entry. +// As such, it doesn't actually contain a table of +// numbers, but it may be a parent to an EggSAnimData or +// an EggXfmAnimData, which do. It may also be a parent +// to another
or , establishing a +// hierarchy of tables. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggTable : public EggGroupNode { +public: + enum TableType { + TT_invalid, + TT_table, + TT_bundle, + }; + + INLINE EggTable(const string &name = ""); + INLINE EggTable(const EggTable ©); + INLINE EggTable &operator = (const EggTable ©); + + INLINE void set_table_type(TableType type); + INLINE TableType get_table_type() const; + + virtual void write(ostream &out, int indent_level) const; + + static TableType string_table_type(const string &string); + +private: + TableType _type; + + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggGroupNode::init_type(); + register_type(_type_handle, "EggTable", + EggGroupNode::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +ostream &operator << (ostream &out, EggTable::TableType t); + +#include "eggTable.I" + +#endif + diff --git a/panda/src/egg/eggTexture.I b/panda/src/egg/eggTexture.I new file mode 100644 index 0000000000..502cf2dd48 --- /dev/null +++ b/panda/src/egg/eggTexture.I @@ -0,0 +1,327 @@ +// Filename: eggTexture.I +// Created by: drose (18Jan99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTexture &EggTexture:: +operator = (const string &filename) { + EggFilenameNode::operator = (filename); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTexture &EggTexture:: +operator = (const char *filename) { + EggFilenameNode::operator = (filename); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTexture &EggTexture:: +operator = (const Filename &filename) { + EggFilenameNode::operator = (filename); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::set_format +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggTexture:: +set_format(Format format) { + _format = format; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::get_format +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTexture::Format EggTexture:: +get_format() const { + return _format; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::set_wrap_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggTexture:: +set_wrap_mode(WrapMode mode) { + _wrap_mode = mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::get_wrap_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTexture::WrapMode EggTexture:: +get_wrap_mode() const { + return _wrap_mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::set_wrap_u +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggTexture:: +set_wrap_u(WrapMode mode) { + _wrap_u = mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::get_wrap_u +// Access: Public +// Description: Returns the amount specified for U wrap. This may be +// unspecified, even if there is an overall wrap value. +//////////////////////////////////////////////////////////////////// +INLINE EggTexture::WrapMode EggTexture:: +get_wrap_u() const { + return _wrap_u; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::determine_wrap_u +// Access: Public +// Description: Determines the appropriate wrap in the U direction. +// This is different from get_wrap_u() in that if the U +// wrap is unspecified, it returns the overall wrap +// value. +//////////////////////////////////////////////////////////////////// +INLINE EggTexture::WrapMode EggTexture:: +determine_wrap_u() const { + return (_wrap_u == WM_unspecified) ? get_wrap_mode() : get_wrap_u(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::set_wrap_v +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggTexture:: +set_wrap_v(WrapMode mode) { + _wrap_v = mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::get_wrap_v +// Access: Public +// Description: Returns the amount specified for V wrap. This may be +// unspecified, even if there is an overall wrap value. +//////////////////////////////////////////////////////////////////// +INLINE EggTexture::WrapMode EggTexture:: +get_wrap_v() const { + return _wrap_v; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::determine_wrap_v +// Access: Public +// Description: Determines the appropriate wrap in the V direction. +// This is different from get_wrap_v() in that if the U +// wrap is unspecified, it returns the overall wrap +// value. +//////////////////////////////////////////////////////////////////// +INLINE EggTexture::WrapMode EggTexture:: +determine_wrap_v() const { + return (_wrap_v == WM_unspecified) ? get_wrap_mode() : get_wrap_v(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::set_minfilter +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggTexture:: +set_minfilter(FilterType type) { + _minfilter = type; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::get_minfilter +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTexture::FilterType EggTexture:: +get_minfilter() const { + return _minfilter; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::set_magfilter +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggTexture:: +set_magfilter(FilterType type) { + _magfilter = type; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::get_magfilter +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTexture::FilterType EggTexture:: +get_magfilter() const { + return _magfilter; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::set_magfilteralpha +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggTexture:: +set_magfilteralpha(FilterType type) { + _magfilteralpha = type; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::get_magfilteralpha +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTexture::FilterType EggTexture:: +get_magfilteralpha() const { + return _magfilteralpha; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::set_magfiltercolor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggTexture:: +set_magfiltercolor(FilterType type) { + _magfiltercolor = type; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::get_magfiltercolor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTexture::FilterType EggTexture:: +get_magfiltercolor() const { + return _magfiltercolor; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::set_env_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggTexture:: +set_env_type(EnvType type) { + _env_type = type; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::get_env_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTexture::EnvType EggTexture:: +get_env_type() const { + return _env_type; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::set_transform +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggTexture:: +set_transform(const LMatrix3d &transform) { + _transform = transform; + _has_transform = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::clear_transform +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggTexture:: +clear_transform() { + _transform = LMatrix3d::ident_mat(); + _has_transform = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::has_transform +// Access: Public +// Description: Returns true if a texture matrix transform has been +// specified for the texture (even if the transform is +// identity). +//////////////////////////////////////////////////////////////////// +INLINE bool EggTexture:: +has_transform() const { + return _has_transform; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::get_transform +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LMatrix3d EggTexture:: +get_transform() const { + nassertr(_has_transform, LMatrix3d::ident_mat()); + return _transform; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::transform_is_identity() +// Access: Public +// Description: Returns true if no texture matrix transform has been +// specified, or if the one specified is the identity +// transform. Returns false only if a nonidentity +// transform has been applied. +//////////////////////////////////////////////////////////////////// +INLINE bool EggTexture:: +transform_is_identity() const { + return (!_has_transform || + _transform.almost_equal(LMatrix3d::ident_mat(), 0.0001)); +} + +//////////////////////////////////////////////////////////////////// +// Function: UniqueEggTextures::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE UniqueEggTextures:: +UniqueEggTextures(int eq) : _eq(eq) { +} + +//////////////////////////////////////////////////////////////////// +// Function: UniqueEggTextures::Function operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool UniqueEggTextures:: +operator ()(const EggTexture *t1, const EggTexture *t2) const { + return t1->sorts_less_than(*t2, _eq); +} + + diff --git a/panda/src/egg/eggTexture.cxx b/panda/src/egg/eggTexture.cxx new file mode 100644 index 0000000000..9c0d65f87e --- /dev/null +++ b/panda/src/egg/eggTexture.cxx @@ -0,0 +1,554 @@ +// Filename: eggTexture.cxx +// Created by: drose (18Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggTexture.h" +#include "eggMiscFuncs.h" + +#include +#include + +TypeHandle EggTexture::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggTexture:: +EggTexture(const string &tref_name, const string &filename) + : EggFilenameNode(tref_name, filename) +{ + _format = F_unspecified; + _wrap_mode = WM_unspecified; + _wrap_u = WM_unspecified; + _wrap_v = WM_unspecified; + _minfilter = FT_unspecified; + _magfilter = FT_unspecified; + _magfilteralpha = FT_unspecified; + _magfiltercolor = FT_unspecified; + _env_type = ET_unspecified; + _has_transform = false; + _transform = LMatrix3d::ident_mat(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggTexture:: +EggTexture(const EggTexture ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggTexture &EggTexture:: +operator = (const EggTexture ©) { + EggFilenameNode::operator = (copy); + EggAlphaMode::operator = (copy); + + _format = copy._format; + _wrap_mode = copy._wrap_mode; + _wrap_u = copy._wrap_u; + _wrap_v = copy._wrap_v; + _minfilter = copy._minfilter; + _magfilter = copy._magfilter; + _magfilteralpha = copy._magfilteralpha; + _magfiltercolor = copy._magfiltercolor; + _env_type = copy._env_type; + _has_transform = copy._has_transform; + _transform = copy._transform; + + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::write +// Access: Public, Virtual +// Description: Writes the texture definition to the indicated output +// stream in Egg format. +//////////////////////////////////////////////////////////////////// +void EggTexture:: +write(ostream &out, int indent_level) const { + write_header(out, indent_level, ""); + enquote_string(out, get_fullpath(), indent_level + 2) << "\n"; + + if (get_format() != F_unspecified) { + indent(out, indent_level + 2) + << " format { " << get_format() << " }\n"; + } + + if (get_wrap_mode() != WM_unspecified) { + indent(out, indent_level + 2) + << " wrap { " << get_wrap_mode() << " }\n"; + } + + if (get_wrap_u() != WM_unspecified) { + indent(out, indent_level + 2) + << " wrapu { " << get_wrap_u() << " }\n"; + } + + if (get_wrap_v() != WM_unspecified) { + indent(out, indent_level + 2) + << " wrapv { " << get_wrap_v() << " }\n"; + } + + if (get_minfilter() != FT_unspecified) { + indent(out, indent_level + 2) + << " minfilter { " << get_minfilter() << " }\n"; + } + + if (get_magfilter() != FT_unspecified) { + indent(out, indent_level + 2) + << " magfilter { " << get_magfilter() << " }\n"; + } + + if (get_magfilteralpha() != FT_unspecified) { + indent(out, indent_level + 2) + << " magfilteralpha { " << get_magfilteralpha() << " }\n"; + } + + if (get_magfiltercolor() != FT_unspecified) { + indent(out, indent_level + 2) + << " magfiltercolor { " << get_magfiltercolor() << " }\n"; + } + + if (get_env_type() != ET_unspecified) { + indent(out, indent_level + 2) + << " envtype { " << get_env_type() << " }\n"; + } + + EggAlphaMode::write(out, indent_level + 2); + + if (has_transform()) { + write_transform(out, _transform, indent_level + 2); + } + + indent(out, indent_level) << "}\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::is_equivalent_to +// Access: Public +// Description: Returns true if the two textures are equivalent in +// all relevant properties (according to eq), false +// otherwise. +// +// The Equivalence parameter, eq, should be set to the +// bitwise OR of the following properties, according to +// what you consider relevant: +// +// EggTexture::E_basename: +// The basename part of the texture filename, without +// the directory prefix *or* the filename extension. +// +// EggTexture::E_extension: +// The extension part of the texture filename. +// +// EggTexture::E_dirname: +// The directory prefix of the texture filename. +// +// EggTexture::E_complete_filename: +// The union of the above three; that is, the complete +// filename, with directory, basename, and extension. +// +// EggTexture::E_transform: +// The texture matrix. +// +// EggTexture::E_attributes: +// All remaining texture attributes (mode, mipmap, +// etc.) except TRef name. +// +// EggTexture::E_tref_name: +// The TRef name. +//////////////////////////////////////////////////////////////////// +bool EggTexture:: +is_equivalent_to(const EggTexture &other, int eq) const { + if ((eq & E_complete_filename) == E_complete_filename) { + if (get_fullpath() != other.get_fullpath()) { + return false; + } + } else { + if (eq & E_basename) { + if (get_basename_wo_extension() != other.get_basename_wo_extension()) { + return false; + } + } + if (eq & E_extension) { + if (get_extension() != other.get_extension()) { + return false; + } + } + if (eq & E_dirname) { + if (get_dirname() != other.get_dirname()) { + return false; + } + } + } + + if (eq & E_transform) { + if (transform_is_identity() != other.transform_is_identity()) { + return false; + } + + if (_has_transform && other._has_transform) { + if (!_transform.almost_equal(other._transform, 0.0001)) { + return false; + } + } + } + + if (eq & E_attributes) { + if (_format != other._format || + _wrap_mode != other._wrap_mode || + _wrap_u != other._wrap_u || + _wrap_v != other._wrap_v || + _minfilter != other._minfilter || + _magfilter != other._magfilter || + _magfilteralpha != other._magfilteralpha || + _magfiltercolor != other._magfiltercolor || + _env_type != other._env_type) { + return false; + } + if (EggAlphaMode::operator != (other)) { + return false; + } + } + + if (eq & E_tref_name) { + if (get_name() != other.get_name()) { + return false; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::sorts_less_than +// Access: Public +// Description: An ordering operator to compare two textures for +// sorting order. This imposes an arbitrary ordering +// useful to identify unique textures, according to the +// indicated Equivalence factor. See +// is_equivalent_to(). +//////////////////////////////////////////////////////////////////// +bool EggTexture:: +sorts_less_than(const EggTexture &other, int eq) const { + if ((eq & E_complete_filename) == E_complete_filename) { + if (get_fullpath() != other.get_fullpath()) { + return get_fullpath() < other.get_fullpath(); + } + } else { + if (eq & E_basename) { + if (get_basename_wo_extension() != other.get_basename_wo_extension()) { + return get_basename_wo_extension() < other.get_basename_wo_extension(); + } + } + if (eq & E_extension) { + if (get_extension() != other.get_extension()) { + return get_extension() < other.get_extension(); + } + } + if (eq & E_dirname) { + if (get_dirname() != other.get_dirname()) { + return get_dirname() < other.get_dirname(); + } + } + } + + if (eq & E_transform) { + bool is_identity = transform_is_identity(); + bool other_is_identity = other.transform_is_identity(); + if (is_identity != other_is_identity) { + return (int)is_identity < (int)other_is_identity; + } + + if (_has_transform && other._has_transform) { + int compare = _transform.compare_to(other._transform); + if (compare != 0) { + return compare < 0; + } + } + } + + if (eq & E_attributes) { + if (_format != other._format) { + return (int)_format < (int)other._format; + } + if (_wrap_mode != other._wrap_mode) { + return (int)_wrap_mode < (int)other._wrap_mode; + } + if (_wrap_u != other._wrap_u) { + return (int)_wrap_u < (int)other._wrap_u; + } + if (_wrap_v != other._wrap_v) { + return (int)_wrap_v < (int)other._wrap_v; + } + if (_minfilter != other._minfilter) { + return (int)_minfilter < (int)other._minfilter; + } + if (_magfilter != other._magfilter) { + return (int)_magfilter < (int)other._magfilter; + } + if (_magfilteralpha != other._magfilteralpha) { + return (int)_magfilteralpha < (int)other._magfilteralpha; + } + if (_magfiltercolor != other._magfiltercolor) { + return (int)_magfiltercolor < (int)other._magfiltercolor; + } + if (_env_type != other._env_type) { + return (int)_env_type < (int)other._env_type; + } + + if (EggAlphaMode::operator != (other)) { + return EggAlphaMode::operator < (other); + } + } + + if (eq & E_tref_name) { + if (get_name() != other.get_name()) { + return get_name() < other.get_name(); + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::string_format +// Access: Public +// Description: Returns the Format value associated with the given +// string representation, or F_unspecified if the string +// does not match any known Format value. +//////////////////////////////////////////////////////////////////// +EggTexture::Format EggTexture:: +string_format(const string &string) { + if (cmp_nocase_uh(string, "rgba") == 0) { + return F_rgba; + } else if (cmp_nocase_uh(string, "rgba12") == 0) { + return F_rgba12; + } else if (cmp_nocase_uh(string, "rgba8") == 0) { + return F_rgba8; + } else if (cmp_nocase_uh(string, "rgba4") == 0) { + return F_rgba4; + + } else if (cmp_nocase_uh(string, "rgb") == 0) { + return F_rgb; + } else if (cmp_nocase_uh(string, "rgb12") == 0) { + return F_rgb12; + } else if (cmp_nocase_uh(string, "rgb8") == 0) { + return F_rgb8; + } else if (cmp_nocase_uh(string, "rgb5") == 0) { + return F_rgb5; + } else if (cmp_nocase_uh(string, "rgba5") == 0) { + return F_rgba5; + } else if (cmp_nocase_uh(string, "rgb332") == 0) { + return F_rgb332; + + } else if (cmp_nocase_uh(string, "luminance_alpha") == 0) { + return F_luminance_alpha; + + } else if (cmp_nocase_uh(string, "red") == 0) { + return F_red; + } else if (cmp_nocase_uh(string, "green") == 0) { + return F_green; + } else if (cmp_nocase_uh(string, "blue") == 0) { + return F_blue; + } else if (cmp_nocase_uh(string, "alpha") == 0) { + return F_alpha; + } else if (cmp_nocase_uh(string, "luminance") == 0) { + return F_luminance; + } else { + return F_unspecified; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::string_wrap_mode +// Access: Public +// Description: Returns the WrapMode value associated with the given +// string representation, or WM_unspecified if the string +// does not match any known WrapMode value. +//////////////////////////////////////////////////////////////////// +EggTexture::WrapMode EggTexture:: +string_wrap_mode(const string &string) { + if (cmp_nocase_uh(string, "repeat") == 0) { + return WM_repeat; + } else if (cmp_nocase_uh(string, "clamp") == 0) { + return WM_clamp; + } else { + return WM_unspecified; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::string_filter_type +// Access: Public +// Description: Returns the FilterType value associated with the given +// string representation, or FT_unspecified if the string +// does not match any known FilterType value. +//////////////////////////////////////////////////////////////////// +EggTexture::FilterType EggTexture:: +string_filter_type(const string &string) { + if (cmp_nocase_uh(string, "point") == 0) { + return FT_point; + } else if (cmp_nocase_uh(string, "linear") == 0) { + return FT_linear; + } else if (cmp_nocase_uh(string, "bilinear") == 0) { + return FT_bilinear; + } else if (cmp_nocase_uh(string, "trilinear") == 0) { + return FT_trilinear; + } else if (cmp_nocase_uh(string, "mipmap_point") == 0) { + return FT_mipmap_point; + } else if (cmp_nocase_uh(string, "mipmap_linear") == 0) { + return FT_mipmap_linear; + } else if (cmp_nocase_uh(string, "mipmap_bilinear") == 0) { + return FT_mipmap_bilinear; + } else if (cmp_nocase_uh(string, "mipmap_trilinear") == 0) { + return FT_mipmap_trilinear; + } else { + return FT_unspecified; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::string_env_type +// Access: Public +// Description: Returns the EnvType value associated with the given +// string representation, or ET_unspecified if the string +// does not match any known EnvType value. +//////////////////////////////////////////////////////////////////// +EggTexture::EnvType EggTexture:: +string_env_type(const string &string) { + if (cmp_nocase_uh(string, "modulate") == 0) { + return ET_modulate; + } else if (cmp_nocase_uh(string, "decal") == 0) { + return ET_decal; + } else { + return ET_unspecified; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Format output operator +// Description: +//////////////////////////////////////////////////////////////////// +ostream &operator << (ostream &out, EggTexture::Format format) { + switch (format) { + case EggTexture::F_unspecified: + return out << "unspecified"; + + case EggTexture::F_rgba: + return out << "rgba"; + case EggTexture::F_rgba12: + return out << "rgba12"; + case EggTexture::F_rgba8: + return out << "rgba8"; + case EggTexture::F_rgba4: + return out << "rgba4"; + + case EggTexture::F_rgb: + return out << "rgb"; + case EggTexture::F_rgb12: + return out << "rgb12"; + case EggTexture::F_rgb8: + return out << "rgb8"; + case EggTexture::F_rgb5: + return out << "rgb5"; + case EggTexture::F_rgba5: + return out << "rgba5"; + case EggTexture::F_rgb332: + return out << "rgb332"; + + case EggTexture::F_luminance_alpha: + return out << "luminance-alpha"; + + case EggTexture::F_red: + return out << "red"; + case EggTexture::F_green: + return out << "green"; + case EggTexture::F_blue: + return out << "blue"; + case EggTexture::F_alpha: + return out << "alpha"; + case EggTexture::F_luminance: + return out << "luminance"; + } + + nassertr(false, out); + return out << "(**invalid**)"; +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapMode output operator +// Description: +//////////////////////////////////////////////////////////////////// +ostream &operator << (ostream &out, EggTexture::WrapMode mode) { + switch (mode) { + case EggTexture::WM_unspecified: + return out << "unspecified"; + case EggTexture::WM_repeat: + return out << "repeat"; + case EggTexture::WM_clamp: + return out << "clamp"; + } + + nassertr(false, out); + return out << "(**invalid**)"; +} + +//////////////////////////////////////////////////////////////////// +// Function: FilterType output operator +// Description: +//////////////////////////////////////////////////////////////////// +ostream &operator << (ostream &out, EggTexture::FilterType type) { + switch (type) { + case EggTexture::FT_unspecified: + return out << "unspecified"; + case EggTexture::FT_point: + return out << "point"; + case EggTexture::FT_linear: + return out << "linear"; + case EggTexture::FT_bilinear: + return out << "bilinear"; + case EggTexture::FT_trilinear: + return out << "trilinear"; + case EggTexture::FT_mipmap_point: + return out << "mipmap_point"; + case EggTexture::FT_mipmap_linear: + return out << "mipmap_linear"; + case EggTexture::FT_mipmap_bilinear: + return out << "mipmap_bilinear"; + case EggTexture::FT_mipmap_trilinear: + return out << "mipmap_trilinear"; + } + + nassertr(false, out); + return out << "(**invalid**)"; +} + +//////////////////////////////////////////////////////////////////// +// Function: EnvType output operator +// Description: +//////////////////////////////////////////////////////////////////// +ostream &operator << (ostream &out, EggTexture::EnvType type) { + switch (type) { + case EggTexture::ET_unspecified: + return out << "unspecified"; + case EggTexture::ET_modulate: + return out << "modulate"; + case EggTexture::ET_decal: + return out << "decal"; + } + + nassertr(false, out); + return out << "(**invalid**)"; +} diff --git a/panda/src/egg/eggTexture.h b/panda/src/egg/eggTexture.h new file mode 100644 index 0000000000..894926a866 --- /dev/null +++ b/panda/src/egg/eggTexture.h @@ -0,0 +1,171 @@ +// Filename: eggTexture.h +// Created by: drose (18Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGTEXTURE_H +#define EGGTEXTURE_H + +#include + +#include "eggAlphaMode.h" +#include "eggFilenameNode.h" + +#include + + +//////////////////////////////////////////////////////////////////// +// Class : EggTexture +// Description : Defines a texture map that may be applied to +// geometry. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggTexture : public EggFilenameNode, public EggAlphaMode { +public: + EggTexture(const string &tref_name, const string &filename); + EggTexture(const EggTexture ©); + EggTexture &operator = (const EggTexture ©); + + INLINE EggTexture &operator = (const string &filename); + INLINE EggTexture &operator = (const char *filename); + INLINE EggTexture &operator = (const Filename ©); + + virtual void write(ostream &out, int indent_level) const; + + enum Equivalence { + E_basename = 0x001, + E_extension = 0x002, + E_dirname = 0x004, + E_complete_filename = 0x007, + E_transform = 0x008, + E_attributes = 0x010, + E_tref_name = 0x020, + }; + + bool is_equivalent_to(const EggTexture &other, int eq) const; + bool sorts_less_than(const EggTexture &other, int eq) const; + + enum Format { + F_unspecified, + F_rgba, F_rgba12, F_rgba8, F_rgba4, F_rgba5, + F_rgb, F_rgb12, F_rgb8, F_rgb5, F_rgb332, + F_luminance_alpha, + F_red, F_green, F_blue, F_alpha, F_luminance, + }; + enum WrapMode { + WM_unspecified, WM_repeat, WM_clamp + }; + enum FilterType { + FT_unspecified, FT_point, FT_linear, FT_bilinear, FT_trilinear, + FT_mipmap_point, FT_mipmap_linear, FT_mipmap_bilinear, + FT_mipmap_trilinear + }; + enum EnvType { + ET_unspecified, ET_modulate, ET_decal + }; + + INLINE void set_format(Format format); + INLINE Format get_format() const; + + INLINE void set_wrap_mode(WrapMode mode); + INLINE WrapMode get_wrap_mode() const; + + INLINE void set_wrap_u(WrapMode mode); + INLINE WrapMode get_wrap_u() const; + INLINE WrapMode determine_wrap_u() const; + + INLINE void set_wrap_v(WrapMode mode); + INLINE WrapMode get_wrap_v() const; + INLINE WrapMode determine_wrap_v() const; + + INLINE void set_minfilter(FilterType type); + INLINE FilterType get_minfilter() const; + + INLINE void set_magfilter(FilterType type); + INLINE FilterType get_magfilter() const; + + INLINE void set_magfilteralpha(FilterType type); + INLINE FilterType get_magfilteralpha() const; + + INLINE void set_magfiltercolor(FilterType type); + INLINE FilterType get_magfiltercolor() const; + + INLINE void set_env_type(EnvType type); + INLINE EnvType get_env_type() const; + + INLINE void set_transform(const LMatrix3d &transform); + INLINE void clear_transform(); + INLINE bool has_transform() const; + INLINE LMatrix3d get_transform() const; + INLINE bool transform_is_identity() const; + + static Format string_format(const string &string); + static WrapMode string_wrap_mode(const string &string); + static FilterType string_filter_type(const string &string); + static EnvType string_env_type(const string &string); + +private: + Format _format; + WrapMode _wrap_mode, _wrap_u, _wrap_v; + FilterType _minfilter, _magfilter, _magfilteralpha, _magfiltercolor; + EnvType _env_type; + bool _has_transform; + LMatrix3d _transform; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggFilenameNode::init_type(); + EggAlphaMode::init_type(); + register_type(_type_handle, "EggTexture", + EggFilenameNode::get_class_type(), + EggAlphaMode::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +/////////////////////////////////////////////////////////////////// +// Class : UniqueEggTextures +// Description : An STL function object for sorting textures into +// order by properties. Returns true if the two +// referenced EggTexture pointers are in sorted order, +// false otherwise. +//////////////////////////////////////////////////////////////////// +class UniqueEggTextures { +public: + INLINE UniqueEggTextures(int eq = ~0); + INLINE bool operator ()(const EggTexture *t1, const EggTexture *t2) const; + + int _eq; +}; + +/////////////////////////////////////////////////////////////////// +// Class : TRefEggTextures +// Description : An STL function object for sorting textures into +// order by TRef name. +//////////////////////////////////////////////////////////////////// +class TRefEggTextures { +public: + INLINE bool operator ()(const EggTexture *t1, const EggTexture *t2) const; +}; + +INLINE ostream &operator << (ostream &out, const EggTexture &n) { + return out << (Filename &)n; +} + +ostream EXPCL_PANDAEGG &operator << (ostream &out, EggTexture::Format format); +ostream EXPCL_PANDAEGG &operator << (ostream &out, EggTexture::WrapMode mode); +ostream EXPCL_PANDAEGG &operator << (ostream &out, EggTexture::FilterType type); +ostream EXPCL_PANDAEGG &operator << (ostream &out, EggTexture::EnvType type); + +#include "eggTexture.I" + +#endif diff --git a/panda/src/egg/eggTextureCollection.I b/panda/src/egg/eggTextureCollection.I new file mode 100644 index 0000000000..3391de1493 --- /dev/null +++ b/panda/src/egg/eggTextureCollection.I @@ -0,0 +1,27 @@ +// Filename: eggTextureCollection.I +// Created by: drose (16Feb00) +// +//////////////////////////////////////////////////////////////////// + +INLINE EggTextureCollection::iterator EggTextureCollection:: +begin() const { + nassertr(_ordered_textures.size() == _textures.size(), + _ordered_textures.begin()); + return _ordered_textures.begin(); +} + +INLINE EggTextureCollection::iterator EggTextureCollection:: +end() const { + return _ordered_textures.end(); +} + +INLINE bool EggTextureCollection:: +empty() const { + return _ordered_textures.empty(); +} + +INLINE EggTextureCollection::size_type EggTextureCollection:: +size() const { + nassertr(_ordered_textures.size() == _textures.size(), 0); + return _ordered_textures.size(); +} diff --git a/panda/src/egg/eggTextureCollection.cxx b/panda/src/egg/eggTextureCollection.cxx new file mode 100644 index 0000000000..382b8d7f85 --- /dev/null +++ b/panda/src/egg/eggTextureCollection.cxx @@ -0,0 +1,464 @@ +// Filename: eggTextureCollection.cxx +// Created by: drose (15Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "eggTextureCollection.h" +#include "eggGroupNode.h" +#include "eggPrimitive.h" +#include "eggTexture.h" + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggTextureCollection:: +EggTextureCollection() { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggTextureCollection:: +EggTextureCollection(const EggTextureCollection ©) : + _textures(copy._textures), + _ordered_textures(copy._ordered_textures) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggTextureCollection &EggTextureCollection:: +operator = (const EggTextureCollection ©) { + _textures = copy._textures; + _ordered_textures = copy._ordered_textures; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::clear +// Access: Public +// Description: Removes all textures from the collection. +//////////////////////////////////////////////////////////////////// +void EggTextureCollection:: +clear() { + _textures.clear(); + _ordered_textures.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::extract_textures +// Access: Public +// Description: Walks the egg hierarchy beginning at the indicated +// node, and removes any EggTextures encountered in the +// hierarchy, adding them to the collection. Returns +// the number of EggTextures encountered. +//////////////////////////////////////////////////////////////////// +int EggTextureCollection:: +extract_textures(EggGroupNode *node) { + // Since this traversal is destructive, we'll handle it within the + // EggGroupNode code. + return node->find_textures(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::insert_textures +// Access: Public +// Description: Adds a series of EggTexture nodes to the beginning of +// the indicated node to reflect each of the textures in +// the collection. Returns the number of texture nodes +// added. +//////////////////////////////////////////////////////////////////// +int EggTextureCollection:: +insert_textures(EggGroupNode *node) { + return insert_textures(node, node->begin()); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::insert_textures +// Access: Public +// Description: Adds a series of EggTexture nodes to the beginning of +// the indicated node to reflect each of the textures in +// the collection. Returns the number of texture nodes +// added. +//////////////////////////////////////////////////////////////////// +int EggTextureCollection:: +insert_textures(EggGroupNode *node, EggGroupNode::iterator position) { + // We add the textures in reverse order because we stick each one at + // the head of the group's children. When we're done, they'll all + // be in the correct order. + + OrderedTextures::reverse_iterator oti; + for (oti = _ordered_textures.rbegin(); + oti != _ordered_textures.rend(); + ++oti) { + node->insert(position, (*oti).p()); + } + + return size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::find_used_textures +// Access: Public +// Description: Walks the egg hierarchy beginning at the indicated +// node, looking for textures that are referenced by +// primitives but are not already members of the +// collection, adding them to the collection. +// +// If this is called following extract_textures(), it +// can be used to pick up any additional texture +// references that appeared in the egg hierarchy (but +// whose EggTexture node was not actually part of the +// hierarchy). +// +// If this is called in lieu of extract_textures(), it +// will fill up the collection with all of the +// referenced textures (and only the referenced +// textures), without destructively removing the +// EggTextures from the hierarchy. +// +// This also has the side effect of incrementing the +// internal usage count for a texture in the collection +// each time a texture reference is encountered. This +// side effect is taken advantage of by +// remove_unused_textures(). +//////////////////////////////////////////////////////////////////// +int EggTextureCollection:: +find_used_textures(EggGroupNode *node) { + int num_found = 0; + + EggGroupNode::iterator ci; + for (ci = node->begin(); + ci != node->end(); + ++ci) { + EggNode *child = *ci; + if (child->is_of_type(EggPrimitive::get_class_type())) { + EggPrimitive *primitive = DCAST(EggPrimitive, child); + if (primitive->has_texture()) { + EggTexture *tex = primitive->get_texture(); + Textures::iterator ti = _textures.find(tex); + if (ti == _textures.end()) { + // Here's a new texture! + num_found++; + _textures.insert(Textures::value_type(tex, 1)); + _ordered_textures.push_back(tex); + } else { + // Here's a texture we'd already known about. Increment its + // usage count. + (*ti).second++; + } + } + + } else if (child->is_of_type(EggGroupNode::get_class_type())) { + EggGroupNode *group_child = DCAST(EggGroupNode, child); + num_found += find_used_textures(group_child); + } + } + + return num_found; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::remove_unused_textures +// Access: Public +// Description: Removes any textures from the collection that aren't +// referenced by any primitives in the indicated egg +// hierarchy. This also, incidentally, adds textures to +// the collection that had been referenced by primitives +// but had not previously appeared in the collection. +//////////////////////////////////////////////////////////////////// +void EggTextureCollection:: +remove_unused_textures(EggGroupNode *node) { + // We'll do this the easy way: First, we'll remove *all* the + // textures from the collection, and then we'll add back only those + // that appear in the hierarchy. + clear(); + find_used_textures(node); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::collapse_equivalent_textures +// Access: Public +// Description: Walks through the collection and collapses together +// any separate textures that are equivalent according +// to the indicated equivalence factor, eq (see +// EggTexture::is_equivalent_to()). The return value is +// the number of textures removed. +// +// This flavor of collapse_equivalent_textures() +// automatically adjusts all the primitives in the egg +// hierarchy to refer to the new texture pointers. +//////////////////////////////////////////////////////////////////// +int EggTextureCollection:: +collapse_equivalent_textures(int eq, EggGroupNode *node) { + TextureReplacement removed; + int num_collapsed = collapse_equivalent_textures(eq, removed); + + // And now walk the egg hierarchy and replace any references to a + // removed texture with its replacement. + replace_textures(node, removed); + + return num_collapsed; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::collapse_equivalent_textures +// Access: Public +// Description: Walks through the collection and collapses together +// any separate textures that are equivalent according +// to the indicated equivalence factor, eq (see +// EggTexture::is_equivalent_to()). The return value is +// the number of textures removed. +// +// This flavor of collapse_equivalent_textures() does +// not adjust any primitives in the egg hierarchy; +// instead, it fills up the 'removed' map with an entry +// for each removed texture, mapping it back to the +// equivalent retained texture. It's up to the user to +// then call replace_textures() with this map, if +// desired, to apply these changes to the egg hierarchy. +//////////////////////////////////////////////////////////////////// +int EggTextureCollection:: +collapse_equivalent_textures(int eq, EggTextureCollection::TextureReplacement &removed) { + int num_collapsed = 0; + + typedef set Collapser; + UniqueEggTextures uet(eq); + Collapser collapser(uet); + + // First, put all of the textures into the Collapser structure, to + // find out the unique textures. + OrderedTextures::const_iterator oti; + for (oti = _ordered_textures.begin(); + oti != _ordered_textures.end(); + ++oti) { + EggTexture *tex = (*oti); + + pair result = collapser.insert(tex); + if (!result.second) { + // This texture is non-unique; another one was already there. + EggTexture *first = *(result.first); + removed.insert(TextureReplacement::value_type(tex, first)); + num_collapsed++; + } + } + + // Now record all of the unique textures only. + clear(); + Collapser::const_iterator ci; + for (ci = collapser.begin(); ci != collapser.end(); ++ci) { + add_texture(*ci); + } + + return num_collapsed; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::replace_textures +// Access: Public, Static +// Description: Walks the egg hierarchy, changing out any reference +// to a texture appearing on the left side of the map +// with its corresponding texture on the right side. +// This is most often done following a call to +// collapse_equivalent_textures(). It does not directly +// affect the Collection. +//////////////////////////////////////////////////////////////////// +void EggTextureCollection:: +replace_textures(EggGroupNode *node, + const EggTextureCollection::TextureReplacement &replace) { + EggGroupNode::iterator ci; + for (ci = node->begin(); + ci != node->end(); + ++ci) { + EggNode *child = *ci; + if (child->is_of_type(EggPrimitive::get_class_type())) { + EggPrimitive *primitive = DCAST(EggPrimitive, child); + if (primitive->has_texture()) { + PT(EggTexture) tex = primitive->get_texture(); + TextureReplacement::const_iterator ri; + ri = replace.find(tex); + if (ri != replace.end()) { + // Here's a texture we want to replace. + primitive->set_texture((*ri).second); + } + } + + } else if (child->is_of_type(EggGroupNode::get_class_type())) { + EggGroupNode *group_child = DCAST(EggGroupNode, child); + replace_textures(group_child, replace); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::uniquify_trefs +// Access: Public +// Description: Guarantees that each texture in the collection has a +// unique TRef name. This is essential before writing +// an egg file. +//////////////////////////////////////////////////////////////////// +void EggTextureCollection:: +uniquify_trefs() { + NameUniquifier nu(".tref", "tref"); + + OrderedTextures::const_iterator oti; + for (oti = _ordered_textures.begin(); + oti != _ordered_textures.end(); + ++oti) { + EggTexture *tex = (*oti); + + tex->set_name(nu.add_name(tex->get_name())); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::sort_by_tref +// Access: Public +// Description: Sorts all the textures into alphabetical order by +// TRef name. Subsequent operations using begin()/end() +// will traverse in this sorted order. +//////////////////////////////////////////////////////////////////// +void EggTextureCollection:: +sort_by_tref() { + sort(_ordered_textures.begin(), _ordered_textures.end(), + NamableOrderByName()); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::add_texture +// Access: Public +// Description: Explicitly adds a new texture to the collection. +// Returns true if the texture was added, false if it +// was already there or if there was some error. +//////////////////////////////////////////////////////////////////// +bool EggTextureCollection:: +add_texture(PT(EggTexture) texture) { + nassertr(_textures.size() == _ordered_textures.size(), false); + + Textures::const_iterator ti; + ti = _textures.find(texture); + if (ti != _textures.end()) { + // This texture is already a member of the collection. + return false; + } + + _textures.insert(Textures::value_type(texture, 0)); + _ordered_textures.push_back(texture); + + nassertr(_textures.size() == _ordered_textures.size(), false); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::remove_texture +// Access: Public +// Description: Explicitly removes a texture from the collection. +// Returns true if the texture was removed, false if it +// wasn't there or if there was some error. +//////////////////////////////////////////////////////////////////// +bool EggTextureCollection:: +remove_texture(PT(EggTexture) texture) { + nassertr(_textures.size() == _ordered_textures.size(), false); + + Textures::iterator ti; + ti = _textures.find(texture); + if (ti == _textures.end()) { + // This texture is not a member of the collection. + return false; + } + + _textures.erase(ti); + + OrderedTextures::iterator oti; + oti = find(_ordered_textures.begin(), _ordered_textures.end(), texture); + nassertr(oti != _ordered_textures.end(), false); + + _ordered_textures.erase(oti); + + nassertr(_textures.size() == _ordered_textures.size(), false); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::create_unique_texture +// Access: Public +// Description: Creates a new texture if there is not already one +// equivalent (according to eq, see +// EggTexture::is_equivalent_to()) to the indicated +// texture, or returns the existing one if there is. +//////////////////////////////////////////////////////////////////// +EggTexture *EggTextureCollection:: +create_unique_texture(const EggTexture ©, int eq) { + // This requires a complete linear traversal, not terribly + // efficient. + OrderedTextures::const_iterator oti; + for (oti = _ordered_textures.begin(); + oti != _ordered_textures.end(); + ++oti) { + EggTexture *tex = (*oti); + if (copy.is_equivalent_to(*tex, eq)) { + return tex; + } + } + + PT(EggTexture) new_texture = new EggTexture(copy); + add_texture(new_texture); + return new_texture; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::find_tref +// Access: Public +// Description: Returns the texture with the indicated TRef name, or +// NULL if no texture matches. +//////////////////////////////////////////////////////////////////// +EggTexture *EggTextureCollection:: +find_tref(const string &tref_name) const { + // This requires a complete linear traversal, not terribly + // efficient. + OrderedTextures::const_iterator oti; + for (oti = _ordered_textures.begin(); + oti != _ordered_textures.end(); + ++oti) { + EggTexture *tex = (*oti); + if (tex->get_name() == tref_name) { + return tex; + } + } + + return (EggTexture *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTextureCollection::find_filename +// Access: Public +// Description: Returns the texture with the indicated filename, or +// NULL if no texture matches. +//////////////////////////////////////////////////////////////////// +EggTexture *EggTextureCollection:: +find_filename(const string &filename) const { + // This requires a complete linear traversal, not terribly + // efficient. + OrderedTextures::const_iterator oti; + for (oti = _ordered_textures.begin(); + oti != _ordered_textures.end(); + ++oti) { + EggTexture *tex = (*oti); + if (tex->get_fullpath() == filename) { + return tex; + } + } + + return (EggTexture *)NULL; +} diff --git a/panda/src/egg/eggTextureCollection.h b/panda/src/egg/eggTextureCollection.h new file mode 100644 index 0000000000..15150377b1 --- /dev/null +++ b/panda/src/egg/eggTextureCollection.h @@ -0,0 +1,95 @@ +// Filename: eggTextureCollection.h +// Created by: drose (15Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGTEXTURECOLLECTION_H +#define EGGTEXTURECOLLECTION_H + +#include + +#include "eggTexture.h" +#include "eggGroupNode.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : EggTextureCollection +// Description : This is a collection of textures by TRef name. It +// can extract the textures from an egg file and sort +// them all together; it can also manage the creation of +// unique textures and the assignment of unique TRef +// names. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggTextureCollection { + + // This is a bit of private interface stuff that must be here as a + // forward reference. This allows us to define the + // EggTextureCollection as an STL container. + +private: + typedef map Textures; + typedef vector OrderedTextures; + +public: + typedef OrderedTextures::const_iterator iterator; + typedef iterator const_iterator; + typedef OrderedTextures::size_type size_type; + + typedef map TextureReplacement; + + // Here begins the actual public interface to EggTextureCollection. + +public: + EggTextureCollection(); + EggTextureCollection(const EggTextureCollection ©); + EggTextureCollection &operator = (const EggTextureCollection ©); + + void clear(); + + int extract_textures(EggGroupNode *node); + int insert_textures(EggGroupNode *node); + int insert_textures(EggGroupNode *node, EggGroupNode::iterator position); + + int find_used_textures(EggGroupNode *node); + void remove_unused_textures(EggGroupNode *node); + + int collapse_equivalent_textures(int eq, EggGroupNode *node); + int collapse_equivalent_textures(int eq, TextureReplacement &removed); + static void replace_textures(EggGroupNode *node, + const TextureReplacement &replace); + + void uniquify_trefs(); + void sort_by_tref(); + + // Can be used to traverse all the textures in the collection, in + // order as last sorted. + INLINE iterator begin() const; + INLINE iterator end() const; + INLINE bool empty() const; + INLINE size_type size() const; + + bool add_texture(PT(EggTexture) texture); + bool remove_texture(PT(EggTexture) texture); + + // create_unique_texture() creates a new texture if there is not + // already one equivalent (according to eq, see + // EggTexture::is_equivalent_to()) to the indicated texture, or + // returns the existing one if there is. + EggTexture *create_unique_texture(const EggTexture ©, int eq); + + // Find a texture with a particular TRef name. + EggTexture *find_tref(const string &tref_name) const; + + // Find a texture with a particular filename. + EggTexture *find_filename(const string &filename) const; + +private: + Textures _textures; + OrderedTextures _ordered_textures; +}; + +#include "eggTextureCollection.I" + +#endif diff --git a/panda/src/egg/eggUtilities.I b/panda/src/egg/eggUtilities.I new file mode 100644 index 0000000000..c9aae85245 --- /dev/null +++ b/panda/src/egg/eggUtilities.I @@ -0,0 +1,121 @@ +// Filename: eggUtilities.I +// Created by: drose (10Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggGroup.h" +#include "eggPrimitive.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: split_vertex +// Description: Splits a vertex into two or more vertices, each an +// exact copy of the original and in the same vertex +// pool. +// +// The splitting is based on some arbitrary property of +// the primitives that own the vertex. In the extreme, +// each primitive may get a different copy of the +// vertex, although it is also possible for some +// primitives to still share vertices. +// +// This decision is made based on the function object +// 'sequence'. This object must define the following +// function: +// +// int operator () (const EggPrimitive *prim) const; +// +// This function returns a sequence number, which +// determines which primitives will share which +// vertices. The sequence number 0 refers to the +// original vertex pointer; other sequence numbers +// indicate new vertices. Other than that, the sequence +// number is totally arbitrary. Primitives for which +// the sequence number is the same will end up sharing +// the same copy of the vertex. +//////////////////////////////////////////////////////////////////// +template +void +split_vertex(EggVertex *vert, const FunctionObject &sequence) { + // Did we start in a happy world? + vert->test_pref_integrity(); + vert->test_gref_integrity(); + + EggVertexPool *pool = vert->get_pool(); + + // Define a map of ints to vert pointers, to indicate which sequence + // numbers we have already created vertices for. + typedef map Sequences; + Sequences _sequences; + + // Get a copy of the list of primitives that reference this vertex. + // We must have a copy because we will be modifying the list as we + // traverse it. + typedef vector Prims; + Prims prims; + prims.reserve(vert->pref_size()); + copy(vert->pref_begin(), vert->pref_end(), back_inserter(prims)); + + // Now walk through the list of primitives that reference this + // vertex. + Prims::const_iterator pri; + for (pri = prims.begin(); pri != prims.end(); ++pri) { + EggPrimitive *prim = *pri; + prim->test_ref_count_integrity(); + + int seq = sequence(prim); + + if (seq != 0) { + // Here's a new sequence number! Have we already defined it? + EggVertex *new_vert = NULL; + + Sequences::const_iterator si = _sequences.find(seq); + + if (si != _sequences.end()) { + // Yes, we've seen this sequence number before. Use the same + // vertex. + new_vert = (*si).second; + + } else { + // No, this is the first time we've encountered this sequence. + // Split the vertex. + new_vert = new EggVertex(*vert); + pool->add_vertex(new_vert); + _sequences[seq] = new_vert; + + // The new vertex gets all the same group memberships as the + // old one. + EggVertex::GroupRef::const_iterator gri; + for (gri = vert->gref_begin(); gri != vert->gref_end(); ++gri) { + EggGroup *group = *gri; + group->ref_vertex(new_vert, group->get_vertex_membership(vert)); + } + } + + // Now replace the vertex in the primitive. + EggPrimitive::iterator pi; + for (pi = prim->begin(); pi != prim->end(); ++pi) { + if (*pi == vert) { + prim->replace(pi, new_vert); + } + } + } + } + +#ifndef NDEBUG + // Now verify everything is still happy. + vert->test_pref_integrity(); + vert->test_gref_integrity(); + + Sequences::const_iterator si; + for (si = _sequences.begin(); + si != _sequences.end(); + ++si) { + EggVertex *new_vert = (*si).second; + new_vert->test_gref_integrity(); + new_vert->test_pref_integrity(); + } +#endif // NDEBUG + +} diff --git a/panda/src/egg/eggUtilities.cxx b/panda/src/egg/eggUtilities.cxx new file mode 100644 index 0000000000..e310d0181d --- /dev/null +++ b/panda/src/egg/eggUtilities.cxx @@ -0,0 +1,39 @@ +// Filename: eggUtilities.cxx +// Created by: drose (28Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggUtilities.h" +#include "eggPrimitive.h" +#include "eggGroupNode.h" + + +//////////////////////////////////////////////////////////////////// +// Function: get_textures_by_filename +// Description: Extracts from the egg subgraph beginning at the +// indicated node a set of all the texture objects +// referenced, grouped together by filename. Texture +// objects that share a common filename (but possibly +// differ in other properties) are returned together in +// the same element of the map. +//////////////////////////////////////////////////////////////////// +void +get_textures_by_filename(const EggNode *node, EggTextureFilenames &result) { + if (node->is_of_type(EggPrimitive::get_class_type())) { + const EggPrimitive *prim = DCAST(EggPrimitive, node); + + if (prim->has_texture()) { + PT(EggTexture) tex = prim->get_texture(); + result[tex->get_fullpath()].insert(tex); + } + + } else if (node->is_of_type(EggGroupNode::get_class_type())) { + const EggGroupNode *group = DCAST(EggGroupNode, node); + + EggGroupNode::const_iterator ci; + for (ci = group->begin(); ci != group->end(); ++ci) { + get_textures_by_filename(*ci, result); + } + } +} + diff --git a/panda/src/egg/eggUtilities.h b/panda/src/egg/eggUtilities.h new file mode 100644 index 0000000000..b7b32f43fa --- /dev/null +++ b/panda/src/egg/eggUtilities.h @@ -0,0 +1,62 @@ +// Filename: eggUtilities.h +// Created by: drose (28Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGUTILITIES_H +#define EGGUTILITIES_H + +//////////////////////////////////////////////////////////////////// +// +// eggUtilities.h +// +// Handy functions that operate on egg structures, but don't +// necessarily belong in any one class. +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "eggTexture.h" + +#include +#include + +#include +#include + +class EggNode; +class EggVertex; + +typedef set EggTextures; +typedef map EggTextureFilenames; + + +//////////////////////////////////////////////////////////////////// +// Function: get_textures_by_filename +// Description: Extracts from the egg subgraph beginning at the +// indicated node a set of all the texture objects +// referenced, grouped together by filename. Texture +// objects that share a common filename (but possibly +// differ in other properties) are returned together in +// the same element of the map. +//////////////////////////////////////////////////////////////////// +void +get_textures_by_filename(const EggNode *node, EggTextureFilenames &result); + + +//////////////////////////////////////////////////////////////////// +// Function: split_vertex +// Description: Splits a vertex into two or more vertices, each an +// exact copy of the original and in the same vertex +// pool. See the more detailed comments in +// eggUtilities.I. +//////////////////////////////////////////////////////////////////// +template +void +split_vertex(EggVertex *vert, const FunctionObject &sequence); + + +#include "eggUtilities.I" + +#endif diff --git a/panda/src/egg/eggVertex.I b/panda/src/egg/eggVertex.I new file mode 100644 index 0000000000..2e2e162d34 --- /dev/null +++ b/panda/src/egg/eggVertex.I @@ -0,0 +1,276 @@ +// Filename: eggVertex.I +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::get_pool +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggVertexPool *EggVertex:: +get_pool() const { + return _pool; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::set_pos +// Access: Public +// Description: Sets the vertex position. This variant sets the +// vertex to a one-dimensional value. +//////////////////////////////////////////////////////////////////// +INLINE void EggVertex:: +set_pos(double pos) { + _num_dimensions = 1; + _pos.set(pos, 0.0, 0.0, 1.0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::set_pos +// Access: Public +// Description: Sets the vertex position. This variant sets the +// vertex to a two-dimensional value. +//////////////////////////////////////////////////////////////////// +INLINE void EggVertex:: +set_pos(const LPoint2d &pos) { + _num_dimensions = 2; + _pos.set(pos[0], pos[1], 0.0, 1.0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::set_pos +// Access: Public +// Description: Sets the vertex position. This variant sets the +// vertex to a three-dimensional value. +//////////////////////////////////////////////////////////////////// +INLINE void EggVertex:: +set_pos(const LPoint3d &pos) { + _num_dimensions = 3; + _pos.set(pos[0], pos[1], pos[2], 1.0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::set_pos +// Access: Public +// Description: Sets the vertex position. This variant sets the +// vertex to a four-dimensional value. +//////////////////////////////////////////////////////////////////// +INLINE void EggVertex:: +set_pos(const LPoint4d &pos) { + _num_dimensions = 4; + _pos = pos; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::set_pos4 +// Access: Public +// Description: This special flavor of set_pos() sets the vertex as a +// four-component value, but does not change the set +// number of dimensions. It's handy for retrieving the +// vertex position via get_pos4, manipulating it, then +// storing it back again, without worrying about the +// number of dimensions it actually had. +//////////////////////////////////////////////////////////////////// +INLINE void EggVertex:: +set_pos4(const LPoint4d &pos) { + _pos = pos; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::get_num_dimensions +// Access: Public +// Description: Returns the number of dimensions the vertex uses. +// Usually this will be 3, but it may be 1, 2, 3, or 4. +//////////////////////////////////////////////////////////////////// +INLINE int EggVertex:: +get_num_dimensions() const { + return _num_dimensions; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::get_pos1 +// Access: Public +// Description: Only valid if get_num_dimensions() returns 1. +// Returns the position as a one-dimensional value. +//////////////////////////////////////////////////////////////////// +INLINE double EggVertex:: +get_pos1() const { + nassertr(_num_dimensions == 1, 0.0); + return _pos[0]; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::get_pos2 +// Access: Public +// Description: Only valid if get_num_dimensions() returns 2. +// Returns the position as a two-dimensional value. +//////////////////////////////////////////////////////////////////// +INLINE LPoint2d EggVertex:: +get_pos2() const { + nassertr(_num_dimensions == 2, LPoint2d(0.0, 0.0)); + return LPoint2d(_pos[0], _pos[1]); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::get_pos3 +// Access: Public +// Description: Only valid if get_num_dimensions() returns 3. +// Returns the position as a three-dimensional value. +//////////////////////////////////////////////////////////////////// +INLINE Vertexd EggVertex:: +get_pos3() const { + nassertr(_num_dimensions == 3, LPoint3d(0.0, 0.0, 0.0)); + return Vertexd(_pos[0], _pos[1], _pos[2]); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::get_pos4 +// Access: Public +// Description: This is always valid, regardless of the value of +// get_num_dimensions. It returns the position as a +// four-dimensional value. If the pos has fewer than +// four dimensions, this value represents the pos +// extended into four-dimensional homogenous space, +// e.g. by adding 1 as the fourth component. +//////////////////////////////////////////////////////////////////// +INLINE LPoint4d EggVertex:: +get_pos4() const { + return _pos; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::get_index +// Access: Public +// Description: Returns the index number of the vertex within its +// pool. +//////////////////////////////////////////////////////////////////// +INLINE int EggVertex:: +get_index() const { + return _index; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::gref_begin +// Access: Public +// Description: Returns an iterator that can, in conjunction with +// gref_end(), be used to traverse the entire set of +// groups that reference this vertex. Each iterator +// returns a pointer to a group. +//////////////////////////////////////////////////////////////////// +INLINE EggVertex::GroupRef::const_iterator EggVertex:: +gref_begin() const { + return _gref.begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::gref_end +// Access: Public +// Description: Returns an iterator that can, in conjunction with +// gref_begin(), be used to traverse the entire set of +// groups that reference this vertex. Each iterator +// returns a pointer to a group. +//////////////////////////////////////////////////////////////////// +INLINE EggVertex::GroupRef::const_iterator EggVertex:: +gref_end() const { + return _gref.end(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::gref_size +// Access: Public +// Description: Returns the number of elements between gref_begin() +// and gref_end(). +//////////////////////////////////////////////////////////////////// +INLINE EggVertex::GroupRef::size_type EggVertex:: +gref_size() const { + return _gref.size(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::has_gref +// Access: Public +// Description: Returns true if the indicated group references this +// vertex, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool EggVertex:: +has_gref(const EggGroup *group) const { + return _gref.count((EggGroup *)group) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::pref_begin +// Access: Public +// Description: Returns an iterator that can, in conjunction with +// pref_end(), be used to traverse the entire set of +// primitives that reference this vertex. Each iterator +// returns a pointer to a primitive. +//////////////////////////////////////////////////////////////////// +INLINE EggVertex::PrimitiveRef::const_iterator EggVertex:: +pref_begin() const { + return _pref.begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::pref_end +// Access: Public +// Description: Returns an iterator that can, in conjunction with +// pref_begin(), be used to traverse the entire set of +// primitives that reference this vertex. Each iterator +// returns a pointer to a primitive. +//////////////////////////////////////////////////////////////////// +INLINE EggVertex::PrimitiveRef::const_iterator EggVertex:: +pref_end() const { + return _pref.end(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::pref_size +// Access: Public +// Description: Returns the number of elements between pref_begin() +// and pref_end(). +//////////////////////////////////////////////////////////////////// +INLINE EggVertex::GroupRef::size_type EggVertex:: +pref_size() const { + return _pref.size(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::has_pref +// Access: Public +// Description: Returns the number of times the vertex appears in the +// indicated primitive, or 0 if it does not appear. +//////////////////////////////////////////////////////////////////// +INLINE int EggVertex:: +has_pref(const EggPrimitive *prim) const { + return _pref.count((EggPrimitive *)prim); +} + + + + +//////////////////////////////////////////////////////////////////// +// Function: UniqueEggVertices::Function operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool UniqueEggVertices:: +operator ()(const EggVertex *v1, const EggVertex *v2) const { + return v1->sorts_less_than(*v2); +} + diff --git a/panda/src/egg/eggVertex.cxx b/panda/src/egg/eggVertex.cxx new file mode 100644 index 0000000000..a5a91a8113 --- /dev/null +++ b/panda/src/egg/eggVertex.cxx @@ -0,0 +1,327 @@ +// Filename: eggVertex.cxx +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggVertex.h" +#include "eggVertexPool.h" +#include "eggParameters.h" +#include "eggGroup.h" +#include "eggMiscFuncs.h" +#include "eggPrimitive.h" + +#include +#include +#include +#include + +#include +#include + +TypeHandle EggVertex::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggVertex:: +EggVertex() { + _pool = NULL; + _index = -1; + set_pos(LPoint3d(0.0, 0.0, 0.0)); + test_pref_integrity(); + test_gref_integrity(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::Copy constructor +// Access: Public +// Description: Copies all properties of the vertex except its vertex +// pool and index number. +//////////////////////////////////////////////////////////////////// +EggVertex:: +EggVertex(const EggVertex ©) + : EggObject(copy), EggAttributes(copy), + _pos(copy._pos), + _num_dimensions(copy._num_dimensions), + _dxyzs(copy._dxyzs) +{ + _pool = NULL; + _index = -1; + test_pref_integrity(); + test_gref_integrity(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::Copy assignment operator +// Access: Public +// Description: Copies all properties of the vertex except its vertex +// pool and index number. +//////////////////////////////////////////////////////////////////// +EggVertex &EggVertex:: +operator = (const EggVertex ©) { + EggObject::operator = (copy); + EggAttributes::operator = (copy); + _pos = copy._pos; + _num_dimensions = copy._num_dimensions; + _dxyzs = copy._dxyzs; + + test_pref_integrity(); + test_gref_integrity(); + + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggVertex:: +~EggVertex() { + // We should never destruct a vertex while it still thinks it + // belongs to a VertexPool. If we do, we've probably lost a + // reference count somewhere. + nassertv(_pool == NULL); + + // Also, a vertex shouldn't be destructed while it's being + // referenced by a group or a primitive, for the same reason. + nassertv(_gref.empty()); + nassertv(_pref.empty()); +} + + + +/////////////////////////////////////////////////////////////////// +// Class : GroupRefEntry +// Description : A temporary class used in EggVertex::write(), below, +// to hold the groups that reference each vertex prior +// to outputting them as a formatted list. +//////////////////////////////////////////////////////////////////// +class GroupRefEntry { +public: + GroupRefEntry(EggGroup *group, double membership) + : _group(group), _membership(membership) { } + + bool operator < (const GroupRefEntry &other) const { + return _group->get_name() < other._group->get_name(); + } + void output(ostream &out) const { + out << _group->get_name() << ":" << _membership; + } + + EggGroup *_group; + double _membership; +}; + +INLINE ostream &operator << (ostream &out, const GroupRefEntry &gre) { + gre.output(out); + return out; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::write +// Access: Public +// Description: Writes the vertex to the indicated output stream in +// Egg format. +//////////////////////////////////////////////////////////////////// +void EggVertex:: +write(ostream &out, int indent_level) const { + test_pref_integrity(); + test_gref_integrity(); + + indent(out, indent_level) + << " " << _index << " {\n"; + + // Now output the position. This might have any number of + // dimensions up to 4. + indent(out, indent_level+1); + for (int i = 0; i < _num_dimensions; i++) { + out << " " << _pos[i]; + } + out << "\n"; + + EggAttributes::write(out, indent_level+2); + + _dxyzs.write(out, indent_level+2); + + // If the vertex is referenced by one or more groups, write that as + // a helpful comment. + if (!_gref.empty()) { + // We need to build a list of group entries. + set gre; + + GroupRef::const_iterator gi; + for (gi = _gref.begin(); gi != _gref.end(); ++gi) { + gre.insert(GroupRefEntry(*gi, (*gi)->get_vertex_membership(this))); + } + + // Now output the list. + write_long_list(out, indent_level + 2, gre.begin(), gre.end(), "// ", + "", 72); + } + + indent(out, indent_level) + << "}\n"; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::sorts_less_than +// Access: Public +// Description: An ordering operator to compare two vertices for +// sorting order. This imposes an arbitrary ordering +// useful to identify unique vertices. +//////////////////////////////////////////////////////////////////// +bool EggVertex:: +sorts_less_than(const EggVertex &other) const { + if (_num_dimensions != other._num_dimensions) { + return _num_dimensions < other._num_dimensions; + } + + int compare = + _pos.compare_to(other._pos, egg_parameters->_pos_threshold); + if (compare != 0) { + return compare < 0; + } + if (_dxyzs != other._dxyzs) { + return _dxyzs < other._dxyzs; + } + + return EggAttributes::sorts_less_than(other); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::get_num_local_coord +// Access: Public +// Description: Returns the number of primitives that own this vertex +// whose vertices are interpreted to be in a local +// coordinate system. +//////////////////////////////////////////////////////////////////// +int EggVertex:: +get_num_local_coord() const { + test_pref_integrity(); + + PrimitiveRef::const_iterator pri; + + int count = 0; + for (pri = pref_begin(); pri != pref_end(); ++pri) { + EggPrimitive *prim = *pri; + count += (prim->is_local_coord() ? 1 : 0); + } + return count; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::get_num_global_coord +// Access: Public +// Description: Returns the number of primitives that own this vertex +// whose vertices are interpreted in the global +// coordinate system. +//////////////////////////////////////////////////////////////////// +int EggVertex:: +get_num_global_coord() const { + test_pref_integrity(); + + PrimitiveRef::const_iterator pri; + + int count = 0; + for (pri = pref_begin(); pri != pref_end(); ++pri) { + EggPrimitive *prim = *pri; + count += (prim->is_local_coord() ? 0 : 1); + } + return count; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::transform +// Access: Public, Virtual +// Description: Applies the indicated transformation matrix to the +// vertex. +//////////////////////////////////////////////////////////////////// +void EggVertex:: +transform(const LMatrix4d &mat) { + _pos = _pos * mat; + + EggMorphVertexList::iterator mi; + for (mi = _dxyzs.begin(); mi != _dxyzs.end(); ++mi) { + // We can safely cast the morph object to a non-const, because + // we're not changing its name, which is the only thing the set + // cares about preserving. + EggMorphVertex &morph = (EggMorphVertex &)(*mi); + + morph.set_offset((*mi).get_offset() * mat); + } + + EggAttributes::transform(mat); +} + +#ifndef NDEBUG + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::test_gref_integrity +// Access: Public +// Description: Verifies that the gref list is correct and that all +// the groups included actually exist and do reference +// the vertex. +//////////////////////////////////////////////////////////////////// +void EggVertex:: +test_gref_integrity() const { + test_ref_count_integrity(); + + GroupRef::const_iterator gri; + + for (gri = gref_begin(); gri != gref_end(); ++gri) { + EggGroup *group = *gri; + nassertv(group != NULL); + group->test_ref_count_integrity(); + + double membership = group->get_vertex_membership(this); + nassertv(membership != 0.0); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::test_pref_integrity +// Access: Public +// Description: Verifies that the pref list is correct and that all +// the primitives included actually exist and do +// reference the vertex. +//////////////////////////////////////////////////////////////////// +void EggVertex:: +test_pref_integrity() const { + test_ref_count_integrity(); + + PrimitiveRef::const_iterator pri; + + for (pri = pref_begin(); pri != pref_end(); ++pri) { + EggPrimitive *prim = *pri; + nassertv(prim != NULL); + prim->test_ref_count_integrity(); + + EggPrimitive::iterator vi; + vi = find(prim->begin(), prim->end(), this); + nassertv(vi != prim->end()); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertex::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void EggVertex:: +output(ostream &out) const { + if (get_pool() == NULL) { + out << "(null):" << get_index(); + } else { + out << get_pool()->get_name() << ":" << get_index(); + } +} + +#endif // NDEBUG diff --git a/panda/src/egg/eggVertex.h b/panda/src/egg/eggVertex.h new file mode 100644 index 0000000000..fc0bfd505b --- /dev/null +++ b/panda/src/egg/eggVertex.h @@ -0,0 +1,148 @@ +// Filename: eggVertex.h +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGVERTEX_H +#define EGGVERTEX_H + +#include + +#include "eggObject.h" +#include "eggAttributes.h" +#include "eggMorphList.h" + +#include +#include +#include + +class EggVertexPool; +class EggGroup; +class EggPrimitive; + + +//////////////////////////////////////////////////////////////////// +// Class : EggVertex +// Description : Any one-, two-, three-, or four-component vertex, +// possibly with attributes such as a normal. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggVertex : public EggObject, public EggAttributes { +public: + typedef set GroupRef; + typedef multiset PrimitiveRef; + + EggVertex(); + EggVertex(const EggVertex ©); + EggVertex &operator = (const EggVertex ©); + ~EggVertex(); + + INLINE EggVertexPool *get_pool() const; + + // The pos might have 1, 2, 3, or 4 dimensions. That complicates + // things a bit. + INLINE void set_pos(double pos); + INLINE void set_pos(const LPoint2d &pos); + INLINE void set_pos(const LPoint3d &pos); + INLINE void set_pos(const LPoint4d &pos); + INLINE void set_pos4(const LPoint4d &pos); + + // get_pos[123] return the pos as the corresponding type. It is an + // error to call any of these without first verifying that + // get_num_dimensions() matches the desired type. However, + // get_pos4() may always be called; it returns the pos as a + // four-component point in homogeneous space (with a 1.0 in the last + // position if the pos has fewer than four components). + INLINE int get_num_dimensions() const; + INLINE double get_pos1() const; + INLINE LPoint2d get_pos2() const; + INLINE Vertexd get_pos3() const; + INLINE LPoint4d get_pos4() const; + + INLINE int get_index() const; + + void write(ostream &out, int indent_level) const; + bool sorts_less_than(const EggVertex &other) const; + + int get_num_local_coord() const; + int get_num_global_coord() const; + + void transform(const LMatrix4d &mat); + + + INLINE GroupRef::const_iterator gref_begin() const; + INLINE GroupRef::const_iterator gref_end() const; + INLINE GroupRef::size_type gref_size() const; + INLINE bool has_gref(const EggGroup *group) const; + + INLINE PrimitiveRef::const_iterator pref_begin() const; + INLINE PrimitiveRef::const_iterator pref_end() const; + INLINE PrimitiveRef::size_type pref_size() const; + INLINE int has_pref(const EggPrimitive *prim) const; + +#ifndef NDEBUG + void test_pref_integrity() const; + void test_gref_integrity() const; +#else + void test_gref_integrity() const { } + void test_pref_integrity() const { } +#endif // NDEBUG + + void output(ostream &out) const; + + EggMorphVertexList _dxyzs; + +private: + EggVertexPool *_pool; + int _index; + LPoint4d _pos; + short _num_dimensions; + GroupRef _gref; + PrimitiveRef _pref; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggObject::init_type(); + EggAttributes::init_type(); + register_type(_type_handle, "EggVertex", + EggObject::get_class_type(), + EggAttributes::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend class EggVertexPool; + friend class EggGroup; + friend class EggPrimitive; +}; + +INLINE ostream &operator << (ostream &out, const EggVertex &vert) { + vert.output(out); + return out; +} + +/////////////////////////////////////////////////////////////////// +// Class : UniqueEggVertices +// Description : An STL function object for sorting vertices into +// order by properties. Returns true if the two +// referenced EggVertex pointers are in sorted order, +// false otherwise. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG UniqueEggVertices { +public: + INLINE bool operator ()(const EggVertex *v1, const EggVertex *v2) const; +}; + +#include "eggVertex.I" + +#endif + + + diff --git a/panda/src/egg/eggVertexPool.I b/panda/src/egg/eggVertexPool.I new file mode 100644 index 0000000000..b7f99953ff --- /dev/null +++ b/panda/src/egg/eggVertexPool.I @@ -0,0 +1,54 @@ +// Filename: eggVertexPool.I +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + + +INLINE EggVertex *EggVertexPool:: +get_vertex(int index) const { + IndexVertices::const_iterator ivi = _index_vertices.find(index); + + if (ivi == _index_vertices.end()) { + return NULL; + } else { + return (*ivi).second; + } +} + +INLINE EggVertex *EggVertexPool:: +operator [](int index) const { + return get_vertex(index); +} + +INLINE int EggVertexPool:: +get_highest_index() const { + if (_index_vertices.empty()) { + return 0; + } + IndexVertices::const_reverse_iterator ivi = _index_vertices.rbegin(); + nassertr((*ivi).first == (*ivi).second->get_index(), 0); + return (*ivi).first; +} + +INLINE EggVertexPool::iterator EggVertexPool:: +begin() const { + nassertr(_index_vertices.size() == _unique_vertices.size(), + iterator(_index_vertices.begin())); + return iterator(_index_vertices.begin()); +} + +INLINE EggVertexPool::iterator EggVertexPool:: +end() const { + return iterator(_index_vertices.end()); +} + +INLINE bool EggVertexPool:: +empty() const { + return _index_vertices.empty(); +} + +INLINE EggVertexPool::size_type EggVertexPool:: +size() const { + nassertr(_index_vertices.size() == _unique_vertices.size(), 0); + return _index_vertices.size(); +} diff --git a/panda/src/egg/eggVertexPool.cxx b/panda/src/egg/eggVertexPool.cxx new file mode 100644 index 0000000000..4fe0323d96 --- /dev/null +++ b/panda/src/egg/eggVertexPool.cxx @@ -0,0 +1,284 @@ +// Filename: eggVertexPool.cxx +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggVertexPool.h" +#include "eggPrimitive.h" +#include "eggUtilities.h" + +#include + +TypeHandle EggVertexPool::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: EggVertexPool::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggVertexPool:: +EggVertexPool(const string &name) : EggNode(name) { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggVertexPool::Copy Constructor +// Access: Public +// Description: Copying a vertex pool is of questionable value, since +// it will copy all of the vertices and assign new +// pointers to them all. There will be no polygons +// referring to the new vertices. +//////////////////////////////////////////////////////////////////// +EggVertexPool:: +EggVertexPool(const EggVertexPool ©) : EggNode(copy) { + iterator i; + for (i = copy.begin(); i != copy.end(); ++i) { + add_vertex(new EggVertex(*(*i)), (*i)->get_index()); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertexPool::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggVertexPool:: +~EggVertexPool() { + // Remove all vertices from the pool when it destructs. + + // Sanity check. + nassertv(_index_vertices.size() == _unique_vertices.size()); + + iterator i; + for (i = begin(); i != end(); ++i) { + // Sanity checks on our internal data structures. + nassertv((*i)->_pool == this); + nassertv(get_vertex((*i)->_index) == (*i)); + + (*i)->_pool = NULL; + (*i)->_index = -1; + } + + _index_vertices.erase(_index_vertices.begin(), _index_vertices.end()); + _unique_vertices.erase(_unique_vertices.begin(), _unique_vertices.end()); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertexPool::add_vertex +// Access: Public +// Description: Adds the indicated vertex to the pool. It is an +// error if the vertex is already a member of this or +// any other pool. The vertex must have been allocated +// from the free store; its pointer will now be owned by +// the vertex pool. If the index number is supplied, +// tries to assign that index number; it is an error if +// the index number is already in use. +//////////////////////////////////////////////////////////////////// +void EggVertexPool:: +add_vertex(PT(EggVertex) vertex, int index) { + // Don't try to add a vertex while it still belongs to another pool. + nassertv(vertex->_pool == NULL); + + if (index == -1) { + index = get_highest_index() + 1; + } + // Always supply an index number >= 0. + nassertv(index >= 0); + + // Don't try to duplicate index numbers within a vertex pool. + nassertv(_index_vertices.find(index) == _index_vertices.end()); + + _unique_vertices.insert(vertex); + _index_vertices[index] = vertex; + + vertex->_pool = this; + vertex->_index = index; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertexPool::create_unique_vertex +// Access: Public +// Description: Creates a new vertex in the pool that is a copy of +// the indicated one and returns it. If there is +// already a vertex in the pool like the indicated one, +// simply returns that one. +//////////////////////////////////////////////////////////////////// +EggVertex *EggVertexPool:: +create_unique_vertex(const EggVertex ©) { + UniqueVertices::iterator uvi; + uvi = _unique_vertices.find((EggVertex *)©); + + if (uvi != _unique_vertices.end()) { + // There was already such a vertex. Return it. + return (*uvi); + } + + // Create a new vertex. + EggVertex *vtx = new EggVertex(copy); + add_vertex(vtx); + return vtx; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertexPool::remove_vertex +// Access: Public +// Description: Removes the vertex from the pool. It is an error if +// the vertex is not already a member of the pool. +//////////////////////////////////////////////////////////////////// +void EggVertexPool:: +remove_vertex(EggVertex *vertex) { + // Make sure the vertex is already a member of this pool. + nassertv(vertex->_pool == this); + + // Sanity check. Is the vertex actually in the pool? + nassertv(get_vertex(vertex->_index) == vertex); + + // Removing the vertex from the indexed list is simple. + _index_vertices.erase(vertex->_index); + + // Removing the vertex from the unique list is a bit trickier--there + // might be several other vertices that are considered identical to + // this one, and so we have to walk through all the identical + // vertices until we find the right one. + UniqueVertices::iterator uvi; + uvi = _unique_vertices.find(vertex); + + // Sanity check. Is the vertex actually in the pool? + nassertv(uvi != _unique_vertices.end()); + + while ((*uvi) != vertex) { + ++uvi; + // Sanity check. Is the vertex actually in the pool? + nassertv(uvi != _unique_vertices.end()); + } + + _unique_vertices.erase(uvi); + + vertex->_pool = NULL; +} + +// A function object for split_vertex(), used in transform(), below. +class IsLocalVertexSplitter { +public: + int operator () (const EggPrimitive *prim) const { + return (prim->is_local_coord() ? 1 : 0); + } +}; + +//////////////////////////////////////////////////////////////////// +// Function: EggVertexPool::transform +// Access: Public +// Description: Applies the indicated transformation matrix to all +// the vertices. However, vertices that are attached to +// primitives that believe their vertices are in a local +// coordinate system are transformed only by the scale +// and rotation component. If a vertex happens to be +// attached both to a local and a global primitive, and +// the transformation includes a translation component, +// the vertex will be split. +//////////////////////////////////////////////////////////////////// +void EggVertexPool:: +transform(const LMatrix4d &mat) { + LVector3d translation = mat.get_row3(3); + + if (translation == LVector3d(0.0, 0.0, 0.0)) { + // If the matrix does not have a translation component, we can + // treat the local and global vertices the same. This makes + // things much easier. + iterator i; + for (i = begin(); i != end(); ++i) { + EggVertex *vert = *i; + vert->transform(mat); + } + + } else { + // The matrix does have a translation component. That means we + // have to treat the global and local vertices differently. + // Yucky. + + // First, transform the global vertices. Get a copy of the list + // of vertices in this pool. We must have a copy because we might + // be modifying the list as we traverse it. + + typedef vector Verts; + Verts verts; + verts.reserve(size()); + copy(begin(), end(), back_inserter(verts)); + + Verts::const_iterator vi; + for (vi = verts.begin(); vi != verts.end(); ++vi) { + EggVertex *vert = *vi; + int num_local_coord = vert->get_num_local_coord(); + int num_global_coord = vert->get_num_global_coord(); + + if (num_global_coord != 0) { + // This vertex will be transformed. + if (num_local_coord != 0) { + // It also needs to be split! Yuck. + split_vertex(vert, IsLocalVertexSplitter()); + } + + vert->transform(mat); + } + } + + // Now transform the local vertices. We can walk through the list + // directly now, because we won't be modifying the list this time. + LMatrix4d local_mat = mat; + local_mat.set_row(3, LVector3d(0.0, 0.0, 0.0)); + + iterator i; + for (i = begin(); i != end(); ++i) { + EggVertex *vert = *i; + if (vert->get_num_local_coord() != 0) { + + // This should be guaranteed by the vertex-splitting logic + // above. + nassertv(vert->get_num_global_coord() == 0); + vert->transform(local_mat); + } + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertexPool::write +// Access: Public +// Description: Writes the vertex pool to the indicated output stream +// in Egg format. +//////////////////////////////////////////////////////////////////// +void EggVertexPool:: +write(ostream &out, int indent_level) const { + write_header(out, indent_level, ""); + + iterator i; + for (i = begin(); i != end(); ++i) { + (*i)->write(out, indent_level+2); + } + + indent(out, indent_level) + << "}\n"; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggVertexPool::r_transform +// Access: Protected, Virtual +// Description: This is called from within the egg code by +// transform(). It applies a transformation matrix +// to the current node in some sensible way, then +// continues down the tree. +// +// The first matrix is the transformation to apply; the +// second is its inverse. The third parameter is the +// coordinate system we are changing to, or CS_default +// if we are not changing coordinate systems. +//////////////////////////////////////////////////////////////////// +void EggVertexPool:: +r_transform(const LMatrix4d &mat, const LMatrix4d &, CoordinateSystem) { + transform(mat); +} diff --git a/panda/src/egg/eggVertexPool.h b/panda/src/egg/eggVertexPool.h new file mode 100644 index 0000000000..67d772c849 --- /dev/null +++ b/panda/src/egg/eggVertexPool.h @@ -0,0 +1,126 @@ +// Filename: eggVertexPool.h +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGVERTEXPOOL_H +#define EGGVERTEXPOOL_H + +#include + +#include "eggVertex.h" +#include "eggNode.h" + +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : EggVertexPool +// Description : A collection of vertices. There may be any number of +// vertex pools in a single egg structure. The vertices +// in a single pool need not necessarily have any +// connection to each other, but it is necessary that +// any one primitive (e.g. a polygon) must pull all its +// vertices from the same pool. +// +// An EggVertexPool is an STL-style container of +// pointers to EggVertex's. Functions add_vertex() and +// remove_vertex() are provided to manipulate the list. +// The list may also be operated on (read-only) via +// iterators and begin()/end(). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggVertexPool : public EggNode { + + // This is a bit of private interface stuff that must be here as a + // forward reference. This allows us to define the EggVertexPool as + // an STL container. + +private: + // IndexVertices is the main storage mechanism of the vertex pool. + // It stores a reference-counting pointer to each vertex, ordered by + // vertex index number. + typedef map IndexVertices; + + // UniqueVertices is an auxiliary indexing mechanism. It stores the + // same vertex pointers as IndexVertices (although these pointers + // are not reference-counted), this time ordered by vertex + // properties. This makes it easy to determine when one or more + // vertices already exist in the pool with identical properties. + typedef multiset UniqueVertices; + +public: + typedef second_of_pair_iterator iterator; + typedef iterator const_iterator; + typedef IndexVertices::size_type size_type; + + // Here begins the actual public interface to EggVertexPool. + +public: + EggVertexPool(const string &name); + EggVertexPool(const EggVertexPool ©); + ~EggVertexPool(); + + // Returns NULL if there is no such vertex. + INLINE EggVertex *get_vertex(int index) const; + EggVertex *operator [](int index) const; + + // Returns 0 if the pool is empty. + INLINE int get_highest_index() const; + + // Can be used to traverse all the vertices in index number order. + INLINE iterator begin() const; + INLINE iterator end() const; + INLINE bool empty() const; + INLINE size_type size() const; + + // add_vertex() adds a freshly-allocated vertex. It is up to the + // user to allocate the vertex. + void add_vertex(PT(EggVertex) vertex, int index = -1); + + // create_unique_vertex() creates a new vertex if there is not + // already one identical to the indicated vertex, or returns the + // existing one if there is. + EggVertex *create_unique_vertex(const EggVertex ©); + + void remove_vertex(EggVertex *vertex); + + void transform(const LMatrix4d &mat); + + void write(ostream &out, int indent_level) const; + +protected: + virtual void r_transform(const LMatrix4d &mat, const LMatrix4d &inv, + CoordinateSystem to_cs); + +private: + UniqueVertices _unique_vertices; + IndexVertices _index_vertices; + + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggNode::init_type(); + register_type(_type_handle, "EggVertexPool", + EggNode::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + +friend class EggVertex; +}; + +#include "eggVertexPool.I" + +#endif diff --git a/panda/src/egg/eggXfmAnimData.I b/panda/src/egg/eggXfmAnimData.I new file mode 100644 index 0000000000..42138381ea --- /dev/null +++ b/panda/src/egg/eggXfmAnimData.I @@ -0,0 +1,186 @@ +// Filename: eggXfmAnimData.I +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggXfmAnimData:: +EggXfmAnimData(const string &name, CoordinateSystem cs) : EggAnimData(name) { + _coordsys = cs; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggXfmAnimData:: +EggXfmAnimData(const EggXfmAnimData ©) + : EggAnimData(copy), + _order(copy._order), + _contents(copy._contents), + _coordsys(copy._coordsys) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggXfmAnimData &EggXfmAnimData:: +operator = (const EggXfmAnimData ©) { + EggAnimData::operator = (copy); + _order = copy._order; + _contents = copy._contents; + _coordsys = copy._coordsys; + + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::set_order +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggXfmAnimData:: +set_order(const string &order) { + _order = order; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::clear_order +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggXfmAnimData:: +clear_order() { + _order = ""; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::has_order +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggXfmAnimData:: +has_order() const { + return !_order.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::get_order +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const string &EggXfmAnimData:: +get_order() const { + return _order; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::set_contents +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggXfmAnimData:: +set_contents(const string &contents) { + _contents = contents; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::clear_contents +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggXfmAnimData:: +clear_contents() { + _contents = ""; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::has_contents +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggXfmAnimData:: +has_contents() const { + return !_contents.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::get_contents +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const string &EggXfmAnimData:: +get_contents() const { + return _contents; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::get_coordinate_system +// Access: Public +// Description: Returns the coordinate system this table believes it +// is defined within. This should always match the +// coordinate system of the EggData structure that owns +// it. It is necessary to store it here because the +// meaning of the h, p, and r columns depends on the +// coordinate system. +//////////////////////////////////////////////////////////////////// +INLINE CoordinateSystem EggXfmAnimData:: +get_coordinate_system() const { + return _coordsys; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::get_num_rows +// Access: Public +// Description: Returns the number of rows in the table. +//////////////////////////////////////////////////////////////////// +INLINE int EggXfmAnimData:: +get_num_rows() const { + if (get_num_cols() == 0) { + return 0; + } + return get_size() / get_num_cols(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::get_num_cols +// Access: Public +// Description: Returns the number of columns in the table. This is +// set according to the "contents" string, which defines +// the meaning of each column. +//////////////////////////////////////////////////////////////////// +INLINE int EggXfmAnimData:: +get_num_cols() const { + return _contents.length(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::get_value +// Access: Public +// Description: Returns the value at the indicated row. Row must be +// in the range 0 <= row < get_num_rows(); col must be +// in the range 0 <= col < get_num_cols(). +//////////////////////////////////////////////////////////////////// +INLINE double EggXfmAnimData:: +get_value(int row, int col) const { + nassertr(get_num_cols() != 0, 0.0); + nassertr(row >= 0 && row < get_num_rows(), 0.0); + nassertr(col >= 0 && col < get_num_cols(), 0.0); + return _data[row * get_num_cols() + col]; +} + + diff --git a/panda/src/egg/eggXfmAnimData.cxx b/panda/src/egg/eggXfmAnimData.cxx new file mode 100644 index 0000000000..1e6fd0e0d5 --- /dev/null +++ b/panda/src/egg/eggXfmAnimData.cxx @@ -0,0 +1,255 @@ +// Filename: eggXfmAnimData.cxx +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggXfmAnimData.h" +#include "eggXfmSAnim.h" +#include "eggSAnimData.h" +#include "eggMiscFuncs.h" +#include "config_egg.h" + +#include +#include +#include +#include + +TypeHandle EggXfmAnimData::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::Conversion constructor +// Access: Public +// Description: Converts the newer-style XfmSAnim table to the +// older-style XfmAnim table. +//////////////////////////////////////////////////////////////////// +EggXfmAnimData:: +EggXfmAnimData(const EggXfmSAnim &convert_from) + : EggAnimData(convert_from.get_name()) +{ + if (convert_from.has_order()) { + set_order(convert_from.get_order()); + } + if (convert_from.has_fps()) { + set_fps(convert_from.get_fps()); + } + _coordsys = convert_from.get_coordinate_system(); + + // First, get the table names so we know how to build up our + // contents string. Also store up the SAnim tables themselves in a + // temporary vector for convenience. + + vector subtables; + + EggXfmSAnim::const_iterator ci; + for (ci = convert_from.begin(); ci != convert_from.end(); ++ci) { + if ((*ci)->is_of_type(EggSAnimData::get_class_type())) { + EggSAnimData *sanim = DCAST(EggSAnimData, *ci); + nassertv(sanim->get_name().length() == 1); + + if (sanim->get_num_rows() > 0) { + subtables.push_back(sanim); + _contents += sanim->get_name()[0]; + } + } + } + + // Now, go through and extract out all the data. + int num_rows = convert_from.get_num_rows(); + for (int row = 0; row < num_rows; row++) { + for (int col = 0; col < subtables.size(); col++) { + EggSAnimData *sanim = subtables[col]; + if (sanim->get_num_rows() == 1) { + add_data(sanim->get_value(0)); + } else { + nassertv(row < sanim->get_num_rows()); + add_data(sanim->get_value(row)); + } + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::get_value +// Access: Public +// Description: Returns the value of the aggregate row of the table +// as a matrix. This is a convenience function that +// treats the 2-d table as if it were a single table of +// matrices. +//////////////////////////////////////////////////////////////////// +void EggXfmAnimData:: +get_value(int row, LMatrix4d &mat) const { + LVector3d scale(1.0, 1.0, 1.0); + LVector3d hpr(0.0, 0.0, 0.0); + LVector3d translate(0.0, 0.0, 0.0); + + for (int col = 0; col < get_num_cols(); col++) { + double value = get_value(row, col); + + switch (_contents[col]) { + case 'i': + scale[0] = value; + break; + + case 'j': + scale[1] = value; + break; + + case 'k': + scale[2] = value; + break; + + case 'h': + hpr[0] = value; + break; + + case 'p': + hpr[1] = value; + break; + + case 'r': + hpr[2] = value; + break; + + case 'x': + translate[0] = value; + break; + + case 'y': + translate[1] = value; + break; + + case 'z': + translate[2] = value; + break; + + default: + // The contents string contained an invalid letter. + nassertv(false); + } + } + + // So now we've got the nine components; build a matrix. + compose_matrix(mat, scale, hpr, translate, _coordsys); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::write +// Access: Public, Virtual +// Description: Writes the data to the indicated output stream in Egg +// format. +//////////////////////////////////////////////////////////////////// +void EggXfmAnimData:: +write(ostream &out, int indent_level) const { + write_header(out, indent_level, ""); + + if (has_fps()) { + indent(out, indent_level + 2) + << " fps { " << get_fps() << " }\n"; + } + + if (has_order()) { + indent(out, indent_level + 2) + << " order { " << get_order() << " }\n"; + } + + if (has_contents()) { + indent(out, indent_level + 2) + << " contents { " << get_contents() << " }\n"; + } + + indent(out, indent_level + 2) << " {\n"; + write_long_list(out, indent_level + 4, _data.begin(), _data.end(), + "", "", 72); + indent(out, indent_level + 2) << "}\n"; + indent(out, indent_level) << "}\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::r_transform +// Access: Protected, Virtual +// Description: Applies the indicated transform to all the rows of +// the table. This actually forces the generation of a +// totally new set of rows. +//////////////////////////////////////////////////////////////////// +void EggXfmAnimData:: +r_transform(const LMatrix4d &mat, const LMatrix4d &inv, + CoordinateSystem to_cs) { + // We need to build an inverse matrix that doesn't reflect the + // translation component. + LMatrix4d inv1 = inv; + inv1.set_row(3, LVector3d(0.0, 0.0, 0.0)); + + // Now we build a temporary copy of the table as an EggXfmSAnim. We + // do this because this kind of table is easier to build and + // optimize. + + if (to_cs == CS_default) { + to_cs = _coordsys; + } + + EggXfmSAnim new_table(get_name(), to_cs); + if (has_fps()) { + new_table.set_fps(get_fps()); + } + if (has_order()) { + new_table.set_order(get_order()); + } + + // Now build up the data into the new table. + LMatrix4d orig_mat; + for (int r = 0; r < get_num_rows(); r++) { + get_value(r, orig_mat); + bool result = new_table.add_data(inv1 * orig_mat * mat); + + if (!result) { + egg_cat.error() + << "Transform from " << _coordsys << " to " << to_cs + << " failed!\n"; + LVector3d scale, hpr, trans; + bool d = decompose_matrix(orig_mat, scale, hpr, trans, _coordsys); + egg_cat.error(false) + << "orig:\n" << orig_mat + << "d = " << d + << "\n scale: " << scale + << "\n hpr: " << hpr + << "\n trans: " << trans << "\n"; + + LMatrix4d new_mat = inv1 * orig_mat * mat; + d = decompose_matrix(new_mat, scale, hpr, trans, to_cs); + egg_cat.error(false) + << "new:\n" << new_mat + << "d = " << d + << "\n scale: " << scale + << "\n hpr: " << hpr + << "\n trans: " << trans << "\n"; + } + + // If this assertion fails, we attempted to transform by a skew + // matrix or some such thing that cannot be represented in an anim + // file. + nassertv(result); + } + + // Now clean out the redundant columns we created. + new_table.optimize(); + + // And copy that back into EggXfmAnimData table form. + EggXfmAnimData copy_table(new_table); + (*this) = copy_table; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmAnimData::r_mark_coordsys +// Access: Protected, Virtual +// Description: This is only called immediately after loading an egg +// file from disk, to propagate the value found in the +// CoordinateSystem entry (or the default Y-up +// coordinate system) to all nodes that care about what +// the coordinate system is. +//////////////////////////////////////////////////////////////////// +void EggXfmAnimData:: +r_mark_coordsys(CoordinateSystem cs) { + _coordsys = cs; +} diff --git a/panda/src/egg/eggXfmAnimData.h b/panda/src/egg/eggXfmAnimData.h new file mode 100644 index 0000000000..bc70ec6798 --- /dev/null +++ b/panda/src/egg/eggXfmAnimData.h @@ -0,0 +1,84 @@ +// Filename: eggXfmAnimData.h +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGXFMANIMDATA_H +#define EGGXFMANIMDATA_H + +#include + +#include "eggAnimData.h" + +class EggXfmSAnim; + +//////////////////////////////////////////////////////////////////// +// Class : EggXfmAnimData +// Description : Corresponding to an entry, this stores a +// two-dimensional table with up to nine columns, one +// for each component of a transformation. This is an +// older syntax of egg anim table, not often used +// currently--it's replaced by EggXfmSAnim. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggXfmAnimData : public EggAnimData { +public: + INLINE EggXfmAnimData(const string &name = "", + CoordinateSystem cs = CS_default); + EggXfmAnimData(const EggXfmSAnim &convert_from); + + INLINE EggXfmAnimData(const EggXfmAnimData ©); + INLINE EggXfmAnimData &operator = (const EggXfmAnimData ©); + + INLINE void set_order(const string &order); + INLINE void clear_order(); + INLINE bool has_order() const; + INLINE const string &get_order() const; + + INLINE void set_contents(const string &contents); + INLINE void clear_contents(); + INLINE bool has_contents() const; + INLINE const string &get_contents() const; + + INLINE CoordinateSystem get_coordinate_system() const; + + INLINE int get_num_rows() const; + INLINE int get_num_cols() const; + INLINE double get_value(int row, int col) const; + + void get_value(int row, LMatrix4d &mat) const; + + virtual void write(ostream &out, int indent_level) const; + +protected: + virtual void r_transform(const LMatrix4d &mat, const LMatrix4d &inv, + CoordinateSystem to_cs); + virtual void r_mark_coordsys(CoordinateSystem cs); + +private: + string _order; + string _contents; + CoordinateSystem _coordsys; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggAnimData::init_type(); + register_type(_type_handle, "EggXfmAnimData", + EggAnimData::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "eggXfmAnimData.I" + +#endif + diff --git a/panda/src/egg/eggXfmSAnim.I b/panda/src/egg/eggXfmSAnim.I new file mode 100644 index 0000000000..e4951f7d1f --- /dev/null +++ b/panda/src/egg/eggXfmSAnim.I @@ -0,0 +1,147 @@ +// Filename: eggXfmSAnim.I +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggXfmSAnim:: +EggXfmSAnim(const string &name, CoordinateSystem cs) : EggGroupNode(name) { + _has_fps = false; + _coordsys = cs; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggXfmSAnim:: +EggXfmSAnim(const EggXfmSAnim ©) + : EggGroupNode(copy), + _fps(copy._fps), + _has_fps(copy._has_fps), + _order(copy._order), + _coordsys(copy._coordsys) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggXfmSAnim &EggXfmSAnim:: +operator = (const EggXfmSAnim ©) { + EggGroupNode::operator = (copy); + _fps = copy._fps; + _has_fps = copy._has_fps; + _order = copy._order; + _coordsys = copy._coordsys; + + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::set_fps +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggXfmSAnim:: +set_fps(double fps) { + _fps = fps; + _has_fps = true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::clear_fps +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggXfmSAnim:: +clear_fps() { + _has_fps = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::has_fps +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggXfmSAnim:: +has_fps() const { + return _has_fps; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::get_fps +// Access: Public +// Description: This is only valid if has_fps() returns true. +//////////////////////////////////////////////////////////////////// +INLINE double EggXfmSAnim:: +get_fps() const { + nassertr(has_fps(), 0.0); + return _fps; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::set_order +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggXfmSAnim:: +set_order(const string &order) { + _order = order; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::clear_order +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggXfmSAnim:: +clear_order() { + _order = ""; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::has_order +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggXfmSAnim:: +has_order() const { + return !_order.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::get_order +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const string &EggXfmSAnim:: +get_order() const { + return _order; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::get_coordinate_system +// Access: Public +// Description: Returns the coordinate system this table believes it +// is defined within. This should always match the +// coordinate system of the EggData structure that owns +// it. It is necessary to store it here because the +// meaning of the h, p, and r columns depends on the +// coordinate system. +//////////////////////////////////////////////////////////////////// +INLINE CoordinateSystem EggXfmSAnim:: +get_coordinate_system() const { + return _coordsys; +} diff --git a/panda/src/egg/eggXfmSAnim.cxx b/panda/src/egg/eggXfmSAnim.cxx new file mode 100644 index 0000000000..92a44f51d4 --- /dev/null +++ b/panda/src/egg/eggXfmSAnim.cxx @@ -0,0 +1,403 @@ +// Filename: eggXfmSAnim.cxx +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggXfmSAnim.h" +#include "eggSAnimData.h" +#include "eggXfmAnimData.h" +#include "eggParameters.h" + +#include +#include + +#include + +TypeHandle EggXfmSAnim::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::Conversion constructor +// Access: Public +// Description: Converts the older-style XfmAnim table to the +// newer-style XfmSAnim table. +//////////////////////////////////////////////////////////////////// +EggXfmSAnim:: +EggXfmSAnim(const EggXfmAnimData &convert_from) + : EggGroupNode(convert_from.get_name()) +{ + _has_fps = false; + _coordsys = convert_from.get_coordinate_system(); + + if (convert_from.has_order()) { + set_order(convert_from.get_order()); + } + if (convert_from.has_fps()) { + set_fps(convert_from.get_fps()); + } + + const string &contents = convert_from.get_contents(); + for (int col = 0; col < convert_from.get_num_cols(); col++) { + EggSAnimData *sanim = new EggSAnimData(contents.substr(col, 1)); + add_child(sanim); + for (int row = 0; row < convert_from.get_num_rows(); row++) { + sanim->add_data(convert_from.get_value(row, col)); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::optimize +// Access: Public +// Description: Optimizes the table by collapsing redundant +// sub-tables. +//////////////////////////////////////////////////////////////////// +void EggXfmSAnim:: +optimize() { + iterator ci = begin(); + while (ci != end()) { + iterator ci_next = ci; + ++ci_next; + + if ((*ci)->is_of_type(EggSAnimData::get_class_type())) { + EggSAnimData *sanim = DCAST(EggSAnimData, *ci); + sanim->optimize(); + + if (sanim->get_num_rows() == 1) { + // If we've optimized down to one value, check to see if it is + // a default value. + double value = sanim->get_value(0); + double default_value; + if (sanim->has_name() && strchr("ijk", sanim->get_name()[0]) != NULL) { + default_value = 1.0; + } else { + default_value = 0.0; + } + + if (fabs(value - default_value) < egg_parameters->_table_threshold) { + // It's a default-valued table, and therefore redundant: + // remove it. + erase(ci); + } + } + } + + ci = ci_next; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::write +// Access: Public, Virtual +// Description: Writes the data to the indicated output stream in Egg +// format. +//////////////////////////////////////////////////////////////////// +void EggXfmSAnim:: +write(ostream &out, int indent_level) const { + test_under_integrity(); + + write_header(out, indent_level, ""); + + if (has_fps()) { + indent(out, indent_level + 2) << " fps { " << get_fps() << " }\n"; + } + + if (has_order()) { + indent(out, indent_level + 2) + << " order { " << get_order() << " }\n"; + } + + EggGroupNode::write(out, indent_level + 2); + indent(out, indent_level) << "}\n"; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::get_num_rows +// Access: Public +// Description: Returns the effective number of rows in the table. +// This is actually the number of rows of the smallest +// subtable larger than one row. This is a convenience +// function that treats the table of tables as if it +// were a single table of matrices. +//////////////////////////////////////////////////////////////////// +int EggXfmSAnim:: +get_num_rows() const { + bool found_any = false; + int min_rows = 1; + + const_iterator ci; + for (ci = begin(); ci != end(); ++ci) { + if ((*ci)->is_of_type(EggSAnimData::get_class_type())) { + EggSAnimData *sanim = DCAST(EggSAnimData, *ci); + if (sanim->get_num_rows() > 1) { + if (!found_any) { + min_rows = sanim->get_num_rows(); + + } else { + min_rows = min(min_rows, sanim->get_num_rows()); + } + } + } + } + + return min_rows; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::get_value +// Access: Public +// Description: Returns the value of the aggregate row of the table +// as a matrix. This is a convenience function that +// treats the table of tables as if it were a single +// table of matrices. It is an error to call this if +// any SAnimData children of this node have an improper +// name (e.g. not a single letter, or not one of +// "ijkphrxyz"). +//////////////////////////////////////////////////////////////////// +void EggXfmSAnim:: +get_value(int row, LMatrix4d &mat) const { + LVector3d scale(1.0, 1.0, 1.0); + LVector3d hpr(0.0, 0.0, 0.0); + LVector3d translate(0.0, 0.0, 0.0); + + const_iterator ci; + for (ci = begin(); ci != end(); ++ci) { + if ((*ci)->is_of_type(EggSAnimData::get_class_type())) { + EggSAnimData *sanim = DCAST(EggSAnimData, *ci); + + if (sanim->get_num_rows() == 0) { + // If the table is totally empty, let's keep the default + // value. + break; + } + + double value; + if (sanim->get_num_rows() == 1) { + value = sanim->get_value(0); + } else { + nassertv(row < sanim->get_num_rows()); + value = sanim->get_value(row); + } + + // Each child SAnimData table should have a one-letter name. + nassertv(sanim->get_name().length() == 1); + + switch (sanim->get_name()[0]) { + case 'i': + scale[0] = value; + break; + + case 'j': + scale[1] = value; + break; + + case 'k': + scale[2] = value; + break; + + case 'h': + hpr[0] = value; + break; + + case 'p': + hpr[1] = value; + break; + + case 'r': + hpr[2] = value; + break; + + case 'x': + translate[0] = value; + break; + + case 'y': + translate[1] = value; + break; + + case 'z': + translate[2] = value; + break; + + default: + // One of the child tables had an invalid name. + nassertv(false); + } + } + } + + // So now we've got the nine components; build a matrix. + compose_matrix(mat, scale, hpr, translate, _coordsys); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::add_data +// Access: Public +// Description: Adds a new matrix to the table, by adding a new row +// to each of the subtables. +// +// This is a convenience function that +// treats the table of tables as if it were a single +// table of matrices. It is an error to call this if +// any SAnimData children of this node have an improper +// name (e.g. not a single letter, or not one of +// "ijkphrxyz"). +// +// This function has the further requirement that all +// nine of the subtables must exist and be of the same +// length. Thus, you probably cannot take an existing +// EggXfmSAnim object and start adding matrices to the +// end; you must clear out the original data first. (As +// a special exception, if no tables exist, they will be +// created.) +// +// This function may fail silently if the matrix cannot +// be decomposed into scale, rotate, and translate. In +// this case, nothing is done and the function returns +// false. +//////////////////////////////////////////////////////////////////// +bool EggXfmSAnim:: +add_data(const LMatrix4d &mat) { + LVector3d scale, hpr, translate; + bool result = decompose_matrix(mat, scale, hpr, translate, _coordsys); + if (!result) { + return false; + } + + if (empty()) { + // If we have no children, create all nine tables now. + const char *table_ids = "ijkphrxyz"; + for (const char *p = table_ids; *p; p++) { + EggSAnimData *sanim = new EggSAnimData(string(1, *p)); + add_child(sanim); + } + } + +#ifndef NDEBUG + int table_length = -1; + int num_tables = 0; +#endif + + const_iterator ci; + for (ci = begin(); ci != end(); ++ci) { + if ((*ci)->is_of_type(EggSAnimData::get_class_type())) { + EggSAnimData *sanim = DCAST(EggSAnimData, *ci); + +#ifndef NDEBUG + num_tables++; + + // Each table must have the same length. + if (table_length < 0) { + table_length = sanim->get_num_rows(); + } else { + nassertr(sanim->get_num_rows() == table_length, false); + } +#endif + + // Each child SAnimData table should have a one-letter name. + nassertr(sanim->get_name().length() == 1, false); + + switch (sanim->get_name()[0]) { + case 'i': + sanim->add_data(scale[0]); + break; + + case 'j': + sanim->add_data(scale[1]); + break; + + case 'k': + sanim->add_data(scale[2]); + break; + + case 'h': + sanim->add_data(hpr[0]); + break; + + case 'p': + sanim->add_data(hpr[1]); + break; + + case 'r': + sanim->add_data(hpr[2]); + break; + + case 'x': + sanim->add_data(translate[0]); + break; + + case 'y': + sanim->add_data(translate[1]); + break; + + case 'z': + sanim->add_data(translate[2]); + break; + + default: + // One of the child tables had an invalid name. + nassertr(false, false); + } + } + } + + nassertr(num_tables == 9, false); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::r_transform +// Access: Protected, Virtual +// Description: Applies the indicated transform to all the rows of +// the table. This actually forces the generation of a +// totally new set of rows. +//////////////////////////////////////////////////////////////////// +void EggXfmSAnim:: +r_transform(const LMatrix4d &mat, const LMatrix4d &inv, + CoordinateSystem to_cs) { + // We need to build an inverse matrix that doesn't reflect the + // translation component. + LMatrix4d inv1 = inv; + inv1.set_row(3, LVector3d(0.0, 0.0, 0.0)); + + // Save a temporary copy of the original data. + EggXfmSAnim original; + original.steal_children(*this); + original = (*this); + + // Now we have no children, so our data is clear. Rebuild it. + if (to_cs != CS_default) { + _coordsys = to_cs; + } + + int num_rows = original.get_num_rows(); + LMatrix4d orig_mat; + for (int r = 0; r < num_rows; r++) { + original.get_value(r, orig_mat); + bool result = add_data(inv1 * orig_mat * mat); + + // If this assertion fails, we attempted to transform by a skew + // matrix or some such thing that cannot be represented in an anim + // file. + nassertv(result); + } + + // Now clean out the redundant columns we created. + optimize(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggXfmSAnim::r_mark_coordsys +// Access: Protected, Virtual +// Description: This is only called immediately after loading an egg +// file from disk, to propagate the value found in the +// CoordinateSystem entry (or the default Y-up +// coordinate system) to all nodes that care about what +// the coordinate system is. +//////////////////////////////////////////////////////////////////// +void EggXfmSAnim:: +r_mark_coordsys(CoordinateSystem cs) { + _coordsys = cs; +} diff --git a/panda/src/egg/eggXfmSAnim.h b/panda/src/egg/eggXfmSAnim.h new file mode 100644 index 0000000000..0cb83182b0 --- /dev/null +++ b/panda/src/egg/eggXfmSAnim.h @@ -0,0 +1,87 @@ +// Filename: eggXfmSAnim.h +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGXFMSANIM_H +#define EGGXFMSANIM_H + + +#include + +#include "eggGroupNode.h" + +class EggXfmAnimData; + +//////////////////////////////////////////////////////////////////// +// Class : EggXfmSAnim +// Description : This corresponds to an entry, which is +// a collection of up to nine entries that +// specify the nine components of a transformation. +// It's implemented as a group that can contain +// any number of EggSAnimData children. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggXfmSAnim : public EggGroupNode { +public: + INLINE EggXfmSAnim(const string &name = "", + CoordinateSystem cs = CS_default); + EggXfmSAnim(const EggXfmAnimData &convert_from); + + INLINE EggXfmSAnim(const EggXfmSAnim ©); + INLINE EggXfmSAnim &operator = (const EggXfmSAnim ©); + + INLINE void set_fps(double type); + INLINE void clear_fps(); + INLINE bool has_fps() const; + INLINE double get_fps() const; + + INLINE void set_order(const string &order); + INLINE void clear_order(); + INLINE bool has_order() const; + INLINE const string &get_order() const; + + INLINE CoordinateSystem get_coordinate_system() const; + + void optimize(); + + int get_num_rows() const; + void get_value(int row, LMatrix4d &mat) const; + bool add_data(const LMatrix4d &mat); + + virtual void write(ostream &out, int indent_level) const; + +protected: + virtual void r_transform(const LMatrix4d &mat, const LMatrix4d &inv, + CoordinateSystem to_cs); + virtual void r_mark_coordsys(CoordinateSystem cs); + +private: + double _fps; + bool _has_fps; + string _order; + CoordinateSystem _coordsys; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggGroupNode::init_type(); + register_type(_type_handle, "EggXfmSAnim", + EggGroupNode::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "eggXfmSAnim.I" + +#endif + + diff --git a/panda/src/egg/lexer.lxx b/panda/src/egg/lexer.lxx new file mode 100644 index 0000000000..af5385c228 --- /dev/null +++ b/panda/src/egg/lexer.lxx @@ -0,0 +1,697 @@ +/* +// Filename: lexer.l +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// +*/ + +%{ +#include "lexerDefs.h" +#include "parserDefs.h" +#include "config_egg.h" +#include "parser.h" + +#include +#include + +#include +#include + +extern "C" int eggyywrap(void); // declared below. + +static int yyinput(void); // declared by flex. + + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// + +// We'll increment line_number and col_number as we parse the file, so +// that we can report the position of an error. +static int line_number = 0; +static int col_number = 0; + +// current_line holds as much of the current line as will fit. Its +// only purpose is for printing it out to report an error to the user. +static const int max_error_width = 1024; +static char current_line[max_error_width + 1]; + +static int error_count = 0; +static int warning_count = 0; + +// This is the pointer to the current input stream. +static istream *inp = NULL; + +// This is the name of the egg file we're parsing. We keep it so we +// can print it out for error messages. +static string egg_filename; + +// This is the initial token state returned by the lexer. It allows +// the yacc grammar to start from initial points. +static int initial_token; + + +//////////////////////////////////////////////////////////////////// +// Defining the interface to the lexer. +//////////////////////////////////////////////////////////////////// + +void +egg_init_lexer(istream &in, const string &filename) { + inp = ∈ + egg_filename = filename; + line_number = 0; + col_number = 0; + error_count = 0; + warning_count = 0; + initial_token = START_EGG; +} + +void +egg_start_group_body() { + /* Set the initial state to begin within a group_body context, + instead of at the beginning of the egg file. */ + initial_token = START_GROUP_BODY; +} + +int +egg_error_count() { + return error_count; +} + +int +egg_warning_count() { + return warning_count; +} + + +//////////////////////////////////////////////////////////////////// +// Internal support functions. +//////////////////////////////////////////////////////////////////// + +int +eggyywrap(void) { + return 1; +} + +void +eggyyerror(const string &msg) { + if (egg_cat.is_error()) { + ostream &out = egg_cat.error(false); + + out << "\nError"; + if (!egg_filename.empty()) { + out << " in " << egg_filename; + } + out + << " at line " << line_number << ", column " << col_number << ":\n" + << setiosflags(Notify::get_literal_flag()) + << current_line << "\n"; + indent(out, col_number-1) + << "^\n" << msg << "\n\n" + << resetiosflags(Notify::get_literal_flag()) << flush; + } + error_count++; +} + +void +eggyyerror(ostringstream &strm) { + string s = strm.str(); + eggyyerror(s); +} + +void +eggyywarning(const string &msg) { + if (egg_cat.is_warning()) { + ostream &out = egg_cat.warning(false); + + out << "\nWarning"; + if (!egg_filename.empty()) { + out << " in " << egg_filename; + } + out + << " at line " << line_number << ", column " << col_number << ":\n" + << setiosflags(Notify::get_literal_flag()) + << current_line << "\n"; + indent(out, col_number-1) + << "^\n" << msg << "\n\n" + << resetiosflags(Notify::get_literal_flag()) << flush; + } + warning_count++; +} + +void +eggyywarning(ostringstream &strm) { + string s = strm.str(); + eggyywarning(s); +} + +// Now define a function to take input from an istream instead of a +// stdio FILE pointer. This is flex-specific. +static void +input_chars(char *buffer, int &result, int max_size) { + nassertv(inp != NULL); + if (*inp) { + inp->read(buffer, max_size); + result = inp->gcount(); + + if (line_number == 0) { + // This is a special case. If we are reading the very first bit + // from the stream, copy it into the current_line array. This + // is because the \n.* rule below, which fills current_line + // normally, doesn't catch the first line. + strncpy(current_line, yytext, max_error_width); + current_line[max_error_width] = '\0'; + line_number++; + col_number = 0; + + // Truncate it at the newline. + char *end = strchr(current_line, '\n'); + if (end != NULL) { + *end = '\0'; + } + } + + } else { + // End of file or I/O error. + result = 0; + } +} +#undef YY_INPUT +#define YY_INPUT(buffer, result, max_size) input_chars(buffer, result, max_size) + +// read_char reads and returns a single character, incrementing the +// supplied line and column numbers as appropriate. A convenience +// function for the scanning functions below. +static int +read_char(int &line, int &col) { + int c = yyinput(); + if (c == '\n') { + line++; + col = 0; + } else { + col++; + } + return c; +} + +// scan_quoted_string reads a string delimited by quotation marks and +// returns it. +static string +scan_quoted_string() { + string result; + + // We don't touch the current line number and column number during + // scanning, so that if we detect an error while scanning the string + // (e.g. an unterminated string), we'll report the error as + // occurring at the start of the string, not at the end--somewhat + // more convenient for the user. + + // Instead of adjusting the global line_number and col_number + // variables, we'll operate on our own local variables for the + // interim. + int line = line_number; + int col = col_number; + + int c; + c = read_char(line, col); + while (c != '"' && c != EOF) { + result += c; + c = read_char(line, col); + } + + if (c == EOF) { + eggyyerror("This quotation mark is unterminated."); + } + + line_number = line; + col_number = col; + + return result; +} + +// eat_c_comment scans past all characters up until the first */ +// encountered. +static void +eat_c_comment() { + // As above, we'll operate on our own local copies of line_number + // and col_number within this function. + + int line = line_number; + int col = col_number; + + int c, last_c; + + last_c = '\0'; + c = read_char(line, col); + while (c != EOF && !(last_c == '*' && c == '/')) { + if (last_c == '/' && c == '*') { + ostringstream errmsg; + errmsg << "This comment contains a nested /* symbol at line " + << line << ", column " << col-1 << "--possibly unclosed?" + << ends; + eggyywarning(errmsg); + } + last_c = c; + c = read_char(line, col); + } + + if (c == EOF) { + eggyyerror("This comment marker is unclosed."); + } + + line_number = line; + col_number = col; +} + + + +// accept() is called below as each piece is pulled off and +// accepted by the lexer; it increments the current column number. +INLINE void accept() { + col_number += yyleng; +} + +%} + +HEX 0x[0-9a-fA-F]* +BINARY 0b[01]* +NUMERIC ([+-]?(([0-9]+[.]?)|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?) + +%% + +%{ + if (initial_token != 0) { + int t = initial_token; + initial_token = 0; + return t; + } +%} + +\n.* { + // New line. Save a copy of the line so we can print it out for the + // benefit of the user in case we get an error. + + strncpy(current_line, yytext+1, max_error_width); + current_line[max_error_width] = '\0'; + line_number++; + col_number=0; + + // Return the whole line to the lexer, except the newline character, + // which we eat. + yyless(1); +} + +[ \t] { + // Eat whitespace. + accept(); +} + +"//".* { + // Eat C++-style comments. + accept(); +} + +"/*" { + // Eat C-style comments. + accept(); + eat_c_comment(); +} + +[{}] { + // Send curly braces as themselves. + accept(); + return eggyytext[0]; +} + + + +"" { + accept(); + return BEZIERCURVE; +} +"" { + accept(); + return BFACE; +} +"" { + accept(); + return BILLBOARD; +} +"" { + accept(); + return BUNDLE; +} +"" { + accept(); + return SCALAR; +} +"" { + accept(); + return CLOSED; +} +"" { + accept(); + return COLLIDE; +} +"" { + accept(); + return COMMENT; +} +"" { + accept(); + return COORDSYSTEM; +} +"" { + accept(); + return CV; +} +"" { + accept(); + return DART; +} +"" { + accept(); + return DNORMAL; +} +"" { + accept(); + return DRGBA; +} +"" { + accept(); + return DUV; +} +"" { + accept(); + return DXYZ; +} +"" { + accept(); + return DCS; +} +"" { + accept(); + return DISTANCE; +} +"" { + accept(); + return DTREF; +} +"" { + accept(); + return DYNAMICVERTEXPOOL; +} +"" { + accept(); + return EXTERNAL_FILE; +} +"" { + accept(); + return FLIGHT; +} +"" { + accept(); + return GROUP; +} +"" { + accept(); + return HIP; +} +"" { + accept(); + return INTANGENT; +} +"" { + accept(); + return JOINT; +} +"" { + accept(); + return KNOTS; +} +"" { + accept(); + return INCLUDE; +} +"" { + accept(); + return INSTANCE; +} +"" { + accept(); + return LOOP; +} +"" { + accept(); + return MATERIAL; +} +"" { + accept(); + return MATRIX3; +} +"" { + accept(); + return MATRIX4; +} +"" { + accept(); + return MODEL; +} +"" { + accept(); + return MREF; +} +"" { + accept(); + return NORMAL; +} +"" { + accept(); + return NURBSCURVE; +} +"" { + accept(); + return NURBSSURFACE; +} +"" { + accept(); + return OBJECTTYPE; +} +"" { + accept(); + return ORDER; +} +"" { + accept(); + return OUTTANGENT; +} +"" { + accept(); + return POINTLIGHT; +} +"" { + accept(); + return POLYGON; +} +"" { + accept(); + return REF; +} +"" { + accept(); + return RGBA; +} +"" { + accept(); + return ROTATE; +} +"" { + accept(); + return ROTX; +} +"" { + accept(); + return ROTY; +} +"" { + accept(); + return ROTZ; +} +"" { + accept(); + return SANIM; +} +"" { + accept(); + return SCALAR; +} +"" { + accept(); + return SCALE; +} +"" { + accept(); + return SEQUENCE; +} +"" { + accept(); + return SHADING; +} +"" { + accept(); + return SWITCH; +} +"" { + accept(); + return SWITCHCONDITION; +} +"
" { + accept(); + return TABLE; +} +"" { + accept(); + return TABLE_V; +} +"" { + accept(); + return TEXLIST; +} +"" { + accept(); + return TEXTURE; +} +"" { + accept(); + return TLENGTHS; +} +"" { + accept(); + return TRANSFORM; +} +"" { + accept(); + return TRANSLATE; +} +"" { + accept(); + return TREF; +} +"" { + accept(); + return TRIM; +} +"" { + accept(); + return TXT; +} +"" { + accept(); + return UKNOTS; +} +"" { + accept(); + return UKNOTS; +} +"" { + accept(); + return UV; +} +"" { + accept(); + return VKNOTS; +} +"" { + accept(); + return VKNOTS; +} +"" { + accept(); + return VERTEX; +} +"" { + accept(); + return VERTEXANIM; +} +"" { + accept(); + return VERTEXPOOL; +} +"" { + accept(); + return VERTEXREF; +} +"" { + accept(); + return XFMANIM; +} +"" { + accept(); + return XFMSANIM; +} + + + +{NUMERIC} { + // An integer or floating-point number. + accept(); + eggyylval._number = atof(eggyytext); + eggyylval._string = yytext; + return NUMBER; +} + +{HEX} { + // A hexadecimal integer number. + accept(); + eggyylval._number = strtoul(yytext+2, NULL, 16); + eggyylval._string = yytext; + return NUMBER; +} + +{BINARY} { + // A binary integer number. + accept(); + eggyylval._number = strtoul(yytext+2, NULL, 2); + eggyylval._string = yytext; + return NUMBER; +} + +"nan"{HEX} { + // not-a-number. These sometimes show up in egg files accidentally. + accept(); + memset(&eggyylval._number, 0, sizeof(eggyylval._number)); + *(unsigned long *)&eggyylval._number = strtoul(yytext+3, NULL, 0); + eggyylval._string = yytext; + return NUMBER; +} + +"inf" { + // infinity. As above. + accept(); + eggyylval._number = HUGE_VAL; + eggyylval._string = yytext; + return NUMBER; +} + +"-inf" { + // minus infinity. As above. + accept(); + eggyylval._number = -HUGE_VAL; + eggyylval._string = yytext; + return NUMBER; +} + + +["] { + // Quoted string. + accept(); + eggyylval._string = scan_quoted_string(); + return STRING; +} + +[^ \t\n{}"]+ { + // Unquoted string. + accept(); + eggyylval._string = yytext; + return STRING; +} diff --git a/panda/src/egg/lexerDefs.h b/panda/src/egg/lexerDefs.h new file mode 100644 index 0000000000..a0c0bfc3fc --- /dev/null +++ b/panda/src/egg/lexerDefs.h @@ -0,0 +1,28 @@ +// Filename: lexerDefs.h +// Created by: drose (17Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LEXER_H +#define LEXER_H + +#include + +#include + +#include + +void egg_init_lexer(istream &in, const string &filename); +void egg_start_group_body(); +int egg_error_count(); +int egg_warning_count(); + +void eggyyerror(const string &msg); +void eggyyerror(ostringstream &strm); + +void eggyywarning(const string &msg); +void eggyywarning(ostringstream &strm); + +int eggyylex(); + +#endif diff --git a/panda/src/egg/parser.yxx b/panda/src/egg/parser.yxx new file mode 100644 index 0000000000..a5697566e0 --- /dev/null +++ b/panda/src/egg/parser.yxx @@ -0,0 +1,2234 @@ +// Filename: parser.y +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +%{ + +#include +#include "parserDefs.h" +#include "lexerDefs.h" +#include "eggObject.h" +#include "eggGroup.h" +#include "eggVertex.h" +#include "eggVertexPool.h" +#include "eggPolygon.h" +#include "eggPoint.h" +#include "eggNurbsSurface.h" +#include "eggNurbsCurve.h" +#include "eggTable.h" +#include "eggSAnimData.h" +#include "eggXfmSAnim.h" +#include "eggXfmAnimData.h" +#include "eggTexture.h" +#include "eggMaterial.h" +#include "eggComment.h" +#include "eggCoordinateSystem.h" +#include "eggExternalReference.h" +#include "eggData.h" + +#include +#include +#include +#include + +#include + +// Because our token type contains objects of type string, which +// require correct copy construction (and not simply memcpying), we +// cannot use bison's built-in auto-stack-grow feature. As an easy +// solution, we ensure here that we have enough yacc stack to start +// with, and that it doesn't ever try to grow. +#define YYINITDEPTH 1000 +#define YYMAXDEPTH 1000 + +// We need a stack of EggObject pointers. Each time we encounter a +// nested EggObject of some kind, we'll allocate a new one of these +// and push it onto the stack. At any given time, the top of the +// stack is the EggObject we are currently scanning. + +typedef vector EggStack; +static EggStack egg_stack; + +// There's one "top-level" egg node, which is where we should parent +// things (e.g. textures) encountered in the egg file that don't have +// an explicit place in the tree. If this is NULL, such things won't +// be parented anywhere. +static EggGroupNode *egg_top_node; + +// We need a table mapping vertex pool names to vertex pools. + +typedef map VertexPools; +static VertexPools vertex_pools; + +// And another one mapping texture names to textures. + +typedef map Textures; +static Textures textures; + +// And again for material names to materials. + +typedef map Materials; +static Materials materials; + +// We need to be able to save the index number requested for a vertex +// temporarily. +static int vertex_index; + +// We need to hold a matrix for a little bit while parsing the +// entries. +static LMatrix4d matrix_3d; +static LMatrix3d matrix_2d; + + +//////////////////////////////////////////////////////////////////// +// Defining the interface to the parser. +//////////////////////////////////////////////////////////////////// + +void +egg_init_parser(istream &in, const string &filename, + EggObject *tos, EggGroupNode *top_node) { + egg_init_lexer(in, filename); + + egg_stack.clear(); + vertex_pools.clear(); + textures.clear(); + materials.clear(); + + egg_stack.push_back(tos); + egg_top_node = top_node; +} + +void +egg_cleanup_parser() { + // Clean these out after we're done, so we don't keep big memory + // structures around needlessly. + egg_stack.clear(); + vertex_pools.clear(); + textures.clear(); + materials.clear(); +} + +%} + +%token <_number> NUMBER +%token <_string> STRING + +%token BEZIERCURVE BFACE BILLBOARD BUNDLE CLOSED COLLIDE COMMENT +%token COORDSYSTEM CV DART +%token DNORMAL DRGBA DUV DXYZ DCS DISTANCE DTREF +%token DYNAMICVERTEXPOOL EXTERNAL_FILE +%token FLIGHT GROUP HIP INTANGENT +%token JOINT KNOTS INCLUDE +%token INSTANCE LOOP MATERIAL MATRIX3 MATRIX4 MODEL MREF NORMAL +%token NURBSCURVE NURBSSURFACE OBJECTTYPE ORDER +%token OUTTANGENT POINTLIGHT POLYGON REF RGBA ROTATE ROTX ROTY ROTZ +%token SANIM SCALAR SCALE SEQUENCE SHADING SWITCH SWITCHCONDITION +%token TABLE TABLE_V TEXLIST TEXTURE TLENGTHS TRANSFORM TRANSLATE +%token TREF TRIM TXT UKNOTS UV VKNOTS VERTEX VERTEXANIM +%token VERTEXPOOL VERTEXREF +%token XFMANIM XFMSANIM + +/* These special tokens are used to set the starting state of the + parser. The lexer places the appropriate one of these on the head + of the input stream. */ +%token START_EGG +%token START_GROUP_BODY + +%type <_egg> node +%type <_egg> coordsystem +%type <_egg> comment +%type <_egg> texture +%type <_egg> material +%type <_egg> external_reference +%type <_egg> vertex_pool +%type <_egg> group +%type <_egg> joint +%type <_egg> instance +%type <_egg> polygon +%type <_egg> point_light +%type <_egg> nurbs_surface +%type <_egg> nurbs_curve +%type <_egg> table +%type <_egg> bundle +%type <_egg> sanim +%type <_egg> xfmanim +%type <_egg> xfm_s_anim + +%type <_string> required_name optional_name +%type <_string> required_string optional_string +%type <_string> string +%type <_string> repeated_string repeated_string_body +%type <_number> group_vertex_membership +%type <_number_list> integer_list +%type <_number_list> real_list +%type <_egg> texture_name +%type <_egg> material_name +%type <_egg> vertex_pool_name + +%type <_number> real +%type <_number> integer +%type <_number> real_or_string + +%% + + +grammar: + START_EGG egg + | START_GROUP_BODY group_body + ; + +/* + * egg + * + * enter: TOS is EggData. + * exit: egg file has been completely read. + * + */ +egg: + empty + | egg node +{ + DCAST(EggData, egg_stack.back())->add_child(DCAST(EggNode, $2)); +} + ; + + +/* + * node + * + * enter: + * exit: returns a new EggNode of some kind. + * + */ +node: + coordsystem + | comment + | texture + | material + | external_reference + | vertex_pool + | group + | joint + | instance + | polygon + | point_light + | nurbs_surface + | nurbs_curve + | table + ; + +/* + * coordsystem + * + * enter: + * exit: returns a new EggCoordinateSystem. + * + */ +coordsystem: + COORDSYSTEM '{' required_string '}' +{ + string strval = $3; + EggCoordinateSystem *cs = new EggCoordinateSystem; + + CoordinateSystem f = parse_coordinate_system_string(strval); + if (f == CS_invalid) { + eggyywarning("Unknown coordinate system " + strval); + } else { + cs->set_value(f); + } + $$ = cs; +} + ; + +/* + * comment + * + * enter: + * exit: returns a new EggComment. + * + */ +comment: + COMMENT optional_name '{' repeated_string '}' +{ + $$ = new EggComment($2, $4); +} + ; + +/* + * texture + * + * enter: + * exit: returns a new EggTexture. + * + */ +texture: + TEXTURE required_name '{' required_string +{ + string tref_name = $2; + string filename = $4; + EggTexture *texture = new EggTexture(tref_name, filename); + + if (textures.find(tref_name) != textures.end()) { + eggyywarning("Duplicate texture name " + tref_name); + } + textures[tref_name] = texture; + + egg_stack.push_back(texture); +} + texture_body '}' +{ + $$ = egg_stack.back(); + egg_stack.pop_back(); +} + ; + +/* + * texture_body + * + * enter: TOS is EggTexture; filename has already been read. + * exit: texture contents have been filled. + * + */ +texture_body: + empty + | texture_body SCALAR required_name '{' real_or_string '}' +{ + EggTexture *texture = DCAST(EggTexture, egg_stack.back()); + string name = $3; + double value = $<_number>5; + string strval = $<_string>5; + + if (cmp_nocase_uh(name, "format") == 0) { + EggTexture::Format f = EggTexture::string_format(strval); + if (f == EggTexture::F_unspecified) { + eggyywarning("Unknown texture format " + strval); + } else { + texture->set_format(f); + } + } else if (cmp_nocase_uh(name, "wrap") == 0) { + EggTexture::WrapMode w = EggTexture::string_wrap_mode(strval); + if (w == EggTexture::WM_unspecified) { + eggyywarning("Unknown texture wrap mode " + strval); + } else { + texture->set_wrap_mode(w); + } + } else if (cmp_nocase_uh(name, "wrapu") == 0) { + EggTexture::WrapMode w = EggTexture::string_wrap_mode(strval); + if (w == EggTexture::WM_unspecified) { + eggyywarning("Unknown texture wrap mode " + strval); + } else { + texture->set_wrap_u(w); + } + } else if (cmp_nocase_uh(name, "wrapv") == 0) { + EggTexture::WrapMode w = EggTexture::string_wrap_mode(strval); + if (w == EggTexture::WM_unspecified) { + eggyywarning("Unknown texture wrap mode " + strval); + } else { + texture->set_wrap_v(w); + } + } else if (cmp_nocase_uh(name, "minfilter") == 0) { + EggTexture::FilterType f = EggTexture::string_filter_type(strval); + if (f == EggTexture::FT_unspecified) { + eggyywarning("Unknown texture filter type " + strval); + } else { + texture->set_minfilter(f); + } + } else if (cmp_nocase_uh(name, "magfilter") == 0) { + EggTexture::FilterType f = EggTexture::string_filter_type(strval); + if (f == EggTexture::FT_unspecified) { + eggyywarning("Unknown texture filter type " + strval); + } else { + texture->set_magfilter(f); + } + } else if (cmp_nocase_uh(name, "magfilteralpha") == 0) { + EggTexture::FilterType f = EggTexture::string_filter_type(strval); + if (f == EggTexture::FT_unspecified) { + eggyywarning("Unknown texture filter type " + strval); + } else { + texture->set_magfilteralpha(f); + } + } else if (cmp_nocase_uh(name, "magfiltercolor") == 0) { + EggTexture::FilterType f = EggTexture::string_filter_type(strval); + if (f == EggTexture::FT_unspecified) { + eggyywarning("Unknown texture filter type " + strval); + } else { + texture->set_magfiltercolor(f); + } + } else if (cmp_nocase_uh(name, "envtype") == 0) { + EggTexture::EnvType e = EggTexture::string_env_type(strval); + if (e == EggTexture::ET_unspecified) { + eggyywarning("Unknown texture env type " + strval); + } else { + texture->set_env_type(e); + } + } else if (cmp_nocase_uh(name, "alpha") == 0) { + EggAlphaMode::AlphaMode a = EggAlphaMode::string_alpha_mode(strval); + if (a == EggAlphaMode::AM_unspecified) { + eggyywarning("Unknown alpha mode " + strval); + } else { + texture->set_alpha_mode(a); + } + } else if (cmp_nocase_uh(name, "draw_order") == 0) { + texture->set_draw_order(value); + } else { + eggyywarning("Unsupported texture scalar: " + name); + } +} + | texture_body transform_2d + ; + +/* + * material + * + * enter: + * exit: returns a new EggMaterial. + * + */ +material: + MATERIAL required_name '{' +{ + string mref_name = $2; + EggMaterial *material = new EggMaterial(mref_name); + + if (materials.find(mref_name) != materials.end()) { + eggyywarning("Duplicate material name " + mref_name); + } + materials[mref_name] = material; + + egg_stack.push_back(material); +} + material_body '}' +{ + $$ = egg_stack.back(); + egg_stack.pop_back(); +} + ; + +/* + * material_body + * + * enter: TOS is EggMaterial; filename has already been read. + * exit: material contents have been filled. + * + */ +material_body: + empty + | material_body SCALAR required_name '{' real_or_string '}' +{ + EggMaterial *material = DCAST(EggMaterial, egg_stack.back()); + string name = $3; + double value = $<_number>5; + + if (cmp_nocase_uh(name, "diffr") == 0) { + Colorf diff = material->get_diff(); + diff[0] = value; + material->set_diff(diff); + } else if (cmp_nocase_uh(name, "diffg") == 0) { + Colorf diff = material->get_diff(); + diff[1] = value; + material->set_diff(diff); + } else if (cmp_nocase_uh(name, "diffb") == 0) { + Colorf diff = material->get_diff(); + diff[2] = value; + material->set_diff(diff); + } else { + eggyywarning("Unsupported material scalar: " + name); + } +} + ; + + +/* + * external_reference + * + * enter: + * exit: returns a new EggExternalReference. + * + */ +external_reference: + EXTERNAL_FILE optional_name '{' required_string '}' +{ + string node_name = $2; + string filename = $4; + EggExternalReference *ref = new EggExternalReference(node_name, filename); + $$ = ref; +} + | string EXTERNAL_FILE optional_name '{' required_string '}' +{ + if (cmp_nocase_uh($1, "group") != 0) { + eggyyerror("keyword 'group' expected"); + } + string node_name = $3; + string filename = $5; + EggExternalReference *ref = new EggExternalReference(node_name, filename); + $$ = ref; +} + ; + +/* + * vertex_pool + * + * enter: + * exit: returns a new EggVertexPool. + * + */ +vertex_pool: + VERTEXPOOL required_name +{ + string name = $2; + EggVertexPool *pool = new EggVertexPool(name); + + if (vertex_pools.find(name) != vertex_pools.end()) { + eggyywarning("Duplicate vertex pool name " + name); + } + vertex_pools[name] = pool; + + egg_stack.push_back(pool); +} + '{' vertex_pool_body '}' +{ + $$ = egg_stack.back(); + egg_stack.pop_back(); +} + ; + + +/* + * vertex_pool_body + * + * enter: TOS is EggVertexPool. + * exit: vertex pool contents have been filled. + * + */ +vertex_pool_body: + empty + | vertex_pool_body vertex + ; + +/* + * vertex + * + * enter: TOS is EggVertexPool. + * exit: new vertex has been added to vertex pool. + * + */ +vertex: + VERTEX +{ + egg_stack.push_back(new EggVertex); +} + '{' vertex_body '}' +{ + PT(EggVertex) vtx = DCAST(EggVertex, egg_stack.back()); + egg_stack.pop_back(); + + DCAST(EggVertexPool, egg_stack.back())->add_vertex(vtx); +} + | VERTEX integer +{ + vertex_index = (int)$2; + EggVertexPool *pool = DCAST(EggVertexPool, egg_stack.back()); + + if (vertex_index < 0) { + ostringstream errmsg; + errmsg << "Ignoring invalid vertex index " << vertex_index + << " in vertex pool " << pool->get_name() << ends; + eggyywarning(errmsg); + vertex_index = -1; + + } else if (pool->get_vertex(vertex_index) != NULL) { + ostringstream errmsg; + errmsg << "Ignoring duplicate vertex index " << vertex_index + << " in vertex pool " << pool->get_name() << ends; + eggyywarning(errmsg); + vertex_index = -1; + } + + // Even if we didn't like the vertex index number, we still need to + // go ahead and parse the vertex. We just won't save it. + + egg_stack.push_back(new EggVertex); +} + '{' vertex_body '}' +{ + PT(EggVertex) vtx = DCAST(EggVertex, egg_stack.back()); + egg_stack.pop_back(); + + EggVertexPool *pool = DCAST(EggVertexPool, egg_stack.back()); + if (vertex_index != -1) { + pool->add_vertex(vtx, vertex_index); + } +} + ; + + +/* + * vertex_body + * + * enter: TOS is EggVertex. + * exit: vertex contents have been filled. + * + */ +vertex_body: + real +{ + DCAST(EggVertex, egg_stack.back())->set_pos($1); +} + | real real +{ + DCAST(EggVertex, egg_stack.back())->set_pos(LPoint2d($1, $2)); +} + | real real real +{ + DCAST(EggVertex, egg_stack.back())->set_pos(LPoint3d($1, $2, $3)); +} + | real real real real +{ + DCAST(EggVertex, egg_stack.back())->set_pos(LPoint4d($1, $2, $3, $4)); +} + | vertex_body UV '{' vertex_uv_body '}' + | vertex_body NORMAL '{' vertex_normal_body '}' + | vertex_body RGBA '{' vertex_color_body '}' + | vertex_body DXYZ string '{' real real real '}' +{ + bool inserted = DCAST(EggVertex, egg_stack.back())->_dxyzs. + insert(EggMorphVertex($3, LVector3d($5, $6, $7))).second; + if (!inserted) { + eggyywarning("Ignoring repeated morph name " + $3); + } +} + | vertex_body DXYZ '{' string real real real '}' +{ + bool inserted = DCAST(EggVertex, egg_stack.back())->_dxyzs. + insert(EggMorphVertex($4, LVector3d($5, $6, $7))).second; + if (!inserted) { + eggyywarning("Ignoring repeated morph name " + $4); + } +} + ; + + +/* + * vertex_uv_body + * + * enter: TOS is EggVertex. + * exit: vertex UV value has been filled. + * + */ +vertex_uv_body: + real real +{ + DCAST(EggVertex, egg_stack.back())->set_uv(TexCoordd($1, $2)); +} + | vertex_uv_body DUV string '{' real real '}' +{ + bool inserted = DCAST(EggVertex, egg_stack.back())->_duvs. + insert(EggMorphTexCoord($3, LVector2d($5, $6))).second; + if (!inserted) { + eggyywarning("Ignoring repeated morph name " + $3); + } +} + | vertex_uv_body DUV '{' string real real '}' +{ + bool inserted = DCAST(EggVertex, egg_stack.back())->_duvs. + insert(EggMorphTexCoord($4, LVector2d($5, $6))).second; + if (!inserted) { + eggyywarning("Ignoring repeated morph name " + $4); + } +} + ; + +/* + * vertex_normal_body + * + * enter: TOS is EggVertex. + * exit: vertex normal value has been filled. + * + */ +vertex_normal_body: + real real real +{ + DCAST(EggVertex, egg_stack.back())->set_normal(Normald($1, $2, $3)); +} + | vertex_normal_body DNORMAL string '{' real real real '}' +{ + bool inserted = DCAST(EggVertex, egg_stack.back())->_dnormals. + insert(EggMorphNormal($3, LVector3d($5, $6, $7))).second; + if (!inserted) { + eggyywarning("Ignoring repeated morph name " + $3); + } +} + | vertex_normal_body DNORMAL '{' string real real real '}' +{ + bool inserted = DCAST(EggVertex, egg_stack.back())->_dnormals. + insert(EggMorphNormal($4, LVector3d($5, $6, $7))).second; + if (!inserted) { + eggyywarning("Ignoring repeated morph name " + $4); + } +} + ; + +/* + * vertex_color_body + * + * enter: TOS is EggVertex. + * exit: vertex color value has been filled. + * + */ +vertex_color_body: + real real real real +{ + DCAST(EggVertex, egg_stack.back())->set_color(Colorf($1, $2, $3, $4)); +} + | vertex_color_body DRGBA string '{' real real real real '}' +{ + bool inserted = DCAST(EggVertex, egg_stack.back())->_drgbas. + insert(EggMorphColor($3, LVector4f($5, $6, $7, $8))).second; + if (!inserted) { + eggyywarning("Ignoring repeated morph name " + $3); + } +} + | vertex_color_body DRGBA '{' string real real real real '}' +{ + bool inserted = DCAST(EggVertex, egg_stack.back())->_drgbas. + insert(EggMorphColor($4, LVector4f($5, $6, $7, $8))).second; + if (!inserted) { + eggyywarning("Ignoring repeated morph name " + $4); + } +} + ; + +/* + * group + * + * enter: + * exit: returns a new EggGroup. + * + */ +group: + GROUP optional_name +{ + EggGroup *group = new EggGroup($2); + egg_stack.push_back(group); +} + '{' group_body '}' +{ + $$ = egg_stack.back(); + egg_stack.pop_back(); +} + ; + +/* + * joint + * + * enter: + * exit: returns a new EggGroup, as a joint. + * + */ +joint: + JOINT optional_name +{ + EggGroup *group = new EggGroup($2); + group->set_group_type(EggGroup::GT_joint); + egg_stack.push_back(group); +} + '{' group_body '}' +{ + $$ = egg_stack.back(); + egg_stack.pop_back(); +} + ; + +/* + * instance + * + * enter: + * exit: returns a new EggGroup, as an instance. + * + */ +instance: + INSTANCE optional_name +{ + EggGroup *group = new EggGroup($2); + group->set_group_type(EggGroup::GT_instance); + egg_stack.push_back(group); +} + '{' group_body '}' +{ + $$ = egg_stack.back(); + egg_stack.pop_back(); +} + ; + +/* + * group_body + * + * enter: TOS is EggGroup. + * exit: group contents have been filled, including children. + * + */ +group_body: + empty + | group_body SCALAR required_name '{' real_or_string '}' +{ + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + string name = $3; + double value = $<_number>5; + string strval = $<_string>5; + + if (cmp_nocase_uh(name, "fps") == 0) { + group->set_switch_fps(value); + } else if (cmp_nocase_uh(name, "no_fog") == 0) { + group->set_nofog_flag(value != 0); + } else if (cmp_nocase_uh(name, "decal") == 0) { + group->set_decal_flag(value != 0); + } else if (cmp_nocase_uh(name, "direct") == 0) { + group->set_direct_flag(value != 0); + } else if (cmp_nocase_uh(name, "alpha") == 0) { + EggAlphaMode::AlphaMode a = EggAlphaMode::string_alpha_mode(strval); + if (a == EggAlphaMode::AM_unspecified) { + eggyywarning("Unknown alpha mode " + strval); + } else { + group->set_alpha_mode(a); + } + } else if (cmp_nocase_uh(name, "draw_order") == 0) { + group->set_draw_order(value); + } else if (cmp_nocase_uh(name, "collide_mask") == 0) { + group->set_collide_mask(value); + } else if (cmp_nocase_uh(name, "from_collide_mask") == 0) { + group->set_from_collide_mask(value); + } else if (cmp_nocase_uh(name, "into_collide_mask") == 0) { + group->set_into_collide_mask(value); + } else { + eggyywarning("Unknown group scalar " + name); + } +} + | group_body BILLBOARD '{' string '}' +{ + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + string strval = $4; + + EggGroup::BillboardType f = EggGroup::string_billboard_type(strval); + if (f == EggGroup::BT_none) { + eggyywarning("Unknown billboard type " + strval); + } else { + group->set_billboard_type(f); + } +} + | group_body COLLIDE optional_name '{' cs_type collide_flags '}' +{ + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + string name = $3; + + group->set_collision_name(name); +} + | group_body DCS '{' integer '}' +{ + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + int value = (int)$4; + group->set_dcs_flag(value!=0); +} + | group_body DART '{' integer '}' +{ + // The traditional flavor of DART, with { 0 } or { 1 }. + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + int value = (int)$4; + group->set_dart_type(value!=0 ? EggGroup::DT_default : EggGroup::DT_none); +} + | group_body DART '{' STRING '}' +{ + // The special flavor of DART, with { sync } or { nosync }. + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + string strval = $4; + + EggGroup::DartType f = EggGroup::string_dart_type(strval); + if (f == EggGroup::DT_none) { + eggyywarning("Unknown dart type " + strval); + } else { + group->set_dart_type(f); + } +} + | group_body SWITCH '{' integer '}' +{ + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + int value = (int)$4; + group->set_switch_flag(value!=0); +} + | group_body OBJECTTYPE '{' required_string '}' +{ + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + string type = $4; + group->set_objecttype(type); +} + | group_body MODEL '{' integer '}' +{ + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + int value = (int)$4; + group->set_model_flag(value!=0); +} + | group_body TEXLIST '{' integer '}' +{ + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + int value = (int)$4; + group->set_texlist_flag(value!=0); +} + | group_body transform_3d + | group_body group_vertex_ref + | group_body switchcondition + | group_body node +{ + DCAST(EggGroup, egg_stack.back())->add_child(DCAST(EggNode, $2)); +} + ; + +/* + * cs_type + * + * enter: TOS is EggGroup. + * exit: group's cs_type value is set according to parsed symbol. + * + */ +cs_type: + string +{ + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + string strval = $1; + + EggGroup::CollisionSolidType f = EggGroup::string_cs_type(strval); + if (f == EggGroup::CST_none) { + eggyywarning("Unknown collision solid type " + strval); + } else { + group->set_cs_type(f); + } +} + ; + +/* + * collide_flags + * + * enter: TOS is EggGroup. + * exit: group's collide flags have been set according to parsed flags. + * + */ +collide_flags: + empty + | collide_flags string +{ + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + string strval = $2; + + EggGroup::CollideFlags f = EggGroup::string_collide_flags(strval); + if (f == EggGroup::CF_none) { + eggyywarning("Unknown collision flag " + strval); + } else { + group->set_collide_flags(group->get_collide_flags() | f); + } +} + ; + +/* + * transform_3d + * + * enter: TOS is EggGroup. + * exit: group's transform matrix has been set. + * + */ +transform_3d: + TRANSFORM +{ + matrix_3d = LMatrix4d::ident_mat(); +} + '{' transform_3d_body '}' +{ + DCAST(EggGroup, egg_stack.back())->set_transform(matrix_3d); +} + ; + + +/* + * transform_3d_body + * + * enter: matrix_3d contains some sensible value. + * exit: matrix_3d has been transformed by the parsed transformation. + * + */ +transform_3d_body: + empty + | transform_3d_body matrix3_3d + | transform_3d_body matrix4_3d + | transform_3d_body translate_3d + | transform_3d_body rotx_3d + | transform_3d_body roty_3d + | transform_3d_body rotz_3d + | transform_3d_body rotate_3d + | transform_3d_body scale_3d + ; + +translate_3d: TRANSLATE '{' real real real '}' +{ + matrix_3d *= LMatrix4d::translate_mat($3, $4, $5); +} + ; + +rotx_3d: ROTX '{' real '}' +{ + matrix_3d *= LMatrix4d::rotate_mat($3, LVector3d(1.0, 0.0, 0.0)); +} + ; + +roty_3d: ROTY '{' real '}' +{ + matrix_3d *= LMatrix4d::rotate_mat($3, LVector3d(0.0, 1.0, 0.0)); +} + ; + +rotz_3d: ROTZ '{' real '}' +{ + matrix_3d *= LMatrix4d::rotate_mat($3, LVector3d(0.0, 0.0, 1.0)); +} + ; + +rotate_3d: ROTATE '{' real real real real '}' +{ + matrix_3d *= LMatrix4d::rotate_mat($3, LVector3d($4, $5, $6)); +} + ; + +scale_3d: SCALE '{' real real real '}' +{ + matrix_3d *= LMatrix4d::scale_mat($3, $4, $5); +} + ; + +matrix4_3d: + MATRIX4 '{' matrix4_3d_body '}' + ; + +matrix4_3d_body: + empty + | real real real real + real real real real + real real real real + real real real real +{ + matrix_3d *= LMatrix4d($1, $2, $3, $4, + $5, $6, $7, $8, + $9, $10, $11, $12, + $13, $14, $15, $16); +} + ; + +matrix3_3d: MATRIX3 '{' matrix3_3d_body '}' + ; + +matrix3_3d_body: + empty + | real real real + real real real + real real real +{ + matrix_3d *= LMatrix4d(LMatrix3d($1, $2, $3, + $4, $5, $6, + $7, $8, $9)); +} + ; + + +/* + * transform_2d + * + * enter: TOS is EggTexture. + * exit: texture's transform matrix has been set. + * + */ +transform_2d: + TRANSFORM +{ + matrix_2d = LMatrix3d::ident_mat(); +} + '{' transform_2d_body '}' +{ + DCAST(EggTexture, egg_stack.back())->set_transform(matrix_2d); +} + ; + + +/* + * transform_2d_body + * + * enter: matrix contains some sensible value. + * exit: matrix has been transformed by the parsed transformation. + * + */ +transform_2d_body: + empty + | transform_2d_body matrix3_2d + | transform_2d_body translate_2d + | transform_2d_body rotate_2d + | transform_2d_body scale_2d + ; + +translate_2d: TRANSLATE '{' real real '}' +{ + matrix_2d *= LMatrix3d::translate_mat($3, $4); +} + ; + +rotate_2d: ROTATE '{' real '}' +{ + matrix_2d *= LMatrix3d::rotate_mat($3); +} + ; + +scale_2d: SCALE '{' real real '}' +{ + matrix_2d *= LMatrix3d::scale_mat($3, $4); +} + ; + +matrix3_2d: MATRIX3 '{' matrix3_2d_body '}' + ; + +matrix3_2d_body: + empty + | real real real + real real real + real real real +{ + matrix_2d *= LMatrix3d($1, $2, $3, + $4, $5, $6, + $7, $8, $9); +} + ; + + +/* + * group_vertex_ref + * + * enter: TOS is EggGroup. + * exit: group vertex list has been filled. + * + */ +group_vertex_ref: + VERTEXREF '{' integer_list group_vertex_membership REF '{' vertex_pool_name '}' '}' +{ + if ($7 != (EggVertexPool *)NULL) { + EggVertexPool *pool = DCAST(EggVertexPool, $7); + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + PTA_double nums = $3; + double membership = $4; + + for (int i = 0; i < nums.size(); i++) { + int index = (int)nums[i]; + EggVertex *vertex = pool->get_vertex(index); + if (vertex == NULL) { + ostringstream errmsg; + errmsg << "No vertex " << index << " in pool " << pool->get_name() + << ends; + eggyyerror(errmsg); + } else { + group->ref_vertex(vertex, membership); + } + } + } +} + ; + + +/* + * group_vertex_membership + * + * enter: + * exit: returns membership component if specified, or 1.0. + * + */ +group_vertex_membership: + empty +{ + $$ = 1.0; +} + | group_vertex_membership SCALAR required_name '{' real_or_string '}' +{ + string name = $3; + double value = $<_number>5; + double result = $1; + + if (cmp_nocase_uh(name, "membership") == 0) { + result = value; + } else { + eggyywarning("Unknown group vertex scalar " + name); + } + + $$ = result; +} + ; + + +/* + * switchcondition + * + * enter: TOS is EggGroup. + * exit: group switchcondition value has been filled. + * + */ +switchcondition: + SWITCHCONDITION '{' switchcondition_body '}' + ; + + +/* + * switchcondition_body + * + * enter: TOS is EggGroup. + * exit: group switchcondition value has been filled. + * + */ +switchcondition_body: + DISTANCE '{' real real VERTEX '{' real real real '}' '}' +{ + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + group->set_lod(EggSwitchConditionDistance($3, $4, LPoint3d($7, $8, $9))); +} + | DISTANCE '{' real real real VERTEX '{' real real real '}' '}' +{ + EggGroup *group = DCAST(EggGroup, egg_stack.back()); + group->set_lod(EggSwitchConditionDistance($3, $4, LPoint3d($8, $9, $10), $5)); +} + ; + + + +/* + * polygon + * + * enter: + * exit: returns a new EggPolygon. + * + */ +polygon: + POLYGON optional_name +{ + egg_stack.push_back(new EggPolygon($2)); +} + '{' primitive_body '}' +{ + $$ = egg_stack.back(); + egg_stack.pop_back(); +} + ; + +/* + * point_light + * + * enter: + * exit: returns a new EggPoint. + * + */ +point_light: + POINTLIGHT optional_name +{ + egg_stack.push_back(new EggPoint($2)); +} + '{' primitive_body '}' +{ + $$ = egg_stack.back(); + egg_stack.pop_back(); +} + ; + +/* + * nurbs_surface + * + * enter: + * exit: returns a new EggNurbsSurface. + * + */ +nurbs_surface: + NURBSSURFACE optional_name +{ + egg_stack.push_back(new EggNurbsSurface($2)); +} + '{' nurbs_surface_body '}' +{ + $$ = egg_stack.back(); + egg_stack.pop_back(); +} + ; + +/* + * nurbs_curve + * + * enter: + * exit: returns a new EggNurbsCurve. + * + */ +nurbs_curve: + NURBSCURVE optional_name +{ + egg_stack.push_back(new EggNurbsCurve($2)); +} + '{' nurbs_curve_body '}' +{ + $$ = egg_stack.back(); + egg_stack.pop_back(); +} + ; + + +/* + * primitive_body + * + * enter: TOS is EggPrimitive. + * exit: primitive attributes and vertices have been filled. + * + */ +primitive_body: + empty + | primitive_body TREF '{' primitive_tref_body '}' + | primitive_body TEXTURE '{' primitive_texture_body '}' + | primitive_body MREF '{' primitive_material_body '}' + | primitive_body primitive_vertex_ref + | primitive_body NORMAL '{' primitive_normal_body '}' + | primitive_body RGBA '{' primitive_color_body '}' + | primitive_body BFACE '{' primitive_bface_body '}' + | primitive_body SCALAR required_name '{' real_or_string '}' +{ + EggPrimitive *primitive = DCAST(EggPrimitive, egg_stack.back()); + string name = $3; + double value = $<_number>5; + string strval = $<_string>5; + + if (cmp_nocase_uh(name, "alpha") == 0) { + EggAlphaMode::AlphaMode a = EggAlphaMode::string_alpha_mode(strval); + if (a == EggAlphaMode::AM_unspecified) { + eggyywarning("Unknown alpha mode " + strval); + } else { + primitive->set_alpha_mode(a); + } + } else if (cmp_nocase_uh(name, "draw_order") == 0) { + primitive->set_draw_order(value); + } else { + eggyywarning("Unknown scalar " + name); + } +} + ; + +/* + * nurbs_surface_body + * + * enter: TOS is EggNurbsSurface. + * exit: primitive attributes and vertices have been filled. + * + */ +nurbs_surface_body: + empty + | nurbs_surface_body TREF '{' primitive_tref_body '}' + | nurbs_surface_body TEXTURE '{' primitive_texture_body '}' + | nurbs_surface_body MREF '{' primitive_material_body '}' + | nurbs_surface_body primitive_vertex_ref + | nurbs_surface_body NORMAL '{' primitive_normal_body '}' + | nurbs_surface_body RGBA '{' primitive_color_body '}' + | nurbs_surface_body BFACE '{' primitive_bface_body '}' + | nurbs_surface_body ORDER '{' nurbs_surface_order_body '}' + | nurbs_surface_body UKNOTS '{' nurbs_surface_uknots_body '}' + | nurbs_surface_body VKNOTS '{' nurbs_surface_vknots_body '}' + | nurbs_surface_body nurbs_curve +{ + EggNurbsCurve *curve = DCAST(EggNurbsCurve, $2); + EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back()); + nurbs->_curves_on_surface.push_back(curve); +} + | nurbs_surface_body TRIM '{' nurbs_surface_trim_body '}' + | nurbs_surface_body SCALAR required_name '{' real_or_string '}' +{ + EggNurbsSurface *primitive = DCAST(EggNurbsSurface, egg_stack.back()); + string name = $3; + double value = $<_number>5; + string strval = $<_string>5; + + if (cmp_nocase_uh(name, "alpha") == 0) { + EggAlphaMode::AlphaMode a = EggAlphaMode::string_alpha_mode(strval); + if (a == EggAlphaMode::AM_unspecified) { + eggyywarning("Unknown alpha mode " + strval); + } else { + primitive->set_alpha_mode(a); + } + } else if (cmp_nocase_uh(name, "draw_order") == 0) { + primitive->set_draw_order(value); + } else if (cmp_nocase_uh(name, "u_subdiv") == 0) { + primitive->set_u_subdiv(value); + } else if (cmp_nocase_uh(name, "v_subdiv") == 0) { + primitive->set_v_subdiv(value); + } else { + eggyywarning("Unknown scalar " + name); + } +} + ; + + +/* + * nurbs_curve_body + * + * enter: TOS is EggNurbsCurve. + * exit: primitive attributes and vertices have been filled. + * + */ +nurbs_curve_body: + empty + | nurbs_curve_body TREF '{' primitive_tref_body '}' + | nurbs_curve_body TEXTURE '{' primitive_texture_body '}' + | nurbs_curve_body MREF '{' primitive_material_body '}' + | nurbs_curve_body primitive_vertex_ref + | nurbs_curve_body NORMAL '{' primitive_normal_body '}' + | nurbs_curve_body RGBA '{' primitive_color_body '}' + | nurbs_curve_body BFACE '{' primitive_bface_body '}' + | nurbs_curve_body ORDER '{' nurbs_curve_order_body '}' + | nurbs_curve_body KNOTS '{' nurbs_curve_knots_body '}' + | nurbs_curve_body SCALAR required_name '{' real_or_string '}' +{ + EggNurbsCurve *primitive = DCAST(EggNurbsCurve, egg_stack.back()); + string name = $3; + double value = $<_number>5; + string strval = $<_string>5; + + if (cmp_nocase_uh(name, "alpha") == 0) { + EggAlphaMode::AlphaMode a = EggAlphaMode::string_alpha_mode(strval); + if (a == EggAlphaMode::AM_unspecified) { + eggyywarning("Unknown alpha mode " + strval); + } else { + primitive->set_alpha_mode(a); + } + } else if (cmp_nocase_uh(name, "draw_order") == 0) { + primitive->set_draw_order(value); + } else if (cmp_nocase_uh(name, "subdiv") == 0) { + primitive->set_subdiv(value); + } else if (cmp_nocase_uh(name, "type") == 0) { + EggCurve::CurveType a = EggCurve::string_curve_type(strval); + if (a == EggCurve::CT_none) { + eggyywarning("Unknown curve type " + strval); + } else { + primitive->set_curve_type(a); + } + + } else { + eggyywarning("Unknown scalar " + name); + } +} + ; + +/* + * primitive_tref_body + * + * enter: TOS is EggPrimitive. + * exit: primitive TREF value has been filled. + * + */ +primitive_tref_body: + texture_name +{ + if ($1 != (EggTexture *)NULL) { + EggTexture *texture = DCAST(EggTexture, $1); + DCAST(EggPrimitive, egg_stack.back())->set_texture(texture); + } +} + ; + +/* + * primitive_texture_body + * + * enter: TOS is EggPrimitive. + * exit: primitive texture value has been filled. + * + */ +primitive_texture_body: + required_name +{ + EggTexture *texture = NULL; + + // Defining a texture on-the-fly. + Filename filename = $1; + string tref_name = filename.get_basename(); + + Textures::iterator vpi = textures.find(tref_name); + if (vpi == textures.end()) { + // The texture was not yet defined. Define it. + texture = new EggTexture(tref_name, filename); + textures[tref_name] = texture; + + if (egg_top_node != NULL) { + egg_top_node->add_child(texture); + } + + } else { + // The texture already existed. Use it. + texture = (*vpi).second; + if (!(filename == texture->get_fullpath())) { + eggyywarning("Using previous path: " + texture->get_fullpath()); + } + } + + nassertr(texture != NULL, 0); + DCAST(EggPrimitive, egg_stack.back())->set_texture(texture); +} + ; + +/* + * primitive_material_body + * + * enter: TOS is EggPrimitive. + * exit: primitive material value has been filled. + * + */ +primitive_material_body: + material_name +{ + if ($1 != (EggMaterial *)NULL) { + EggMaterial *material = DCAST(EggMaterial, $1); + DCAST(EggPrimitive, egg_stack.back())->set_material(material); + } +} + ; + +/* + * primitive_normal_body + * + * enter: TOS is EggPrimitive. + * exit: primitive normal value has been filled. + * + */ +primitive_normal_body: + real real real +{ + DCAST(EggPrimitive, egg_stack.back())->set_normal(Normald($1, $2, $3)); +} + | primitive_normal_body DNORMAL string '{' real real real '}' +{ + bool inserted = DCAST(EggPrimitive, egg_stack.back())->_dnormals. + insert(EggMorphNormal($3, LVector3d($5, $6, $7))).second; + if (!inserted) { + eggyywarning("Ignoring repeated morph name " + $3); + } +} + | primitive_normal_body DNORMAL '{' string real real real '}' +{ + bool inserted = DCAST(EggPrimitive, egg_stack.back())->_dnormals. + insert(EggMorphNormal($4, LVector3d($5, $6, $7))).second; + if (!inserted) { + eggyywarning("Ignoring repeated morph name " + $4); + } +} + ; + +/* + * primitive_color_body + * + * enter: TOS is EggPrimitive. + * exit: primitive color value has been filled. + * + */ +primitive_color_body: + real real real real +{ + DCAST(EggPrimitive, egg_stack.back())->set_color(Colorf($1, $2, $3, $4)); +} + | primitive_color_body DRGBA string '{' real real real real '}' +{ + bool inserted = DCAST(EggPrimitive, egg_stack.back())->_drgbas. + insert(EggMorphColor($3, LVector4f($5, $6, $7, $8))).second; + if (!inserted) { + eggyywarning("Ignoring repeated morph name " + $3); + } +} + | primitive_color_body DRGBA '{' string real real real real '}' +{ + bool inserted = DCAST(EggPrimitive, egg_stack.back())->_drgbas. + insert(EggMorphColor($4, LVector4f($5, $6, $7, $8))).second; + if (!inserted) { + eggyywarning("Ignoring repeated morph name " + $4); + } +} + ; + +/* + * primitive_bface_body + * + * enter: TOS is EggPrimitive. + * exit: primitive BFace value has been filled. + * + */ +primitive_bface_body: + integer +{ + EggPrimitive *primitive = DCAST(EggPrimitive, egg_stack.back()); + int value = (int)$1; + primitive->set_bface_flag(value!=0); +} + ; + +/* + * primitive_vertex_ref + * + * enter: TOS is EggPrimitive. + * exit: primitive vertex list has been filled. + * + */ +primitive_vertex_ref: + VERTEXREF '{' integer_list REF '{' vertex_pool_name '}' '}' +{ + if ($6 != (EggVertexPool *)NULL) { + EggVertexPool *pool = DCAST(EggVertexPool, $6); + EggPrimitive *prim = DCAST(EggPrimitive, egg_stack.back()); + PTA_double nums = $3; + + for (int i = 0; i < nums.size(); i++) { + int index = (int)nums[i]; + EggVertex *vertex = pool->get_vertex(index); + if (vertex == NULL) { + ostringstream errmsg; + errmsg << "No vertex " << index << " in pool " << pool->get_name() + << ends; + eggyyerror(errmsg); + } else { + prim->add_vertex(vertex); + } + } + } +} + ; + +/* + * nurbs_surface_order_body + * + * enter: TOS is EggNurbsSurface. + * exit: U-order and V-order have been set. + * + */ +nurbs_surface_order_body: + integer integer +{ + EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back()); + int u_order = (int)$1; + int v_order = (int)$2; + nurbs->set_u_order(u_order); + nurbs->set_v_order(v_order); +} + ; + +/* + * nurbs_surface_uknots_body + * + * enter: TOS is EggNurbsSurface. + * exit: U-Knots list has been set. + * + */ +nurbs_surface_uknots_body: + real_list +{ + EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back()); + PTA_double nums = $1; + + nurbs->set_num_u_knots(nums.size()); + for (int i = 0; i < nums.size(); i++) { + nurbs->set_u_knot(i, nums[i]); + } +} + ; + +/* + * nurbs_surface_vknots_body + * + * enter: TOS is EggNurbsSurface. + * exit: V-Knots list has been set. + * + */ +nurbs_surface_vknots_body: + real_list +{ + EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back()); + PTA_double nums = $1; + + nurbs->set_num_v_knots(nums.size()); + for (int i = 0; i < nums.size(); i++) { + nurbs->set_v_knot(i, nums[i]); + } +} + ; + +/* + * nurbs_surface_trim_body + * + * enter: TOS is EggNurbsSurface. + * exit: A trim curve sequence has been defined. + * + */ +nurbs_surface_trim_body: + empty +{ + EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back()); + nurbs->_trims.push_back(EggNurbsSurface::Trim()); +} + | nurbs_surface_trim_body LOOP '{' nurbs_surface_trim_loop_body '}' + ; + +/* + * nurbs_surface_trim_loop_body + * + * enter: TOS is EggNurbsSurface, with at least one trim curve started. + * exit: A trim curve loop has been defined. + * + */ +nurbs_surface_trim_loop_body: + empty +{ + EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back()); + nassertr(!nurbs->_trims.empty(), 0); + nurbs->_trims.back().push_back(EggNurbsSurface::Loop()); +} + | nurbs_surface_trim_loop_body nurbs_curve +{ + EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, egg_stack.back()); + nassertr(!nurbs->_trims.empty(), 0); + nassertr(!nurbs->_trims.back().empty(), 0); + EggNurbsCurve *curve = DCAST(EggNurbsCurve, $2); + nurbs->_trims.back().back().push_back(curve); +} + ; + + +/* + * nurbs_curve_order_body + * + * enter: TOS is EggNurbsCurve. + * exit: U-order and V-order have been set. + * + */ +nurbs_curve_order_body: + integer +{ + EggNurbsCurve *nurbs = DCAST(EggNurbsCurve, egg_stack.back()); + int order = (int)$1; + nurbs->set_order(order); +} + ; + +/* + * nurbs_curve_knots_body + * + * enter: TOS is EggNurbsCurve. + * exit: U-Knots list has been set. + * + */ +nurbs_curve_knots_body: + real_list +{ + EggNurbsCurve *nurbs = DCAST(EggNurbsCurve, egg_stack.back()); + PTA_double nums = $1; + + nurbs->set_num_knots(nums.size()); + for (int i = 0; i < nums.size(); i++) { + nurbs->set_knot(i, nums[i]); + } +} + ; + + +/* + * table + * + * enter: + * exit: returns a new EggTable, corresponding to a
entry. + * + */ +table: + TABLE optional_name +{ + EggTable *table = new EggTable($2); + table->set_table_type(EggTable::TT_table); + egg_stack.push_back(table); +} + '{' table_body '}' +{ + $$ = egg_stack.back(); + egg_stack.pop_back(); +} + ; + + +/* + * bundle + * + * enter: + * exit: returns a new EggTable, corresponding to a entry. + * + */ +bundle: + BUNDLE optional_name +{ + EggTable *table = new EggTable($2); + table->set_table_type(EggTable::TT_bundle); + egg_stack.push_back(table); +} + '{' table_body '}' +{ + $$ = egg_stack.back(); + egg_stack.pop_back(); +} + ; + + +/* + * table_body + * + * enter: TOS is EggTable. + * exit: table contents have been filled, including children. + * + */ +table_body: + empty + | table_body table +{ + DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, $2)); +} + | table_body bundle +{ + DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, $2)); +} + | table_body sanim +{ + DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, $2)); +} + | table_body xfmanim +{ + DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, $2)); +} + | table_body xfm_s_anim +{ + DCAST(EggTable, egg_stack.back())->add_child(DCAST(EggNode, $2)); +} + ; + + +/* + * sanim + * + * enter: + * exit: returns a new EggSAnimData. + * + */ +sanim: + SANIM optional_name +{ + EggSAnimData *anim_data = new EggSAnimData($2); + egg_stack.push_back(anim_data); +} + '{' sanim_body '}' +{ + $$ = egg_stack.back(); + egg_stack.pop_back(); +} + ; + + +/* + * sanim_body + * + * enter: TOS is EggSAnimData. + * exit: sanim contents have been filled. + * + */ +sanim_body: + empty + | sanim_body SCALAR required_name '{' real_or_string '}' +{ + EggSAnimData *anim_data = DCAST(EggSAnimData, egg_stack.back()); + string name = $3; + double value = $<_number>5; + + if (cmp_nocase_uh(name, "fps") == 0) { + anim_data->set_fps(value); + } else { + eggyywarning("Unsupported S$Anim scalar: " + name); + } +} + | sanim_body TABLE_V '{' real_list '}' +{ + DCAST(EggSAnimData, egg_stack.back())->set_data($4); +} + ; + +/* + * xfmanim + * + * enter: + * exit: returns a new EggXfmAnimData. + * + */ +xfmanim: + XFMANIM optional_name +{ + EggXfmAnimData *anim_data = new EggXfmAnimData($2); + egg_stack.push_back(anim_data); +} + '{' xfmanim_body '}' +{ + $$ = egg_stack.back(); + egg_stack.pop_back(); +} + ; + + +/* + * xfmanim_body + * + * enter: TOS is EggXfmAnimData. + * exit: xfmanim contents have been filled, including children. + * + */ +xfmanim_body: + empty + | xfmanim_body SCALAR required_name '{' real_or_string '}' +{ + EggXfmAnimData *anim_data = DCAST(EggXfmAnimData, egg_stack.back()); + string name = $3; + double value = $<_number>5; + string strval = $<_string>5; + + if (cmp_nocase_uh(name, "fps") == 0) { + anim_data->set_fps(value); + } else if (cmp_nocase_uh(name, "order") == 0) { + anim_data->set_order(strval); + } else if (cmp_nocase_uh(name, "contents") == 0) { + anim_data->set_contents(strval); + } else { + eggyywarning("Unsupported Xfm$Anim scalar: " + name); + } +} + | xfmanim_body TABLE_V '{' real_list '}' +{ + DCAST(EggXfmAnimData, egg_stack.back())->set_data($4); +} + ; + +/* + * xfm_s_anim + * + * enter: + * exit: returns a new EggXfmSAnim. + * + */ +xfm_s_anim: + XFMSANIM optional_name +{ + EggXfmSAnim *anim_group = new EggXfmSAnim($2); + egg_stack.push_back(anim_group); +} + '{' xfm_s_anim_body '}' +{ + $$ = egg_stack.back(); + egg_stack.pop_back(); +} + ; + + +/* + * xfm_s_anim_body + * + * enter: TOS is EggXfmSAnim. + * exit: xfm_s_anim contents have been filled, including children. + * + */ +xfm_s_anim_body: + empty + | xfm_s_anim_body SCALAR required_name '{' real_or_string '}' +{ + EggXfmSAnim *anim_group = DCAST(EggXfmSAnim, egg_stack.back()); + string name = $3; + double value = $<_number>5; + string strval = $<_string>5; + + if (cmp_nocase_uh(name, "fps") == 0) { + anim_group->set_fps(value); + } else if (cmp_nocase_uh(name, "order") == 0) { + anim_group->set_order(strval); + } else { + eggyywarning("Unsupported Xfm$Anim_S$ scalar: " + name); + } +} + | xfm_s_anim_body sanim +{ + DCAST(EggXfmSAnim, egg_stack.back())->add_child(DCAST(EggNode, $2)); +} + ; + + +/* + * integer_list + * + * enter: + * exit: returns a list of parsed integers. + * + */ +integer_list: + empty +{ + $$ = PTA_double(0); +} + | integer_list integer +{ + $$.push_back((double)$2); +} + ; + +/* + * real_list + * + * enter: + * exit: returns a list of parsed reals. + * + */ +real_list: + empty +{ + $$ = PTA_double(0); +} + | real_list real +{ + $$.push_back($2); +} + ; + +/* + * texture_name + * + * enter: + * exit: Returns an EggTexture pointer, or NULL. + * + */ +texture_name: + required_name +{ + string name = $1; + Textures::iterator vpi = textures.find(name); + if (vpi == textures.end()) { + eggyyerror("Unknown texture " + name); + $$ = PT(EggObject)(); + } else { + $$ = (*vpi).second; + } +} + ; + +/* + * material_name + * + * enter: + * exit: Returns an EggMaterial pointer, or NULL. + * + */ +material_name: + required_name +{ + string name = $1; + Materials::iterator vpi = materials.find(name); + if (vpi == materials.end()) { + eggyyerror("Unknown material " + name); + $$ = PT(EggObject)(); + } else { + $$ = (*vpi).second; + } +} + ; + +/* + * vertex_pool_name + * + * enter: + * exit: Returns an EggVertexPool pointer, or NULL. + * + */ +vertex_pool_name: + required_name +{ + string name = $1; + VertexPools::iterator vpi = vertex_pools.find(name); + if (vpi == vertex_pools.end()) { + eggyyerror("Unknown vertex pool " + name); + $$ = PT(EggObject)(); + } else { + $$ = (*vpi).second; + } +} + ; + + +/* + * required_name + * + * enter: + * exit: Returns a nonempty string as the name of an EggObject. + * + */ +required_name: + empty +{ + eggyyerror("Name required."); + $$ = ""; +} + | string + ; + + +/* + * optional_name + * + * enter: + * exit: Returns a possibly-empty string as the name of an EggObject. + * + */ +optional_name: + optional_string + ; + + +/* + * required_string + * + * enter: + * exit: Returns a nonempty string. + * + */ +required_string: + empty +{ + eggyyerror("String required."); + $$ = ""; +} + | string + ; + +/* + * optional_string + * + * enter: + * exit: Returns a possibly-empty string. + * + */ +optional_string: + empty +{ + $$ = ""; +} + | string + ; + +/* + * string + * + * enter: + * exit: Returns a nonempty string. This is different from required_string + * in that the grammar requires it to be nonempty, so that: (a) + * error messages are more obtuse, and (b) the grammar is less + * ambiguous. Use it whenever required_string does not work. + * + */ +string: + NUMBER +{ + $$ = $<_string>1; +} + | STRING + ; + +/* + * repeated_string + * + * enter: + * exit: Returns a possibly-empty string, which might consist of a number + * of strings or numbers in a row, concatenated together with an + * implicit newline between. + * + */ +repeated_string: + empty +{ + $$ = ""; +} + | repeated_string_body +{ + $$ = $1; +} + ; + +/* + * repeated_string_body + * + * enter: + * exit: Returns a nonempty string, which might consist of a number + * of strings or numbers in a row, concatenated together with an + * implicit newline between. + * + */ +repeated_string_body: + string +{ + $$ = $1; +} + | repeated_string_body string +{ + $$ = $1 + "\n" + $2; +} + ; + +/* + * real + * + * enter: + * exit: Returns an integer or floating-pointer number. + * + */ +real: + NUMBER + ; + +/* + * real_or_string + * + * enter: + * exit: Returns a number as ($<_number>1) or a string (as $<_string>1). + * + */ +real_or_string: + NUMBER +{ + $<_number>$ = $1; + $<_string>$ = $<_string>1; +} + | STRING +{ + $<_number>$ = 0.0; + $<_string>$ = $1; +} + ; + + +/* + * integer + * + * enter: + * exit: Returns an integer number (stored in a double value). + * + */ +integer: + NUMBER +{ + int i = (int)$1; + if ((double)i != $1) { + eggyywarning("Integer expected."); + $$ = (double)i; + } +} + ; + +empty: + ; diff --git a/panda/src/egg/parserDefs.h b/panda/src/egg/parserDefs.h new file mode 100644 index 0000000000..a083e68534 --- /dev/null +++ b/panda/src/egg/parserDefs.h @@ -0,0 +1,45 @@ +// Filename: parserDefs.h +// Created by: drose (17Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARSER_H +#define PARSER_H + +#include + +#include "eggObject.h" + +#include +#include +#include + +#include + +class EggGroupNode; + +void egg_init_parser(istream &in, const string &filename, + EggObject *tos, EggGroupNode *egg_top_node); + +void egg_cleanup_parser(); + +// This structure holds the return value for each token. +// Traditionally, this is a union, and is declared with the %union +// declaration in the parser.y file, but unions are pretty worthless +// in C++ (you can't include an object that has member functions in a +// union), so we'll use a class instead. That means we need to +// declare it externally, here. + +class EggTokenType { +public: + double _number; + string _string; + PT(EggObject) _egg; + PTA_double _number_list; +}; + +// The yacc-generated code expects to use the symbol 'YYSTYPE' to +// refer to the above class. +#define YYSTYPE EggTokenType + +#endif diff --git a/panda/src/egg/test_egg.cxx b/panda/src/egg/test_egg.cxx new file mode 100644 index 0000000000..6730fc24ea --- /dev/null +++ b/panda/src/egg/test_egg.cxx @@ -0,0 +1,29 @@ +// Filename: test_egg.cxx +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "eggData.h" +#include + + +int +main(int argc, char *argv[]) { + if (argc != 2) { + nout << "Specify an egg file to load.\n"; + exit(1); + } + const char *egg_filename = argv[1]; + + EggData data; + data.set_coordinate_system(CS_default); + + if (data.read(egg_filename)) { + data.resolve_externals(""); + data.write_egg(cout); + } else { + nout << "Errors.\n"; + } + return (0); +} + diff --git a/panda/src/egg2sg/Sources.pp b/panda/src/egg2sg/Sources.pp new file mode 100644 index 0000000000..daa48e6879 --- /dev/null +++ b/panda/src/egg2sg/Sources.pp @@ -0,0 +1,31 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET egg2sg + #define LOCAL_LIBS \ + egg builder loader chan char switchnode cull + + #define SOURCES \ + animBundleMaker.cxx animBundleMaker.h characterMaker.cxx \ + characterMaker.h computedVerticesMaker.I computedVerticesMaker.cxx \ + computedVerticesMaker.h config_egg2sg.cxx config_egg2sg.h \ + deferredArcProperty.cxx deferredArcProperty.h \ + deferredArcTraverser.cxx deferredArcTraverser.h eggBinner.cxx \ + eggBinner.h eggLoader.cxx eggLoader.h load_egg_file.cxx \ + load_egg_file.h loaderFileTypeEgg.cxx loaderFileTypeEgg.h + + #define INSTALL_HEADERS \ + load_egg_file.h + +#end lib_target + +#begin test_bin_target + #define TARGET test_loader + #define LOCAL_LIBS \ + egg2sg + + #define SOURCES \ + test_loader.cxx + +#end test_bin_target + diff --git a/panda/src/egg2sg/animBundleMaker.cxx b/panda/src/egg2sg/animBundleMaker.cxx new file mode 100644 index 0000000000..85f175e8ab --- /dev/null +++ b/panda/src/egg2sg/animBundleMaker.cxx @@ -0,0 +1,324 @@ +// Filename: animBundleMaker.cxx +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "animBundleMaker.h" +#include "config_egg2sg.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleMaker::Construtor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +AnimBundleMaker:: +AnimBundleMaker(EggTable *root) : _root(root) { + _fps = 0.0; + _num_frames = 1; + + _ok_fps = true; + _ok_num_frames = true; + + inspect_tree(root); + + if (!_ok_fps) { + egg2sg_cat.warning() + << "AnimBundle " << _root->get_name() + << " specifies contradictory frame rates.\n"; + } else if (_fps == 0.0) { + egg2sg_cat.warning() + << "AnimBundle " << _root->get_name() + << " does not specify a frame rate.\n"; + _fps = 24.0; + } + + if (!_ok_num_frames) { + egg2sg_cat.warning() + << "AnimBundle " << _root->get_name() + << " specifies contradictory number of frames.\n"; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleMaker::make_node +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +AnimBundleNode *AnimBundleMaker:: +make_node() { + return new AnimBundleNode(make_bundle()); +} + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleMaker::make_bundle +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +AnimBundle *AnimBundleMaker:: +make_bundle() { + AnimBundle *bundle = new AnimBundle(_root->get_name(), _fps, _num_frames); + + EggTable::const_iterator ci; + for (ci = _root->begin(); ci != _root->end(); ++ci) { + if ((*ci)->is_of_type(EggTable::get_class_type())) { + EggTable *child = DCAST(EggTable, *ci); + build_hierarchy(child, bundle); + } + } + + bundle->sort_descendants(); + + return bundle; +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleMaker::inspect_tree +// Access: Private +// Description: Walks the egg tree, getting out the fps and the +// number of frames. +//////////////////////////////////////////////////////////////////// +void AnimBundleMaker:: +inspect_tree(EggNode *egg_node) { + if (egg_node->is_of_type(EggAnimData::get_class_type())) { + // Check frame rate. + EggAnimData *egg_anim = DCAST(EggAnimData, egg_node); + if (egg_anim->has_fps()) { + if (_fps == 0.0) { + _fps = egg_anim->get_fps(); + } else if (_fps != egg_anim->get_fps()) { + // Whoops! This table differs in opinion from the other tables. + _fps = min(_fps, (float)egg_anim->get_fps()); + _ok_fps = false; + } + } + } + + if (egg_node->is_of_type(EggXfmSAnim::get_class_type())) { + // Check frame rate. + EggXfmSAnim *egg_anim = DCAST(EggXfmSAnim, egg_node); + if (egg_anim->has_fps()) { + if (_fps == 0.0) { + _fps = egg_anim->get_fps(); + } else if (_fps != egg_anim->get_fps()) { + // Whoops! This table differs in opinion from the other tables. + _fps = min(_fps, (float)egg_anim->get_fps()); + _ok_fps = false; + } + } + } + + if (egg_node->is_of_type(EggSAnimData::get_class_type())) { + // Check number of frames. + EggSAnimData *egg_anim = DCAST(EggSAnimData, egg_node); + int num_frames = egg_anim->get_num_rows(); + + if (num_frames > 1) { + if (_num_frames == 1) { + _num_frames = num_frames; + } else if (_num_frames != num_frames) { + // Whoops! Another disagreement. + _num_frames = min(_num_frames, num_frames); + _ok_num_frames = false; + } + } + } + + if (egg_node->is_of_type(EggXfmAnimData::get_class_type())) { + // Check number of frames. + EggXfmAnimData *egg_anim = DCAST(EggXfmAnimData, egg_node); + int num_frames = egg_anim->get_num_rows(); + + if (num_frames > 1) { + if (_num_frames == 1) { + _num_frames = num_frames; + } else if (_num_frames != num_frames) { + // Whoops! Another disagreement. + _num_frames = min(_num_frames, num_frames); + _ok_num_frames = false; + } + } + } + + if (egg_node->is_of_type(EggGroupNode::get_class_type())) { + // Now recurse. + EggGroupNode *group = DCAST(EggGroupNode, egg_node); + EggGroupNode::const_iterator ci; + for (ci = group->begin(); ci != group->end(); ++ci) { + inspect_tree(*ci); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleMaker::build_hierarchy +// Access: Private +// Description: Walks the egg tree again, creating the AnimChannels +// as appropriate. +//////////////////////////////////////////////////////////////////// +void AnimBundleMaker:: +build_hierarchy(EggTable *egg_table, AnimGroup *parent) { + AnimGroup *this_node = NULL; + + // First, scan the children of egg_table for anim data tables. If + // any of them is named "xform", it's a special case--this one + // stands for the egg_table node itself. Don't ask me why. + + EggTable::const_iterator ci; + for (ci = egg_table->begin(); ci != egg_table->end(); ++ci) { + if ((*ci)->get_name() == "xform") { + if (this_node == NULL) { + this_node = create_xfm_channel((*ci), egg_table->get_name(), parent); + } else { + egg2sg_cat.warning() + << "Duplicate xform table under node " + << egg_table->get_name() << "\n"; + } + } + } + + // If none of them were named "xform", just create a plain old + // AnimGroup. + if (this_node == NULL) { + this_node = new AnimGroup(parent, egg_table->get_name()); + } + + // Now walk the children again, creating any leftover tables, and + // recursing. + for (ci = egg_table->begin(); ci != egg_table->end(); ++ci) { + if ((*ci)->get_name() == "xform") { + // Skip this one. We already got it. + } else if ((*ci)->is_of_type(EggSAnimData::get_class_type())) { + EggSAnimData *egg_anim = DCAST(EggSAnimData, *ci); + create_s_channel(egg_anim, egg_anim->get_name(), this_node); + + } else if ((*ci)->is_of_type(EggTable::get_class_type())) { + EggTable *child = DCAST(EggTable, *ci); + build_hierarchy(child, this_node); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleMaker::create_s_channel +// Access: Private +// Description: Creates an AnimChannelScalarTable corresponding to +// the given EggSAnimData structure. +//////////////////////////////////////////////////////////////////// +AnimChannelScalarTable *AnimBundleMaker:: +create_s_channel(EggSAnimData *egg_anim, const string &name, + AnimGroup *parent) { + AnimChannelScalarTable *table + = new AnimChannelScalarTable(parent, name); + + // First we have to copy the table data from PTA_double to + // PTA_float. + PTA_float new_data(egg_anim->get_num_rows()); + for (int i = 0; i < egg_anim->get_num_rows(); i++) { + new_data[i] = (float)egg_anim->get_value(i); + } + + // Now we can assign the table. + table->set_table(new_data); + + return table; +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleMaker::create_xfm_channel (EggNode) +// Access: Private +// Description: Creates an AnimChannelMatrixXfmTable corresponding to +// the given EggNode structure, if possible. +//////////////////////////////////////////////////////////////////// +AnimChannelMatrixXfmTable *AnimBundleMaker:: +create_xfm_channel(EggNode *egg_node, const string &name, + AnimGroup *parent) { + if (egg_node->is_of_type(EggXfmAnimData::get_class_type())) { + EggXfmAnimData *egg_anim = DCAST(EggXfmAnimData, egg_node); + EggXfmSAnim new_anim(*egg_anim); + return create_xfm_channel(&new_anim, name, parent); + + } else if (egg_node->is_of_type(EggXfmSAnim::get_class_type())) { + EggXfmSAnim *egg_anim = DCAST(EggXfmSAnim, egg_node); + return create_xfm_channel(egg_anim, name, parent); + } + + egg2sg_cat.warning() + << "Inappropriate node named xform under node " + << name << "\n"; + return NULL; +} + + +//////////////////////////////////////////////////////////////////// +// Function: AnimBundleMaker::create_xfm_channel (EggXfmSAnim) +// Access: Private +// Description: Creates an AnimChannelMatrixXfmTable corresponding to +// the given EggXfmSAnim structure. +//////////////////////////////////////////////////////////////////// +AnimChannelMatrixXfmTable *AnimBundleMaker:: +create_xfm_channel(EggXfmSAnim *egg_anim, const string &name, + AnimGroup *parent) { + AnimChannelMatrixXfmTable *table + = new AnimChannelMatrixXfmTable(parent, name); + + // The EggXfmSAnim structure has a number of children which are + // EggSAnimData tables. Each of these represents a separate + // component of the transform data, and will be added to the table. + + EggXfmSAnim::const_iterator ci; + for (ci = egg_anim->begin(); ci != egg_anim->end(); ++ci) { + if ((*ci)->is_of_type(EggSAnimData::get_class_type())) { + EggSAnimData *child = DCAST(EggSAnimData, *ci); + + if (child->get_name().empty()) { + egg2sg_cat.warning() + << "Unnamed subtable of " << name + << "\n"; + } else { + char table_id = child->get_name()[0]; + + if (child->get_name().length() > 1 || + !table->is_valid_id(table_id)) { + egg2sg_cat.warning() + << "Unexpected table name " << child->get_name() + << ", child of " << name << "\n"; + + } else if (table->has_table(table_id)) { + egg2sg_cat.warning() + << "Duplicate table definition for " << table_id + << " under " << name << "\n"; + + } else { + + // Now we have to copy the table data from PTA_double to + // PTA_float. + PTA_float new_data(child->get_num_rows()); + for (int i = 0; i < child->get_num_rows(); i++) { + new_data[i] = (float)child->get_value(i); + } + + // Now we can assign the table. + table->set_table(table_id, new_data); + } + } + } + } + + return table; +} diff --git a/panda/src/egg2sg/animBundleMaker.h b/panda/src/egg2sg/animBundleMaker.h new file mode 100644 index 0000000000..3877e10663 --- /dev/null +++ b/panda/src/egg2sg/animBundleMaker.h @@ -0,0 +1,62 @@ +// Filename: animBundleMaker.h +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANIMBUNDLEMAKER_H +#define ANIMBUNDLEMAKER_H + +#include + +#include + +#include + +class EggNode; +class EggGroupNode; +class EggTable; +class EggXfmSAnim; +class EggSAnimData; +class AnimGroup; +class AnimBundle; +class AnimBundleNode; +class AnimChannelScalarTable; +class AnimChannelMatrixXfmTable; + +/////////////////////////////////////////////////////////////////// +// Class : AnimBundleMaker +// Description : Converts an EggTable hierarchy, beginning with a +// entry, into an AnimBundle hierarchy. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG AnimBundleMaker { +public: + AnimBundleMaker(EggTable *root); + + AnimBundleNode *make_node(); + +private: + AnimBundle *make_bundle(); + + void inspect_tree(EggNode *node); + void build_hierarchy(EggTable *egg_table, AnimGroup *parent); + + AnimChannelScalarTable * + create_s_channel(EggSAnimData *egg_anim, const string &name, + AnimGroup *parent); + AnimChannelMatrixXfmTable * + create_xfm_channel(EggNode *egg_node, const string &name, + AnimGroup *parent); + AnimChannelMatrixXfmTable * + create_xfm_channel(EggXfmSAnim *egg_anim, const string &name, + AnimGroup *parent); + + float _fps; + int _num_frames; + bool _ok_fps; + bool _ok_num_frames; + + EggTable *_root; + +}; + +#endif diff --git a/panda/src/egg2sg/characterMaker.cxx b/panda/src/egg2sg/characterMaker.cxx new file mode 100644 index 0000000000..94531ab040 --- /dev/null +++ b/panda/src/egg2sg/characterMaker.cxx @@ -0,0 +1,361 @@ +// Filename: characterMaker.cxx +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "characterMaker.h" +#include "eggLoader.h" +#include "config_egg2sg.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: CharacterMaker::Construtor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CharacterMaker:: +CharacterMaker(EggGroup *root, EggLoader &loader) + : _egg_root(root), _loader(loader) { + + _character_node = new Character(_egg_root->get_name()); + _bundle = _character_node->get_bundle(); + + _morph_root = new PartGroup(_bundle, "morph"); + _skeleton_root = new PartGroup(_bundle, ""); +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterMaker::make_node +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Character *CharacterMaker:: +make_node() { + make_bundle(); + _character_node->_parts = _parts; + return _character_node; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CharacterMaker::egg_to_part +// Access: Public +// Description: Returns the PartGroup node associated with the given +// egg node. If the egg node is not a node in the +// character's hierarchy, returns the top of the +// character's hierarchy. +//////////////////////////////////////////////////////////////////// +PartGroup *CharacterMaker:: +egg_to_part(EggNode *egg_node) const { + int index = egg_to_index(egg_node); + if (index < 0) { + // If there's a reference to the geometry outside of the + // character, just return the root of the character. + return _bundle; + } + nassertr(index < _parts.size(), NULL); + return _parts[index]; +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterMaker::egg_to_index +// Access: Public +// Description: Returns the index number associated with the +// PartGroup node for the given egg node, or -1. +//////////////////////////////////////////////////////////////////// +int CharacterMaker:: +egg_to_index(EggNode *egg_node) const { + NodeMap::const_iterator nmi = _node_map.find(egg_node); + if (nmi == _node_map.end()) { + return -1; + } + return (*nmi).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterMaker::part_to_node +// Access: Public +// Description: Returns the scene graph node associated with the +// given PartGroup node, if there is one. If the +// PartGroup does not have an associated node, returns +// the character's top node. +//////////////////////////////////////////////////////////////////// +NamedNode *CharacterMaker:: +part_to_node(PartGroup *part) const { + if (part->is_of_type(CharacterJoint::get_class_type())) { + CharacterJoint *joint = DCAST(CharacterJoint, part); + if (joint->_geom_node != (NamedNode *)NULL) { + return joint->_geom_node; + } + } + + return _character_node; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CharacterMaker::create_slider +// Access: Public +// Description: Creates a new morph slider of the given name, and +// returns its index. This is actually called by +// ComputedVerticesMaker, which is responsible for +// identifying all the unique morph target names. +//////////////////////////////////////////////////////////////////// +int CharacterMaker:: +create_slider(const string &name) { + CharacterSlider *slider = new CharacterSlider(_morph_root, name); + int index = _parts.size(); + _parts.push_back(slider); + return index; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CharacterMaker::make_bundle +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +CharacterJointBundle *CharacterMaker:: +make_bundle() { + build_joint_hierarchy(_egg_root, _skeleton_root); + _bundle->sort_descendants(); + + parent_joint_nodes(_skeleton_root); + make_geometry(_egg_root); + + _character_node->_computed_vertices = + _comp_verts_maker.make_computed_vertices(_character_node, *this); + + return _bundle; +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterMaker::build_hierarchy +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CharacterMaker:: +build_joint_hierarchy(EggNode *egg_node, PartGroup *part) { + int index = -1; + + if (egg_node->is_of_type(EggGroup::get_class_type())) { + EggGroup *egg_group = DCAST(EggGroup, egg_node); + + // Each joint we come across is significant, and gets added to the + // hierarchy. Non-joints we encounter are ignored. + if (egg_group->get_group_type() == EggGroup::GT_joint) { + // We need to get the transform of the joint, and then convert + // it to single-precision. + LMatrix4d matd; + if (egg_group->has_transform()) { + matd = egg_group->get_transform(); + } else { + matd = LMatrix4d::ident_mat(); + } + + LMatrix4f matf(matd(0,0), matd(0,1), matd(0,2), matd(0,3), + matd(1,0), matd(1,1), matd(1,2), matd(1,3), + matd(2,0), matd(2,1), matd(2,2), matd(2,3), + matd(3,0), matd(3,1), matd(3,2), matd(3,3)); + + CharacterJoint *joint = + new CharacterJoint(part, egg_group->get_name(), matf); + index = _parts.size(); + _parts.push_back(joint); + + PartGroup *pgroup = (PartGroup *)joint; + + if (egg_group->get_dcs_flag()) { + // If the joint requested an explicit DCS, create a node for + // it. + joint->_geom_node = new NamedNode(egg_group->get_name()); + } + + part = joint; + } + + EggGroup::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + build_joint_hierarchy((*ci), part); + } + } + + _node_map[egg_node] = index; +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterMaker::parent_joint_nodes +// Access: Private +// Description: Walks the joint hierarchy, and parents any explicit +// nodes created for the joints under the character +// node. +//////////////////////////////////////////////////////////////////// +void CharacterMaker:: +parent_joint_nodes(PartGroup *part) { + if (part->is_of_type(CharacterJoint::get_class_type())) { + CharacterJoint *joint = DCAST(CharacterJoint, part); + NamedNode *joint_node = joint->_geom_node; + if (joint_node != NULL) { + RenderRelation *arc = new RenderRelation(_character_node, joint_node); + joint->add_net_transform(arc); + arc->set_transition(new TransformTransition(joint->_net_transform)); + } + } + + for (int i = 0; i < part->get_num_children(); i++) { + parent_joint_nodes(part->get_child(i)); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterMaker::make_geometry +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CharacterMaker:: +make_geometry(EggNode *egg_node) { + if (egg_node->is_of_type(EggPrimitive::get_class_type())) { + EggPrimitive *egg_primitive = DCAST(EggPrimitive, egg_node); + if (!egg_primitive->empty()) { + EggGroupNode *prim_home = determine_primitive_home(egg_primitive); + + if (prim_home == NULL) { + // This is a totally dynamic primitive that lives under the + // character's node. + make_dynamic_primitive(egg_primitive, _egg_root); + + } else { + // This is a static primitive that lives under a particular + // node. + make_static_primitive(egg_primitive, prim_home); + } + } + } + + if (egg_node->is_of_type(EggGroupNode::get_class_type())) { + EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node); + + EggGroupNode::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + make_geometry(*ci); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterMaker::make_static_primitive +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CharacterMaker:: +make_static_primitive(EggPrimitive *egg_primitive, EggGroupNode *prim_home) { + NamedNode *node = part_to_node(egg_to_part(prim_home)); + + // We need this funny transform to convert from the coordinate + // space of the original vertices to that of the new joint node. + LMatrix4d transform = + egg_primitive->get_vertex_frame() * + prim_home->get_node_frame_inv(); + + _loader.make_nonindexed_primitive(egg_primitive, node, &transform); +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterMaker::make_dynamic_primitive +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CharacterMaker:: +make_dynamic_primitive(EggPrimitive *egg_primitive, EggGroupNode *prim_home) { + NamedNode *node = part_to_node(egg_to_part(prim_home)); + + LMatrix4d transform = + egg_primitive->get_vertex_frame() * + prim_home->get_node_frame_inv(); + + _loader.make_indexed_primitive(egg_primitive, node, &transform, + _comp_verts_maker); +} + +//////////////////////////////////////////////////////////////////// +// Function: CharacterMaker::determine_primitive_home +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +EggGroupNode *CharacterMaker:: +determine_primitive_home(EggPrimitive *egg_primitive) { + // A primitive's vertices may be referenced by any joint in the + // character. Or, the primitive itself may be explicitly placed + // under a joint. + + // If any of the vertices are referenced by multiple joints, or if + // any two vertices are referenced by different joints, then the + // entire primitive must be considered dynamic. (We'll indicate a + // dynamic primitive by returning NULL.) + + // We need to keep track of the one joint we've encountered so far, + // to see if all the vertices are referenced by the same joint. + EggGroupNode *home = NULL; + + EggPrimitive::const_iterator vi; + for (vi = egg_primitive->begin(); + vi != egg_primitive->end(); + ++vi) { + EggVertex *vertex = (*vi); + if (vertex->gref_size() > 1) { + // This vertex is referenced by multiple joints; the primitive + // is dynamic. + return NULL; + } + + EggGroupNode *vertex_home; + + if (vertex->gref_size() == 0) { + // This vertex is not referenced at all, which means it belongs + // right where it is. + vertex_home = egg_primitive->get_parent(); + } else { + nassertr(vertex->gref_size() == 1, NULL); + // This vertex is referenced exactly once. + vertex_home = *vertex->gref_begin(); + } + + if (home != NULL && home != vertex_home) { + // Oops, two vertices are referenced by different joints! The + // primitive is dynamic. + return NULL; + } + + home = vertex_home; + } + + // This shouldn't be possible, unless there are no vertices--but we + // check for that before calling this function. + nassertr(home != NULL, NULL); + + // So, all the vertices are assigned to the same group. This means + // the polygon belongs entirely to one joint. If that joint happens + // to have a flag, and will therefore be an explicit animated + // node, we might as well put the polygon under that joint. + // Otherwise, we'll leave the polygon under the character node and + // animate it there explicitly. + + if (home->is_of_type(EggGroup::get_class_type())) { + EggGroup *egg_group = DCAST(EggGroup, home); + if (egg_group->get_dcs_flag()) { + return home; + } + } + + return NULL; +} diff --git a/panda/src/egg2sg/characterMaker.h b/panda/src/egg2sg/characterMaker.h new file mode 100644 index 0000000000..5ec08c543f --- /dev/null +++ b/panda/src/egg2sg/characterMaker.h @@ -0,0 +1,77 @@ +// Filename: characterMaker.h +// Created by: drose (23Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CHARACTERMAKER_H +#define CHARACTERMAKER_H + +#include + +#include "computedVerticesMaker.h" + +#include +#include + +#include +#include + +class EggNode; +class EggGroup; +class EggGroupNode; +class EggPrimitive; +class PartGroup; +class CharacterJointBundle; +class Character; +class CharacterSlider; +class MovingPartBase; +class NamedNode; +class EggLoader; + +/////////////////////////////////////////////////////////////////// +// Class : CharacterMaker +// Description : Converts an EggGroup hierarchy, beginning with a +// group with set, to a character node with +// joints. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG CharacterMaker { +public: + CharacterMaker(EggGroup *root, EggLoader &loader); + + Character *make_node(); + + PartGroup *egg_to_part(EggNode *egg_node) const; + int egg_to_index(EggNode *egg_node) const; + NamedNode *part_to_node(PartGroup *part) const; + + int create_slider(const string &name); + +private: + CharacterJointBundle *make_bundle(); + void build_joint_hierarchy(EggNode *egg_node, PartGroup *part); + void parent_joint_nodes(PartGroup *part); + + void make_geometry(EggNode *egg_node); + + void make_static_primitive(EggPrimitive *egg_primitive, + EggGroupNode *prim_home); + void make_dynamic_primitive(EggPrimitive *egg_primitive, + EggGroupNode *prim_home); + EggGroupNode *determine_primitive_home(EggPrimitive *egg_primitive); + + typedef map NodeMap; + NodeMap _node_map; + + typedef vector_PartGroupStar Parts; + Parts _parts; + + EggLoader &_loader; + EggGroup *_egg_root; + Character *_character_node; + CharacterJointBundle *_bundle; + ComputedVerticesMaker _comp_verts_maker; + PartGroup *_morph_root; + PartGroup *_skeleton_root; +}; + +#endif diff --git a/panda/src/egg2sg/computedVerticesMaker.I b/panda/src/egg2sg/computedVerticesMaker.I new file mode 100644 index 0000000000..134ce28cc7 --- /dev/null +++ b/panda/src/egg2sg/computedVerticesMaker.I @@ -0,0 +1,4 @@ +// Filename: computedVerticesMaker.I +// Created by: drose (01Mar99) +// +//////////////////////////////////////////////////////////////////// diff --git a/panda/src/egg2sg/computedVerticesMaker.cxx b/panda/src/egg2sg/computedVerticesMaker.cxx new file mode 100644 index 0000000000..ae2589be74 --- /dev/null +++ b/panda/src/egg2sg/computedVerticesMaker.cxx @@ -0,0 +1,497 @@ +// Filename: computedVerticesMaker.cxx +// Created by: drose (01Mar99) +// +//////////////////////////////////////////////////////////////////// + +#include "computedVerticesMaker.h" +#include "characterMaker.h" + +#include +#include +#include +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMaker::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ComputedVerticesMaker:: +ComputedVerticesMaker() : + _coords(0), _norms(0), _colors(0), _texcoords(0) +{ + _current_vc = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMaker::begin_new_space +// Access: Public +// Description: Should be called before beginning the definition for +// a new transform space. +//////////////////////////////////////////////////////////////////// +void ComputedVerticesMaker:: +begin_new_space() { + _current_jw.clear(); + _current_vc = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMaker::add_joint +// Access: Public +// Description: Adds the joint with its associated membership amount +// to the current transform space definition. +//////////////////////////////////////////////////////////////////// +void ComputedVerticesMaker:: +add_joint(EggNode *joint, double membership) { + // This must be called between a call to begin_new_space() and + // mark_space(). + assert(_current_vc == NULL); + + if (membership == 0.0) { + return; + } + + assert(membership > 0.0); + + JointWeights::iterator jwi = _current_jw.find(joint); + + if (jwi != _current_jw.end()) { + // We'd already added this joint previously. Increment its total + // membership. + (*jwi).second += membership; + } else { + // This is the first time we've added this joint. + _current_jw[joint] = membership; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMaker::mark_space +// Access: Public +// Description: Completes the definition of a transform space as a +// set of joints and memberships. From this point until +// the next call to begin_new_space(), vertices may be +// added to the transform space via calls to +// add_vertex(), add_normal(), etc. +//////////////////////////////////////////////////////////////////// +void ComputedVerticesMaker:: +mark_space() { + // This must be called after a call to begin_new_space(). + assert(_current_vc == NULL); + + _current_jw.normalize_weights(); + + // This will look up a previously-defined VertexCollection, if we've + // used this transform space before, or it will implicitly create a + // new one if we haven't. + _current_vc = &_transforms[_current_jw]; +} + + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMaker::add_vertex +// Access: Public +// Description: Adds a vertex value to the currently-defined +// transform space, and returns its index number within +// the array. +//////////////////////////////////////////////////////////////////// +int ComputedVerticesMaker:: +add_vertex(const Vertexd &vertex, const EggMorphVertexList &morphs, + const LMatrix4d &transform) { + // This must be called after a call to mark_space(), and before a + // call to begin_new_space(). + assert(_current_vc != NULL); + + Vertexf tv = LCAST(float, vertex * transform); + int index = _current_vc->_vmap.add_value(tv, morphs, _coords); + _current_vc->_vindex.insert(index); + + // Now create any morph sliders. + EggMorphVertexList::const_iterator mli; + for (mli = morphs.begin(); mli != morphs.end(); ++mli) { + const EggMorphVertex &morph = (*mli); + LVector3d offset = morph.get_offset() * transform; + if (!offset.almost_equal(LVector3d(0.0, 0.0, 0.0), 0.0001)) { + MorphList &mlist = _morphs[morph.get_name()]; + + // Have we already morphed this vertex? + VertexMorphList::iterator vmi = mlist._vmorphs.find(index); + if (vmi != mlist._vmorphs.end()) { + // Yes, we have. + assert(offset.almost_equal(LCAST(double, (*vmi).second), 0.0001)); + } else { + // No, we haven't yet; morph it now. + mlist._vmorphs[index] = LCAST(float, offset); + } + } + } + + return index; +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMaker::add_normal +// Access: Public +// Description: Adds a normal value to the currently-defined +// transform space, and returns its index number within +// the array. +//////////////////////////////////////////////////////////////////// +int ComputedVerticesMaker:: +add_normal(const Normald &normal, const EggMorphNormalList &morphs, + const LMatrix4d &transform) { + // This must be called after a call to mark_space(), and before a + // call to begin_new_space(). + assert(_current_vc != NULL); + + Normalf tn = LCAST(float, normal * transform); + int index = _current_vc->_nmap.add_value(tn, morphs, _norms); + _current_vc->_nindex.insert(index); + + // Now create any morph sliders. + EggMorphNormalList::const_iterator mli; + for (mli = morphs.begin(); mli != morphs.end(); ++mli) { + const EggMorphNormal &morph = (*mli); + LVector3d offset = morph.get_offset() * transform; + if (!offset.almost_equal(LVector3d(0.0, 0.0, 0.0), 0.0001)) { + MorphList &mlist = _morphs[morph.get_name()]; + + // Have we already morphed this normal? + NormalMorphList::iterator vmi = mlist._nmorphs.find(index); + if (vmi != mlist._nmorphs.end()) { + // Yes, we have. + assert(offset.almost_equal(LCAST(double, (*vmi).second), 0.0001)); + } else { + // No, we haven't yet; morph it now. + mlist._nmorphs[index] = LCAST(float, offset); + } + } + } + + return index; +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMaker::add_texcoord +// Access: Public +// Description: Adds a texcoord value to the array (texture +// coordinates are unrelated to the current transform +// space), and returns its index number within the +// array. +//////////////////////////////////////////////////////////////////// +int ComputedVerticesMaker:: +add_texcoord(const TexCoordd &texcoord, const EggMorphTexCoordList &morphs, + const LMatrix3d &transform) { + TexCoordf ttc = LCAST(float, texcoord * transform); + int index = _tmap.add_value(ttc, morphs, _texcoords); + _tindex.insert(index); + + // Now create any morph sliders. + EggMorphTexCoordList::const_iterator mli; + for (mli = morphs.begin(); mli != morphs.end(); ++mli) { + const EggMorphTexCoord &morph = (*mli); + LVector2d offset = morph.get_offset() * transform; + if (!offset.almost_equal(LVector2d(0.0, 0.0), 0.0001)) { + MorphList &mlist = _morphs[morph.get_name()]; + + // Have we already morphed this texcoord? + TexCoordMorphList::iterator vmi = mlist._tmorphs.find(index); + if (vmi != mlist._tmorphs.end()) { + // Yes, we have. + assert(offset.almost_equal(LCAST(double, (*vmi).second), 0.0001)); + } else { + // No, we haven't yet; morph it now. + mlist._tmorphs[index] = LCAST(float, offset); + } + } + } + + return index; +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMaker::add_color +// Access: Public +// Description: Adds a color value to the array (color values +// are unrelated to the current transform space), and +// returns its index number within the array. +//////////////////////////////////////////////////////////////////// +int ComputedVerticesMaker:: +add_color(const Colorf &color, const EggMorphColorList &morphs) { + int index = _cmap.add_value(color, morphs, _colors); + _cindex.insert(index); + + // Now create any morph sliders. + EggMorphColorList::const_iterator mli; + for (mli = morphs.begin(); mli != morphs.end(); ++mli) { + const EggMorphColor &morph = (*mli); + LVector4f offset = morph.get_offset(); + if (!offset.almost_equal(LVector4f(0.0, 0.0, 0.0, 0.0), 0.0001)) { + MorphList &mlist = _morphs[morph.get_name()]; + + // Have we already morphed this color? + ColorMorphList::iterator vmi = mlist._cmorphs.find(index); + if (vmi != mlist._cmorphs.end()) { + // Yes, we have. + assert(offset.almost_equal((*vmi).second, 0.0001)); + } else { + // No, we haven't yet; morph it now. + mlist._cmorphs[index] = offset; + } + } + } + + return index; +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMaker::make_computed_vertices +// Access: Public +// Description: After all spaces have been defined and all vertices +// added, creates a new ComputedVertices object and +// returns it. +//////////////////////////////////////////////////////////////////// +ComputedVertices *ComputedVerticesMaker:: +make_computed_vertices(Character *character, CharacterMaker &char_maker) { + // We must first build up a set of all the unique kinds of vertex + // transforms. + typedef set VertexTransforms; + VertexTransforms transforms; + + TransformSpaces::const_iterator tsi; + for (tsi = _transforms.begin(); + tsi != _transforms.end(); + ++tsi) { + const JointWeights &jw = (*tsi).first; + const VertexCollection &vc = (*tsi).second; + + JointWeights::const_iterator jwi; + for (jwi = jw.begin(); jwi != jw.end(); ++jwi) { + double weight = (*jwi).second; + EggNode *egg_joint = (*jwi).first; + int joint_index = char_maker.egg_to_index(egg_joint); + + // Look for a VertexTransform that matches this template. + ComputedVertices::VertexTransform new_vt; + new_vt._joint_index = joint_index; + new_vt._effect = (float)weight; + + // This will either insert the VertexTransform into the set and + // return its newly-created iterator, or it will return the + // iterator referring to the previously-inserted VertexTransform + // like this. + VertexTransforms::iterator vti = transforms.insert(new_vt).first; + + // We can discard the const-ness of the set's iterator, because + // we will only be changing a part of the VertexTransform that + // doesn't affect its sort order within the set. + ComputedVertices::VertexTransform &insert_vt = + (ComputedVertices::VertexTransform &)*vti; + + // Now add in all the vertices and normals. + copy(vc._vindex.begin(), vc._vindex.end(), + back_inserter(insert_vt._vindex)); + copy(vc._nindex.begin(), vc._nindex.end(), + back_inserter(insert_vt._nindex)); + } + } + + // Ok, now we have the set of all VertexTransforms. Create a + // ComputedVertices object that reflects this. + ComputedVertices *comp_verts = new ComputedVertices; + copy(transforms.begin(), transforms.end(), + back_inserter(comp_verts->_transforms)); + + character->_cv._coords = _coords; + character->_cv._norms = _norms; + character->_cv._colors = _colors; + character->_cv._texcoords = _texcoords; + + // Finally, add in all the morph definitions. + Morphs::const_iterator mi; + for (mi = _morphs.begin(); mi != _morphs.end(); ++mi) { + const string &name = (*mi).first; + const MorphList &mlist = (*mi).second; + + int slider_index = char_maker.create_slider(name); + + if (!mlist._vmorphs.empty()) { + // We push an empty MorphVertex object and then modify it, + // rather than filling it first and then pushing it, just to + // avoid unnecessary copying of data. + comp_verts->_vertex_morphs.push_back(ComputedVerticesMorphVertex()); + ComputedVerticesMorphVertex &mv = comp_verts->_vertex_morphs.back(); + mv._slider_index = slider_index; + + VertexMorphList::const_iterator vmi; + for (vmi = mlist._vmorphs.begin(); + vmi != mlist._vmorphs.end(); + ++vmi) { + mv._morphs.push_back(ComputedVerticesMorphValue3((*vmi).first, + (*vmi).second)); + } + } + + if (!mlist._nmorphs.empty()) { + comp_verts->_normal_morphs.push_back(ComputedVerticesMorphNormal()); + ComputedVerticesMorphNormal &mv = comp_verts->_normal_morphs.back(); + mv._slider_index = slider_index; + + NormalMorphList::const_iterator vmi; + for (vmi = mlist._nmorphs.begin(); + vmi != mlist._nmorphs.end(); + ++vmi) { + mv._morphs.push_back(ComputedVerticesMorphValue3((*vmi).first, + (*vmi).second)); + } + } + + if (!mlist._tmorphs.empty()) { + comp_verts->_texcoord_morphs.push_back(ComputedVerticesMorphTexCoord()); + ComputedVerticesMorphTexCoord &mv = comp_verts->_texcoord_morphs.back(); + mv._slider_index = slider_index; + + TexCoordMorphList::const_iterator vmi; + for (vmi = mlist._tmorphs.begin(); + vmi != mlist._tmorphs.end(); + ++vmi) { + mv._morphs.push_back(ComputedVerticesMorphValue2((*vmi).first, + (*vmi).second)); + } + } + + if (!mlist._cmorphs.empty()) { + comp_verts->_color_morphs.push_back(ComputedVerticesMorphColor()); + ComputedVerticesMorphColor &mv = comp_verts->_color_morphs.back(); + mv._slider_index = slider_index; + + ColorMorphList::const_iterator vmi; + for (vmi = mlist._cmorphs.begin(); + vmi != mlist._cmorphs.end(); + ++vmi) { + mv._morphs.push_back(ComputedVerticesMorphValue4((*vmi).first, + (*vmi).second)); + } + } + } + + comp_verts->make_orig(character); + return comp_verts; +} + + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMaker::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ComputedVerticesMaker:: +write(ostream &out) const { + out << "ComputedVerticesMaker, " + << _transforms.size() << " transform spaces, " + << _coords.size() << " vertices, " + << _norms.size() << " normals, " + << _texcoords.size() << " uvs, " + << _colors.size() << " colors.\n"; + TransformSpaces::const_iterator tsi; + for (tsi = _transforms.begin(); tsi != _transforms.end(); ++tsi) { + const JointWeights &jw = (*tsi).first; + const VertexCollection &vc = (*tsi).second; + out << " " << jw << " has " + << vc._vindex.size() << " vertices and " + << vc._nindex.size() << " normals\n"; + } + + Morphs::const_iterator mi; + for (mi = _morphs.begin(); mi != _morphs.end(); ++mi) { + const string &name = (*mi).first; + const MorphList &mlist = (*mi).second; + out << name << " morphs " + << mlist._vmorphs.size() << " vertices, " + << mlist._nmorphs.size() << " normals, " + << mlist._tmorphs.size() << " uvs, and " + << mlist._cmorphs.size() << " colors.\n"; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMaker::JointWeights::Ordering operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool ComputedVerticesMaker::JointWeights:: +operator < (const JointWeights &other) const { + const_iterator i = begin(); + const_iterator j = other.begin(); + + while (i != end() && j != other.end()) { + if ((*i).first != (*j).first) { + return (*i).first < (*j).first; + } + if ((*i).second != (*j).second) { + return (*i).second < (*j).second; + } + ++i; + ++j; + } + + if (i == end() && j != other.end()) { + // The first i.size() items are equivalent, but list j is longer. + return true; + } + + if (i != end() && j == other.end()) { + // The first j.size() items are equivalent, but list i is longer. + return false; + } + + // The lists are equivalent. + return false; +} + + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMaker::JointWeights::normalize_weights +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ComputedVerticesMaker::JointWeights:: +normalize_weights() { + if (!empty()) { + double net_weight = 0.0; + + iterator i; + for (i = begin(); i != end(); ++i) { + double weight = (*i).second; + assert(weight > 0.0); + net_weight += weight; + } + assert(net_weight != 0.0); + + for (i = begin(); i != end(); ++i) { + (*i).second /= net_weight; + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMaker::JointWeights::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ComputedVerticesMaker::JointWeights:: +output(ostream &out) const { + out << "jw("; + if (!empty()) { + const_iterator i = begin(); + out << (*i).first->get_name() << ":" << (*i).second; + for (++i; i != end(); ++i) { + out << " " << (*i).first->get_name() << ":" << (*i).second; + } + } + out << ")"; +} diff --git a/panda/src/egg2sg/computedVerticesMaker.h b/panda/src/egg2sg/computedVerticesMaker.h new file mode 100644 index 0000000000..12e2dd2e71 --- /dev/null +++ b/panda/src/egg2sg/computedVerticesMaker.h @@ -0,0 +1,132 @@ +// Filename: computedVerticesMaker.h +// Created by: drose (01Mar99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COMPUTEDVERTICESMAKER_H +#define COMPUTEDVERTICESMAKER_H + +#include + +#include "computedVerticesMakerEntity.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +class ComputedVertices; +class CharacterMaker; +class EggNode; + +/////////////////////////////////////////////////////////////////// +// Class : ComputedVerticesMaker +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG ComputedVerticesMaker { +public: + ComputedVerticesMaker(); + + void begin_new_space(); + void add_joint(EggNode *joint, double membership); + void mark_space(); + + int add_vertex(const Vertexd &vertex, const EggMorphVertexList &morphs, + const LMatrix4d &transform); + int add_normal(const Normald &normal, const EggMorphNormalList &morphs, + const LMatrix4d &transform); + int add_texcoord(const TexCoordd &texcoord, + const EggMorphTexCoordList &morphs, + const LMatrix3d &transform); + int add_color(const Colorf &color, const EggMorphColorList &morphs); + + ComputedVertices *make_computed_vertices(Character *character, + CharacterMaker &char_maker); + + void write(ostream &out) const; + +public: + PTA_Vertexf _coords; + PTA_Normalf _norms; + PTA_Colorf _colors; + PTA_TexCoordf _texcoords; + +protected: + typedef map VertexMorphList; + typedef map NormalMorphList; + typedef map TexCoordMorphList; + typedef map ColorMorphList; + class MorphList { + public: + VertexMorphList _vmorphs; + NormalMorphList _nmorphs; + TexCoordMorphList _tmorphs; + ColorMorphList _cmorphs; + }; + + typedef map Morphs; + Morphs _morphs; + + typedef set Vertices; + + Vertices _cindex; + Vertices _tindex; + + ComputedVerticesMakerTexCoordMap _tmap; + ComputedVerticesMakerColorMap _cmap; + +#ifdef WIN32_VC +public: +#endif + + class JointWeights: public map { + public: + bool operator < (const JointWeights &other) const; + void normalize_weights(); + + void output(ostream &out) const; + }; + +protected: + class VertexCollection { + public: + Vertices _vindex; + Vertices _nindex; + + ComputedVerticesMakerVertexMap _vmap; + ComputedVerticesMakerNormalMap _nmap; + }; + + typedef map TransformSpaces; + TransformSpaces _transforms; + + class VertexTransform { + public: + EggNode *_joint; + float _effect; + }; + + JointWeights _current_jw; + VertexCollection *_current_vc; + + friend inline ostream &operator << (ostream &, const JointWeights &); +}; + +inline ostream & +operator << (ostream &out, const ComputedVerticesMaker::JointWeights &jw) { + jw.output(out); + return out; +} + +#include "computedVerticesMaker.I" + +#endif + diff --git a/panda/src/egg2sg/computedVerticesMakerEntity.I b/panda/src/egg2sg/computedVerticesMakerEntity.I new file mode 100644 index 0000000000..2790294a65 --- /dev/null +++ b/panda/src/egg2sg/computedVerticesMakerEntity.I @@ -0,0 +1,71 @@ +// Filename: computedVerticesMakerEntity.I +// Created by: drose (02Mar99) +// +//////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMakerEntity::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ComputedVerticesMakerEntity:: +ComputedVerticesMakerEntity(const ValueType &value, const MorphType &morphs) + : _value(value), _morphs(morphs) { +} + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMakerEntity::Ordering operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +bool ComputedVerticesMakerEntity:: +operator < (const ComputedVerticesMakerEntity &other) const { + // First, check the value. This is some vector type, which we + // compare componentwise. + int compare = _value.compare_to(other._value); + if (compare != 0) { + return compare < 0; + } + + // The values are identical; compare the morphs. + return _morphs < other._morphs; +} + + +//////////////////////////////////////////////////////////////////// +// Function: ComputedVerticesMakerEntityMap::add_value +// Access: Public +// Description: Creates a new entry for the value type and morph +// combination, if it is unique, and returns its new +// index number, or returns the index number of a +// previously-created, identical value type and morph. +// +// The PTA table is updated as each new index number is +// allocated so that table[index] == value. It is also +// used to determine the next available index number. +//////////////////////////////////////////////////////////////////// +template +int ComputedVerticesMakerEntityMap:: +add_value(const ValueType &value, const MorphType &morphs, + PTA(ValueType) &table) { + // First, see if we have such an entity already. + ComputedVerticesMakerEntity entity(value, morphs); + MapType::const_iterator mi = _map.find(entity); + if (mi != _map.end()) { + // We do! Return its index number. + return (*mi).second; + } + + // No, this is the first time we've encountered this combination. + // Define it. + int index = table.size(); + table.push_back(value); + _map[entity] = index; + + return index; +}; + diff --git a/panda/src/egg2sg/computedVerticesMakerEntity.h b/panda/src/egg2sg/computedVerticesMakerEntity.h new file mode 100644 index 0000000000..90b6d05e1a --- /dev/null +++ b/panda/src/egg2sg/computedVerticesMakerEntity.h @@ -0,0 +1,63 @@ +// Filename: computedVerticesMakerEntity.h +// Created by: drose (02Mar99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COMPUTEDVERTICESMAKERENTITY_H +#define COMPUTEDVERTICESMAKERENTITY_H + +#include + +#include +#include +#include +#include + +#include + +/////////////////////////////////////////////////////////////////// +// Class : ComputedVerticesMakerEntity +// Description : This represents a single vertex value, or color +// value, or normal value, or some such thing, added to +// the ComputedVerticesMaker. This supports +// ComputedVerticesMakerEntityMap, below, which is used +// by ComputedVerticesMaker to collect together vertex +// values with identical values. +//////////////////////////////////////////////////////////////////// +template +class EXPCL_PANDAEGG ComputedVerticesMakerEntity { +public: + INLINE ComputedVerticesMakerEntity(const ValueType &value, + const MorphType &morphs); + bool + operator < (const ComputedVerticesMakerEntity &other) const; + + ValueType _value; + const MorphType &_morphs; +}; + + +/////////////////////////////////////////////////////////////////// +// Class : ComputedVerticesMakerEntityMap +// Description : A map of some kind of entity, above, to an integer +// index number. This collects together identical +// vertices into a common index number. +//////////////////////////////////////////////////////////////////// +template +class EXPCL_PANDAEGG ComputedVerticesMakerEntityMap { +public: + int add_value(const ValueType &value, const MorphType &morphs, + PTA(ValueType) &table); + + typedef map, int> MapType; + MapType _map; +}; + +typedef ComputedVerticesMakerEntityMap ComputedVerticesMakerVertexMap; +typedef ComputedVerticesMakerEntityMap ComputedVerticesMakerNormalMap; +typedef ComputedVerticesMakerEntityMap ComputedVerticesMakerTexCoordMap; +typedef ComputedVerticesMakerEntityMap ComputedVerticesMakerColorMap; + +#include "computedVerticesMakerEntity.I" + +#endif diff --git a/panda/src/egg2sg/config_egg2sg.cxx b/panda/src/egg2sg/config_egg2sg.cxx new file mode 100644 index 0000000000..7b4d8393d8 --- /dev/null +++ b/panda/src/egg2sg/config_egg2sg.cxx @@ -0,0 +1,61 @@ +// Filename: config_egg2sg.cxx +// Created by: drose (01Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_egg2sg.h" +#include "loaderFileTypeEgg.h" + +#include +#include +#include + +ConfigureDef(config_egg2sg); +NotifyCategoryDef(egg2sg, ""); + +const bool egg_mesh = config_egg2sg.GetBool("egg-mesh", true); +const bool egg_retesselate_coplanar = config_egg2sg.GetBool("egg-retesselate-coplanar", true); +const bool egg_unroll_fans = config_egg2sg.GetBool("egg-unroll-fans", true); +const bool egg_show_tstrips = config_egg2sg.GetBool("egg-show-tstrips", false); +const bool egg_show_qsheets = config_egg2sg.GetBool("egg-show-qsheets", false); +const bool egg_show_quads = config_egg2sg.GetBool("egg-show-quads", false); +const bool egg_false_color = (egg_show_tstrips | egg_show_qsheets | egg_show_quads); +const bool egg_show_normals = config_egg2sg.GetBool("egg-show-normals", false); +const double egg_normal_scale = config_egg2sg.GetDouble("egg-normal-scale", 1.0); +const bool egg_subdivide_polys = config_egg2sg.GetBool("egg-subdivide-polys", true); +const bool egg_consider_fans = config_egg2sg.GetBool("egg-consider-fans", true); +const double egg_max_tfan_angle = config_egg2sg.GetDouble("egg-max-tfan-angle", 40.0); +const int egg_min_tfan_tris = config_egg2sg.GetInt("egg-min-tfan-tris", 4); +const double egg_coplanar_threshold = config_egg2sg.GetDouble("egg-coplanar-threshold", 0.01); +const bool egg_ignore_mipmaps = config_egg2sg.GetBool("egg-ignore-mipmaps", false); +const bool egg_ignore_filters = config_egg2sg.GetBool("egg-ignore-filters", false); +const bool egg_ignore_clamp = config_egg2sg.GetBool("egg-ignore-clamp", false); +const bool egg_always_decal_textures = config_egg2sg.GetBool("egg-always-decal-textures", false); +const bool egg_ignore_decals = config_egg2sg.GetBool("egg-ignore-decals", false); +const bool egg_flatten = config_egg2sg.GetBool("egg-flatten", true); + +// It is almost always a bad idea to set this true. +const bool egg_flatten_siblings = config_egg2sg.GetBool("egg-flatten-siblings", false); + +const bool egg_show_collision_solids = config_egg2sg.GetBool("egg-show-collision-solids", false); + +CoordinateSystem egg_coordinate_system; + +ConfigureFn(config_egg2sg) { + LoaderFileTypeEgg::init_type(); + + string csstr = config_egg2sg.GetString("egg-coordinate-system", "default"); + CoordinateSystem cs = parse_coordinate_system_string(csstr); + + if (cs == CS_invalid) { + egg2sg_cat.error() + << "Unexpected egg-coordinate-system string: " << csstr << "\n"; + cs = CS_default; + } + egg_coordinate_system = (cs == CS_default) ? + default_coordinate_system : cs; + + LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_ptr(); + + reg->register_type(new LoaderFileTypeEgg); +} diff --git a/panda/src/egg2sg/config_egg2sg.h b/panda/src/egg2sg/config_egg2sg.h new file mode 100644 index 0000000000..751d74eff5 --- /dev/null +++ b/panda/src/egg2sg/config_egg2sg.h @@ -0,0 +1,45 @@ +// Filename: config_egg2sg.h +// Created by: drose (01Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_EGG2SG_H +#define CONFIG_EGG2SG_H + +#include + +#include +#include +#include +#include + +ConfigureDecl(config_egg2sg, EXPCL_PANDAEGG, EXPTP_PANDAEGG); +NotifyCategoryDecl(egg2sg, EXPCL_PANDAEGG, EXPTP_PANDAEGG); + +extern const bool egg_mesh; +extern const bool egg_retesselate_coplanar; +extern const bool egg_unroll_fans; +extern const bool egg_show_tstrips; +extern const bool egg_show_qsheets; +extern const bool egg_show_quads; +extern const bool egg_false_color; +extern const bool egg_show_normals; +extern const double egg_normal_scale; +extern const bool egg_subdivide_polys; +extern const bool egg_consider_fans; +extern const double egg_max_tfan_angle; +extern const int egg_min_tfan_tris; +extern const double egg_coplanar_threshold; +extern CoordinateSystem egg_coordinate_system; +extern const bool egg_ignore_mipmaps; +extern const bool egg_ignore_filters; +extern const bool egg_ignore_clamp; +extern const bool egg_always_decal_textures; +extern const bool egg_ignore_decals; +extern const bool egg_flatten; +extern const bool egg_flatten_siblings; +extern const bool egg_show_collision_solids; + + + +#endif diff --git a/panda/src/egg2sg/deferredArcProperty.cxx b/panda/src/egg2sg/deferredArcProperty.cxx new file mode 100644 index 0000000000..685e522903 --- /dev/null +++ b/panda/src/egg2sg/deferredArcProperty.cxx @@ -0,0 +1,90 @@ +// Filename: deferredArcProperty.cxx +// Created by: drose (04Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "deferredArcProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: DeferredArcProperty::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DeferredArcProperty:: +DeferredArcProperty() { + _flags = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: DeferredArcProperty::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DeferredArcProperty:: +DeferredArcProperty(const DeferredArcProperty ©) : + _flags(copy._flags), + _from_collide_mask(copy._from_collide_mask), + _into_collide_mask(copy._into_collide_mask) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DeferredArcProperty::Copy Assignment +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DeferredArcProperty:: +operator = (const DeferredArcProperty ©) { + _flags = copy._flags; + _from_collide_mask = copy._from_collide_mask; + _into_collide_mask = copy._into_collide_mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: DeferredArcProperty::compose +// Access: Public +// Description: Composes this state with the next one encountered on +// a lower arc during the apply traversal. +//////////////////////////////////////////////////////////////////// +void DeferredArcProperty:: +compose(const DeferredArcProperty &other) { + _flags |= other._flags; + + if ((other._flags & F_has_from_collide_mask) != 0) { + _from_collide_mask = other._from_collide_mask; + } + + if ((other._flags & F_has_into_collide_mask) != 0) { + _into_collide_mask = other._into_collide_mask; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DeferredArcProperty::apply_to_arc +// Access: Public +// Description: Applies whatever state is appropriate to the arc. +//////////////////////////////////////////////////////////////////// +void DeferredArcProperty:: +apply_to_arc(NodeRelation *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: DeferredArcProperty::apply_to_node +// Access: Public +// Description: Applies whatever state is appropriate to the node. +//////////////////////////////////////////////////////////////////// +void DeferredArcProperty:: +apply_to_node(Node *node) { + if (node->is_of_type(CollisionNode::get_class_type())) { + CollisionNode *cnode = DCAST(CollisionNode, node); + if ((_flags & F_has_from_collide_mask) != 0) { + cnode->set_from_collide_mask(_from_collide_mask); + } + if ((_flags & F_has_into_collide_mask) != 0) { + cnode->set_into_collide_mask(_into_collide_mask); + } + } +} + diff --git a/panda/src/egg2sg/deferredArcProperty.h b/panda/src/egg2sg/deferredArcProperty.h new file mode 100644 index 0000000000..5dd4d9ab16 --- /dev/null +++ b/panda/src/egg2sg/deferredArcProperty.h @@ -0,0 +1,54 @@ +// Filename: deferredArcProperty.h +// Created by: drose (04Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DEFERREDARCPROPERTY_H +#define DEFERREDARCPROPERTY_H + +#include + +#include + +class NodeRelation; +class Node; + +/////////////////////////////////////////////////////////////////// +// Class : DeferredArcProperty +// Description : This class keeps track of all the state we must make +// note of during the graph traversal, but cannot apply +// immediately. An instance of this class may be +// assigned to arcs as they are created, and then later, +// after the geometry has been created, the graph will +// be traversed again and the state will be applied. +// +// This class is only local to this package; it is not +// exported. +//////////////////////////////////////////////////////////////////// +class DeferredArcProperty { +public: + DeferredArcProperty(); + DeferredArcProperty(const DeferredArcProperty ©); + void operator = (const DeferredArcProperty ©); + + void compose(const DeferredArcProperty &other); + + void apply_to_arc(NodeRelation *arc); + void apply_to_node(Node *node); + + +public: + enum Flags { + F_has_from_collide_mask = 0x0001, + F_has_into_collide_mask = 0x0002, + }; + + int _flags; + CollideMask _from_collide_mask; + CollideMask _into_collide_mask; +}; + +typedef map DeferredArcs; + + +#endif diff --git a/panda/src/egg2sg/deferredArcTraverser.cxx b/panda/src/egg2sg/deferredArcTraverser.cxx new file mode 100644 index 0000000000..b0bd1490a4 --- /dev/null +++ b/panda/src/egg2sg/deferredArcTraverser.cxx @@ -0,0 +1,43 @@ +// Filename: deferredArcTraverser.cxx +// Created by: drose (04Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "deferredArcTraverser.h" + +//////////////////////////////////////////////////////////////////// +// Function: DeferredArcTraverser::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DeferredArcTraverser:: +DeferredArcTraverser(const DeferredArcs &deferred_arcs) : + _deferred_arcs(deferred_arcs) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DeferredArcTraverser::forward_arc +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool DeferredArcTraverser:: +forward_arc(NodeRelation *arc, NullTransitionWrapper &, + NullAttributeWrapper &, NullAttributeWrapper &, + DeferredArcProperty &level_state) { + + // Do we have a DeferredArcProperty associated with this arc? + DeferredArcs::const_iterator dai; + dai = _deferred_arcs.find(arc); + + if (dai != _deferred_arcs.end()) { + const DeferredArcProperty &def = (*dai).second; + level_state.compose(def); + } + + // Now apply the accumulated state to both the arc and its node. + level_state.apply_to_arc(arc); + level_state.apply_to_node(arc->get_child()); + + return true; +} diff --git a/panda/src/egg2sg/deferredArcTraverser.h b/panda/src/egg2sg/deferredArcTraverser.h new file mode 100644 index 0000000000..b893bc6423 --- /dev/null +++ b/panda/src/egg2sg/deferredArcTraverser.h @@ -0,0 +1,44 @@ +// Filename: deferredArcTraverser.h +// Created by: drose (04Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DEFERREDARCTRAVERSER_H +#define DEFERREDARCTRAVERSER_H + +#include + +#include "deferredArcProperty.h" + +#include +#include +#include + +#include + +class NodeRelation; + +/////////////////////////////////////////////////////////////////// +// Class : DeferredArcTraverser +// Description : This class is used after all of the nodes have been +// built to go back and apply down all of the +// DeferredArcProperties that might have been built of +// for each arc. It's a standard Panda +// TraverserVisitor. +// +// This class is only local to this package; it is not +// exported. +//////////////////////////////////////////////////////////////////// +class DeferredArcTraverser : public TraverserVisitor { +public: + DeferredArcTraverser(const DeferredArcs &deferred_arcs); + + bool forward_arc(NodeRelation *arc, NullTransitionWrapper &, + NullAttributeWrapper &, NullAttributeWrapper &, + DeferredArcProperty &level_state); + + const DeferredArcs &_deferred_arcs; +}; + + +#endif diff --git a/panda/src/egg2sg/eggBinner.cxx b/panda/src/egg2sg/eggBinner.cxx new file mode 100644 index 0000000000..5f5b54e5e8 --- /dev/null +++ b/panda/src/egg2sg/eggBinner.cxx @@ -0,0 +1,70 @@ +// Filename: eggBinner.cxx +// Created by: drose (17Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "eggBinner.h" + +#include +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Function: EggBinner::get_bin_number +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int EggBinner:: +get_bin_number(const EggNode *node) { + if (node->is_of_type(EggGroup::get_class_type())) { + const EggGroup *group = DCAST(EggGroup, node); + if (group->has_lod()) { + return (int)BN_lod; + } + } + + return (int)BN_none; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggBinner::sorts_less +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool EggBinner:: +sorts_less(int bin_number, const EggNode *a, const EggNode *b) { + assert((BinNumber)bin_number == BN_lod); + + const EggGroup *ga = DCAST(EggGroup, a); + const EggGroup *gb = DCAST(EggGroup, b); + + const EggSwitchCondition &swa = ga->get_lod(); + const EggSwitchCondition &swb = gb->get_lod(); + + // For now, this is the only kind of switch condition there is. + const EggSwitchConditionDistance &swda = + *DCAST(EggSwitchConditionDistance, &swa); + const EggSwitchConditionDistance &swdb = + *DCAST(EggSwitchConditionDistance, &swb); + + // Group LOD nodes in order by switching center. + return (swda._center.compare_to(swdb._center) < 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggBinner::collapse_group +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool EggBinner:: +collapse_group(const EggGroup *group, int) { + if (group->get_dart_type() != EggGroup::DT_none) { + // A group with the flag set means to create a character. + // We can't turn the top character node into an LOD. + return false; + } + + return true; +} diff --git a/panda/src/egg2sg/eggBinner.h b/panda/src/egg2sg/eggBinner.h new file mode 100644 index 0000000000..7d05d41a6a --- /dev/null +++ b/panda/src/egg2sg/eggBinner.h @@ -0,0 +1,43 @@ +// Filename: eggBinner.h +// Created by: drose (17Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGBINNER_H +#define EGGBINNER_H + +#include + +#include + +/////////////////////////////////////////////////////////////////// +// Class : EggBinner +// Description : A special binner used only within this package to +// pre-process the egg tree for the loader and group +// things together as appropriate. +// +// Presently, it only groups related LOD children +// together under a single LOD node. +//////////////////////////////////////////////////////////////////// +class EggBinner : public EggBinMaker { +public: + + // The BinNumber serves to identify why a particular EggBin was + // created. + enum BinNumber { + BN_none = 0, + BN_lod, + }; + + virtual int + get_bin_number(const EggNode *node); + + virtual bool + sorts_less(int bin_number, const EggNode *a, const EggNode *b); + + virtual bool + collapse_group(const EggGroup *group, int bin_number); +}; + + +#endif diff --git a/panda/src/egg2sg/eggLoader.cxx b/panda/src/egg2sg/eggLoader.cxx new file mode 100644 index 0000000000..6ed092c090 --- /dev/null +++ b/panda/src/egg2sg/eggLoader.cxx @@ -0,0 +1,1613 @@ +// Filename: eggLoader.cxx +// Created by: drose (21Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "eggLoader.h" +#include "animBundleMaker.h" +#include "characterMaker.h" +#include "computedVerticesMaker.h" +#include "eggBinner.h" +#include "config_egg2sg.h" +#include "deferredArcProperty.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// This class is used in make_node(EggBin *) to sort LOD instances in +// order by switching distance. +class LODInstance { +public: + LODInstance(EggNode *egg_node, RenderRelation *arc); + bool operator < (const LODInstance &other) const { + return _d->_switch_in < other._d->_switch_in; + } + + RenderRelation *_arc; + const EggSwitchConditionDistance *_d; +}; + +LODInstance:: +LODInstance(EggNode *egg_node, RenderRelation *arc) { + assert(arc != NULL); + _arc = arc; + + // We expect this egg node to be an EggGroup with an LOD + // specification. That's what the EggBinner collected together, + // after all. + EggGroup *egg_group = DCAST(EggGroup, egg_node); + assert(egg_group->has_lod()); + const EggSwitchCondition &sw = egg_group->get_lod(); + + // For now, this is the only kind of switch condition there is. + _d = DCAST(EggSwitchConditionDistance, &sw); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggLoader:: +EggLoader() { + // We need to enforce whatever coordinate system the user asked for. + _data.set_coordinate_system(egg_coordinate_system); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggLoader:: +EggLoader(const EggData &data) : + _data(data) +{ +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::build_graph +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void EggLoader:: +build_graph() { + _deferred_arcs.clear(); + + // First, bin up the LOD nodes. + EggBinner binner; + binner.make_bins(&_data); + + // Then load up all of the textures. + load_textures(); + + // Now build up the scene graph. + _root = new NamedNode; + _root->set_name(_data.get_egg_filename().get_basename()); + make_node(&_data, _root); + _builder.build(); + + reset_directs(); + reparent_decals(); + + apply_deferred_arcs(_root); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::reparent_decals +// Access: Public +// Description: For each node representing a decal base geometry +// (i.e. a node corresponding to an EggGroup with the +// decal flag set), move all of its nested geometry +// directly below the GeomNode representing the group. +//////////////////////////////////////////////////////////////////// +void EggLoader:: +reparent_decals() { + Decals::const_iterator di; + for (di = _decals.begin(); di != _decals.end(); ++di) { + RenderRelation *arc = (*di); + nassertv(arc != (RenderRelation *)NULL); + NamedNode *node = DCAST(NamedNode, arc->get_child()); + nassertv(node != (NamedNode *)NULL); + + // First, search for the GeomNode. + GeomNode *geom = NULL; + int num_children = + node->get_num_children(RenderRelation::get_class_type()); + for (int i = 0; i < num_children; i++) { + NodeRelation *child_arc = + node->get_child(RenderRelation::get_class_type(), i); + nassertv(child_arc != (NodeRelation *)NULL); + Node *child = child_arc->get_child(); + nassertv(child != (Node *)NULL); + + if (child->is_of_type(GeomNode::get_class_type())) { + if (geom != (GeomNode *)NULL) { + // Oops, too many GeomNodes. + egg2sg_cat.warning() + << "Decal onto " << node->get_name() + << " uses base geometry with multiple states.\n"; + break; + } + DCAST_INTO_V(geom, child); + } + } + + if (geom == (GeomNode *)NULL) { + // No children were GeomNodes. + egg2sg_cat.warning() + << "Ignoring decal onto " << node->get_name() + << "; no geometry within group.\n"; + } else { + // Now reparent all of the non-GeomNodes to this node. We have + // to be careful so we don't get lost as we self-modify this + // list. + int i = 0; + while (i < num_children) { + NodeRelation *child_arc = + node->get_child(RenderRelation::get_class_type(), i); + nassertv(child_arc != (NodeRelation *)NULL); + Node *child = child_arc->get_child(); + nassertv(child != (Node *)NULL); + + if (child->is_of_type(GeomNode::get_class_type())) { + i++; + } else { + child_arc->change_parent(geom); + num_children--; + } + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::reset_directs +// Access: Public +// Description: This applies to all of the nodes marked with the +// "render" flag, i.e. direct rendering of a subgraph, +// in depth-first order, as opposed to state-sorting +// within the subgraph. For each such node, it moves +// all the transitions from the first GeomNode under +// that node up to the node itself, just so we'll be +// able to state-sort at least the tops of the +// subgraphs. +//////////////////////////////////////////////////////////////////// +void EggLoader:: +reset_directs() { + Directs::const_iterator di; + for (di = _directs.begin(); di != _directs.end(); ++di) { + RenderRelation *arc = (*di); + nassertv(arc != (RenderRelation *)NULL); + NamedNode *node = DCAST(NamedNode, arc->get_child()); + nassertv(node != (NamedNode *)NULL); + + // First, search for the first GeomNode. + GeomNode *geom = NULL; + NodeRelation *child_arc = NULL; + + int num_children = + node->get_num_children(RenderRelation::get_class_type()); + for (int i = 0; i < num_children && geom == (GeomNode *)NULL; i++) { + child_arc = node->get_child(RenderRelation::get_class_type(), i); + nassertv(child_arc != (NodeRelation *)NULL); + Node *child = child_arc->get_child(); + nassertv(child != (Node *)NULL); + + if (child->is_of_type(GeomNode::get_class_type())) { + DCAST_INTO_V(geom, child); + } + } + + if (geom != (GeomNode *)NULL) { + // Now copy all of the GeomNode's transitions up to its parent. + nassertv(child_arc != (NodeRelation *)NULL); + arc->copy_transitions_from(child_arc); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_nonindexed_primitive +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void EggLoader:: +make_nonindexed_primitive(EggPrimitive *egg_prim, NamedNode *parent, + const LMatrix4d *transform) { + BuilderBucket bucket; + setup_bucket(bucket, parent, egg_prim); + + LMatrix4d mat = egg_prim->get_vertex_to_node(); + + if (transform != NULL) { + mat = (*transform) * mat; + } + + BuilderPrim bprim; + bprim.set_type(BPT_poly); + if (egg_prim->is_of_type(EggPoint::get_class_type())) { + bprim.set_type(BPT_point); + } + + if (egg_prim->has_normal()) { + bprim.set_normal(egg_prim->get_normal() * mat); + } + if (egg_prim->has_color() && !egg_false_color) { + bprim.set_color(egg_prim->get_color()); + } + + bool has_vert_color = true; + EggPrimitive::const_iterator vi; + for (vi = egg_prim->begin(); vi != egg_prim->end(); ++vi) { + EggVertex *egg_vert = *vi; + BuilderVertex bvert(egg_vert->get_pos3() * mat); + + if (egg_vert->has_normal()) { + bvert.set_normal(egg_vert->get_normal() * mat); + } + if (egg_vert->has_color() && !egg_false_color) { + bvert.set_color(egg_vert->get_color()); + } else { + // If any vertex doesn't have a color, we can't use any of the + // vertex colors. + has_vert_color = false; + } + if (egg_vert->has_uv()) { + TexCoordd uv = egg_vert->get_uv(); + if (egg_prim->has_texture() && + egg_prim->get_texture()->has_transform()) { + // If we have a texture matrix, apply it. + uv = uv * egg_prim->get_texture()->get_transform(); + } + bvert.set_texcoord(uv); + } + + bprim.add_vertex(bvert); + } + + // Finally, if the primitive didn't have a color, and it didn't have + // vertex color, make it white. + if (!egg_prim->has_color() && !has_vert_color && !egg_false_color) { + bprim.set_color(Colorf(1.0, 1.0, 1.0, 1.0)); + } + + _builder.add_prim(bucket, bprim); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_indexed_primitive +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void EggLoader:: +make_indexed_primitive(EggPrimitive *egg_prim, NamedNode *parent, + const LMatrix4d *transform, + ComputedVerticesMaker &_comp_verts_maker) { + BuilderBucket bucket; + setup_bucket(bucket, parent, egg_prim); + + bucket.set_coords(_comp_verts_maker._coords); + bucket.set_normals(_comp_verts_maker._norms); + bucket.set_texcoords(_comp_verts_maker._texcoords); + bucket.set_colors(_comp_verts_maker._colors); + + LMatrix4d mat = egg_prim->get_vertex_to_node(); + + if (transform != NULL) { + mat = (*transform) * mat; + } + + BuilderPrimI bprim; + bprim.set_type(BPT_poly); + if (egg_prim->is_of_type(EggPoint::get_class_type())) { + bprim.set_type(BPT_point); + } + + if (egg_prim->has_normal()) { + // Define the transform space of the polygon normal. This will be + // the average of all the vertex transform spaces. + _comp_verts_maker.begin_new_space(); + EggPrimitive::const_iterator vi; + for (vi = egg_prim->begin(); vi != egg_prim->end(); ++vi) { + EggVertex *egg_vert = *vi; + EggVertex::GroupRef::const_iterator gri; + for (gri = egg_vert->gref_begin(); gri != egg_vert->gref_end(); ++gri) { + EggGroup *egg_joint = (*gri); + double membership = egg_joint->get_vertex_membership(egg_vert); + _comp_verts_maker.add_joint(egg_joint, membership); + } + } + _comp_verts_maker.mark_space(); + + int nindex = + _comp_verts_maker.add_normal(egg_prim->get_normal(), + egg_prim->_dnormals, mat); + + bprim.set_normal(nindex); + } + + if (egg_prim->has_color() && !egg_false_color) { + int cindex = + _comp_verts_maker.add_color(egg_prim->get_color(), + egg_prim->_drgbas); + bprim.set_color(cindex); + } + + bool has_vert_color = true; + EggPrimitive::const_iterator vi; + for (vi = egg_prim->begin(); vi != egg_prim->end(); ++vi) { + EggVertex *egg_vert = *vi; + + // Set up the ComputedVerticesMaker for the coordinate space of + // the vertex. + _comp_verts_maker.begin_new_space(); + if (egg_vert->gref_size() == 0) { + // This vertex belongs where the primitive is. + EggGroupNode *egg_joint = egg_prim->get_parent(); + _comp_verts_maker.add_joint(egg_joint, 1.0); + + } else { + // This vertex belongs in the joint or joints that reference it. + EggVertex::GroupRef::const_iterator gri; + for (gri = egg_vert->gref_begin(); gri != egg_vert->gref_end(); ++gri) { + EggGroup *egg_joint = (*gri); + double membership = egg_joint->get_vertex_membership(egg_vert); + _comp_verts_maker.add_joint(egg_joint, membership); + } + } + _comp_verts_maker.mark_space(); + + int vindex = + _comp_verts_maker.add_vertex(egg_vert->get_pos3(), + egg_vert->_dxyzs, mat); + BuilderVertexI bvert(vindex); + + if (egg_vert->has_normal()) { + int nindex = + _comp_verts_maker.add_normal(egg_vert->get_normal(), + egg_vert->_dnormals, + mat); + bvert.set_normal(nindex); + } + + if (egg_vert->has_color() && !egg_false_color) { + int cindex = + _comp_verts_maker.add_color(egg_vert->get_color(), + egg_vert->_drgbas); + bvert.set_color(cindex); + } else { + // If any vertex doesn't have a color, we can't use any of the + // vertex colors. + has_vert_color = false; + } + + if (egg_vert->has_uv()) { + TexCoordd uv = egg_vert->get_uv(); + LMatrix3d mat; + + if (egg_prim->has_texture() && + egg_prim->get_texture()->has_transform()) { + // If we have a texture matrix, apply it. + mat = egg_prim->get_texture()->get_transform(); + } else { + mat = LMatrix3d::ident_mat(); + } + + int tindex = + _comp_verts_maker.add_texcoord(uv, egg_vert->_duvs, mat); + bvert.set_texcoord(tindex); + } + + bprim.add_vertex(bvert); + } + + // Finally, if the primitive didn't have a color, and it didn't have + // vertex color, make it white. + if (!egg_prim->has_color() && !has_vert_color && !egg_false_color) { + int cindex = + _comp_verts_maker.add_color(Colorf(1.0, 1.0, 1.0, 1.0), + EggMorphColorList()); + bprim.set_color(cindex); + } + + _builder.add_prim(bucket, bprim); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::load_textures +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void EggLoader:: +load_textures() { + // First, collect all the textures that are referenced. + EggTextureCollection tc; + tc.find_used_textures(&_data); + + // Collapse the textures down by filename only. Should we also + // differentiate by attributes? Maybe. + EggTextureCollection::TextureReplacement replace; + tc.collapse_equivalent_textures(EggTexture::E_complete_filename, + replace); + + EggTextureCollection::iterator ti; + for (ti = tc.begin(); ti != tc.end(); ++ti) { + PT(EggTexture) egg_tex = (*ti); + + TextureDef def; + if (load_texture(def, egg_tex)) { + // Now associate the pointers, so we'll be able to look up the + // Texture pointer given an EggTexture pointer, later. + _textures[egg_tex] = def; + } + } + + // Finally, associate all of the removed texture references back to + // the same pointers as the others. + EggTextureCollection::TextureReplacement::const_iterator ri; + for (ri = replace.begin(); ri != replace.end(); ++ri) { + PT(EggTexture) orig = (*ri).first; + PT(EggTexture) repl = (*ri).second; + + _textures[orig] = _textures[repl]; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::load_texture +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +bool EggLoader:: +load_texture(TextureDef &def, const EggTexture *egg_tex) { + Texture *tex = TexturePool::load_texture(egg_tex->get_fullpath()); + if (tex == (Texture *)NULL) { + return false; + } + PT(TextureApplyTransition) apply = + new TextureApplyTransition(TextureApplyProperty::M_modulate); + + apply_texture_attributes(tex, egg_tex); + apply_texture_apply_attributes(apply, egg_tex); + + def._texture = new TextureTransition(tex); + def._apply = *(_texture_applies.insert(apply).first); + + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::apply_texture_attributes +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void EggLoader:: +apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) { + tex->set_name(egg_tex->get_fullpath().c_str()); + + switch (egg_tex->determine_wrap_u()) { + case EggTexture::WM_repeat: + tex->set_wrapu(Texture::WM_repeat); + break; + + case EggTexture::WM_clamp: + if (egg_ignore_clamp) { + egg2sg_cat.warning() + << "Ignoring clamp request\n"; + tex->set_wrapu(Texture::WM_repeat); + } else { + tex->set_wrapu(Texture::WM_clamp); + } + break; + } + + switch (egg_tex->determine_wrap_v()) { + case EggTexture::WM_repeat: + tex->set_wrapv(Texture::WM_repeat); + break; + + case EggTexture::WM_clamp: + if (egg_ignore_clamp) { + egg2sg_cat.warning() + << "Ignoring clamp request\n"; + tex->set_wrapv(Texture::WM_repeat); + } else { + tex->set_wrapv(Texture::WM_clamp); + } + break; + } + + switch (egg_tex->get_minfilter()) { + case EggTexture::FT_point: + tex->set_minfilter(Texture::FT_nearest); + break; + + case EggTexture::FT_linear: + case EggTexture::FT_bilinear: + case EggTexture::FT_trilinear: + if (egg_ignore_filters) { + egg2sg_cat.warning() + << "Ignoring minfilter request\n"; + tex->set_minfilter(Texture::FT_nearest); + } else { + tex->set_minfilter(Texture::FT_linear); + } + break; + + case EggTexture::FT_mipmap_point: + if (egg_ignore_filters) { + egg2sg_cat.warning() + << "Ignoring minfilter request\n"; + tex->set_minfilter(Texture::FT_nearest); + } else if (egg_ignore_mipmaps) { + egg2sg_cat.warning() + << "Ignoring mipmap request\n"; + tex->set_minfilter(Texture::FT_nearest); + } else { + tex->set_minfilter(Texture::FT_nearest_mipmap_nearest); + } + break; + + case EggTexture::FT_mipmap_linear: + case EggTexture::FT_mipmap_bilinear: + case EggTexture::FT_mipmap_trilinear: + if (egg_ignore_filters) { + egg2sg_cat.warning() + << "Ignoring minfilter request\n"; + tex->set_minfilter(Texture::FT_nearest); + } else if (egg_ignore_mipmaps) { + egg2sg_cat.warning() + << "Ignoring mipmap request\n"; + tex->set_minfilter(Texture::FT_linear); + } else { + tex->set_minfilter(Texture::FT_linear_mipmap_linear); + } + break; + + case EggTexture::FT_unspecified: + // Default is bilinear, unless egg_ignore_filters is specified. + if (egg_ignore_filters) { + tex->set_minfilter(Texture::FT_nearest); + } else { + tex->set_minfilter(Texture::FT_linear); + } + } + + switch (egg_tex->get_magfilter()) { + case EggTexture::FT_point: + case EggTexture::FT_mipmap_point: + tex->set_magfilter(Texture::FT_nearest); + break; + + case EggTexture::FT_linear: + case EggTexture::FT_bilinear: + case EggTexture::FT_trilinear: + case EggTexture::FT_mipmap_linear: + case EggTexture::FT_mipmap_bilinear: + case EggTexture::FT_mipmap_trilinear: + if (egg_ignore_filters) { + egg2sg_cat.warning() + << "Ignoring magfilter request\n"; + tex->set_magfilter(Texture::FT_nearest); + } else { + tex->set_magfilter(Texture::FT_linear); + } + break; + + case EggTexture::FT_unspecified: + // Default is bilinear, unless egg_ignore_filters is specified. + if (egg_ignore_filters) { + tex->set_magfilter(Texture::FT_nearest); + } else { + tex->set_magfilter(Texture::FT_linear); + } + } + + if (tex->_pbuffer->get_num_components() == 1) { + switch (egg_tex->get_format()) { + case EggTexture::F_red: + tex->_pbuffer->set_format(PixelBuffer::F_red); + break; + case EggTexture::F_green: + tex->_pbuffer->set_format(PixelBuffer::F_green); + break; + case EggTexture::F_blue: + tex->_pbuffer->set_format(PixelBuffer::F_blue); + break; + case EggTexture::F_alpha: + tex->_pbuffer->set_format(PixelBuffer::F_alpha); + break; + case EggTexture::F_luminance: + tex->_pbuffer->set_format(PixelBuffer::F_luminance); + break; + + case EggTexture::F_unspecified: + break; + + default: + egg2sg_cat.error() + << "Ignoring inappropriate format " << egg_tex->get_format() + << " for 1-component texture " << egg_tex->get_name() << "\n"; + } + + } else if (tex->_pbuffer->get_num_components() == 2) { + switch (egg_tex->get_format()) { + case EggTexture::F_luminance_alpha: + tex->_pbuffer->set_format(PixelBuffer::F_luminance_alpha); + break; + + case EggTexture::F_unspecified: + break; + + default: + egg2sg_cat.error() + << "Ignoring inappropriate format " << egg_tex->get_format() + << " for 2-component texture " << egg_tex->get_name() << "\n"; + } + + } else if (tex->_pbuffer->get_num_components() == 3) { + switch (egg_tex->get_format()) { + case EggTexture::F_rgb: + tex->_pbuffer->set_format(PixelBuffer::F_rgb); + break; + case EggTexture::F_rgb12: + if (tex->_pbuffer->get_component_width() >= 2) { + // Only do this if the component width supports it. + tex->_pbuffer->set_format(PixelBuffer::F_rgb12); + } else { + egg2sg_cat.error() + << "Ignoring inappropriate format " << egg_tex->get_format() + << " for 8-bit texture " << egg_tex->get_name() << "\n"; + } + break; + case EggTexture::F_rgb8: + case EggTexture::F_rgba8: + // We'll quietly accept RGBA8 for a 3-component texture, since + // flt2egg generates these for 3-component as well as for + // 4-component textures. + tex->_pbuffer->set_format(PixelBuffer::F_rgb8); + break; + case EggTexture::F_rgb5: + tex->_pbuffer->set_format(PixelBuffer::F_rgb5); + break; + case EggTexture::F_rgba5: + tex->_pbuffer->set_format(PixelBuffer::F_rgba5); + break; + case EggTexture::F_rgb332: + tex->_pbuffer->set_format(PixelBuffer::F_rgb332); + break; + + case EggTexture::F_unspecified: + break; + + default: + egg2sg_cat.error() + << "Ignoring inappropriate format " << egg_tex->get_format() + << " for 3-component texture " << egg_tex->get_name() << "\n"; + } + + } else if (tex->_pbuffer->get_num_components() == 4) { + switch (egg_tex->get_format()) { + case EggTexture::F_rgba: + tex->_pbuffer->set_format(PixelBuffer::F_rgba); + break; + case EggTexture::F_rgba12: + if (tex->_pbuffer->get_component_width() >= 2) { + // Only do this if the component width supports it. + tex->_pbuffer->set_format(PixelBuffer::F_rgba12); + } else { + egg2sg_cat.error() + << "Ignoring inappropriate format " << egg_tex->get_format() + << " for 8-bit texture " << egg_tex->get_name() << "\n"; + } + break; + case EggTexture::F_rgba8: + tex->_pbuffer->set_format(PixelBuffer::F_rgba8); + break; + case EggTexture::F_rgba4: + tex->_pbuffer->set_format(PixelBuffer::F_rgba4); + break; + case EggTexture::F_rgba5: + tex->_pbuffer->set_format(PixelBuffer::F_rgba5); + break; + + case EggTexture::F_unspecified: + break; + + default: + egg2sg_cat.error() + << "Ignoring inappropriate format " << egg_tex->get_format() + << " for 4-component texture " << egg_tex->get_name() << "\n"; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::apply_texture_apply_attributes +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void EggLoader:: +apply_texture_apply_attributes(TextureApplyTransition *apply, + const EggTexture *egg_tex) { + if (egg_always_decal_textures) { + apply->set_mode(TextureApplyProperty::M_decal); + + } else { + switch (egg_tex->get_env_type()) { + case EggTexture::ET_modulate: + apply->set_mode(TextureApplyProperty::M_modulate); + break; + + case EggTexture::ET_decal: + apply->set_mode(TextureApplyProperty::M_decal); + break; + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::setup_bucket +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void EggLoader:: +setup_bucket(BuilderBucket &bucket, NamedNode *parent, + EggPrimitive *egg_prim) { + bucket._node = parent; + bucket._mesh = egg_mesh; + bucket._retesselate_coplanar = egg_retesselate_coplanar; + bucket._unroll_fans = egg_unroll_fans; + bucket._show_tstrips = egg_show_tstrips; + bucket._show_qsheets = egg_show_qsheets; + bucket._show_quads = egg_show_quads; + bucket._show_normals = egg_show_normals; + bucket._normal_scale = egg_normal_scale; + bucket._subdivide_polys = egg_subdivide_polys; + bucket._consider_fans = egg_consider_fans; + bucket._max_tfan_angle = egg_max_tfan_angle; + bucket._min_tfan_tris = egg_min_tfan_tris; + bucket._coplanar_threshold = egg_coplanar_threshold; + + // If a primitive has a name that does not begin with a digit, it + // should be used to group primitives together--i.e. each primitive + // with the same name gets placed into the same GeomNode. However, + // if a prim's name begins with a digit, just ignore it. + if (egg_prim->has_name() && !isdigit(egg_prim->get_name()[0])) { + bucket.set_name(egg_prim->get_name()); + } + + // Assign the appropriate properties to the bucket. + EggAlphaMode::AlphaMode am = egg_prim->get_alpha_mode(); + bool implicit_alpha = false; + + bucket._trans.set_transition(new TextureTransition(TextureTransition::off())); + if (egg_prim->has_texture()) { + PT(EggTexture) egg_tex = egg_prim->get_texture(); + // If the primitive didn't specify an alpha mode, allow the + // texture to specify one. + if (am == EggAlphaMode::AM_unspecified) { + am = egg_tex->get_alpha_mode(); + } + + const TextureDef &def = _textures[egg_tex]; + if (def._texture != (TextureTransition *)NULL) { + bucket._trans.set_transition(def._texture); + bucket._trans.set_transition(def._apply); + + // If neither the primitive nor the texture specified an alpha + // mode, assume it should be alpha'ed if the texture has an + // alpha channel. + if (am == EggAlphaMode::AM_unspecified) { + Texture *tex = def._texture->get_texture(); + nassertv(tex != (Texture *)NULL); + int num_components = tex->_pbuffer->get_num_components(); + if (num_components == 2 || num_components == 4 || + (num_components == 1 && egg_tex->get_format() == EggTexture::F_alpha)) { + implicit_alpha = true; + } + } + } + } + + // Also check the color of the primitive to see if we should assume + // alpha based on the alpha values specified in the egg file. + if (am == EggAlphaMode::AM_unspecified) { + if (egg_prim->has_color()) { + if (egg_prim->get_color()[3] != 1.0) { + implicit_alpha = true; + } + } + EggPrimitive::const_iterator vi; + for (vi = egg_prim->begin(); + !implicit_alpha && vi != egg_prim->end(); + ++vi) { + if ((*vi)->has_color()) { + if ((*vi)->get_color()[3] != 1.0) { + implicit_alpha = true; + } + } + } + + if (implicit_alpha) { + am = EggAlphaMode::AM_on; + } + } + + switch (am) { + case EggAlphaMode::AM_on: + case EggAlphaMode::AM_blend: + bucket._trans.set_transition(new TransparencyTransition(TransparencyProperty::M_alpha)); + break; + + case EggAlphaMode::AM_blend_no_occlude: + bucket._trans.set_transition(new TransparencyTransition(TransparencyProperty::M_alpha_sorted)); + break; + + case EggAlphaMode::AM_ms: + bucket._trans.set_transition(new TransparencyTransition(TransparencyProperty::M_multisample)); + break; + + case EggAlphaMode::AM_ms_mask: + bucket._trans.set_transition(new TransparencyTransition(TransparencyProperty::M_multisample_mask)); + break; + + default: + // bucket._trans.set_transition(new TransparencyTransition(TransparencyProperty::M_none)); + break; + } + + if (egg_prim->get_bface_flag()) { + // The primitive is marked with backface culling disabled--we want + // to see both sides. + bucket._trans.set_transition(new CullFaceTransition(CullFaceProperty::M_cull_none)); + + /* + Not sure if we need to enforce this between children. + } else { + bucket._trans.set_transition(new CullFaceTransition(CullFaceProperty::M_cull_clockwise)); + */ + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_node +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +RenderRelation *EggLoader:: +make_node(EggNode *egg_node, NamedNode *parent) { + if (egg_node->is_of_type(EggPrimitive::get_class_type())) { + return make_node(DCAST(EggPrimitive, egg_node), parent); + } else if (egg_node->is_of_type(EggBin::get_class_type())) { + return make_node(DCAST(EggBin, egg_node), parent); + } else if (egg_node->is_of_type(EggGroup::get_class_type())) { + return make_node(DCAST(EggGroup, egg_node), parent); + } else if (egg_node->is_of_type(EggTable::get_class_type())) { + return make_node(DCAST(EggTable, egg_node), parent); + } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) { + return make_node(DCAST(EggGroupNode, egg_node), parent); + } + + return (RenderRelation *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_node (EggPrimitive) +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +RenderRelation *EggLoader:: +make_node(EggPrimitive *egg_prim, NamedNode *parent) { + assert(parent != NULL); + assert(!parent->is_of_type(GeomNode::get_class_type())); + + egg_prim->cleanup(); + make_nonindexed_primitive(egg_prim, parent); + return (RenderRelation *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_node (EggBin) +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +RenderRelation *EggLoader:: +make_node(EggBin *egg_bin, NamedNode *parent) { + // Presently, an EggBin can only mean an LOD node (i.e. a parent of + // one or more EggGroups with LOD specifications). Later it might + // mean other things as well. + + assert((EggBinner::BinNumber)egg_bin->get_bin_number() == EggBinner::BN_lod); + LODNode *lod_node = new LODNode; + lod_node->set_name(egg_bin->get_name()); + + vector instances; + + EggGroup::const_iterator ci; + for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) { + RenderRelation *arc = make_node(*ci, lod_node); + assert(arc != (RenderRelation *)NULL); + LODInstance instance(*ci, arc); + instances.push_back(instance); + } + + // Now that we've created all of our children, put them in the + // proper order and tell the LOD node about them. + sort(instances.begin(), instances.end()); + + if (!instances.empty()) { + // Set up the LOD node's center. All of the children should have + // the same center, because that's how we binned them. + lod_node->_lod._center = LCAST(float, instances[0]._d->_center); + } + + for (size_t i = 0; i < instances.size(); i++) { + // Put the children in the proper order within the scene graph. + const LODInstance &instance = instances[i]; + + // All of the children should have the same center, because that's + // how we binned them. + assert(lod_node->_lod._center.almost_equal + (LCAST(float, instance._d->_center), 0.01)); + + instance._arc->set_sort(i); + + // Tell the LOD node about this child's switching distances. + lod_node->add_switch(instance._d->_switch_in, instance._d->_switch_out); + } + + return create_group_arc(egg_bin, parent, lod_node); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_node (EggGroup) +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +RenderRelation *EggLoader:: +make_node(EggGroup *egg_group, NamedNode *parent) { + NamedNode *node = NULL; + + if (egg_group->has_objecttype()) { + // We'll allow recursive expansion of ObjectType strings--but we + // don't want to get caught in a cycle. Keep track of the strings + // we've expanded so far. + set expanded; + vector expanded_history; + + while (egg_group->has_objecttype()) { + string objecttype = egg_group->get_objecttype(); + if (!expanded.insert(objecttype).second) { + egg2sg_cat.error() + << "Cycle in ObjectType expansions:\n"; + copy(expanded_history.begin(), expanded_history.end(), + ostream_iterator(egg2sg_cat.error(false), " -> ")); + egg2sg_cat.error(false) << objecttype << "\n"; + break; + } + expanded_history.push_back(objecttype); + + // Now clear the group's ObjectType flag. We'll only loop back + // here again if we end up setting this during the ObjectType + // expansion; e.g. the expansion string itself contains an + // reference. + egg_group->clear_objecttype(); + + // Now try to find the egg syntax that the given objecttype is + // shorthand for. First, look in the config file. + + string egg_syntax = + config_egg2sg.GetString("egg-object-type-" + objecttype, "none"); + + if (egg_syntax == "none") { + // It wasn't defined in a config file. Maybe it's built in? + + if (cmp_nocase_uh(objecttype, "barrier") == 0) { + egg_syntax = " { Polyset descend }"; + + } else if (cmp_nocase_uh(objecttype, "solidpoly") == 0) { + egg_syntax = " { Polyset descend solid }"; + + } else if (cmp_nocase_uh(objecttype, "turnstile") == 0) { + egg_syntax = " { Polyset descend turnstile }"; + + } else if (cmp_nocase_uh(objecttype, "trigger") == 0) { + egg_syntax = " { Polyset descend intangible }"; + + } else if (cmp_nocase_uh(objecttype, "eye_trigger") == 0) { + egg_syntax = " { Polyset descend intangible center }"; + + } else if (cmp_nocase_uh(objecttype, "bubble") == 0) { + egg_syntax = " { Sphere keep descend }"; + + } else if (cmp_nocase_uh(objecttype, "missile") == 0) { + egg_syntax = " missile { Sphere keep descend event }"; + + } else if (cmp_nocase_uh(objecttype, "ghost") == 0) { + egg_syntax = " collide-mask { 0 }"; + + } else if (cmp_nocase_uh(objecttype, "backstage") == 0) { + // Ignore "backstage" geometry. + return NULL; + + } else { + egg2sg_cat.warning() + << "Ignoring unknown ObjectType " << objecttype << "\n"; + break; + } + } + + if (!egg_syntax.empty()) { + if (!egg_group->parse_egg(egg_syntax)) { + egg2sg_cat.error() + << "Error while parsing definition for ObjectType " + << objecttype << "\n"; + } + } + } + } + + if (egg_group->get_dart_type() != EggGroup::DT_none) { + // A group with the flag set means to create a character. + CharacterMaker char_maker(egg_group, *this); + node = char_maker.make_node(); + + } else if (egg_group->get_cs_type() != EggGroup::CST_none && + egg_group->get_cs_type() != EggGroup::CST_geode) { + // A collision group: create collision geometry. + node = new CollisionNode; + node->set_name(egg_group->get_name()); + + make_collision_solids(egg_group, egg_group, (CollisionNode *)node); + if ((egg_group->get_collide_flags() & EggGroup::CF_keep) != 0) { + // If we also specified to keep the geometry, continue the + // traversal. + EggGroup::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + make_node(*ci, parent); + } + } + + RenderRelation *arc = create_group_arc(egg_group, parent, node); + + if (!egg_show_collision_solids) { + arc->set_transition(new PruneTransition()); + } + return arc; + + } else if (egg_group->get_switch_flag() && + egg_group->get_switch_fps() != 0.0) { + // Create a sequence node. + node = new SequenceNode(1.0 / egg_group->get_switch_fps()); + node->set_name(egg_group->get_name()); + + EggGroup::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + make_node(*ci, node); + } + + } else { + // A normal group; just create a normal node, and traverse. + node = new NamedNode; + node->set_name(egg_group->get_name()); + + EggGroup::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + make_node(*ci, node); + } + } + + if (node == (NamedNode *)NULL) { + return NULL; + } + return create_group_arc(egg_group, parent, node); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::create_group_arc +// Access: Private +// Description: Creates the arc parenting a new group to the scene +// graph, and applies any relevant transitions to the +// arc according to the EggGroup node that inspired the +// group. +//////////////////////////////////////////////////////////////////// +RenderRelation *EggLoader:: +create_group_arc(EggGroup *egg_group, NamedNode *parent, NamedNode *node) { + RenderRelation *arc = new RenderRelation(parent, node); + + // If the group had a transform, apply it to the arc. + if (egg_group->has_transform()) { + LMatrix4f matf = LCAST(float, egg_group->get_transform()); + arc->set_transition(new TransformTransition(matf)); + } + + // If the group has a billboard flag, apply that. + switch (egg_group->get_billboard_type()) { + case EggGroup::BT_point_camera_relative: + arc->set_transition(new BillboardTransition(BillboardTransition::point_eye())); + break; + + case EggGroup::BT_point_world_relative: + arc->set_transition(new BillboardTransition(BillboardTransition::point_world())); + break; + + case EggGroup::BT_axis: + arc->set_transition(new BillboardTransition(BillboardTransition::axis())); + break; + + case EggGroup::BT_none: + break; + } + + if (egg_group->get_decal_flag()) { + if (egg_ignore_decals) { + egg2sg_cat.warning() + << "Ignoring decal flag on " << egg_group->get_name() << "\n"; + } + + // If the group has the "decal" flag set, it means that all of the + // descendant groups will be decaled onto the geometry within + // this group. This means we'll need to reparent things a bit + // afterward. + _decals.insert(arc); + + // We'll also set up the DecalTransition now. + arc->set_transition(new DecalTransition); + + } + + if (egg_group->get_direct_flag()) { + // If the group has the "direct" flag set, it means that + // everything at this node and below should be rendered in direct + // mode, i.e. in depth-first tree order, without state-sorting. + + arc->set_transition(new DirectRenderTransition); + + // We'll also want to set up the transitions on this arc to + // reflect the geometry at the top of the tree below this node, so + // we get good state-sorting behavior. We'll have to do this + // later. + _directs.insert(arc); + } + + // If the group specified some property that should propagate down + // to the leaves, we have to remember this arc and apply the + // property later, after we've created the actual geometry. + DeferredArcProperty def; + if (egg_group->has_collide_mask()) { + def._from_collide_mask = egg_group->get_collide_mask(); + def._into_collide_mask = egg_group->get_collide_mask(); + def._flags |= + DeferredArcProperty::F_has_from_collide_mask | + DeferredArcProperty::F_has_into_collide_mask; + } + if (egg_group->has_from_collide_mask()) { + def._from_collide_mask = egg_group->get_from_collide_mask(); + def._flags |= DeferredArcProperty::F_has_from_collide_mask; + } + if (egg_group->has_into_collide_mask()) { + def._into_collide_mask = egg_group->get_into_collide_mask(); + def._flags |= DeferredArcProperty::F_has_into_collide_mask; + } + + if (def._flags != 0) { + _deferred_arcs[arc] = def; + } + + return arc; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_node (EggTable) +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +RenderRelation *EggLoader:: +make_node(EggTable *egg_table, NamedNode *parent) { + if (egg_table->get_table_type() != EggTable::TT_bundle) { + // We only do anything with bundles. Isolated tables are treated + // as ordinary groups. + return make_node(DCAST(EggGroupNode, egg_table), parent); + } + + // It's an actual bundle, so make an AnimBundle from it and its + // descendants. + AnimBundleMaker bundle_maker(egg_table); + AnimBundleNode *node = bundle_maker.make_node(); + return new RenderRelation(parent, node); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_node (EggGroupNode) +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +RenderRelation *EggLoader:: +make_node(EggGroupNode *egg_group, NamedNode *parent) { + NamedNode *node = new NamedNode; + node->set_name(egg_group->get_name()); + + EggGroupNode::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + make_node(*ci, node); + } + + return new RenderRelation(parent, node); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_collision_solids +// Access: Private +// Description: Creates CollisionSolids corresponding to the +// collision geometry indicated at the given node and +// below. +//////////////////////////////////////////////////////////////////// +void EggLoader:: +make_collision_solids(EggGroup *start_group, EggGroup *egg_group, + CollisionNode *cnode) { + if (egg_group->get_cs_type() != EggGroup::CST_none) { + start_group = egg_group; + } + + switch (start_group->get_cs_type()) { + case EggGroup::CST_none: + case EggGroup::CST_geode: + // No collision flags; do nothing. Don't even traverse further. + return; + + case EggGroup::CST_inverse_sphere: + // These aren't presently supported. + egg2sg_cat.warning() + << "Not presently supported: { " + << egg_group->get_cs_type() << " }\n"; + break; + + case EggGroup::CST_plane: + make_collision_plane(egg_group, cnode); + break; + + case EggGroup::CST_polygon: + make_collision_polygon(egg_group, cnode); + break; + + case EggGroup::CST_polyset: + make_collision_polyset(egg_group, cnode); + break; + + case EggGroup::CST_sphere: + make_collision_sphere(egg_group, cnode); + break; + } + + if ((start_group->get_collide_flags() & EggGroup::CF_descend) != 0) { + // Now pick up everything below. + EggGroup::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + if ((*ci)->is_of_type(EggGroup::get_class_type())) { + make_collision_solids(start_group, DCAST(EggGroup, *ci), cnode); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_collision_plane +// Access: Private +// Description: Creates a single CollisionPlane corresponding +// to the first polygon associated with this group. +//////////////////////////////////////////////////////////////////// +void EggLoader:: +make_collision_plane(EggGroup *egg_group, CollisionNode *cnode) { + EggGroup *geom_group = find_collision_geometry(egg_group); + if (geom_group != (EggGroup *)NULL) { + EggGroup::const_iterator ci; + for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) { + if ((*ci)->is_of_type(EggPolygon::get_class_type())) { + CollisionPlane *csplane = + create_collision_plane(DCAST(EggPolygon, *ci)); + if (csplane != (CollisionPlane *)NULL) { + cnode->add_solid(csplane); + return; + } + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_collision_polygon +// Access: Private +// Description: Creates a single CollisionPolygon corresponding +// to the first polygon associated with this group. +//////////////////////////////////////////////////////////////////// +void EggLoader:: +make_collision_polygon(EggGroup *egg_group, CollisionNode *cnode) { + EggGroup *geom_group = find_collision_geometry(egg_group); + if (geom_group != (EggGroup *)NULL) { + EggGroup::const_iterator ci; + for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) { + if ((*ci)->is_of_type(EggPolygon::get_class_type())) { + CollisionPolygon *cspoly = + create_collision_polygon(DCAST(EggPolygon, *ci)); + if (cspoly != (CollisionPolygon *)NULL) { + cnode->add_solid(cspoly); + return; + } + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_collision_polyset +// Access: Private +// Description: Creates a series of CollisionPolygons corresponding +// to the polygons associated with this group. +//////////////////////////////////////////////////////////////////// +void EggLoader:: +make_collision_polyset(EggGroup *egg_group, CollisionNode *cnode) { + EggGroup *geom_group = find_collision_geometry(egg_group); + if (geom_group != (EggGroup *)NULL) { + EggGroup::const_iterator ci; + for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) { + if ((*ci)->is_of_type(EggPolygon::get_class_type())) { + CollisionPolygon *cspoly = + create_collision_polygon(DCAST(EggPolygon, *ci)); + if (cspoly != (CollisionPolygon *)NULL) { + cnode->add_solid(cspoly); + } + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_collision_sphere +// Access: Private +// Description: Creates a single CollisionSphere corresponding +// to the polygons associated with this group. +//////////////////////////////////////////////////////////////////// +void EggLoader:: +make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode) { + EggGroup *geom_group = find_collision_geometry(egg_group); + if (geom_group != (EggGroup *)NULL) { + // Collect all of the vertices. + set vertices; + + EggGroup::const_iterator ci; + for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) { + if ((*ci)->is_of_type(EggPrimitive::get_class_type())) { + EggPrimitive *prim = DCAST(EggPrimitive, *ci); + EggPrimitive::const_iterator pi; + for (pi = prim->begin(); pi != prim->end(); ++pi) { + vertices.insert(*pi); + } + } + } + + // Now average together all of the vertices to get a center. + int num_vertices = 0; + LPoint3d center(0.0, 0.0, 0.0); + set::const_iterator vi; + + for (vi = vertices.begin(); vi != vertices.end(); ++vi) { + EggVertex *vtx = (*vi); + if (vtx->get_num_dimensions() == 3) { + center += vtx->get_pos3(); + num_vertices++; + + } else if (vtx->get_num_dimensions() == 4) { + LPoint4d p4 = vtx->get_pos4(); + if (p4[3] != 0.0) { + center += LPoint3d(p4[0], p4[1], p4[2]) / p4[3]; + num_vertices++; + } + } + } + + if (num_vertices > 0) { + center /= (double)num_vertices; + + // And the furthest vertex determines the radius. + double radius2 = 0.0; + for (vi = vertices.begin(); vi != vertices.end(); ++vi) { + EggVertex *vtx = (*vi); + if (vtx->get_num_dimensions() == 3) { + LVector3d v = vtx->get_pos3() - center; + radius2 = max(radius2, v.length_squared()); + + } else if (vtx->get_num_dimensions() == 4) { + LPoint4d p = vtx->get_pos4(); + if (p[3] != 0.0) { + LVector3d v = LPoint3d(p[0], p[1], p[2]) / p[3] - center; + radius2 = max(radius2, v.length_squared()); + } + } + } + + float radius = sqrtf(radius2); + CollisionSphere *cssphere = + new CollisionSphere(LCAST(float, center), radius); + cnode->add_solid(cssphere); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::find_collision_geometry +// Access: Private +// Description: Looks for the node, at or below the indicated node, +// that contains the associated collision geometry. +//////////////////////////////////////////////////////////////////// +EggGroup *EggLoader:: +find_collision_geometry(EggGroup *egg_group) { + if ((egg_group->get_collide_flags() & EggGroup::CF_descend) != 0) { + // If we have the "descend" instruction, we'll get to it when we + // get to it. Don't worry about it now. + return egg_group; + } + + // Does this group have any polygons? + EggGroup::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + if ((*ci)->is_of_type(EggPolygon::get_class_type())) { + // Yes! Use this group. + return egg_group; + } + } + + // Well, the group had no polygons; look for a child group that has + // the same collision type. + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + if ((*ci)->is_of_type(EggGroup::get_class_type())) { + EggGroup *child_group = DCAST(EggGroup, *ci); + if (child_group->get_cs_type() == egg_group->get_cs_type()) { + return child_group; + } + } + } + + // We got nothing. + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::create_collision_plane +// Access: Private +// Description: Creates a single CollisionPlane from the indicated +// EggPolygon. +//////////////////////////////////////////////////////////////////// +CollisionPlane *EggLoader:: +create_collision_plane(EggPolygon *egg_poly) { + egg_poly->cleanup(); + + vector vertices; + if (!egg_poly->empty()) { + EggPolygon::const_iterator vi; + vi = egg_poly->begin(); + + Vertexd vert = (*vi)->get_pos3(); + vertices.push_back(LCAST(float, vert)); + + Vertexd last_vert = vert; + ++vi; + while (vi != egg_poly->end()) { + vert = (*vi)->get_pos3(); + if (!vert.almost_equal(last_vert)) { + vertices.push_back(LCAST(float, vert)); + } + + last_vert = vert; + ++vi; + } + } + + if (vertices.size() < 3) { + return NULL; + } + Planef plane(vertices[0], vertices[1], vertices[2]); + return new CollisionPlane(plane); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::create_collision_polygon +// Access: Private +// Description: Creates a single CollisionPolygon from the indicated +// EggPolygon. +//////////////////////////////////////////////////////////////////// +CollisionPolygon *EggLoader:: +create_collision_polygon(EggPolygon *egg_poly) { + egg_poly->cleanup(); + + vector vertices; + if (!egg_poly->empty()) { + EggPolygon::const_iterator vi; + vi = egg_poly->begin(); + + Vertexd vert = (*vi)->get_pos3(); + vertices.push_back(LCAST(float, vert)); + + Vertexd last_vert = vert; + ++vi; + while (vi != egg_poly->end()) { + vert = (*vi)->get_pos3(); + if (!vert.almost_equal(last_vert)) { + vertices.push_back(LCAST(float, vert)); + } + + last_vert = vert; + ++vi; + } + } + + if (vertices.size() < 3) { + return NULL; + } + return new CollisionPolygon(vertices.begin(), vertices.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::apply_deferred_arcs +// Access: Private +// Description: Walks back over the tree and applies the +// DeferredArcProperties that were saved up along the +// way. +//////////////////////////////////////////////////////////////////// +void EggLoader:: +apply_deferred_arcs(Node *root) { + DeferredArcTraverser trav(_deferred_arcs); + + df_traverse(root, trav, NullAttributeWrapper(), DeferredArcProperty(), + RenderRelation::get_class_type()); +} diff --git a/panda/src/egg2sg/eggLoader.h b/panda/src/egg2sg/eggLoader.h new file mode 100644 index 0000000000..94c864abbb --- /dev/null +++ b/panda/src/egg2sg/eggLoader.h @@ -0,0 +1,122 @@ +// Filename: eggLoader.h +// Created by: drose (21Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGLOADER_H +#define EGGLOADER_H + +#include + +#include "deferredArcTraverser.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class EggNode; +class EggBin; +class EggGroup; +class EggTable; +class EggPrimitive; +class EggPolygon; +class ComputedVerticesMaker; +class RenderRelation; +class CollisionNode; +class CollisionPlane; +class CollisionPolygon; + +/////////////////////////////////////////////////////////////////// +// Class : EggLoader +// Description : Converts an egg data structure, possibly read from an +// egg file but not necessarily, into a scene graph +// suitable for rendering. +// +// This class isn't exported from this package. +//////////////////////////////////////////////////////////////////// +class EggLoader { +public: + EggLoader(); + EggLoader(const EggData &data); + + void build_graph(); + void reparent_decals(); + void reset_directs(); + + + void make_nonindexed_primitive(EggPrimitive *egg_prim, NamedNode *parent, + const LMatrix4d *transform = NULL); + + void make_indexed_primitive(EggPrimitive *egg_prim, NamedNode *parent, + const LMatrix4d *transform, + ComputedVerticesMaker &_comp_verts_maker); + +private: + class TextureDef { + public: + PT(TextureTransition) _texture; + PT(TextureApplyTransition) _apply; + }; + + void load_textures(); + bool load_texture(TextureDef &def, const EggTexture *egg_tex); + void apply_texture_attributes(Texture *tex, const EggTexture *egg_tex); + void apply_texture_apply_attributes(TextureApplyTransition *apply, + const EggTexture *egg_tex); + + void setup_bucket(BuilderBucket &bucket, NamedNode *parent, + EggPrimitive *egg_prim); + + RenderRelation *make_node(EggNode *egg_node, NamedNode *parent); + RenderRelation *make_node(EggPrimitive *egg_prim, NamedNode *parent); + RenderRelation *make_node(EggBin *egg_bin, NamedNode *parent); + RenderRelation *make_node(EggGroup *egg_group, NamedNode *parent); + RenderRelation *create_group_arc(EggGroup *egg_group, NamedNode *parent, + NamedNode *node); + RenderRelation *make_node(EggTable *egg_table, NamedNode *parent); + RenderRelation *make_node(EggGroupNode *egg_group, NamedNode *parent); + + void make_collision_solids(EggGroup *start_group, EggGroup *egg_group, + CollisionNode *cnode); + void make_collision_plane(EggGroup *egg_group, CollisionNode *cnode); + void make_collision_polygon(EggGroup *egg_group, CollisionNode *cnode); + void make_collision_polyset(EggGroup *egg_group, CollisionNode *cnode); + void make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode); + EggGroup *find_collision_geometry(EggGroup *egg_group); + CollisionPlane *create_collision_plane(EggPolygon *egg_poly); + CollisionPolygon *create_collision_polygon(EggPolygon *egg_poly); + + void apply_deferred_arcs(Node *root); + + + Builder _builder; + + typedef set > TextureApplies; + TextureApplies _texture_applies; + + typedef map Textures; + Textures _textures; + + typedef set Decals; + Decals _decals; + + typedef set Directs; + Directs _directs; + + DeferredArcs _deferred_arcs; + +public: + PT_NamedNode _root; + EggData _data; +}; + + +#endif diff --git a/panda/src/egg2sg/load_egg_file.cxx b/panda/src/egg2sg/load_egg_file.cxx new file mode 100644 index 0000000000..4e3dd18536 --- /dev/null +++ b/panda/src/egg2sg/load_egg_file.cxx @@ -0,0 +1,86 @@ +// Filename: load_egg_file.cxx +// Created by: drose (09Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "load_egg_file.h" +#include "eggLoader.h" +#include "config_egg2sg.h" + +#include +#include + +static PT_NamedNode +load_from_loader(EggLoader &loader) { + loader._data.resolve_externals(); + + loader.build_graph(); + + if (loader._root != (NamedNode *)NULL && egg_flatten) { + SceneGraphReducer gr(RenderRelation::get_class_type()); + int num_reduced = gr.flatten(loader._root, egg_flatten_siblings); + egg2sg_cat.info() << "Flattened " << num_reduced << " arcs.\n"; + } + + return loader._root; +} + +//////////////////////////////////////////////////////////////////// +// Function: load_egg_file +// Description: A convenience function. Loads up the indicated egg +// file, and returns the root of a scene graph. Returns +// NULL if the file cannot be read for some reason. +//////////////////////////////////////////////////////////////////// +PT_NamedNode +load_egg_file(const string &filename, CoordinateSystem cs) { + Filename egg_filename = Filename::text_filename(filename); + if (!EggData::resolve_egg_filename(egg_filename)) { + egg2sg_cat.error() + << "Could not find " << egg_filename << "\n"; + return NULL; + } + + egg2sg_cat.info() + << "Reading " << egg_filename << "\n"; + + ifstream file; + if (!egg_filename.open_read(file)) { + egg2sg_cat.error() + << "Could not open " << egg_filename << " for reading.\n"; + return NULL; + } + + EggLoader loader; + loader._data.set_egg_filename(egg_filename); + if (cs != CS_default) { + loader._data.set_coordinate_system(cs); + } + + if (!loader._data.read(file)) { + egg2sg_cat.error() + << "Error reading " << egg_filename << "\n"; + return NULL; + } + + return load_from_loader(loader); +} + +//////////////////////////////////////////////////////////////////// +// Function: load_egg_data +// Description: Another convenience function; works like +// load_egg_file() but starts from an already-filled +// EggData structure. The structure is destroyed in the +// loading. +//////////////////////////////////////////////////////////////////// +PT_NamedNode +load_egg_data(EggData &data) { + // We temporarily shuttle the children to a holding node so we can + // copy them into the EggLoader's structure without it complaining. + EggGroupNode children_holder; + children_holder.steal_children(data); + + EggLoader loader(data); + loader._data.steal_children(children_holder); + + return load_from_loader(loader); +} diff --git a/panda/src/egg2sg/load_egg_file.h b/panda/src/egg2sg/load_egg_file.h new file mode 100644 index 0000000000..e25c8ed983 --- /dev/null +++ b/panda/src/egg2sg/load_egg_file.h @@ -0,0 +1,40 @@ +// Filename: load_egg_file.h +// Created by: drose (09Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LOAD_EGG_FILE_H +#define LOAD_EGG_FILE_H + +#include + +#include +#include +#include + +class EggData; + +//////////////////////////////////////////////////////////////////// +// Function: load_egg_file +// Description: A convenience function; the primary interface to this +// package. Loads up the indicated egg file, and +// returns the root of a scene graph. Returns NULL if +// the file cannot be read for some reason. +// +// Also see the EggLoader class, which can exercise a +// bit more manual control over the loading process. +//////////////////////////////////////////////////////////////////// +EXPCL_PANDAEGG PT_NamedNode +load_egg_file(const string &filename, CoordinateSystem cs = CS_default); + +//////////////////////////////////////////////////////////////////// +// Function: load_egg_data +// Description: Another convenience function; works like +// load_egg_file() but starts from an already-filled +// EggData structure. The structure is destroyed in the +// loading. +//////////////////////////////////////////////////////////////////// +EXPCL_PANDAEGG PT_NamedNode +load_egg_data(EggData &data); + +#endif diff --git a/panda/src/egg2sg/loaderFileTypeEgg.cxx b/panda/src/egg2sg/loaderFileTypeEgg.cxx new file mode 100644 index 0000000000..1a82ecb295 --- /dev/null +++ b/panda/src/egg2sg/loaderFileTypeEgg.cxx @@ -0,0 +1,66 @@ +// Filename: loaderFileTypeEgg.cxx +// Created by: drose (20Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "loaderFileTypeEgg.h" +#include "load_egg_file.h" + +#include + +TypeHandle LoaderFileTypeEgg::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeEgg::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +LoaderFileTypeEgg:: +LoaderFileTypeEgg() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeEgg::get_name +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string LoaderFileTypeEgg:: +get_name() const { + return "Egg"; +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeEgg::get_extension +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string LoaderFileTypeEgg:: +get_extension() const { + return "egg"; +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeEgg::resolve_filename +// Access: Public, Virtual +// Description: Searches for the indicated filename on whatever paths +// are appropriate to this file type, and updates it if +// it is found. It is not necessary to call this before +// calling load_file(), but it doesn't hurt; this is +// useful for when the loader needs to know the full +// pathname to the exact file it will be loading. +//////////////////////////////////////////////////////////////////// +void LoaderFileTypeEgg:: +resolve_filename(Filename &path) const { + EggData::resolve_egg_filename(path); +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeEgg::load_file +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PT_Node LoaderFileTypeEgg:: +load_file(const Filename &path, bool) const { + PT_NamedNode result = load_egg_file(path); + return result.p(); +} diff --git a/panda/src/egg2sg/loaderFileTypeEgg.h b/panda/src/egg2sg/loaderFileTypeEgg.h new file mode 100644 index 0000000000..e88cf39b0c --- /dev/null +++ b/panda/src/egg2sg/loaderFileTypeEgg.h @@ -0,0 +1,46 @@ +// Filename: loaderFileTypeEgg.h +// Created by: drose (20Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LOADERFILETYPEEGG_H +#define LOADERFILETYPEEGG_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : LoaderFileTypeEgg +// Description : This defines the Loader interface to read Egg files. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG LoaderFileTypeEgg : public LoaderFileType { +public: + LoaderFileTypeEgg(); + + virtual string get_name() const; + virtual string get_extension() const; + + virtual void resolve_filename(Filename &path) const; + virtual PT_Node load_file(const Filename &path, bool report_errors) const; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + LoaderFileType::init_type(); + register_type(_type_handle, "LoaderFileTypeEgg", + LoaderFileType::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif + diff --git a/panda/src/egg2sg/test_loader.cxx b/panda/src/egg2sg/test_loader.cxx new file mode 100644 index 0000000000..40a1ef4a74 --- /dev/null +++ b/panda/src/egg2sg/test_loader.cxx @@ -0,0 +1,99 @@ +// Filename: test_loader.cxx +// Created by: drose (21Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "load_egg_file.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class PrintNodes : + public TraverserVisitor { +public: + PrintNodes() { + _indent_level = 0; + } + bool reached_node(Node *node, AttributeWrapper &state, NullLevelState &) { + if (node->is_of_type(GeomNode::get_class_type())) { + GeomNode *geomNode = (GeomNode *)node; + indent(nout, _indent_level) + << *geomNode << ", " << geomNode->get_num_geoms() + << " geoms:\n"; + int num_geoms = geomNode->get_num_geoms(); + for (int i = 0; i < num_geoms; i++) { + dDrawable *draw = geomNode->get_geom(i); + if (draw->is_of_type(Geom::get_class_type())) { + Geom *geom = DCAST(Geom, draw); + // geom->output_verbose(nout); + indent(nout, _indent_level + 2) << *geom << "\n"; + } else { + indent(nout, _indent_level + 2) << draw->get_type() << "\n"; + } + } + } + return true; + } + bool forward_arc(NodeRelation *arc, TransitionWrapper &trans, + AttributeWrapper &pre, AttributeWrapper &post, + NullLevelState &) { + indent(nout, _indent_level) << *arc << " has:\n"; + trans.write(nout, _indent_level + 4); + _indent_level += 2; + return true; + } + void backward_arc(NodeRelation *arc, TransitionWrapper &trans, + AttributeWrapper &pre, AttributeWrapper &post, + const NullLevelState &) { + _indent_level -= 2; + } + int _indent_level; +}; + +int +main(int argc, char *argv[]) { + if (argc < 2) { + nout << "Specify an egg file to load.\n"; + exit(1); + } + + PT_NamedNode root = new NamedNode("root"); + + for (int i = 1; i < argc; i++) { + const char *egg_filename = argv[i]; + + PT_NamedNode egg_root = load_egg_file(egg_filename); + if (egg_root == NULL) { + nout << "Unable to load " << egg_filename << ".\n"; + exit(1); + } + + new RenderRelation(root, egg_root); + } + + PrintNodes pn; + df_traverse(root, pn, AllAttributesWrapper(), NullLevelState(), + RenderRelation::get_class_type()); + + return(0); +} diff --git a/panda/src/event/Sources.pp b/panda/src/event/Sources.pp new file mode 100644 index 0000000000..db5d22c609 --- /dev/null +++ b/panda/src/event/Sources.pp @@ -0,0 +1,22 @@ +#define LOCAL_LIBS ipc express pandabase +#define OTHER_LIBS interrogatedb dconfig dtoolutil + +#begin lib_target + #define TARGET event + + #define SOURCES \ + config_event.cxx config_event.h event.cxx event.h eventHandler.cxx \ + eventHandler.h eventParameter.I eventParameter.cxx \ + eventParameter.h eventQueue.I eventQueue.cxx eventQueue.h \ + eventReceiver.cxx eventReceiver.h pt_Event.cxx pt_Event.h \ + throw_event.I throw_event.h + + #define INSTALL_HEADERS \ + event.h eventHandler.h eventParameter.I eventParameter.h \ + eventQueue.h eventQueue.I throw_event.h throw_event.I \ + eventReceiver.h \ + pt_Event.h + + #define IGATESCAN all + +#end lib_target diff --git a/panda/src/event/config_event.cxx b/panda/src/event/config_event.cxx new file mode 100644 index 0000000000..136564f6f1 --- /dev/null +++ b/panda/src/event/config_event.cxx @@ -0,0 +1,24 @@ +// Filename: config_event.cxx +// Created by: drose (14Dec99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_event.h" +#include "event.h" +#include "eventHandler.h" +#include "eventParameter.h" + +#include + +Configure(config_event); +NotifyCategoryDef(event, ""); + +ConfigureFn(config_event) { + Event::init_type(); + EventHandler::init_type(); + EventStoreInt::init_type("EventStoreInt"); + EventStoreDouble::init_type("EventStoreDouble"); + EventStoreString::init_type("EventStoreString"); +// EventStoreVec3::init_type("EventStoreVec3"); +} + diff --git a/panda/src/event/config_event.h b/panda/src/event/config_event.h new file mode 100644 index 0000000000..129ce1f070 --- /dev/null +++ b/panda/src/event/config_event.h @@ -0,0 +1,15 @@ +// Filename: config_event.h +// Created by: drose (14Dec99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_EVENT_H +#define CONFIG_EVENT_H + +#include + +#include + +NotifyCategoryDecl(event, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS); + +#endif diff --git a/panda/src/event/event.cxx b/panda/src/event/event.cxx new file mode 100644 index 0000000000..ceefc92fb3 --- /dev/null +++ b/panda/src/event/event.cxx @@ -0,0 +1,128 @@ +// Filename: event.cxx +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "event.h" +#include "config_event.h" + +TypeHandle Event::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: Event::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Event:: +Event(const string &event_name, EventReceiver *receiver) : + Namable(event_name) +{ + _receiver = receiver; +} + +//////////////////////////////////////////////////////////////////// +// Function: Event::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Event:: +Event(const Event ©) : + Namable(copy), + _parameters(copy._parameters), + _receiver(copy._receiver) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: Event::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Event:: +operator = (const Event ©) { + Namable::operator = (copy); + _parameters = copy._parameters; + _receiver = copy._receiver; +} + +//////////////////////////////////////////////////////////////////// +// Function: Event::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Event:: +~Event() { +} + +//////////////////////////////////////////////////////////////////// +// Function: Event::add_parameter +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Event:: +add_parameter(const EventParameter &obj) { + _parameters.push_back(obj); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Event::get_num_parameters +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int Event:: +get_num_parameters() const { + return _parameters.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Event::get_parameter +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EventParameter Event:: +get_parameter(int n) const { + nassertr(n >= 0 && n < _parameters.size(), EventParameter(0)); + return _parameters[n]; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Event::has_receiver +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool Event:: +has_receiver() const { + return _receiver != (EventReceiver *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: Event::get_receiver +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EventReceiver *Event:: +get_receiver() const { + return _receiver; +} + +//////////////////////////////////////////////////////////////////// +// Function: Event::set_receiver +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Event:: +set_receiver(EventReceiver *receiver) { + _receiver = receiver; +} + +//////////////////////////////////////////////////////////////////// +// Function: Event::clear_receiver +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Event:: +clear_receiver() { + _receiver = (EventReceiver *)NULL; +} diff --git a/panda/src/event/event.h b/panda/src/event/event.h new file mode 100644 index 0000000000..6fc794fff0 --- /dev/null +++ b/panda/src/event/event.h @@ -0,0 +1,68 @@ +// Filename: event.h +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EVENT_H +#define EVENT_H + +#include + +#include "eventParameter.h" + + +#include +#include + +class EventReceiver; + +//////////////////////////////////////////////////////////////////// +// Class : Event +// Description : A named event, possibly with parameters. Anyone in +// any thread may throw an event at any time; there will +// be one process responsible for reading and dispacting +// on the events (but not necessarily immediately). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS Event : public TypedReferenceCount, public Namable { +public: + Event(const string &event_name, EventReceiver *receiver = NULL); + Event(const Event ©); + void operator = (const Event ©); + ~Event(); + + void add_parameter(const EventParameter &obj); + + int get_num_parameters() const; + EventParameter get_parameter(int n) const; + + bool has_receiver() const; + EventReceiver *get_receiver() const; + void set_receiver(EventReceiver *receiver); + void clear_receiver(); + +protected: + typedef vector ParameterList; + ParameterList _parameters; + EventReceiver *_receiver; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + Namable::init_type(); + register_type(_type_handle, "Event", + TypedReferenceCount::get_class_type(), + Namable::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/event/eventHandler.cxx b/panda/src/event/eventHandler.cxx new file mode 100644 index 0000000000..e0652bf239 --- /dev/null +++ b/panda/src/event/eventHandler.cxx @@ -0,0 +1,146 @@ +// Filename: eventHandler.cxx +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "eventHandler.h" +#include "eventQueue.h" + +TypeHandle EventHandler::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: EventHandler::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EventHandler:: +EventHandler(EventQueue *queue) : _queue(*queue) { +} + +//////////////////////////////////////////////////////////////////// +// Function: EventHandler::process_events +// Access: Public +// Description: The main processing loop of the EventHandler. This +// function must be called periodically to service +// events. Walks through each pending event and calls +// its assigned hooks. +//////////////////////////////////////////////////////////////////// +void EventHandler:: +process_events() { + while (!_queue.is_queue_empty()) { + dispatch_event(_queue.dequeue_event()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EventHandler::dispatch_event +// Access: Public, Virtual +// Description: Calls the hooks assigned to the indicated single +// event. +//////////////////////////////////////////////////////////////////// +void EventHandler:: +dispatch_event(const CPT_Event &event) { + // Is the event name defined in the hook table? It will be if + // anyone has ever assigned a hook to this particular event name. + Hooks::const_iterator hi; + hi = _hooks.find(event->get_name()); + + if (hi != _hooks.end()) { + // Yes, it is! Now walk through all the functions assigned to + // that event name. + const Functions &functions = (*hi).second; + + Functions::const_iterator fi; + for (fi = functions.begin(); fi != functions.end(); ++fi) { + (*fi)(event); + } + } + + // now for callback hooks + CallbackHooks::const_iterator cbhi; + cbhi = _cbhooks.find(event->get_name()); + + if (cbhi != _cbhooks.end()) { + // found one + const CallbackFunctions &functions = (*cbhi).second; + + CallbackFunctions::const_iterator cbfi; + for (cbfi = functions.begin(); cbfi != functions.end(); ++cbfi) { + ((*cbfi).first)(event, (*cbfi).second); + } + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: EventHandler::add_hook +// Access: Public +// Description: Adds the indicated function to the list of those that +// will be called when the named event is thrown. +// Returns true if the function was successfully added, +// false if it was already defined on the indicated +// event name. +//////////////////////////////////////////////////////////////////// +bool EventHandler:: +add_hook(const string &event_name, EventFunction *function) { + return _hooks[event_name].insert(function).second; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EventHandler::add_hook +// Access: Public +// Description: Adds the indicated function to the list of those that +// will be called when the named event is thrown. +// Returns true if the function was successfully added, +// false if it was already defined on the indicated +// event name. This version records an untyped pointer +// to user callback data. +//////////////////////////////////////////////////////////////////// +bool EventHandler:: +add_hook(const string &event_name, EventCallbackFunction *function, + void *data) { + return _cbhooks[event_name].insert(CallbackFunction(function, data)).second; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EventHandler::remove_hook +// Access: Public +// Description: Removes the indicated function from the named event +// hook. Returns true if the hook was removed, false if +// it wasn't there in the first place. +//////////////////////////////////////////////////////////////////// +bool EventHandler:: +remove_hook(const string &event_name, EventFunction *function) { + return _hooks[event_name].erase(function) != 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EventHandler::remove_hook +// Access: Public +// Description: Removes the indicated function from the named event +// hook. Returns true if the hook was removed, false if +// it wasn't there in the first place. This version +// takes an untyped pointer to user callback data. +//////////////////////////////////////////////////////////////////// +bool EventHandler:: +remove_hook(const string &event_name, EventCallbackFunction *function, + void *data) { + return _cbhooks[event_name].erase(CallbackFunction(function, data)) != 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EventHandler::remove_all_hooks +// Access: Public +// Description: Removes all hooks assigned to all events. +//////////////////////////////////////////////////////////////////// +void EventHandler:: +remove_all_hooks() { + _hooks.clear(); + _cbhooks.clear(); +} + diff --git a/panda/src/event/eventHandler.h b/panda/src/event/eventHandler.h new file mode 100644 index 0000000000..708da1375a --- /dev/null +++ b/panda/src/event/eventHandler.h @@ -0,0 +1,81 @@ +// Filename: eventHandler.h +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EVENTHANDLER_H +#define EVENTHANDLER_H + +#include + +#include "event.h" +#include "pt_Event.h" + +#include +#include + +class EventQueue; + +//////////////////////////////////////////////////////////////////// +// Class : EventHandler +// Description : A class to monitor events from the C++ side of +// things. It maintains a set of "hooks", function +// pointers assigned to event names, and calls the +// appropriate hooks when the matching event is +// detected. +// +// This class is not necessary when the hooks are +// detected and processed entirely by the scripting +// language, e.g. via Scheme hooks. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS EventHandler : public TypedObject { +public: + // Define a function type suitable for receiving events. + typedef void EventFunction(CPT_Event); + typedef void EventCallbackFunction(CPT(Event), void*); + + EventHandler(EventQueue *queue); + + void process_events(); + virtual void dispatch_event(const CPT_Event &event); + + bool add_hook(const string &event_name, EventFunction *function); + bool add_hook(const string &event_name, EventCallbackFunction *function, + void*); + bool remove_hook(const string &event_name, EventFunction *function); + bool remove_hook(const string &event_name, EventCallbackFunction *function, + void*); + + void remove_all_hooks(); + +protected: + + typedef set Functions; + typedef map Hooks; + typedef pair CallbackFunction; + typedef set CallbackFunctions; + typedef map CallbackHooks; + + Hooks _hooks; + CallbackHooks _cbhooks; + EventQueue &_queue; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedObject::init_type(); + register_type(_type_handle, "EventHandler", + TypedObject::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/event/eventParameter.I b/panda/src/event/eventParameter.I new file mode 100644 index 0000000000..60a502a035 --- /dev/null +++ b/panda/src/event/eventParameter.I @@ -0,0 +1,227 @@ +// Filename: eventParameter.I +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + + + +template +TypeHandle EventStoreValue::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::Pointer constructor +// Access: Public +// Description: Defines an EventParameter that stores a pointer to +// any kind of TypedReferenceCount object. This is the +// most general constructor. +//////////////////////////////////////////////////////////////////// +INLINE EventParameter:: +EventParameter(const TypedReferenceCount *ptr) : _ptr(ptr) { } + + +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::Integer constructor +// Access: Public +// Description: Defines an EventParameter that stores an integer +// value. +//////////////////////////////////////////////////////////////////// +INLINE EventParameter:: +EventParameter(int value) : _ptr(new EventStoreInt(value)) { } + + +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::Double constructor +// Access: Public +// Description: Defines an EventParameter that stores a +// floating-point value. +//////////////////////////////////////////////////////////////////// +INLINE EventParameter:: +EventParameter(double value) : _ptr(new EventStoreDouble(value)) { } + + +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::String constructor +// Access: Public +// Description: Defines an EventParameter that stores a string value. +//////////////////////////////////////////////////////////////////// +INLINE EventParameter:: +EventParameter(const string &value) : _ptr(new EventStoreString(value)) { } + + +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::LVecBase3f constructor +// Access: Public +// Description: Defines an EventParameter that stores a +// three-component numeric value. +//////////////////////////////////////////////////////////////////// +#if 0 +INLINE EventParameter:: +EventParameter(const LVecBase3f &value) : _ptr(new EventStoreVec3(value)) { } +#endif + + +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::Copy constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EventParameter:: +EventParameter(const EventParameter &other) : _ptr(other._ptr) { } + + +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EventParameter &EventParameter:: +operator = (const EventParameter &other) { + _ptr = other._ptr; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::is_int +// Access: Public +// Description: Returns true if the EventParameter stores an integer +// value, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool EventParameter:: +is_int() const { + nassertr(_ptr != (TypedReferenceCount *)NULL, false); + return _ptr->is_of_type(EventStoreInt::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::get_int_value +// Access: Public +// Description: Retrieves the value stored in the EventParameter. It +// is only valid to call this if is_int() has already +// returned true. +//////////////////////////////////////////////////////////////////// +INLINE int EventParameter:: +get_int_value() const { + nassertr(is_int(), 0); + // We can't use DCAST, because EventStoreValue::init_type() breaks + // convention and takes a parameter. But the above assertion should + // protect us. + return ((const EventStoreInt *)_ptr.p())->get_value(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::is_double +// Access: Public +// Description: Returns true if the EventParameter stores a double +// floating-point value, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool EventParameter:: +is_double() const { + nassertr(_ptr != (TypedReferenceCount *)NULL, false); + return _ptr->is_of_type(EventStoreDouble::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::get_double_value +// Access: Public +// Description: Retrieves the value stored in the EventParameter. It +// is only valid to call this if is_double() has already +// returned true. +//////////////////////////////////////////////////////////////////// +INLINE double EventParameter:: +get_double_value() const { + nassertr(is_double(), 0.0); + return ((const EventStoreDouble *)_ptr.p())->get_value(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::is_string +// Access: Public +// Description: Returns true if the EventParameter stores a string +// value, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool EventParameter:: +is_string() const { + nassertr(_ptr != (TypedReferenceCount *)NULL, false); + return _ptr->is_of_type(EventStoreString::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::get_string_value +// Access: Public +// Description: Retrieves the value stored in the EventParameter. It +// is only valid to call this if is_string() has already +// returned true. +//////////////////////////////////////////////////////////////////// +INLINE string EventParameter:: +get_string_value() const { + nassertr(is_string(), ""); + return ((const EventStoreString *)_ptr.p())->get_value(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::is_vec3 +// Access: Public +// Description: Returns true if the EventParameter stores a +// three-component vector value, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool EventParameter:: +is_vec3() const { + nassertr(_ptr != (TypedReferenceCount *)NULL, false); + return false; +// return _ptr->is_of_type(EventStoreVec3::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::get_vec3_value +// Access: Public +// Description: Retrieves the value stored in the EventParameter. It +// is only valid to call this if is_vec3() has already +// returned true. +//////////////////////////////////////////////////////////////////// +#if 0 +INLINE LVecBase3f EventParameter:: +get_vec3_value() const { + nassertr(is_vec3(), LVecBase3f(0.0, 0.0, 0.0)); + return ((const EventStoreVec3 *)_ptr.p())->get_value(); +} +#endif + +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::get_ptr +// Access: Public +// Description: Retrieves a pointer to the actual value stored in the +// parameter. The TypeHandle of this pointer may be +// examined to determine the actual type of parameter it +// contains. This is the only way to retrieve the value +// when it is not one of the above predefined types. +//////////////////////////////////////////////////////////////////// +INLINE const TypedReferenceCount *EventParameter:: +get_ptr() const { + return _ptr; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EventStoreValue::set_value +// Access: Public +// Description: Changes the value stored in the parameter. It is +// dangerous to do this for a parameter already added to +// an event, since the parameters may be shared. +//////////////////////////////////////////////////////////////////// +template +INLINE void EventStoreValue:: +set_value(const Type &value) { + _value = value; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EventStoreValue::get_value +// Access: Public +// Description: Retrieves the value stored in the parameter. +//////////////////////////////////////////////////////////////////// +template +INLINE Type EventStoreValue:: +get_value() const { + return _value; +} diff --git a/panda/src/event/eventParameter.cxx b/panda/src/event/eventParameter.cxx new file mode 100644 index 0000000000..b579532be1 --- /dev/null +++ b/panda/src/event/eventParameter.cxx @@ -0,0 +1,11 @@ +// Filename: eventParameter.cxx +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "eventParameter.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/event/eventParameter.h b/panda/src/event/eventParameter.h new file mode 100644 index 0000000000..ca488fc2c5 --- /dev/null +++ b/panda/src/event/eventParameter.h @@ -0,0 +1,119 @@ +// Filename: eventParameter.h +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EVENTPARAMETER_H +#define EVENTPARAMETER_H + +#include + +#include +#include +#include +#include +//#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : EventParameter +// Description : An optional parameter associated with an event. Each +// event may have zero or more of these. Each parameter +// stores a pointer to a TypedReferenceCount object, +// which of course could be pretty much anything. To +// store a simple value like a double or a string, the +// EventParameter constructors transparently use the +// EventStoreValue template class, defined below. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS EventParameter { +public: + INLINE EventParameter(const TypedReferenceCount *ptr); + INLINE EventParameter(int value); + INLINE EventParameter(double value); + INLINE EventParameter(const string &value); +// INLINE EventParameter(const LVecBase3f &value); + + INLINE EventParameter(const EventParameter ©); + INLINE EventParameter &operator = (const EventParameter ©); + + // These functions are conveniences to easily determine if the + // EventParameter is one of the predefined parameter types, and + // retrieve the corresponding value. Of course, it is possible that + // the EventParameter is some user-defined type, and is none of + // these. + INLINE bool is_int() const; + INLINE int get_int_value() const; + INLINE bool is_double() const; + INLINE double get_double_value() const; + INLINE bool is_string() const; + INLINE string get_string_value() const; + INLINE bool is_vec3() const; +// INLINE LVecBase3f get_vec3_value() const; + + INLINE const TypedReferenceCount *get_ptr() const; + +private: + CPT(TypedReferenceCount) _ptr; +}; + + +//////////////////////////////////////////////////////////////////// +// Class : EventStoreValue +// Description : A handy class object for storing simple values (like +// integers or strings) passed along with an Event. +// This is essentially just a wrapper around whatever +// data type you like, to make it a TypedReferenceCount +// object which can be passed along inside an +// EventParameter. +//////////////////////////////////////////////////////////////////// +template +class EventStoreValue : public TypedReferenceCount { +public: + EventStoreValue(const Type &value) : _value(value) { } + + INLINE void set_value(const Type &value); + INLINE Type get_value() const; + + Type _value; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(const string &type_name) { + TypedReferenceCount::init_type(); + register_type(_type_handle, type_name, + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() { + // In this case, we can't do anything, since we don't have the + // class' type_name. + return get_class_type(); + } + +private: + static TypeHandle _type_handle; +}; + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS, EventStoreValue); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS, EventStoreValue); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS, EventStoreValue); +//EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS, EventStoreValue); + +typedef EventStoreValue EventStoreInt; +typedef EventStoreValue EventStoreDouble; +typedef EventStoreValue EventStoreString; +//typedef EventStoreValue EventStoreVec3; + +#include "eventParameter.I" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif + diff --git a/panda/src/event/eventQueue.I b/panda/src/event/eventQueue.I new file mode 100644 index 0000000000..1564687dec --- /dev/null +++ b/panda/src/event/eventQueue.I @@ -0,0 +1,20 @@ +// Filename: eventQueue.I +// Created by: drose (05May00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EventQueue::get_global_event_queue +// Access: Public +// Description: Returns a pointer to the one global EventQueue +// object. If the global object has not yet been +// created, this will create it. +//////////////////////////////////////////////////////////////////// +INLINE EventQueue *EventQueue:: +get_global_event_queue() { + if (_global_event_queue == NULL) { + make_global_event_queue(); + } + return _global_event_queue; +} diff --git a/panda/src/event/eventQueue.cxx b/panda/src/event/eventQueue.cxx new file mode 100644 index 0000000000..940e8c42dd --- /dev/null +++ b/panda/src/event/eventQueue.cxx @@ -0,0 +1,81 @@ +// Filename: eventQueue.cxx +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "eventQueue.h" +#include "config_event.h" + +EventQueue *EventQueue::_global_event_queue = NULL; + + +//////////////////////////////////////////////////////////////////// +// Function: EventQueue::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EventQueue:: +EventQueue() { +} + +//////////////////////////////////////////////////////////////////// +// Function: EventQueue::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EventQueue:: +~EventQueue() { +} + +//////////////////////////////////////////////////////////////////// +// Function: EventQueue::queue_event +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void EventQueue:: +queue_event(CPT_Event event) { + mutex_lock lock(_lock); + if (_queue.is_full()) { + event_cat.error() + << "Ignoring event " << *event << "; event queue full.\n"; + } else { + _queue.insert(event); + if (event_cat.is_debug()) { + event_cat.debug() + << "Throwing event " << *event << "\n"; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EventQueue::is_queue_empty +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool EventQueue:: +is_queue_empty() const { + return _queue.is_empty(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EventQueue::dequeue_event +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPT_Event EventQueue:: +dequeue_event() { + // We need no mutex protection here, as long as there is only one + // thread extracting events. The magic of circular buffers. + return _queue.extract(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EventQueue::make_global_event_queue +// Access: Protected, Static +// Description: +//////////////////////////////////////////////////////////////////// +void EventQueue:: +make_global_event_queue() { + _global_event_queue = new EventQueue; +} diff --git a/panda/src/event/eventQueue.h b/panda/src/event/eventQueue.h new file mode 100644 index 0000000000..b267a5299b --- /dev/null +++ b/panda/src/event/eventQueue.h @@ -0,0 +1,50 @@ +// Filename: eventQueue.h +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EVENTQUEUE_H +#define EVENTQUEUE_H + +#include + +#include "event.h" +#include "pt_Event.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : EventQueue +// Description : A queue of pending events. As events are thrown, +// they are added to this queue; eventually, they will +// be extracted out again by an EventHandler and +// processed. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS EventQueue { +public: + enum { max_events = 500 }; + + EventQueue(); + ~EventQueue(); + + void queue_event(CPT_Event event); + + bool is_queue_empty() const; + CPT_Event dequeue_event(); + + INLINE static EventQueue * + get_global_event_queue(); + +protected: + CircBuffer _queue; + + static void make_global_event_queue(); + static EventQueue *_global_event_queue; + + mutex _lock; +}; + +#include "eventQueue.I" + +#endif diff --git a/panda/src/event/eventReceiver.cxx b/panda/src/event/eventReceiver.cxx new file mode 100644 index 0000000000..1d461c2b4e --- /dev/null +++ b/panda/src/event/eventReceiver.cxx @@ -0,0 +1,8 @@ +// Filename: eventReceiver.cxx +// Created by: drose (14Dec99) +// +//////////////////////////////////////////////////////////////////// + +#include "eventReceiver.h" + +TypeHandle EventReceiver::_type_handle; diff --git a/panda/src/event/eventReceiver.h b/panda/src/event/eventReceiver.h new file mode 100644 index 0000000000..97b639ffe9 --- /dev/null +++ b/panda/src/event/eventReceiver.h @@ -0,0 +1,36 @@ +// Filename: eventReceiver.h +// Created by: drose (14Dec99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EVENTRECEIVER_H +#define EVENTRECEIVER_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : EventReceiver +// Description : An abstract base class for anything that might care +// about receiving events. An object that might receive +// an event should inherit from this class; each event +// may be sent with an optional EventReceiver pointer. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS EventReceiver { + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + register_type(_type_handle, "EventReceiver"); + } + +private: + static TypeHandle _type_handle; +}; + +#endif + + diff --git a/panda/src/event/pt_Event.cxx b/panda/src/event/pt_Event.cxx new file mode 100644 index 0000000000..fa7e2ec52c --- /dev/null +++ b/panda/src/event/pt_Event.cxx @@ -0,0 +1,11 @@ +// Filename: pt_Event.cxx +// Created by: drose (26May00) +// +//////////////////////////////////////////////////////////////////// + +#include "pt_Event.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/event/pt_Event.h b/panda/src/event/pt_Event.h new file mode 100644 index 0000000000..f9273437b1 --- /dev/null +++ b/panda/src/event/pt_Event.h @@ -0,0 +1,33 @@ +// Filename: pt_Event.h +// Created by: drose (26May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PT_EVENT_H +#define PT_EVENT_H + +#include + +#include "event.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PT_Event +// Description : A PointerTo. This is defined here solely we +// can explicitly export the template class. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS, PointerToBase) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS, PointerTo) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS, ConstPointerTo) + +typedef PointerTo PT_Event; +typedef ConstPointerTo CPT_Event; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/event/throw_event.I b/panda/src/event/throw_event.I new file mode 100644 index 0000000000..5e70a831ae --- /dev/null +++ b/panda/src/event/throw_event.I @@ -0,0 +1,48 @@ +// Filename: throw_event.I +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + + +INLINE void +throw_event(const CPT_Event &event) { + EventQueue::get_global_event_queue()->queue_event(event); +} + +INLINE void +throw_event(const string &event_name) { + EventQueue::get_global_event_queue()->queue_event(new Event(event_name)); +} + +INLINE void +throw_event(const string &event_name, + const EventParameter &p1) { + Event *event = new Event(event_name); + event->add_parameter(p1); + EventQueue::get_global_event_queue()->queue_event(event); +} + +INLINE void +throw_event(const string &event_name, + const EventParameter &p1, + const EventParameter &p2) { + Event *event = new Event(event_name); + event->add_parameter(p1); + event->add_parameter(p2); + EventQueue::get_global_event_queue()->queue_event(event); +} + +INLINE void +throw_event(const string &event_name, + const EventParameter &p1, + const EventParameter &p2, + const EventParameter &p3) { + Event *event = new Event(event_name); + event->add_parameter(p1); + event->add_parameter(p2); + event->add_parameter(p3); + EventQueue::get_global_event_queue()->queue_event(event); +} + + + diff --git a/panda/src/event/throw_event.h b/panda/src/event/throw_event.h new file mode 100644 index 0000000000..57f2b7bd65 --- /dev/null +++ b/panda/src/event/throw_event.h @@ -0,0 +1,29 @@ +// Filename: throw_event.h +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef THROW_EVENT_H +#define THROW_EVENT_H + +#include + +#include "eventQueue.h" +#include "pt_Event.h" + +// A handful of convenience functions to throw events. +INLINE void throw_event(const CPT_Event &event); +INLINE void throw_event(const string &event_name); +INLINE void throw_event(const string &event_name, + const EventParameter &p1); +INLINE void throw_event(const string &event_name, + const EventParameter &p1, + const EventParameter &p2); +INLINE void throw_event(const string &event_name, + const EventParameter &p1, + const EventParameter &p2, + const EventParameter &p3); + +#include "throw_event.I" + +#endif diff --git a/panda/src/express/Sources.pp b/panda/src/express/Sources.pp new file mode 100644 index 0000000000..cd39662ed3 --- /dev/null +++ b/panda/src/express/Sources.pp @@ -0,0 +1,44 @@ +#define LOCAL_LIBS pandabase +#define OTHER_LIBS interrogatedb dconfig dtoolutil + +#begin lib_target + #define TARGET express + + #define SOURCES \ + bigEndian.I bigEndian.cxx bigEndian.h buffer.I buffer.cxx buffer.h \ + circBuffer.I circBuffer.h clockObject.I clockObject.cxx \ + clockObject.h config_express.cxx config_express.h datagram.I \ + datagram.cxx datagram.h datagramGenerator.I datagramGenerator.cxx \ + datagramGenerator.h datagramIterator.cxx datagramIterator.h \ + datagramSink.I datagramSink.cxx datagramSink.h \ + get_config_path.cxx get_config_path.h \ + indent.I indent.cxx indent.h littleEndian.I \ + littleEndian.cxx littleEndian.h memoryUsage.I memoryUsage.cxx \ + memoryUsage.h memoryUsagePointers.I memoryUsagePointers.cxx \ + memoryUsagePointers.h multifile.I multifile.cxx multifile.h \ + namable.I namable.cxx namable.h numeric_types.h patchfile.I \ + patchfile.cxx patchfile.h pointerTo.I pointerTo.h referenceCount.I \ + referenceCount.cxx referenceCount.h tokenBoard.I tokenBoard.h \ + trueClock.I trueClock.cxx trueClock.h typeHandle.I typeHandle.cxx \ + typeHandle.h typedReferenceCount.I typedReferenceCount.cxx \ + typedReferenceCount.h typedef.h + + #define INSTALL_HEADERS \ + bigEndian.I bigEndian.h buffer.I buffer.h circBuffer.I \ + circBuffer.h clockObject.I clockObject.h config_express.h \ + datagram.I datagram.h datagramIterator.h \ + datagramSink.I datagramSink.h datagramGenerator.I \ + datagramGenerator.h get_config_path.h \ + indent.I indent.h littleEndian.I littleEndian.h \ + memoryUsage.I memoryUsage.h memoryUsagePointers.I \ + memoryUsagePointers.h multifile.I multifile.h numeric_types.h \ + pointerTo.I pointerTo.h referenceCount.I referenceCount.h \ + tokenBoard.h trueClock.I trueClock.h typeHandle.I typeHandle.h \ + typedReferenceCount.I typedReferenceCount.h typedef.h \ + namable.I namable.h tokenBoard.I patchfile.h patchfile.I + + #define IGATESCAN all + + #define IGATESCAN all + +#end lib_target diff --git a/panda/src/express/bigEndian.I b/panda/src/express/bigEndian.I new file mode 100644 index 0000000000..23e0c2b9af --- /dev/null +++ b/panda/src/express/bigEndian.I @@ -0,0 +1,91 @@ +// Filename: bigEndian.I +// Created by: drose (23Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifdef IS_LITTLE_ENDIAN + +//////////////////////////////////////////////////////////////////// +// Function: BigEndian::Constructor +// Access: Public +// Description: Reverses the bytes in the indicated string. +//////////////////////////////////////////////////////////////////// +INLINE BigEndian:: +BigEndian(const string &string) { + reverse_assign(string.data(), string.length()); +} + +//////////////////////////////////////////////////////////////////// +// Function: BigEndian::Constructor +// Access: Public +// Description: Reverses the bytes in the indicated string. +//////////////////////////////////////////////////////////////////// +INLINE BigEndian:: +BigEndian(const char *data, size_t length) { + reverse_assign(data, length); +} + +//////////////////////////////////////////////////////////////////// +// Function: BigEndian::Copy Constructor +// Access: Public +// Description: Since this is assigned from another BigEndian, +// presumably already reversed, this does *not* reverse +// the byte order. +//////////////////////////////////////////////////////////////////// +INLINE BigEndian:: +BigEndian(const BigEndian &other) : _str(other._str) { +} + +//////////////////////////////////////////////////////////////////// +// Function: BigEndian::String Assignment Operator +// Access: Public +// Description: Reverses the bytes in the indicated string. +//////////////////////////////////////////////////////////////////// +INLINE void BigEndian:: +operator =(const string &string) { + reverse_assign(string.data(), string.length()); +} + +//////////////////////////////////////////////////////////////////// +// Function: BigEndian::Copy Assignment Operator +// Access: Public +// Description: Since this is assigned from another BigEndian, +// presumably already reversed, this does *not* reverse +// the byte order. +//////////////////////////////////////////////////////////////////// +INLINE void BigEndian:: +operator =(const BigEndian &other) { + _str = other._str; +} + +//////////////////////////////////////////////////////////////////// +// Function: BigEndian::string Typecast Operator +// Access: Public +// Description: Returns the data as a string. +//////////////////////////////////////////////////////////////////// +INLINE BigEndian:: +operator const string &() const { + return _str; +} + +//////////////////////////////////////////////////////////////////// +// Function: BigEndian::data +// Access: Public +// Description: Returns the data in the string. +//////////////////////////////////////////////////////////////////// +INLINE const char *BigEndian:: +data() const { + return _str.data(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BigEndian::length +// Access: Public +// Description: Returns the number of bytes in the string. +//////////////////////////////////////////////////////////////////// +INLINE size_t BigEndian:: +length() const { + return _str.length(); +} + +#endif // IS_LITTLE_ENDIAN diff --git a/panda/src/express/bigEndian.cxx b/panda/src/express/bigEndian.cxx new file mode 100644 index 0000000000..b128b68e0e --- /dev/null +++ b/panda/src/express/bigEndian.cxx @@ -0,0 +1,25 @@ +// Filename: bigEndian.cxx +// Created by: drose (23Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "bigEndian.h" + +#ifdef IS_LITTLE_ENDIAN + +//////////////////////////////////////////////////////////////////// +// Function: BigEndian::reverse_assign +// Access: Private +// Description: Actually does the data reversal. +//////////////////////////////////////////////////////////////////// +void BigEndian:: +reverse_assign(const char *data, size_t length) { + _str = ""; + _str.reserve(length); + + for (size_t i = 0; i < length; i++) { + _str += data[length - 1 - i]; + } +} + +#endif // IS_LITTLE_ENDIAN diff --git a/panda/src/express/bigEndian.h b/panda/src/express/bigEndian.h new file mode 100644 index 0000000000..8c566c11e0 --- /dev/null +++ b/panda/src/express/bigEndian.h @@ -0,0 +1,60 @@ +// Filename: bigEndian.h +// Created by: drose (23Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BIGENDIAN_H +#define BIGENDIAN_H + +#include + +#include "numeric_types.h" + +#ifdef IS_LITTLE_ENDIAN + +//////////////////////////////////////////////////////////////////// +// Class : BigEndian +// Description : BigEndian is a special string-like class that +// automatically reverses the byte order when it is +// assigned from either a char buffer or a true +// string--but only when compiling for a littleendian +// architecture. On bigendian machines, BigEndian +// is defined to map directly to string. +// +// See also LittleEndian. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS BigEndian { +public: + INLINE BigEndian(const string &string); + INLINE BigEndian(const char *data, size_t length); + INLINE BigEndian(const BigEndian &other); + + INLINE void operator =(const string &string); + INLINE void operator =(const BigEndian &other); + + INLINE operator const string &() const; + INLINE const char *data() const; + INLINE size_t length() const; + +private: + void reverse_assign(const char *data, size_t length); + + string _str; +}; + +#else // !IS_LITTLE_ENDIAN + +#define BigEndian string + +#endif // IS_LITTLE_ENDIAN + +#include "bigEndian.I" + +#endif + + + + + + + diff --git a/panda/src/express/buffer.I b/panda/src/express/buffer.I new file mode 100644 index 0000000000..93ed07d0b7 --- /dev/null +++ b/panda/src/express/buffer.I @@ -0,0 +1,14 @@ +// Filename: buffer.I +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Buffer::get_length +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int Buffer:: +get_length(void) const { + return _length; +} diff --git a/panda/src/express/buffer.cxx b/panda/src/express/buffer.cxx new file mode 100644 index 0000000000..3d26fadb04 --- /dev/null +++ b/panda/src/express/buffer.cxx @@ -0,0 +1,44 @@ +// Filename: buffer.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// Copyright (C) 1992,93,94,95,96,97,98 +// Walt Disney Imagineering, Inc. +// +// These coded instructions, statements, data structures and +// computer programs contain unpublished proprietary information of +// Walt Disney Imagineering and are protected by Federal copyright +// law. They may not be disclosed to third parties or copied or +// duplicated in any form, in whole or in part, without the prior +// written consent of Walt Disney Imagineering Inc. +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "buffer.h" + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Buffer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Buffer:: +Buffer(int size) { + _length = size; + _buffer = new char[_length]; +} + +//////////////////////////////////////////////////////////////////// +// Function: Buffer::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Buffer:: +~Buffer() { + delete _buffer; +} diff --git a/panda/src/express/buffer.h b/panda/src/express/buffer.h new file mode 100644 index 0000000000..7cdcd3120d --- /dev/null +++ b/panda/src/express/buffer.h @@ -0,0 +1,39 @@ +// Filename: buffer.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef BUFFER_H +#define BUFFER_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include +#include "typedef.h" +#include "referenceCount.h" + +//////////////////////////////////////////////////////////////////// +// Class : Buffer +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS Buffer : public ReferenceCount { +public: + Buffer(int size); + ~Buffer(); + + INLINE int get_length(void) const; + +#ifndef CPPPARSER +// hidden from interrogate +public: + char *_buffer; +#endif + +private: + int _length; +}; + +#include "buffer.I" + +#endif diff --git a/panda/src/express/circBuffer.I b/panda/src/express/circBuffer.I new file mode 100644 index 0000000000..020297e89d --- /dev/null +++ b/panda/src/express/circBuffer.I @@ -0,0 +1,98 @@ +// Filename: circBuffer.I +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "config_express.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: CircBuffer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE CircBuffer:: +CircBuffer() { + _in = _out = 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CircBuffer::is_empty +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool CircBuffer:: +is_empty() const { + return _in == _out; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CircBuffer::is_full +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool CircBuffer:: +is_full() const { + return _in == _out-1 || (_in==max_size && _out==0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: CircBuffer::peek +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE const Thing &CircBuffer:: +peek() const { + nassertr(!is_empty(), *(new Thing)); + return _array[_out]; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CircBuffer::extract +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE Thing CircBuffer:: +extract() { + nassertr(!is_empty(), Thing()); + Thing temp = _array[_out]; + + // We need to clear out the old element to force its destructor to + // be called; it might be important. This will generate yet another + // UMR warning in Purify if the default constructor doesn't fully + // initialize the class. + _array[_out] = Thing(); + + _out = (_out+1)%(max_size+1); + return temp; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CircBuffer::insert +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void CircBuffer:: +insert(const Thing &t) { + if (is_full()) { + express_cat.error() + << "Circular buffer is full; cannot add requests.\n"; + } else { + _array[_in] = t; + _in = (_in+1)%(max_size+1); + } +} diff --git a/panda/src/express/circBuffer.h b/panda/src/express/circBuffer.h new file mode 100644 index 0000000000..dbcafcd1a8 --- /dev/null +++ b/panda/src/express/circBuffer.h @@ -0,0 +1,41 @@ +// Filename: circBuffer.h +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CIRCBUFFER_H +#define CIRCBUFFER_H + +#include + +//////////////////////////////////////////////////////////////////// +// Class : CircBuffer +// Description : This class implements a queue of some type via a +// circular buffer. The circular buffer has the +// advantage that no synchronization is required when +// one process adds to the queue while another process +// extracts. It works for any kind of Thing that has a +// valid assignment operator and copy constructor +// defined. +//////////////////////////////////////////////////////////////////// +template +class CircBuffer { +public: + INLINE CircBuffer(); + + INLINE bool is_empty() const; + INLINE bool is_full() const; + + INLINE const Thing &peek() const; + INLINE Thing extract(); + + INLINE void insert(const Thing &t); + +protected: + Thing _array[max_size+1]; + int _in, _out; +}; + +#include "circBuffer.I" + +#endif diff --git a/panda/src/express/clockObject.I b/panda/src/express/clockObject.I new file mode 100644 index 0000000000..c517eb14df --- /dev/null +++ b/panda/src/express/clockObject.I @@ -0,0 +1,180 @@ +// Filename: clockObject.I +// Created by: drose (17Feb00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ClockObject::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ClockObject:: +~ClockObject() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ClockObject::set_mode +// Access: Public +// Description: Changes the mode of the clock, e.g. from normal +// (real-time) to non-real-time. In non-real-time mode, +// tick() will add the value of dt to the value returned +// by get_time(), regardless of how much time has +// actually passed. In normal, real-time mode, tick() +// will set the value returned by get_time() to the +// current real time. +//////////////////////////////////////////////////////////////////// +INLINE void ClockObject:: +set_mode(ClockObject::Mode mode) { + _mode = mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClockObject::get_mode +// Access: Public +// Description: Returns the current mode of the clock. See +// set_mode(). +//////////////////////////////////////////////////////////////////// +INLINE ClockObject::Mode ClockObject:: +get_mode() const { + return _mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClockObject::get_time +// Access: Public +// Description: Returns the time in seconds as of the last time +// tick() was called (typically, this will be as of the +// start of the current frame). +// +// This is generally the kind of time you want to ask +// for in most rendering and animation contexts, since +// it's important that all of the animation for a given +// frame remains in sync with each other. +//////////////////////////////////////////////////////////////////// +INLINE double ClockObject:: +get_time() const { + return _reported_time; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClockObject::get_intra_frame_time +// Access: Public +// Description: Returns the number of seconds that have elapsed since +// the start of the frame. This might be useful for +// certain specialty applications, for instance to +// measure how much time we've spent working on this one +// particular frame. +// +// This function returns an honest value whether the +// ClockObject is in normal, real-time mode or in +// non-real-time mode. +//////////////////////////////////////////////////////////////////// +INLINE double ClockObject:: +get_intra_frame_time() const { + return get_real_time() - _frame_time; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClockObject::get_real_time +// Access: Public +// Description: Returns the actual number of seconds elapsed since +// the ClockObject was created, or since it was last +// reset. This is useful for doing real timing +// measurements, e.g. for performance statistics. +//////////////////////////////////////////////////////////////////// +INLINE double ClockObject:: +get_real_time() const { + return (_true_clock->get_real_time() - _start_time); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClockObject::reset +// Access: Public +// Description: Simultaneously resets both the time and the frame +// count to zero. +//////////////////////////////////////////////////////////////////// +INLINE void ClockObject:: +reset() { + set_time(0.0); + set_frame_count(0); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClockObject::set_frame_count +// Access: Public +// Description: Resets the number of frames counted to the indicated +// number. Also see reset() and set_time(). +//////////////////////////////////////////////////////////////////// +INLINE void ClockObject:: +set_frame_count(int frame_count) { + _frame_count = frame_count; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClockObject::get_frame_count +// Access: Public +// Description: Returns the number of times tick() has been called +// since the ClockObject was created, or since it was +// last reset. This is generally the number of frames +// that have been rendered. +//////////////////////////////////////////////////////////////////// +INLINE int ClockObject:: +get_frame_count() const { + return _frame_count; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClockObject::get_frame_rate +// Access: Public +// Description: Returns the average frame rate since the last reset. +// This is simply the total number of frames divided by +// the total elapsed time. +//////////////////////////////////////////////////////////////////// +INLINE double ClockObject:: +get_frame_rate() const { + return (double)get_frame_count() / get_time(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClockObject::get_dt +// Access: Public +// Description: Returns the elapsed time for the previous frame: the +// number of seconds elapsed between the last two calls +// to tick(). +//////////////////////////////////////////////////////////////////// +INLINE double ClockObject:: +get_dt() const { + return _dt; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClockObject::set_dt +// Access: Public +// Description: In non-real-time mode, sets the number of seconds +// that should appear to elapse between frames. +//////////////////////////////////////////////////////////////////// +INLINE void ClockObject:: +set_dt(double dt) { + if (_mode != M_non_real_time) { + express_cat.error() + << "ClockObject cannot set dt in real-time mode." << endl; + return; + } + + _dt = dt; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClockObject::get_global_clock +// Access: Public +// Description: Returns a pointer to the global ClockObject. This is +// the ClockObject that most code should use for +// handling scene graph rendering and animation. +//////////////////////////////////////////////////////////////////// +INLINE ClockObject *ClockObject:: +get_global_clock() { + if (_global_clock == (ClockObject *)NULL) { + _global_clock = new ClockObject; + } + return _global_clock; +} diff --git a/panda/src/express/clockObject.cxx b/panda/src/express/clockObject.cxx new file mode 100644 index 0000000000..2832afdd3b --- /dev/null +++ b/panda/src/express/clockObject.cxx @@ -0,0 +1,76 @@ +// Filename: clockObject.cxx +// Created by: drose (17Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "clockObject.h" + +ClockObject *ClockObject::_global_clock = (ClockObject *)NULL; + + +//////////////////////////////////////////////////////////////////// +// Function: ClockObject::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ClockObject:: +ClockObject() { + _true_clock = TrueClock::get_ptr(); + _mode = M_normal; + _start_time = _true_clock->get_real_time(); + _frame_count = 0; + _frame_time = 0.0; + _reported_time = 0.0; + _dt = 0.0; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClockObject::set_time +// Access: Public +// Description: Resets the clock to the indicated time. Also see +// reset() and set_frame_count(). +//////////////////////////////////////////////////////////////////// +void ClockObject:: +set_time(double time) { + double true_time = _true_clock->get_real_time(); + _start_time = true_time - time; + _frame_time = time; + + switch (_mode) { + case M_normal: + _reported_time = _frame_time; + break; + + case M_non_real_time: + _reported_time = time; + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ClockObject::tick +// Access: Public +// Description: Instructs the clock that a new frame has just begun. +// In normal, real-time mode, get_time() will henceforth +// report the time as of this instant as the current +// start-of-frame time. In non-real-time mode, +// get_time() will be incremented by the value of dt. +//////////////////////////////////////////////////////////////////// +void ClockObject:: +tick() { + double old_time = _frame_time; + _frame_time = get_real_time(); + + switch (_mode) { + case M_normal: + _dt = _frame_time - old_time; + _reported_time = _frame_time; + break; + + case M_non_real_time: + _reported_time += _dt; + break; + } + + _frame_count++; +} diff --git a/panda/src/express/clockObject.h b/panda/src/express/clockObject.h new file mode 100644 index 0000000000..abb51e7952 --- /dev/null +++ b/panda/src/express/clockObject.h @@ -0,0 +1,85 @@ +// Filename: clockObject.h +// Created by: drose (19Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CLOCKOBJECT_H +#define CLOCKOBJECT_H + +#include + +#include "trueClock.h" +#include "config_express.h" + +//////////////////////////////////////////////////////////////////// +// Class : ClockObject +// Description : A ClockObject keeps track of elapsed real time and +// discrete time. It can run in two modes: In normal +// mode, get_time() returns the time as of the last time +// tick() was called. This is the "discrete" time, and +// is usually used to get the time as of, for instance, +// the beginning of the current frame. In non-real-time +// mode, get_time() returns a constant increment since +// the last time tick() was called; this is useful when +// it is desirable to fake the clock out, for instance +// for non-real-time animation rendering. +// +// In both modes, get_real_time() always returns the +// elapsed real time in seconds since the ClockObject +// was constructed, or since it was last reset. +// +// You can create your own ClockObject whenever you want +// to have your own local timer. There is also a +// default, global ClockObject intended to represent +// global time for the application; this is normally set +// up to tick every frame so that its get_time() will +// return the time for the current frame. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS ClockObject { +public: + enum Mode { + M_normal, + M_non_real_time, + }; + + ClockObject(); + INLINE ~ClockObject(); + + INLINE void set_mode(Mode mode); + INLINE Mode get_mode() const; + + INLINE double get_time() const; + INLINE double get_intra_frame_time() const; + INLINE double get_real_time() const; + + INLINE void reset(); + + void set_time(double time); + INLINE void set_frame_count(int frame_count); + + INLINE int get_frame_count() const; + INLINE double get_frame_rate() const; + + INLINE double get_dt() const; + INLINE void set_dt(double dt); + + void tick(); + + INLINE static ClockObject *get_global_clock(); + +private: + TrueClock *_true_clock; + Mode _mode; + double _start_time; + int _frame_count; + double _frame_time; + double _reported_time; + double _dt; + + static ClockObject *_global_clock; +}; + +#include "clockObject.I" + +#endif + diff --git a/panda/src/express/config_express.cxx b/panda/src/express/config_express.cxx new file mode 100644 index 0000000000..b6a1f969b2 --- /dev/null +++ b/panda/src/express/config_express.cxx @@ -0,0 +1,50 @@ +// Filename: config_express.cxx +// Created by: cary (04Jan00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_express.h" +#include "clockObject.h" +#include "typeHandle.h" +#include "referenceCount.h" +#include "typedReferenceCount.h" +#include "datagram.h" + +#include + +ConfigureDef(config_express); +NotifyCategoryDef(express, ""); + +extern void init_system_type_handles(); + +ConfigureFn(config_express) { +// ClockObject::init_ptr(); + TypedObject::init_type(); + ReferenceCount::init_type(); + TypedReferenceCount::init_type(); + init_system_type_handles(); + Datagram::init_type(); +} + + +// Set this true to enable tracking of ReferenceCount pointer +// allocation/deallcation via the MemoryUsage object. This is +// primarily useful for detecting memory leaks. It has no effect when +// compiling in NDEBUG mode. + +// This variable is no longer defined here; instead, it's a member of +// MemoryUsage. + +//const bool track_memory_usage = config_express.GetBool("track-memory-usage", false); + +const int patchfile_window_size = + config_express.GetInt("patchfile-window-size", 16); + +const int patchfile_increment_size = + config_express.GetInt("patchfile-increment-size", 8); + +const int patchfile_buffer_size = + config_express.GetInt("patchfile-buffer-size", 4096); + +const int patchfile_zone_size = + config_express.GetInt("patchfile-zone-size", 10000); diff --git a/panda/src/express/config_express.h b/panda/src/express/config_express.h new file mode 100644 index 0000000000..5399a2b853 --- /dev/null +++ b/panda/src/express/config_express.h @@ -0,0 +1,28 @@ +// Filename: config_express.h +// Created by: cary (04Jan00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __CONFIG_EXPRESS_H__ +#define __CONFIG_EXPRESS_H__ + +#include +#include +#include + +ConfigureDecl(config_express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS); +NotifyCategoryDecl(express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS); + +// Actually, we can't determine this config variable the normal way, +// because we must be able to access it at static init time. Instead +// of declaring it a global constant, we'll make it a member of +// MemoryUsage. + +//extern EXPCL_PANDAEXPRESS const bool track_memory_usage; + +extern const int patchfile_window_size; +extern const int patchfile_increment_size; +extern const int patchfile_buffer_size; +extern const int patchfile_zone_size; + +#endif /* __CONFIG_UTIL_H__ */ diff --git a/panda/src/express/datagram.I b/panda/src/express/datagram.I new file mode 100644 index 0000000000..e496ddc4be --- /dev/null +++ b/panda/src/express/datagram.I @@ -0,0 +1,395 @@ +// Filename: datagram.I +// Created by: drose (06Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::Constructor +// Access: Public +// Description: Constructs an empty datagram. +//////////////////////////////////////////////////////////////////// +INLINE Datagram:: +Datagram() { +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::Constructor +// Access: Public +// Description: Constructs a datagram from an existing block of data. +//////////////////////////////////////////////////////////////////// +INLINE Datagram:: +Datagram(const void *data, size_t size) : _message((const char *)data, size) { +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::Constructor +// Access: Public +// Description: Constructs a datagram from an existing block of data. +//////////////////////////////////////////////////////////////////// +INLINE Datagram:: +Datagram(const string &data) : _message(data) { +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Datagram:: +Datagram(const Datagram ©) : + _message(copy._message) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +operator = (const Datagram ©) { + _message = copy._message; +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_bool +// Access: Public +// Description: Adds a boolean value to the datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_bool(bool b) { + add_uint8(b); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_int8 +// Access: Public +// Description: Adds a signed 8-bit integer to the datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_int8(PN_int8 int8) { + _message += LittleEndian((char *)&int8, sizeof(int8)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_uint8 +// Access: Public +// Description: Adds an unsigned 8-bit integer to the datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_uint8(PN_uint8 uint8) { + _message += LittleEndian((char *)&uint8, sizeof(uint8)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_int16 +// Access: Public +// Description: Adds a signed 16-bit integer to the datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_int16(PN_int16 int16) { + _message += LittleEndian((char *)&int16, sizeof(int16)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_int32 +// Access: Public +// Description: Adds a signed 32-bit integer to the datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_int32(PN_int32 int32) { + _message += LittleEndian((char *)&int32, sizeof(int32)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_int64 +// Access: Public +// Description: Adds a signed 64-bit integer to the datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_int64(PN_int64 int64) { + _message += LittleEndian((char *)&int64, sizeof(int64)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_uint16 +// Access: Public +// Description: Adds an unsigned 16-bit integer to the datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_uint16(PN_uint16 uint16) { + _message += LittleEndian((char *)&uint16, sizeof(uint16)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_uint32 +// Access: Public +// Description: Adds an unsigned 32-bit integer to the datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_uint32(PN_uint32 uint32) { + _message += LittleEndian((char *)&uint32, sizeof(uint32)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_uint64 +// Access: Public +// Description: Adds an unsigned 64-bit integer to the datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_uint64(PN_uint64 uint64) { + _message += LittleEndian((char *)&uint64, sizeof(uint64)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_float32 +// Access: Public +// Description: Adds a 32-bit single-precision floating-point number +// to the datagram. Since this kind of float is not +// necessarily portable across different architectures, +// special care is required. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_float32(float float32) { + // For now, we assume the float format is portable across all + // architectures we are concerned with. If we come across one that + // is different, we will have to convert. + nassertv(sizeof(float32) == 4); + _message += LittleEndian((char *)&float32, sizeof(float32)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_float64 +// Access: Public +// Description: Adds a 64-bit floating-point number to the datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_float64(PN_float64 float64) { + _message += LittleEndian((char *)&float64, sizeof(float64)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_be_int16 +// Access: Public +// Description: Adds a signed 16-bit big-endian integer to the +// datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_be_int16(PN_int16 int16) { + _message += BigEndian((char *)&int16, sizeof(int16)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_be_int32 +// Access: Public +// Description: Adds a signed 32-bit big-endian integer to the +// datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_be_int32(PN_int32 int32) { + _message += BigEndian((char *)&int32, sizeof(int32)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_be_int64 +// Access: Public +// Description: Adds a signed 64-bit big-endian integer to the +// datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_be_int64(PN_int64 int64) { + _message += BigEndian((char *)&int64, sizeof(int64)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_be_uint16 +// Access: Public +// Description: Adds an unsigned 16-bit big-endian integer to the +// datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_be_uint16(PN_uint16 uint16) { + _message += BigEndian((char *)&uint16, sizeof(uint16)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_be_uint32 +// Access: Public +// Description: Adds an unsigned 32-bit big-endian integer to the +// datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_be_uint32(PN_uint32 uint32) { + _message += BigEndian((char *)&uint32, sizeof(uint32)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_be_uint64 +// Access: Public +// Description: Adds an unsigned 64-bit big-endian integer to the +// datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_be_uint64(PN_uint64 uint64) { + _message += BigEndian((char *)&uint64, sizeof(uint64)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_be_float32 +// Access: Public +// Description: Adds a 32-bit single-precision big-endian +// floating-point number to the datagram. Since this +// kind of float is not necessarily portable across +// different architectures, special care is required. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_be_float32(float float32) { + // For now, we assume the float format is portable across all + // architectures we are concerned with. If we come across one that + // is different, we will have to convert. + nassertv(sizeof(float32) == 4); + _message += BigEndian((char *)&float32, sizeof(float32)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_be_float64 +// Access: Public +// Description: Adds a 64-bit big-endian floating-point number to the +// datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_be_float64(PN_float64 float64) { + _message += BigEndian((char *)&float64, sizeof(float64)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_string +// Access: Public +// Description: Adds a variable-length string to the datagram. This +// actually adds a count followed by n bytes. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_string(const string &str) { + // The max sendable length for a string is 2^16. + nassertv(str.length() <= (PN_uint16)0xffff); + + // Strings always are preceded by their length + add_uint16(str.length()); + + // Add the string + _message += str; +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::add_fixed_string +// Access: Public +// Description: Adds a fixed-length string to the datagram. If the +// string given is less than the requested size, this +// will pad the string out with zeroes; if it is greater +// than the requested size, this will silently truncate +// the string. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +add_fixed_string(const string &str, size_t size) { + if (str.length() < size) { + _message += str; + _message += string(size - str.length(), '\0'); + + } else { // str.length() >= size + _message += str.substr(0, size); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::pad_bytes +// Access: Public +// Description: Adds the indicated number of zero bytes to the +// datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +pad_bytes(size_t size) { + _message += string(size, '\0'); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::append_data +// Access: Public +// Description: Appends some more raw data to the end of the +// datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +append_data(const void *data, size_t size) { + _message += string((const char *)data, size); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::append_data +// Access: Public +// Description: Appends some more raw data to the end of the +// datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Datagram:: +append_data(const string &data) { + _message += data; +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::get_message +// Access: Public +// Description: Returns the datagram's data as a string. +//////////////////////////////////////////////////////////////////// +INLINE const string &Datagram:: +get_message() const { + return _message; +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::get_data +// Access: Public +// Description: Returns a pointer to the beginning of the datagram's +// data. +//////////////////////////////////////////////////////////////////// +INLINE const void *Datagram:: +get_data() const { + return _message.data(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::get_length +// Access: Public +// Description: Returns the number of bytes in the datagram. +//////////////////////////////////////////////////////////////////// +INLINE size_t Datagram:: +get_length() const { + return _message.length(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::operator == +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool Datagram:: +operator == (const Datagram &other) const { + return _message == other._message; +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::operator != +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool Datagram:: +operator != (const Datagram &other) const { + return _message != other._message; +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::operator < +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool Datagram:: +operator < (const Datagram &other) const { + return _message < other._message; +} diff --git a/panda/src/express/datagram.cxx b/panda/src/express/datagram.cxx new file mode 100644 index 0000000000..79a3baab7f --- /dev/null +++ b/panda/src/express/datagram.cxx @@ -0,0 +1,68 @@ +// Filename: datagram.cxx +// Created by: drose (06Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "datagram.h" + +#include + +#include // for sprintf(). + +TypeHandle Datagram::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +Datagram:: +~Datagram() { +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::clear +// Access: Public, Virtual +// Description: Resets the datagram to empty, in preparation for +// building up a new datagram. +//////////////////////////////////////////////////////////////////// +void Datagram:: +clear() { + _message = string(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Datagram::dump_hex +// Access: Public +// Description: Writes a representation of the entire datagram +// contents, as a sequence of hex (and ASCII) values. +//////////////////////////////////////////////////////////////////// +void Datagram:: +dump_hex(ostream &out) const { + for (size_t line = 0; line < _message.length(); line += 16) { + char hex[12]; + sprintf(hex, "%04x ", line); + out << hex; + + size_t p; + for (p = line; p < line + 16; p++) { + if (p < _message.length()) { + char hex[12]; + sprintf(hex, " %02x", ((unsigned int)_message[p]) & 0xff); + out << hex; + } else { + out << " "; + } + } + out << " "; + for (p = line; p < line + 16 && p < _message.length(); p++) { + if (isgraph(_message[p]) || _message[p] == ' ') { + out << (char)_message[p]; + } else { + out << "."; + } + } + out << "\n"; + } +} + diff --git a/panda/src/express/datagram.h b/panda/src/express/datagram.h new file mode 100644 index 0000000000..8f44acca97 --- /dev/null +++ b/panda/src/express/datagram.h @@ -0,0 +1,111 @@ +// Filename: datagram.h +// Created by: drose (06Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DATAGRAM_H +#define DATAGRAM_H + +#include + +#include "numeric_types.h" +#include "typeHandle.h" +#include "littleEndian.h" +#include "bigEndian.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : Datagram +// Description : An ordered list of data elements, formatted in memory +// for transmission over a socket or writing to a data +// file. +// +// Data elements should be added one at a time, in +// order, to the Datagram. The nature and contents of +// the data elements are totally up to the user. When a +// Datagram has been transmitted and received, its data +// elements may be extracted using a DatagramIterator; +// it is up to the caller to know the correct type of +// each data element in order. +// +// A Datagram is itself headerless; it is simply a +// collection of data elements. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS Datagram : public TypedObject { +public: + INLINE Datagram(); + INLINE Datagram(const void *data, size_t size); + INLINE Datagram(const string &data); + INLINE Datagram(const Datagram ©); + INLINE void operator = (const Datagram ©); + + virtual ~Datagram(); + + virtual void clear(); + void dump_hex(ostream &out) const; + + INLINE void add_bool(bool b); + INLINE void add_int8(PN_int8 int8); + INLINE void add_uint8(PN_uint8 uint8); + + // The default numeric packing is little-endian. + INLINE void add_int16(PN_int16 int16); + INLINE void add_int32(PN_int32 int32); + INLINE void add_int64(PN_int64 int64); + INLINE void add_uint16(PN_uint16 uint16); + INLINE void add_uint32(PN_uint32 uint32); + INLINE void add_uint64(PN_uint64 uint64); + INLINE void add_float32(float float32); + INLINE void add_float64(PN_float64 float64); + + // These functions pack numbers big-endian, in case that's desired. + INLINE void add_be_int16(PN_int16 int16); + INLINE void add_be_int32(PN_int32 int32); + INLINE void add_be_int64(PN_int64 int64); + INLINE void add_be_uint16(PN_uint16 uint16); + INLINE void add_be_uint32(PN_uint32 uint32); + INLINE void add_be_uint64(PN_uint64 uint64); + INLINE void add_be_float32(float float32); + INLINE void add_be_float64(PN_float64 float64); + + INLINE void add_string(const string &str); + INLINE void add_fixed_string(const string &str, size_t size); + + INLINE void pad_bytes(size_t size); + INLINE void append_data(const void *data, size_t size); + INLINE void append_data(const string &data); + + INLINE const string &get_message() const; + INLINE const void *get_data() const; + INLINE size_t get_length() const; + + INLINE bool operator == (const Datagram &other) const; + INLINE bool operator != (const Datagram &other) const; + INLINE bool operator < (const Datagram &other) const; + +private: + string _message; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedObject::init_type(); + register_type(_type_handle, "Datagram", + TypedObject::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "datagram.I" + +#endif diff --git a/panda/src/express/datagramGenerator.I b/panda/src/express/datagramGenerator.I new file mode 100644 index 0000000000..19a09f1a06 --- /dev/null +++ b/panda/src/express/datagramGenerator.I @@ -0,0 +1,15 @@ +// Filename: datagramGenerator.I +// Created by: jason (07Jun00) +// + + +//////////////////////////////////////////////////////////////////// +// Function: DatagramGenerator::Constructor +// Access: Public +// Description: Does nothing since this is class is just +// the definition of an interface +//////////////////////////////////////////////////////////////////// + +INLINE DatagramGenerator::DatagramGenerator(void){ +} + diff --git a/panda/src/express/datagramGenerator.cxx b/panda/src/express/datagramGenerator.cxx new file mode 100644 index 0000000000..d57305823c --- /dev/null +++ b/panda/src/express/datagramGenerator.cxx @@ -0,0 +1,16 @@ +// Filename: datagramGenerator.cxx +// Created by: jason (07Jun00) +// + +#include + +#include "datagramGenerator.h" + +//////////////////////////////////////////////////////////////////// +// Function: DatagramGenerator::Destructor +// Access: Public +// Description: Does nothing since this is class is just +// the definition of an interface +//////////////////////////////////////////////////////////////////// +DatagramGenerator::~DatagramGenerator(void){ +} diff --git a/panda/src/express/datagramGenerator.h b/panda/src/express/datagramGenerator.h new file mode 100644 index 0000000000..52810a363d --- /dev/null +++ b/panda/src/express/datagramGenerator.h @@ -0,0 +1,30 @@ +// Filename: datagramGenerator.h +// Created by: jason (07Jun00) +// + +#ifndef DATAGRAMGENERATOR_H +#define DATAGRAMGENERATOR_H + +#include + +#include "datagram.h" + +//////////////////////////////////////////////////////////////////// +// Class : DatagramGenerator +// Description : This class defines the abstract interace to any +// source of datagrams, whether it be from a file or +// from the net +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS DatagramGenerator { +public: + INLINE DatagramGenerator(void); + virtual ~DatagramGenerator(void); + + virtual bool get_datagram(Datagram& dataBlock) = 0; + virtual bool empty(void) = 0; + virtual bool is_valid(void) = 0; +}; + +#include "datagramGenerator.I" + +#endif diff --git a/panda/src/express/datagramIterator.cxx b/panda/src/express/datagramIterator.cxx new file mode 100644 index 0000000000..7aa80720a8 --- /dev/null +++ b/panda/src/express/datagramIterator.cxx @@ -0,0 +1,518 @@ +// Filename: datagramIterator.cxx +// Created by: jns (07Feb00) +// + +#include "datagramIterator.h" +#include "littleEndian.h" +#include "bigEndian.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DatagramIterator:: +DatagramIterator(const Datagram &datagram, size_t offset) : + _datagram(datagram), + _current_index(offset) +{ + nassertv(_current_index <= _datagram.get_length()); +} + +// Various ways to get data and increment the iterator... +// Cut-and-paste-orama + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_bool +// Access: Public +// Description: Extracts a boolean value. +//////////////////////////////////////////////////////////////////// +bool DatagramIterator:: +get_bool() { + nassertr(_current_index < _datagram.get_length(), 0); + + PN_uint8 tempvar; + LittleEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return (tempvar != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_int8 +// Access: Public +// Description: Extracts a signed 8-bit integer. +//////////////////////////////////////////////////////////////////// +PN_int8 DatagramIterator:: +get_int8() { + nassertr(_current_index < _datagram.get_length(), 0); + + PN_int8 tempvar; + LittleEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_uint8 +// Access: Public +// Description: Extracts an unsigned 8-bit integer. +//////////////////////////////////////////////////////////////////// +PN_uint8 DatagramIterator:: +get_uint8() { + nassertr(_current_index < _datagram.get_length(), 0); + + PN_uint8 tempvar; + LittleEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_int16 +// Access: Public +// Description: Extracts a signed 16-bit integer. +//////////////////////////////////////////////////////////////////// +PN_int16 DatagramIterator:: +get_int16() { + nassertr(_current_index < _datagram.get_length(), 0); + + PN_int16 tempvar; + LittleEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_int32 +// Access: Public +// Description: Extracts a signed 32-bit integer. +//////////////////////////////////////////////////////////////////// +PN_int32 DatagramIterator:: +get_int32() { + nassertr(_current_index < _datagram.get_length(), 0); + + PN_int32 tempvar; + LittleEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_int64 +// Access: Public +// Description: Extracts a signed 64-bit integer. +//////////////////////////////////////////////////////////////////// +PN_int64 DatagramIterator:: +get_int64() { + nassertr(_current_index < _datagram.get_length(), 0); + + PN_int64 tempvar; + LittleEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_uint16 +// Access: Public +// Description: Extracts an unsigned 16-bit integer. +//////////////////////////////////////////////////////////////////// +PN_uint16 DatagramIterator:: +get_uint16() { + nassertr(_current_index < _datagram.get_length(), 0); + + PN_uint16 tempvar; + LittleEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_uint32 +// Access: Public +// Description: Extracts an unsigned 32-bit integer. +//////////////////////////////////////////////////////////////////// +PN_uint32 DatagramIterator:: +get_uint32() { + nassertr(_current_index < _datagram.get_length(), 0); + + PN_uint32 tempvar; + LittleEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_uint64 +// Access: Public +// Description: Extracts an unsigned 64-bit integer. +//////////////////////////////////////////////////////////////////// +PN_uint64 DatagramIterator:: +get_uint64() { + nassertr(_current_index < _datagram.get_length(), 0); + + PN_uint64 tempvar; + LittleEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_float32 +// Access: Public +// Description: Extracts a 32-bit single-precision floating-point +// number. Since this kind of float is not necessarily +// portable across different architectures, special care +// is required. +//////////////////////////////////////////////////////////////////// +float DatagramIterator:: +get_float32() { + // For now, we assume the float format is portable across all + // architectures we are concerned with. If we come across one that + // is different, we will have to convert. + nassertr(sizeof(float) == 4, 0.0); + nassertr(_current_index < _datagram.get_length(), 0.0); + + float tempvar; + BigEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0.0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_float64 +// Access: Public +// Description: Extracts a 64-bit floating-point number. +//////////////////////////////////////////////////////////////////// +PN_float64 DatagramIterator:: +get_float64() { + nassertr(_current_index < _datagram.get_length(), 0.0); + + PN_float64 tempvar; + LittleEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0.0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_be_int16 +// Access: Public +// Description: Extracts a signed 16-bit big-endian integer. +//////////////////////////////////////////////////////////////////// +PN_int16 DatagramIterator:: +get_be_int16() { + nassertr(_current_index < _datagram.get_length(), 0); + + PN_int16 tempvar; + BigEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_be_int32 +// Access: Public +// Description: Extracts a signed 32-bit big-endian integer. +//////////////////////////////////////////////////////////////////// +PN_int32 DatagramIterator:: +get_be_int32() { + nassertr(_current_index < _datagram.get_length(), 0); + + PN_int32 tempvar; + BigEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_be_int64 +// Access: Public +// Description: Extracts a signed 64-bit big-endian integer. +//////////////////////////////////////////////////////////////////// +PN_int64 DatagramIterator:: +get_be_int64() { + nassertr(_current_index < _datagram.get_length(), 0); + + PN_int64 tempvar; + BigEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_be_uint16 +// Access: Public +// Description: Extracts an unsigned 16-bit big-endian integer. +//////////////////////////////////////////////////////////////////// +PN_uint16 DatagramIterator:: +get_be_uint16() { + nassertr(_current_index < _datagram.get_length(), 0); + + PN_uint16 tempvar; + BigEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_be_uint32 +// Access: Public +// Description: Extracts an unsigned 32-bit big-endian integer. +//////////////////////////////////////////////////////////////////// +PN_uint32 DatagramIterator:: +get_be_uint32() { + nassertr(_current_index < _datagram.get_length(), 0); + + PN_uint32 tempvar; + BigEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_be_uint64 +// Access: Public +// Description: Extracts an unsigned 64-bit big-endian integer. +//////////////////////////////////////////////////////////////////// +PN_uint64 DatagramIterator:: +get_be_uint64() { + nassertr(_current_index < _datagram.get_length(), 0); + + PN_uint64 tempvar; + BigEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_be_float32 +// Access: Public +// Description: Extracts a 32-bit big-endian single-precision +// floating-point number. Since this kind of float is +// not necessarily portable across different +// architectures, special care is required. +//////////////////////////////////////////////////////////////////// +float DatagramIterator:: +get_be_float32() { + // For now, we assume the float format is portable across all + // architectures we are concerned with. If we come across one that + // is different, we will have to convert. + nassertr(sizeof(float) == 4, 0.0); + nassertr(_current_index < _datagram.get_length(), 0.0); + + float tempvar; + BigEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0.0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_be_float64 +// Access: Public +// Description: Extracts a 64-bit big-endian floating-point number. +//////////////////////////////////////////////////////////////////// +PN_float64 DatagramIterator:: +get_be_float64() { + nassertr(_current_index < _datagram.get_length(), 0.0); + + PN_float64 tempvar; + BigEndian s = + _datagram.get_message().substr(_current_index, sizeof(tempvar)); + + nassertr(s.length() == sizeof(tempvar), 0.0); + memcpy((void *)&tempvar, (void *)s.data(), sizeof(tempvar)); + _current_index += sizeof(tempvar); + + return tempvar; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_string +// Access: Public +// Description: Extracts a variable-length string. +//////////////////////////////////////////////////////////////////// +string DatagramIterator:: +get_string() { + // First, get the length of the string + PN_uint16 s_len = get_uint16(); + + nassertr(_current_index + s_len <= _datagram.get_length(), ""); + + string s = + _datagram.get_message().substr(_current_index, s_len); + + nassertr(s.length() == s_len, ""); + _current_index += s_len; + + return s; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_fixed_string +// Access: Public +// Description: Extracts a fixed-length string. However, if a zero +// byte occurs within the string, it marks the end of +// the string. +//////////////////////////////////////////////////////////////////// +string DatagramIterator:: +get_fixed_string(size_t size) { + nassertr(_current_index + size <= _datagram.get_length(), ""); + + string s = + _datagram.get_message().substr(_current_index, size); + _current_index += size; + + size_t zero_byte = s.find('\0'); + return s.substr(0, zero_byte); +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::skip_bytes +// Access: Public +// Description: Skips over the indicated number of bytes in the +// datagram. +//////////////////////////////////////////////////////////////////// +void DatagramIterator:: +skip_bytes(size_t size) { + nassertv(_current_index + size <= _datagram.get_length()); + _current_index += size; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::extract_bytes +// Access: Public +// Description: Extracts the indicated number of bytes in the +// datagram and returns them as a string. +//////////////////////////////////////////////////////////////////// +string DatagramIterator:: +extract_bytes(size_t size) { + nassertr(_current_index + size <= _datagram.get_length(), ""); + int start = _current_index; + + _current_index += size; + return _datagram.get_message().substr(start, size); +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_remaining_bytes +// Access: Public +// Description: Returns the remaining bytes in the datagram as a +// string, but does not extract them from the iterator. +//////////////////////////////////////////////////////////////////// +string DatagramIterator:: +get_remaining_bytes() const { + nassertr(_current_index <= _datagram.get_length(), ""); + return _datagram.get_message().substr(_current_index); +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_remaining_size +// Access: Public +// Description: Return the bytes left in the datagram. +//////////////////////////////////////////////////////////////////// +int DatagramIterator:: +get_remaining_size() const { + return _datagram.get_length() - _current_index; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramIterator::get_datagram +// Access: Public +// Description: Return the datagram of this iterator. +//////////////////////////////////////////////////////////////////// +const Datagram &DatagramIterator:: +get_datagram() const { + return _datagram; +} diff --git a/panda/src/express/datagramIterator.h b/panda/src/express/datagramIterator.h new file mode 100644 index 0000000000..a5477c55fc --- /dev/null +++ b/panda/src/express/datagramIterator.h @@ -0,0 +1,63 @@ +// Filename: datagramIterator.h +// Created by: jns (07Feb00) +// + +#ifndef DATAGRAMITERATOR_H +#define DATAGRAMITERATOR_H + +#include + +#include "datagram.h" +#include "numeric_types.h" + +//////////////////////////////////////////////////////////////////// +// Class : DatagramIterator +// Description : A class to retrieve the individual data elements +// previously stored in a Datagram. Elements may be +// retrieved one at a time; it is up to the caller to +// know the correct type and order of each element. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS DatagramIterator { +public: + DatagramIterator(const Datagram &datagram, size_t offset = 0); + + bool get_bool(); + PN_int8 get_int8(); + PN_uint8 get_uint8(); + + PN_int16 get_int16(); + PN_int32 get_int32(); + PN_int64 get_int64(); + PN_uint16 get_uint16(); + PN_uint32 get_uint32(); + PN_uint64 get_uint64(); + float get_float32(); + PN_float64 get_float64(); + + PN_int16 get_be_int16(); + PN_int32 get_be_int32(); + PN_int64 get_be_int64(); + PN_uint16 get_be_uint16(); + PN_uint32 get_be_uint32(); + PN_uint64 get_be_uint64(); + float get_be_float32(); + PN_float64 get_be_float64(); + + string get_string(); + string get_fixed_string(size_t size); + + void skip_bytes(size_t size); + string extract_bytes(size_t size); + + string get_remaining_bytes() const; + int get_remaining_size() const; + + const Datagram &get_datagram() const; + +private: + const Datagram &_datagram; + size_t _current_index; +}; + + +#endif diff --git a/panda/src/express/datagramSink.I b/panda/src/express/datagramSink.I new file mode 100644 index 0000000000..8eba958f07 --- /dev/null +++ b/panda/src/express/datagramSink.I @@ -0,0 +1,14 @@ +// Filename: datagramSink.I +// Created by: jason (07Jun00) +// + + +//////////////////////////////////////////////////////////////////// +// Function: DatagramSink::Constructor +// Access: Public +// Description: Does nothing since this is class is just +// the definition of an interface +//////////////////////////////////////////////////////////////////// + +INLINE DatagramSink::DatagramSink(void){ +} diff --git a/panda/src/express/datagramSink.cxx b/panda/src/express/datagramSink.cxx new file mode 100644 index 0000000000..95b9849920 --- /dev/null +++ b/panda/src/express/datagramSink.cxx @@ -0,0 +1,16 @@ +// Filename: datagramSink.cxx +// Created by: jason (07Jun00) +// + +#include + +#include "datagramSink.h" + +//////////////////////////////////////////////////////////////////// +// Function: DatagramSink::Destructor +// Access: Public +// Description: Does nothing since this is class is just +// the definition of an interface +//////////////////////////////////////////////////////////////////// +DatagramSink::~DatagramSink(void){ +} diff --git a/panda/src/express/datagramSink.h b/panda/src/express/datagramSink.h new file mode 100644 index 0000000000..5f4d821899 --- /dev/null +++ b/panda/src/express/datagramSink.h @@ -0,0 +1,29 @@ +// Filename: datagramSink.h +// Created by: jason (07Jun00) +// + +#ifndef DATAGRAMSINK_H +#define DATAGRAMSINK_H + +#include + +#include "datagram.h" + +//////////////////////////////////////////////////////////////////// +// Class : DatagramSink +// Description : This class defines the abstract interface to sending +// datagrams to any target, whether it be into a file +// or across the net +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS DatagramSink { +public: + INLINE DatagramSink(void); + virtual ~DatagramSink(void); + + virtual bool put_datagram(const Datagram& dataBlock) = 0; +}; + +#include "datagramSink.I" + +#endif + diff --git a/panda/src/express/get_config_path.cxx b/panda/src/express/get_config_path.cxx new file mode 100644 index 0000000000..a6599fa50d --- /dev/null +++ b/panda/src/express/get_config_path.cxx @@ -0,0 +1,52 @@ +// Filename: get_config_path.cxx +// Created by: drose (01Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "get_config_path.h" +#include "config_express.h" + + +//////////////////////////////////////////////////////////////////// +// Function: get_config_path +// Description: A generic function for reading path strings +// (e.g. model-path, texture-path, etc.) from the Config +// database. It automatically handles concatenating +// together multiple appearances of the indicated +// variable name as a single long path string. +// +// static_ptr must be a statically-defined string +// pointer, unique to each different config_var_name. +// It should be initialized to NULL. This will +// automatically be allocated and filled with the string +// path the first time this function is called; +// thereafter, the same string value will be returned. +// This allows the function to work during static init +// time when we can't be sure what has or hasn't been +// already initialized. +//////////////////////////////////////////////////////////////////// +const DSearchPath & +get_config_path(const string &config_var_name, DSearchPath *&static_ptr) { + if (static_ptr == (DSearchPath *)NULL) { + static_ptr = new DSearchPath; + + Config::ConfigTable::Symbol all_defs; + config_express.GetAll(config_var_name, all_defs); + if (!all_defs.empty()) { + Config::ConfigTable::Symbol::reverse_iterator si = + all_defs.rbegin(); + (*static_ptr).append_path((*si).Val()); + ++si; + while (si != all_defs.rend()) { + (*static_ptr).append_path((*si).Val()); + ++si; + } + } + if (express_cat.is_debug()) { + express_cat.debug() + << config_var_name << " is " << *static_ptr << "\n"; + } + } + + return *static_ptr; +} diff --git a/panda/src/express/get_config_path.h b/panda/src/express/get_config_path.h new file mode 100644 index 0000000000..658e5a94c2 --- /dev/null +++ b/panda/src/express/get_config_path.h @@ -0,0 +1,34 @@ +// Filename: get_config_path.h +// Created by: drose (01Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GET_CONFIG_PATH_H +#define GET_CONFIG_PATH_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Function: get_config_path +// Description: A generic function for reading path strings +// (e.g. model-path, texture-path, etc.) from the Config +// database. It automatically handles concatenating +// together multiple appearances of the indicated +// variable name as a single long path string. +// +// static_ptr must be a statically-defined string +// pointer, unique to each different config_var_name. +// It should be initialized to NULL. This will +// automatically be allocated and filled with the string +// path the first time this function is called; +// thereafter, the same string value will be returned. +// This allows the function to work during static init +// time when we can't be sure what has or hasn't been +// already initialized. +//////////////////////////////////////////////////////////////////// +EXPCL_PANDAEXPRESS const DSearchPath & +get_config_path(const string &config_var_name, DSearchPath *&static_ptr); + +#endif diff --git a/panda/src/express/indent.I b/panda/src/express/indent.I new file mode 100644 index 0000000000..ec28bc1fde --- /dev/null +++ b/panda/src/express/indent.I @@ -0,0 +1,56 @@ +// Filename: indent.I +// Created by: drose (15Feb99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: write_long_list +// Description: Writes a list of things to the indicated output +// stream, with a space separating each item. One or +// more lines will be written, and the lines will +// automatically be broken such that no line exceeds +// max_col columns if possible. +//////////////////////////////////////////////////////////////////// +template +void +write_long_list(ostream &out, int indent_level, + InputIterator first, InputIterator last, + string first_prefix, string later_prefix, + int max_col) { + if (later_prefix.empty()) { + later_prefix = first_prefix; + } + + if (first != last) { + // We have to use an intermediate strstream object so we can + // count the number of characters the item will have when it is + // output. + ostringstream item; + item << *first; + string str = item.str(); + + indent(out, indent_level) << first_prefix << str; + int col = indent_level + first_prefix.length() + str.length(); + + ++first; + + while (first != last) { + ostringstream item; + item << *first; + string str = item.str(); + + col += 1 + str.length(); + if (col > max_col) { + out << "\n"; + indent(out, indent_level) << later_prefix << str; + col = indent_level + later_prefix.length() + str.length(); + + } else { + out << " " << str; + } + + ++first; + } + out << "\n"; + } +} diff --git a/panda/src/express/indent.cxx b/panda/src/express/indent.cxx new file mode 100644 index 0000000000..0ccd4d5ef4 --- /dev/null +++ b/panda/src/express/indent.cxx @@ -0,0 +1,18 @@ +// Filename: indent.cxx +// Created by: drose (05May00) +// +//////////////////////////////////////////////////////////////////// + +#include "indent.h" + +//////////////////////////////////////////////////////////////////// +// Function: indent +// Description: +//////////////////////////////////////////////////////////////////// +ostream & +indent(ostream &out, int indent_level) { + for (int i = 0; i < indent_level; i++) { + out << ' '; + } + return out; +} diff --git a/panda/src/express/indent.h b/panda/src/express/indent.h new file mode 100644 index 0000000000..da1c6a6a35 --- /dev/null +++ b/panda/src/express/indent.h @@ -0,0 +1,42 @@ +// Filename: indent.h +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INDENT_H +#define INDENT_H + +#include + +//////////////////////////////////////////////////////////////////// +// Function: indent +// Description: A handy function for doing text formatting. This +// function simply outputs the indicated number of +// spaces to the given output stream, returning the +// stream itself. Useful for indenting a series of +// lines of text by a given amount. +//////////////////////////////////////////////////////////////////// +EXPCL_PANDAEXPRESS ostream & +indent(ostream &out, int indent_level); + +//////////////////////////////////////////////////////////////////// +// Function: write_long_list +// Description: Writes a list of things to the indicated output +// stream, with a space separating each item. One or +// more lines will be written, and the lines will +// automatically be broken such that no line exceeds +// max_col columns if possible. +//////////////////////////////////////////////////////////////////// +template +void +write_long_list(ostream &out, int indent_level, + InputIterator ifirst, InputIterator ilast, + string first_prefix = "", + string later_prefix = "", + int max_col = 72); + +#include "indent.I" + +#endif + + diff --git a/panda/src/express/littleEndian.I b/panda/src/express/littleEndian.I new file mode 100644 index 0000000000..550daf4c6c --- /dev/null +++ b/panda/src/express/littleEndian.I @@ -0,0 +1,92 @@ +// Filename: littleEndian.I +// Created by: drose (09Feb00) +// +//////////////////////////////////////////////////////////////////// + + +#ifdef IS_BIG_ENDIAN + +//////////////////////////////////////////////////////////////////// +// Function: LittleEndian::Constructor +// Access: Public +// Description: Reverses the bytes in the indicated string. +//////////////////////////////////////////////////////////////////// +INLINE LittleEndian:: +LittleEndian(const string &string) { + reverse_assign(string.data(), string.length()); +} + +//////////////////////////////////////////////////////////////////// +// Function: LittleEndian::Constructor +// Access: Public +// Description: Reverses the bytes in the indicated string. +//////////////////////////////////////////////////////////////////// +INLINE LittleEndian:: +LittleEndian(const char *data, size_t length) { + reverse_assign(data, length); +} + +//////////////////////////////////////////////////////////////////// +// Function: LittleEndian::Copy Constructor +// Access: Public +// Description: Since this is assigned from another LittleEndian, +// presumably already reversed, this does *not* reverse +// the byte order. +//////////////////////////////////////////////////////////////////// +INLINE LittleEndian:: +LittleEndian(const LittleEndian &other) : _str(other._str) { +} + +//////////////////////////////////////////////////////////////////// +// Function: LittleEndian::String Assignment Operator +// Access: Public +// Description: Reverses the bytes in the indicated string. +//////////////////////////////////////////////////////////////////// +INLINE void LittleEndian:: +operator =(const string &string) { + reverse_assign(string.data(), string.length()); +} + +//////////////////////////////////////////////////////////////////// +// Function: LittleEndian::Copy Assignment Operator +// Access: Public +// Description: Since this is assigned from another LittleEndian, +// presumably already reversed, this does *not* reverse +// the byte order. +//////////////////////////////////////////////////////////////////// +INLINE void LittleEndian:: +operator =(const LittleEndian &other) { + _str = other._str; +} + +//////////////////////////////////////////////////////////////////// +// Function: LittleEndian::string Typecast Operator +// Access: Public +// Description: Returns the data as a string. +//////////////////////////////////////////////////////////////////// +INLINE LittleEndian:: +operator const string &() const { + return _str; +} + +//////////////////////////////////////////////////////////////////// +// Function: LittleEndian::data +// Access: Public +// Description: Returns the data in the string. +//////////////////////////////////////////////////////////////////// +INLINE const char *LittleEndian:: +data() const { + return _str.data(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LittleEndian::length +// Access: Public +// Description: Returns the number of bytes in the string. +//////////////////////////////////////////////////////////////////// +INLINE size_t LittleEndian:: +length() const { + return _str.length(); +} + +#endif // IS_BIG_ENDIAN diff --git a/panda/src/express/littleEndian.cxx b/panda/src/express/littleEndian.cxx new file mode 100644 index 0000000000..7c41185f62 --- /dev/null +++ b/panda/src/express/littleEndian.cxx @@ -0,0 +1,25 @@ +// Filename: littleEndian.cxx +// Created by: drose (09Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "littleEndian.h" + +#ifdef IS_BIG_ENDIAN + +//////////////////////////////////////////////////////////////////// +// Function: LittleEndian::reverse_assign +// Access: Private +// Description: Actually does the data reversal. +//////////////////////////////////////////////////////////////////// +void LittleEndian:: +reverse_assign(const char *data, size_t length) { + _str = ""; + _str.reserve(length); + + for (size_t i = 0; i < length; i++) { + _str += data[length - 1 - i]; + } +} + +#endif // IS_BIG_ENDIAN diff --git a/panda/src/express/littleEndian.h b/panda/src/express/littleEndian.h new file mode 100644 index 0000000000..85ef935013 --- /dev/null +++ b/panda/src/express/littleEndian.h @@ -0,0 +1,62 @@ +// Filename: littleEndian.h +// Created by: drose (09Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LITTLEENDIAN_H +#define LITTLEENDIAN_H + +#include + +#include "numeric_types.h" + +#ifdef IS_BIG_ENDIAN + +//////////////////////////////////////////////////////////////////// +// Class : LittleEndian +// Description : LittleEndian is a special string-like class that +// automatically reverses the byte order when it is +// assigned from either a char buffer or a true +// string--but only when compiling for a bigendian +// architecture. On littleendian machines, LittleEndian +// is defined to map directly to string. +// +// This is a sneaky interface to automatically handle +// numeric conversions so that network data is always +// sent littleendian. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS LittleEndian { +public: + INLINE LittleEndian(const string &string); + INLINE LittleEndian(const char *data, size_t length); + INLINE LittleEndian(const LittleEndian &other); + + INLINE void operator =(const string &string); + INLINE void operator =(const LittleEndian &other); + + INLINE operator const string &() const; + INLINE const char *data() const; + INLINE size_t length() const; + +private: + void reverse_assign(const char *data, size_t length); + + string _str; +}; + +#else // !IS_BIG_ENDIAN + +#define LittleEndian string + +#endif // IS_BIG_ENDIAN + +#include "littleEndian.I" + +#endif + + + + + + + diff --git a/panda/src/express/memoryUsage.I b/panda/src/express/memoryUsage.I new file mode 100644 index 0000000000..ac6fa89e45 --- /dev/null +++ b/panda/src/express/memoryUsage.I @@ -0,0 +1,216 @@ +// Filename: memoryUsage.I +// Created by: drose (25May00) +// +//////////////////////////////////////////////////////////////////// + +#ifdef NDEBUG + +// In NDEBUG mode, these are all defined to do nothing. + +INLINE bool MemoryUsage:: +get_track_memory_usage() { + return false; +} + +INLINE void MemoryUsage:: +record_pointer(ReferenceCount *) { +} + +INLINE void MemoryUsage:: +update_type(ReferenceCount *, TypeHandle) { +} + +INLINE void MemoryUsage:: +remove_pointer(ReferenceCount *) { +} + +#else // NDEBUG + +// In the normal compilation mode, most of these functions vector into +// their corresponding ns_* non-static variants. + + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::track_memory_usage +// Access: Public, Static +// Description: Returns true if the user has Configured the variable +// 'track-memory-usage' to true, indicating that this +// class will be in effect. If this returns false, the +// user has indicated not to do any of this. +//////////////////////////////////////////////////////////////////// +INLINE bool MemoryUsage:: +get_track_memory_usage() { + return get_global_ptr()->_track_memory_usage; +} + +#ifndef __GNUC__ + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::record_pointer +// Access: Public, Static +// Description: Indicates that the given pointer has been recently +// allocated. +//////////////////////////////////////////////////////////////////// +INLINE void MemoryUsage:: +record_pointer(ReferenceCount *ptr) { + get_global_ptr()->ns_record_pointer(ptr); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::update_type +// Access: Public, Static +// Description: Associates the indicated type with the given pointer. +//////////////////////////////////////////////////////////////////// +INLINE void MemoryUsage:: +update_type(ReferenceCount *ptr, TypeHandle type) { + get_global_ptr()->ns_update_type(ptr, type); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::remove_pointer +// Access: Public, Static +// Description: Indicates that the given pointer has been recently +// freed. +//////////////////////////////////////////////////////////////////// +INLINE void MemoryUsage:: +remove_pointer(ReferenceCount *ptr) { + get_global_ptr()->ns_remove_pointer(ptr); +} + +#endif // __GNUC__ + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::get_num_pointers +// Access: Public, Static +// Description: Returns the number of pointers currently active. +//////////////////////////////////////////////////////////////////// +INLINE int MemoryUsage:: +get_num_pointers() { + return get_global_ptr()->ns_get_num_pointers(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::get_pointers +// Access: Public, Static +// Description: Fills the indicated MemoryUsagePointers with the set +// of all pointers currently active. +//////////////////////////////////////////////////////////////////// +INLINE void MemoryUsage:: +get_pointers(MemoryUsagePointers &result) { + get_global_ptr()->ns_get_pointers(result); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::get_pointers_of_type +// Access: Public, Static +// Description: Fills the indicated MemoryUsagePointers with the set +// of all pointers of the indicated type currently +// active. +//////////////////////////////////////////////////////////////////// +INLINE void MemoryUsage:: +get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) { + get_global_ptr()->ns_get_pointers_of_type(result, type); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::get_pointers_of_age +// Access: Public, Static +// Description: Fills the indicated MemoryUsagePointers with the set +// of all pointers that were allocated within the range +// of the indicated number of seconds ago. +//////////////////////////////////////////////////////////////////// +INLINE void MemoryUsage:: +get_pointers_of_age(MemoryUsagePointers &result, double from, double to) { + get_global_ptr()->ns_get_pointers_of_age(result, from, to); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::get_pointers_with_zero_count +// Access: Public, Static +// Description: Fills the indicated MemoryUsagePointers with the set +// of all currently active pointers (that is, pointers +// allocated since the last call to freeze(), and not +// yet freed) that have a zero reference count. +// +// Generally, an undeleted pointer with a zero reference +// count means its reference count has never been +// incremented beyond zero (since once it has been +// incremented, the only way it can return to zero would +// free the pointer). This may include objects that are +// allocated statically or on the stack, which are never +// intended to be deleted. Or, it might represent a +// programmer or compiler error. +// +// This function has the side-effect of incrementing +// each of their reference counts by one, thus +// preventing them from ever being freed--but since they +// hadn't been freed anyway, probably no additional harm +// is done. +//////////////////////////////////////////////////////////////////// +INLINE void MemoryUsage:: +get_pointers_with_zero_count(MemoryUsagePointers &result) { + get_global_ptr()->ns_get_pointers_with_zero_count(result); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::freeze +// Access: Public, Static +// Description: 'Freezes' all pointers currently stored so that they +// are no longer reported; only newly allocate pointers +// from this point on will appear in future information +// requests. This makes it easier to differentiate +// between continuous leaks and one-time memory +// allocations. +//////////////////////////////////////////////////////////////////// +INLINE void MemoryUsage:: +freeze() { + get_global_ptr()->ns_freeze(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::show_current_types +// Access: Public, Static +// Description: Shows the breakdown of types of all of the +// active pointers. +//////////////////////////////////////////////////////////////////// +INLINE void MemoryUsage:: +show_current_types() { + get_global_ptr()->ns_show_current_types(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::show_trend_types +// Access: Public, Static +// Description: Shows the breakdown of types of all of the +// pointers allocated and freed since the last call to +// freeze(). +//////////////////////////////////////////////////////////////////// +INLINE void MemoryUsage:: +show_trend_types() { + get_global_ptr()->ns_show_trend_types(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::show_current_ages +// Access: Public, Static +// Description: Shows the breakdown of ages of all of the +// active pointers. +//////////////////////////////////////////////////////////////////// +INLINE void MemoryUsage:: +show_current_ages() { + get_global_ptr()->ns_show_current_ages(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::show_trend_ages +// Access: Public, Static +// Description: Shows the breakdown of ages of all of the +// pointers allocated and freed since the last call to +// freeze(). +//////////////////////////////////////////////////////////////////// +INLINE void MemoryUsage:: +show_trend_ages() { + get_global_ptr()->ns_show_trend_ages(); +} + +#endif // NDEBUG diff --git a/panda/src/express/memoryUsage.cxx b/panda/src/express/memoryUsage.cxx new file mode 100644 index 0000000000..495f336006 --- /dev/null +++ b/panda/src/express/memoryUsage.cxx @@ -0,0 +1,620 @@ +// Filename: memoryUsage.cxx +// Created by: drose (25May00) +// +//////////////////////////////////////////////////////////////////// + +#include "memoryUsage.h" +#include "memoryUsagePointers.h" +#include "clockObject.h" +#include "typedReferenceCount.h" + +#ifndef NDEBUG +// Nothing in this module gets compiled in NDEBUG mode. + +#include "config_express.h" + +#include + +MemoryUsage *MemoryUsage::_global_ptr = (MemoryUsage *)NULL; + +// The cutoff ages, in seconds, for the various buckets in the AgeHistogram. +double MemoryUsage::AgeHistogram::_cutoff[MemoryUsage::AgeHistogram::num_buckets] = { + 0.0, + 0.1, + 1.0, + 10.0, + 60.0, +}; + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::MemoryInfo::get_type +// Access: Public +// Description: Returns the best known type, dynamic or static, of +// the pointer. +//////////////////////////////////////////////////////////////////// +TypeHandle MemoryUsage::MemoryInfo:: +get_type() { + // If we don't want to consider the dynamic type any further, use + // what we've got. + if (!_reconsider_dynamic_type) { + if (_dynamic_type == TypeHandle::none()) { + return _static_type; + } + return _dynamic_type; + } + + // Otherwise, examine the pointer again and make sure it's still the + // best information we have. We have to do this each time because + // if we happen to be examining the pointer from within the + // constructor or destructor, its dynamic type will appear to be + // less-specific than it actually is, so our idea of what type this + // thing is could change from time to time. + determine_dynamic_type(); + + // Now return the more specific of the two. + TypeHandle type = _static_type; + update_type_handle(type, _dynamic_type); + + return type; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::MemoryInfo::determine_dynamic_type +// Access: Public +// Description: Tries to determine the actual type of the object to +// which this thing is pointed, if possible. +//////////////////////////////////////////////////////////////////// +void MemoryUsage::MemoryInfo:: +determine_dynamic_type() { + if (_reconsider_dynamic_type && _static_type != TypeHandle::none()) { + // See if we know enough now to infer the dynamic type from the + // pointer. We can do this only if our static type is known to + // inherit from TypedReferenceCount--see the comments about this + // sort of thing in MemoryUsagePointers::get_typed_pointer(). + + if (_static_type.is_derived_from(TypedReferenceCount::get_class_type())) { + TypedReferenceCount *typed_ref = (TypedReferenceCount *)_ptr; + TypeHandle got_type = typed_ref->get_type(); + + if (got_type == TypeHandle::none()) { + express_cat.warning() + << "Found an unregistered type in a " << _static_type + << " pointer:\n" + << "Check derived types of " << _static_type + << " and make sure that all are being initialized.\n"; + _dynamic_type = _static_type; + _reconsider_dynamic_type = false; + return; + } + + update_type_handle(_dynamic_type, got_type); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::MemoryInfo::update_type_handle +// Access: Public +// Description: Updates the given destination TypeHandle with the +// refined TypeHandle, if it is in fact more specific +// than the original value for the destination. +//////////////////////////////////////////////////////////////////// +void MemoryUsage::MemoryInfo:: +update_type_handle(TypeHandle &destination, TypeHandle refined) { + if (refined == TypeHandle::none()) { + express_cat.error() + << "Attempt to update type of " << (void *)_ptr + << "(type is " << get_type() + << ") to an undefined type!\n"; + + } else if (destination == refined) { + // Updating with the same type, no problem. + + } else if (destination.is_derived_from(refined)) { + // Updating with a less-specific type, no problem. + + } else if (refined.is_derived_from(destination)) { + // Updating with a more-specific type, no problem. + destination = refined; + + } else { + express_cat.error() + << "Pointer " << (void *)_ptr << " previously indicated as type " + << destination << " is now type " << refined << "!\n"; + destination = refined; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::TypeHistogram::add_info +// Access: Public +// Description: Adds a single entry to the histogram. +//////////////////////////////////////////////////////////////////// +void MemoryUsage::TypeHistogram:: +add_info(TypeHandle type) { + _counts[type]++; +} + + +// This class is a temporary class used only in TypeHistogram::show(), +// below, to sort the types in descending order by counts. +class TypeHistogramCountSorter { +public: + TypeHistogramCountSorter(int count, TypeHandle type) { + _count = count; + _type = type; + } + bool operator < (const TypeHistogramCountSorter &other) const { + return _count > other._count; + } + int _count; + TypeHandle _type; +}; + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::TypeHistogram::show +// Access: Public +// Description: Shows the contents of the histogram to nout. +//////////////////////////////////////////////////////////////////// +void MemoryUsage::TypeHistogram:: +show() const { + // First, copy the relevant information to a vector so we can sort + // by counts. + vector count_sorter; + Counts::const_iterator ci; + for (ci = _counts.begin(); ci != _counts.end(); ++ci) { + count_sorter.push_back + (TypeHistogramCountSorter((*ci).second, (*ci).first)); + } + + sort(count_sorter.begin(), count_sorter.end()); + + vector::const_iterator vi; + for (vi = count_sorter.begin(); vi != count_sorter.end(); ++vi) { + nout << (*vi)._type << " : " << (*vi)._count << " pointers.\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::TypeHistogram::clear +// Access: Public +// Description: Resets the histogram in preparation for new data. +//////////////////////////////////////////////////////////////////// +void MemoryUsage::TypeHistogram:: +clear() { + _counts.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::AgeHistogram::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +MemoryUsage::AgeHistogram:: +AgeHistogram() { + clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::AgeHistogram::add_info +// Access: Public +// Description: Adds a single entry to the histogram. +//////////////////////////////////////////////////////////////////// +void MemoryUsage::AgeHistogram:: +add_info(double age) { + int bucket = choose_bucket(age); + nassertv(bucket >= 0 && bucket < num_buckets); + _counts[bucket]++; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::AgeHistogram::show +// Access: Public +// Description: Shows the contents of the histogram to nout. +//////////////////////////////////////////////////////////////////// +void MemoryUsage::AgeHistogram:: +show() const { + for (int i = 0; i < num_buckets - 1; i++) { + nout << _cutoff[i] << " to " << _cutoff[i + 1] << " seconds old : " + << _counts[i] << " pointers.\n"; + } + nout << _cutoff[num_buckets - 1] << " seconds old and up : " + << _counts[num_buckets - 1] << " pointers.\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::AgeHistogram::clear +// Access: Public +// Description: Resets the histogram in preparation for new data. +//////////////////////////////////////////////////////////////////// +void MemoryUsage::AgeHistogram:: +clear() { + for (int i = 0; i < num_buckets; i++) { + _counts[i] = 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::AgeHistogram::choose_bucket +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +int MemoryUsage::AgeHistogram:: +choose_bucket(double age) const { + for (int i = num_buckets - 1; i >= 0; i--) { + if (age >= _cutoff[i]) { + return i; + } + } + nassertr(false, 0); + return 0; +} + +#if defined(__GNUC__) && !defined(NDEBUG) + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::record_pointer +// Access: Public, Static +// Description: Indicates that the given pointer has been recently +// allocated. +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +record_pointer(ReferenceCount *ptr) { + get_global_ptr()->ns_record_pointer(ptr); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::update_type +// Access: Public, Static +// Description: Associates the indicated type with the given pointer. +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +update_type(ReferenceCount *ptr, TypeHandle type) { + get_global_ptr()->ns_update_type(ptr, type); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::remove_pointer +// Access: Public, Static +// Description: Indicates that the given pointer has been recently +// freed. +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +remove_pointer(ReferenceCount *ptr) { + get_global_ptr()->ns_remove_pointer(ptr); +} + +#endif // __GNUC__ && !NDEBUG + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::Constructor +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +MemoryUsage:: +MemoryUsage() { + // We must get this here instead of in config_express.cxx, because we + // need to know it at static init time, and who knows when the code + // in config_express will be executed. + _track_memory_usage = + config_express.GetBool("track-memory-usage", false); + + _freeze_index = 0; + _count = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::get_global_ptr +// Access: Private, Static +// Description: Returns the pointer to the only MemoryUsage object in +// the world. +//////////////////////////////////////////////////////////////////// +MemoryUsage *MemoryUsage:: +get_global_ptr() { + if (_global_ptr == (MemoryUsage *)NULL) { + _global_ptr = new MemoryUsage; + } + return _global_ptr; +} + + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::ns_record_pointer +// Access: Private +// Description: Indicates that the given pointer has been recently +// allocated. +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +ns_record_pointer(ReferenceCount *ptr) { + if (_track_memory_usage) { + MemoryInfo info; + info._ptr = ptr; + info._static_type = ReferenceCount::get_class_type(); + info._dynamic_type = ReferenceCount::get_class_type(); + info._time = ClockObject::get_global_clock()->get_real_time(); + info._freeze_index = _freeze_index; + info._reconsider_dynamic_type = true; + + Table::iterator ti; + ti = _table.find(ptr); + if (ti != _table.end()) { + express_cat.error() << "Pointer " << (void *)ptr << " recorded twice!\n"; + (*ti).second = info; + } else { + _table[ptr] = info; + _count++; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::ns_update_type +// Access: Private +// Description: Associates the indicated type with the given pointer. +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +ns_update_type(ReferenceCount *ptr, TypeHandle type) { + if (_track_memory_usage) { + Table::iterator ti; + ti = _table.find(ptr); + if (ti == _table.end()) { + express_cat.error() + << "Attempt to update type to " << type << " for unrecorded pointer " + << (void *)ptr << "!\n"; + return; + } + + MemoryInfo &info = (*ti).second; + info.update_type_handle(info._static_type, type); + info.determine_dynamic_type(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::ns_remove_pointer +// Access: Private +// Description: Indicates that the given pointer has been recently +// freed. +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +ns_remove_pointer(ReferenceCount *ptr) { + if (_track_memory_usage) { + Table::iterator ti; + ti = _table.find(ptr); + if (ti == _table.end()) { + express_cat.error() + << "Attempt to remove pointer " << (void *)ptr + << ", not in table.\n" + << "Possibly a double-destruction.\n"; + nassertv(false); + return; + } + + MemoryInfo &info = (*ti).second; + if (info._freeze_index == _freeze_index) { + double now = ClockObject::get_global_clock()->get_real_time(); + _count--; + _trend_types.add_info(info.get_type()); + _trend_ages.add_info(now - info._time); + } + + _table.erase(ti); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::ns_get_num_pointers +// Access: Private +// Description: Returns the number of pointers currently active. +//////////////////////////////////////////////////////////////////// +int MemoryUsage:: +ns_get_num_pointers() { + nassertr(_track_memory_usage, 0); + return _count; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::ns_get_pointers +// Access: Private +// Description: Fills the indicated MemoryUsagePointers with the set +// of all pointers currently active. +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +ns_get_pointers(MemoryUsagePointers &result) { + nassertv(_track_memory_usage); + result.clear(); + + double now = ClockObject::get_global_clock()->get_real_time(); + Table::iterator ti; + for (ti = _table.begin(); ti != _table.end(); ++ti) { + MemoryInfo &info = (*ti).second; + if (info._freeze_index == _freeze_index) { + result.add_entry((*ti).first, info.get_type(), now - info._time); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::ns_get_pointers_of_type +// Access: Private +// Description: Fills the indicated MemoryUsagePointers with the set +// of all pointers of the indicated type currently +// active. +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +ns_get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) { + nassertv(_track_memory_usage); + result.clear(); + + double now = ClockObject::get_global_clock()->get_real_time(); + Table::iterator ti; + for (ti = _table.begin(); ti != _table.end(); ++ti) { + MemoryInfo &info = (*ti).second; + if (info._freeze_index == _freeze_index) { + TypeHandle info_type = info.get_type(); + if (info_type != TypeHandle::none() && + info_type.is_derived_from(type)) { + result.add_entry((*ti).first, info_type, now - info._time); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::ns_get_pointers_of_age +// Access: Private +// Description: Fills the indicated MemoryUsagePointers with the set +// of all pointers that were allocated within the range +// of the indicated number of seconds ago. +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +ns_get_pointers_of_age(MemoryUsagePointers &result, + double from, double to) { + nassertv(_track_memory_usage); + result.clear(); + + double now = ClockObject::get_global_clock()->get_real_time(); + Table::iterator ti; + for (ti = _table.begin(); ti != _table.end(); ++ti) { + MemoryInfo &info = (*ti).second; + if (info._freeze_index == _freeze_index) { + double age = now - info._time; + if ((age >= from && age <= to) || + (age >= to && age <= from)) { + result.add_entry((*ti).first, info.get_type(), age); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::ns_get_pointers_with_zero_count +// Access: Private +// Description: Fills the indicated MemoryUsagePointers with the set +// of all currently active pointers (that is, pointers +// allocated since the last call to freeze(), and not +// yet freed) that have a zero reference count. +// +// Generally, an undeleted pointer with a zero reference +// count means its reference count has never been +// incremented beyond zero (since once it has been +// incremented, the only way it can return to zero would +// free the pointer). This may include objects that are +// allocated statically or on the stack, which are never +// intended to be deleted. Or, it might represent a +// programmer or compiler error. +// +// This function has the side-effect of incrementing +// each of their reference counts by one, thus +// preventing them from ever being freed--but since they +// hadn't been freed anyway, probably no additional harm +// is done. +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +ns_get_pointers_with_zero_count(MemoryUsagePointers &result) { + nassertv(_track_memory_usage); + result.clear(); + + double now = ClockObject::get_global_clock()->get_real_time(); + Table::iterator ti; + for (ti = _table.begin(); ti != _table.end(); ++ti) { + MemoryInfo &info = (*ti).second; + if (info._freeze_index == _freeze_index) { + if ((*ti).first->get_count() == 0) { + (*ti).first->ref(); + result.add_entry((*ti).first, info.get_type(), now - info._time); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::ns_freeze +// Access: Private +// Description: 'Freezes' all pointers currently stored so that they +// are no longer reported; only newly allocate pointers +// from this point on will appear in future information +// requests. This makes it easier to differentiate +// between continuous leaks and one-time memory +// allocations. +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +ns_freeze() { + _count = 0; + _trend_types.clear(); + _trend_ages.clear(); + _freeze_index++; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::ns_show_current_types +// Access: Private +// Description: Shows the breakdown of types of all of the +// active pointers. +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +ns_show_current_types() { + TypeHistogram hist; + + Table::iterator ti; + for (ti = _table.begin(); ti != _table.end(); ++ti) { + MemoryInfo &info = (*ti).second; + if (info._freeze_index == _freeze_index) { + hist.add_info(info.get_type()); + } + } + + hist.show(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::ns_show_trend_types +// Access: Private +// Description: Shows the breakdown of types of all of the +// pointers allocated and freed since the last call to +// freeze(). +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +ns_show_trend_types() { + _trend_types.show(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::ns_show_current_ages +// Access: Private +// Description: Shows the breakdown of ages of all of the +// active pointers. +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +ns_show_current_ages() { + AgeHistogram hist; + double now = ClockObject::get_global_clock()->get_real_time(); + + Table::iterator ti; + for (ti = _table.begin(); ti != _table.end(); ++ti) { + MemoryInfo &info = (*ti).second; + if (info._freeze_index == _freeze_index) { + hist.add_info(now - info._time); + } + } + + hist.show(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::ns_show_trend_ages +// Access: Private +// Description: Shows the breakdown of ages of all of the +// pointers allocated and freed since the last call to +// freeze(). +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +ns_show_trend_ages() { + _trend_ages.show(); +} + + + +#endif // NDEBUG diff --git a/panda/src/express/memoryUsage.h b/panda/src/express/memoryUsage.h new file mode 100644 index 0000000000..2ca0bd2ef8 --- /dev/null +++ b/panda/src/express/memoryUsage.h @@ -0,0 +1,147 @@ +// Filename: memoryUsage.h +// Created by: drose (25May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MEMORYUSAGE_H +#define MEMORYUSAGE_H + +#include + +#include "typeHandle.h" + +#include + +class ReferenceCount; +class MemoryUsagePointers; + +//////////////////////////////////////////////////////////////////// +// Class : MemoryUsage +// Description : This class is used strictly for debugging purposes, +// specifically for tracking memory leaks of +// reference-counted objects: it keeps a record of every +// such object currently allocated. +// +// When compiled with NDEBUG set, this entire class does +// nothing and compiles to nothing. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS MemoryUsage { +public: + INLINE static bool get_track_memory_usage(); + +#if defined(__GNUC__) && !defined(NDEBUG) + // There seems to be a problem with egcs-2.91.66: it gets confused + // with too many nested inline functions, and sets the wrong pointer + // as 'this'. Yucky. The workaround is to make these functions + // non-inline, but this is inner-loop stuff, and we'd rather not pay + // the price universally. So we only compile them non-inline when + // we're building on GCC and not building in NDEBUG mode (in NDEBUG + // mode, these functions do nothing anyway). + static void record_pointer(ReferenceCount *ptr); + static void update_type(ReferenceCount *ptr, TypeHandle type); + static void remove_pointer(ReferenceCount *ptr); +#else // __GNUC__ && !NDEBUG + INLINE static void record_pointer(ReferenceCount *ptr); + INLINE static void update_type(ReferenceCount *ptr, TypeHandle type); + INLINE static void remove_pointer(ReferenceCount *ptr); +#endif // __GNUC__ && !NDEBUG + +#ifndef NDEBUG + INLINE static int get_num_pointers(); + INLINE static void get_pointers(MemoryUsagePointers &result); + INLINE static void get_pointers_of_type(MemoryUsagePointers &result, + TypeHandle type); + INLINE static void get_pointers_of_age(MemoryUsagePointers &result, + double from, double to); + INLINE static void get_pointers_with_zero_count(MemoryUsagePointers &result); + + INLINE static void freeze(); + + INLINE static void show_current_types(); + INLINE static void show_trend_types(); + INLINE static void show_current_ages(); + INLINE static void show_trend_ages(); + +private: + MemoryUsage(); + static MemoryUsage *get_global_ptr(); + + void ns_record_pointer(ReferenceCount *ptr); + void ns_update_type(ReferenceCount *ptr, TypeHandle type); + void ns_remove_pointer(ReferenceCount *ptr); + + int ns_get_num_pointers(); + void ns_get_pointers(MemoryUsagePointers &result); + void ns_get_pointers_of_type(MemoryUsagePointers &result, + TypeHandle type); + void ns_get_pointers_of_age(MemoryUsagePointers &result, + double from, double to); + void ns_get_pointers_with_zero_count(MemoryUsagePointers &result); + void ns_freeze(); + + void ns_show_current_types(); + void ns_show_trend_types(); + void ns_show_current_ages(); + void ns_show_trend_ages(); + + static MemoryUsage *_global_ptr; + + class MemoryInfo { + public: + TypeHandle get_type(); + void determine_dynamic_type(); + void update_type_handle(TypeHandle &destination, TypeHandle refined); + + ReferenceCount *_ptr; + TypeHandle _static_type; + TypeHandle _dynamic_type; + + double _time; + int _freeze_index; + bool _reconsider_dynamic_type; + }; + + typedef map Table; + Table _table; + int _freeze_index; + int _count; + + + class TypeHistogram { + public: + void add_info(TypeHandle type); + void show() const; + void clear(); + + private: + typedef map Counts; + Counts _counts; + }; + TypeHistogram _trend_types; + + class AgeHistogram { + public: + AgeHistogram(); + void add_info(double age); + void show() const; + void clear(); + + private: + int choose_bucket(double age) const; + + enum { num_buckets = 5 }; + int _counts[num_buckets]; + static double _cutoff[num_buckets]; + }; + AgeHistogram _trend_ages; + + + bool _track_memory_usage; + +#endif +}; + +#include "memoryUsage.I" + +#endif + diff --git a/panda/src/express/memoryUsagePointers.I b/panda/src/express/memoryUsagePointers.I new file mode 100644 index 0000000000..675e0433fd --- /dev/null +++ b/panda/src/express/memoryUsagePointers.I @@ -0,0 +1,59 @@ +// Filename: memoryUsagePointers.I +// Created by: drose (25May00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointers::Entry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +MemoryUsagePointers::Entry:: +Entry(ReferenceCount *ptr, TypeHandle type, double age) : + _ptr(ptr), + _type(type), + _age(age) +{ + _ptr->ref(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointers::Entry::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +MemoryUsagePointers::Entry:: +Entry(const Entry ©) : + _ptr(copy._ptr), + _type(copy._type), + _age(copy._age) +{ + _ptr->ref(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointers::Entry::Copy Assigment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void MemoryUsagePointers::Entry:: +operator = (const Entry ©) { + if (_ptr != copy._ptr) { + _ptr->unref(); + _ptr = copy._ptr; + _ptr->ref(); + } + _type = copy._type; + _age = copy._age; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointers::Entry::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +MemoryUsagePointers::Entry:: +~Entry() { + _ptr->unref(); +} + diff --git a/panda/src/express/memoryUsagePointers.cxx b/panda/src/express/memoryUsagePointers.cxx new file mode 100644 index 0000000000..08d040f89b --- /dev/null +++ b/panda/src/express/memoryUsagePointers.cxx @@ -0,0 +1,154 @@ +// Filename: memoryUsagePointers.cxx +// Created by: drose (25May00) +// +//////////////////////////////////////////////////////////////////// + +#include "memoryUsagePointers.h" + + +#ifndef NDEBUG +// Nothing in this module gets compiled in NDEBUG mode. + +#include "config_express.h" +#include "referenceCount.h" +#include "typedReferenceCount.h" + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointers::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +MemoryUsagePointers:: +MemoryUsagePointers() { +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointers::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +MemoryUsagePointers:: +~MemoryUsagePointers() { +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointers::get_num_pointers +// Access: Public +// Description: Returns the number of pointers in the set. +//////////////////////////////////////////////////////////////////// +int MemoryUsagePointers:: +get_num_pointers() const { + return _entries.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointers::get_pointer +// Access: Public +// Description: Returns the nth pointer of the set. +//////////////////////////////////////////////////////////////////// +ReferenceCount *MemoryUsagePointers:: +get_pointer(int n) const { + nassertr(n >= 0 && n < get_num_pointers(), NULL); + return _entries[n]._ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointers::get_typed_pointer +// Access: Public +// Description: Returns the nth pointer of the set, typecast to a +// TypedObject if possible. If the pointer is not a +// TypedObject or if the cast cannot be made, returns +// NULL. +//////////////////////////////////////////////////////////////////// +TypedObject *MemoryUsagePointers:: +get_typed_pointer(int n) const { + nassertr(n >= 0 && n < get_num_pointers(), NULL); + ReferenceCount *ptr = _entries[n]._ptr; + + TypeHandle type = _entries[n]._type; + + // We can only cast-across to a TypedObject when we explicitly know + // the inheritance path. Most of the time, this will be via + // TypedReferenceCount. There are classes defined in other packages + // that inherit from TypedObject and ReferenceCount separately (like + // Node), but we can't do anything about that here without knowing + // about the particular class. (Actually, we couldn't do anything + // about Node anyway, because it inherits virtually from + // ReferenceCount.) + + // RTTI can't help us here, because ReferenceCount has no virtual + // functions, so we can't use C++'s new dynamic_cast feature. + + if (type != TypeHandle::none() && + type.is_derived_from(TypedReferenceCount::get_class_type())) { + return (TypedReferenceCount *)ptr; + } + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointers::get_type +// Access: Public +// Description: Returns the actual type of the nth pointer, if it is +// known. +//////////////////////////////////////////////////////////////////// +TypeHandle MemoryUsagePointers:: +get_type(int n) const { + nassertr(n >= 0 && n < get_num_pointers(), TypeHandle::none()); + return _entries[n]._type; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointers::get_type_name +// Access: Public +// Description: Returns the type name of the nth pointer, if it is +// known. +//////////////////////////////////////////////////////////////////// +string MemoryUsagePointers:: +get_type_name(int n) const { + nassertr(n >= 0 && n < get_num_pointers(), ""); + return get_type(n).get_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointers::get_age +// Access: Public +// Description: Returns the age of the nth pointer: the number of +// seconds elapsed between the time it was allocated and +// the time it was added to this set via a call to +// MemoryUsage::get_pointers(). +//////////////////////////////////////////////////////////////////// +double MemoryUsagePointers:: +get_age(int n) const { + nassertr(n >= 0 && n < get_num_pointers(), 0.0); + return _entries[n]._age; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointers::clear +// Access: Public +// Description: Empties the set of pointers. +//////////////////////////////////////////////////////////////////// +void MemoryUsagePointers:: +clear() { + _entries.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointers::clear +// Access: Private +// Description: Adds a new entry to the set. Intended to be called +// only by MemoryUsage. +//////////////////////////////////////////////////////////////////// +void MemoryUsagePointers:: +add_entry(ReferenceCount *ptr, TypeHandle type, double age) { + // We can't safly add pointers with a zero reference count. They + // might be statically-allocated or something, and if we try to add + // them they'll try to destruct when the PointerTo later goes away. + if (ptr->get_count() != 0) { + _entries.push_back(Entry(ptr, type, age)); + } +} + + +#endif // NDEBUG diff --git a/panda/src/express/memoryUsagePointers.h b/panda/src/express/memoryUsagePointers.h new file mode 100644 index 0000000000..85e56b8ff0 --- /dev/null +++ b/panda/src/express/memoryUsagePointers.h @@ -0,0 +1,83 @@ +// Filename: memoryUsagePointers.h +// Created by: drose (25May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MEMORYUSAGEPOINTERS_H +#define MEMORYUSAGEPOINTERS_H + +#include + +#include "typeHandle.h" +#include "pointerTo.h" +#include "referenceCount.h" + +#include + +#ifndef NDEBUG +//////////////////////////////////////////////////////////////////// +// Class : MemoryUsagePointers +// Description : This is a list of pointers returned by a MemoryUsage +// object in response to some query. + +// Warning: once pointers are stored in a +// MemoryUsagePointers object, they are +// reference-counted, and will not be freed until the +// MemoryUsagePointers object is freed (or clear() is +// called on the object). However, they may not even be +// freed then; pointers may leak once they have been +// added to this structure. This is because we don't +// store enough information in this structure to +// correctly free the pointers that have been added. +// Since this is intended primarily as a debugging tool, +// this is not a major issue. +// +// This class is just a user interface to talk about +// pointers stored in a MemoryUsage object. It doesn't +// even exist when compiled with NDEBUG. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS MemoryUsagePointers { +public: + MemoryUsagePointers(); + ~MemoryUsagePointers(); + + int get_num_pointers() const; + ReferenceCount *get_pointer(int n) const; + TypedObject *get_typed_pointer(int n) const; + TypeHandle get_type(int n) const; + string get_type_name(int n) const; + double get_age(int n) const; + + void clear(); + +private: + void add_entry(ReferenceCount *ptr, TypeHandle type, double age); + + class Entry { + public: + INLINE Entry(ReferenceCount *ptr, TypeHandle type, double age); + INLINE Entry(const Entry ©); + INLINE void operator = (const Entry ©); + INLINE ~Entry(); + + // We have an ordinary pointer to a type ReferenceCount, and not a + // PT(ReferenceCount), because we can't actually delete this thing + // (since ReferenceCount has no public destructor). If we can't + // delete it, we can't make a PointerTo it, since PointerTo wants + // to be able to delete things. + ReferenceCount *_ptr; + TypeHandle _type; + double _age; + }; + + typedef vector Entries; + Entries _entries; + friend class MemoryUsage; +}; + +#include "memoryUsagePointers.I" + +#endif // NDEBUG + +#endif + diff --git a/panda/src/express/multifile.I b/panda/src/express/multifile.I new file mode 100644 index 0000000000..13ad5de5d3 --- /dev/null +++ b/panda/src/express/multifile.I @@ -0,0 +1,29 @@ +// Filename: multifile.I +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::write_header +// Access: Private +// Description: Writes the header uncompressed with platform- +// independent byte ordering +//////////////////////////////////////////////////////////////////// +INLINE void Multifile:: +write_header(ofstream &write_stream) { + _datagram.clear(); + _datagram.add_uint32(_magic_number); + _datagram.add_int32(_num_mfiles); + string msg = _datagram.get_message(); + write_stream.write((char *)msg.data(), msg.length()); +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::get_header_length +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int Multifile:: +get_header_length(void) const { + return _header_length; +} diff --git a/panda/src/express/multifile.cxx b/panda/src/express/multifile.cxx new file mode 100644 index 0000000000..f0fc7112af --- /dev/null +++ b/panda/src/express/multifile.cxx @@ -0,0 +1,631 @@ +// Filename: multifile.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// Copyright (C) 1992,93,94,95,96,97,98 +// Walt Disney Imagineering, Inc. +// +// These coded instructions, statements, data structures and +// computer programs contain unpublished proprietary information of +// Walt Disney Imagineering and are protected by Federal copyright +// law. They may not be disclosed to third parties or copied or +// duplicated in any form, in whole or in part, without the prior +// written consent of Walt Disney Imagineering Inc. +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include +#include "multifile.h" +#include "config_express.h" +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +PN_uint32 Multifile::_magic_number = 0xbeeffeeb; + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::Memfile::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Multifile::Memfile:: +Memfile(void) { + reset(); + _header_length_buf_length = sizeof(_header_length); + _header_length_buf = new char[_header_length_buf_length]; +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::Memfile::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Multifile::Memfile:: +~Memfile(void) { + if (_buffer != (char *)0L) + delete _buffer; + delete _header_length_buf; +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::Memfile::reset +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Multifile::Memfile:: +reset(void) { + _header_length_parsed = false; + _header_parsed = false; + _header_length = 0; + _buffer_length = 0; + _buffer = (char *)0L; + _file_open = false; + _bytes_written = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::Memfile::parse_header_length +// Access: Public +// Description: Fills up _datagram until it has sizeof(_header_length) +// bytes and then extracts _header_length from _datagram. +// Returns true when this has been accomplished. +// Advances start to end of header_length. +//////////////////////////////////////////////////////////////////// +bool Multifile::Memfile:: +parse_header_length(char *&start, int &size) { + if (_header_length_parsed == true) + return true; + + int bytes_so_far = _datagram.get_length(); + if (bytes_so_far + size < _header_length_buf_length) { + _datagram.append_data(start, size); + return false; + } + + _datagram.append_data(start, _header_length_buf_length - bytes_so_far); + nassertr((int)_datagram.get_length() == _header_length_buf_length, false); + + // Advance start and adjust size + start += _header_length_buf_length; + nassertr(size >= _header_length_buf_length, false); + size -= _header_length_buf_length; + + DatagramIterator di(_datagram); + _header_length = di.get_int32(); + + nassertr(_header_length > _header_length_buf_length + (int)sizeof(_buffer_length), false); + + _header_length_parsed = true; + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::Memfile::parse_header +// Access: Public +// Description: Returns true when a complete header has been parsed. +// Advances the start pointer to the end of the header. +//////////////////////////////////////////////////////////////////// +bool Multifile::Memfile:: +parse_header(char *&start, int& size) { + if (_header_parsed == true) + return true; + + // Determine header length + if (parse_header_length(start, size) == false) + return false; + + int bytes_so_far = _datagram.get_length(); + + // Make sure we don't exceed the length of the header + int tsize = size; + if ((size + bytes_so_far) >= _header_length) { + nassertr(bytes_so_far <= _header_length, false); + tsize = _header_length - bytes_so_far; + _header_parsed = true; + } + + // Accumulate bytes collected so far + _datagram.append_data(start, tsize); + + // Fill in data for the memfile + if (_header_parsed == true) { + nassertr((int)_datagram.get_length() == _header_length, false); + DatagramIterator di(_datagram); + PN_int32 header_length = di.get_int32(); + nassertr(header_length == _header_length, false); + _name = di.extract_bytes(_header_length - _header_length_buf_length - + sizeof(_buffer_length)); + _buffer_length = di.get_int32(); + + // Advance start pointer to the end of the header + start += tsize; + nassertr(size >= tsize, false); + size -= tsize; + _datagram.clear(); + } + + return _header_parsed; +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::Memfile::read +// Access: Public +// Description: Reads from an individual file +//////////////////////////////////////////////////////////////////// +bool Multifile::Memfile:: +read(const Filename &name) { + ifstream read_stream; + if (!name.open_read(read_stream)) { + express_cat.error() + << "Multifile::Memfile() - Failed to open input file: " + << name << endl; + return false; + } + + express_cat.debug() + << "Reading file: " << name << endl; + + _header_length = name.length() + sizeof(_header_length) + + sizeof(_buffer_length); + _name = name; + + // Determine the length of the file + read_stream.seekg(0, ios::end); + _buffer_length = read_stream.tellg(); + _buffer = new char[_buffer_length]; + + // Read the file + read_stream.seekg(0, ios::beg); + read_stream.read(_buffer, _buffer_length); + + return (_buffer_length > 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::Memfile::read_multi +// Access: Public +// Description: Reads a Memfile from an open Multifile ifstream +//////////////////////////////////////////////////////////////////// +bool Multifile::Memfile:: +read_from_multifile(ifstream &read_stream) { + read_stream.read(_header_length_buf, _header_length_buf_length); + char *start = _header_length_buf; + int size = _header_length_buf_length; + if (parse_header_length(start, size) == false) { + express_cat.error() + << "Multifile::Memfile::read_from_multifile() - invalid header " + << "length" << endl; + return false; + } + + // Read the rest of the header + char *header_buf = new char[_header_length - _header_length_buf_length]; + read_stream.read(header_buf, _header_length - _header_length_buf_length); + start = header_buf; + size = _header_length; + if (parse_header(start, size) == false) { + delete header_buf; + express_cat.error() + << "Multifile::Memfile::read_from_multifile() - Invalid header" + << endl; + return false; + } + + delete header_buf; + + express_cat.debug() + << "Multifile::Memfile::read_from_multifile() - Got a valid Memfile " + << "header: name: " << _name << " length: " << _buffer_length << endl; + + _buffer = new char[_buffer_length]; + read_stream.read(_buffer, _buffer_length); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::Memfile::write +// Access: Public +// Description: Writes to a individual file +//////////////////////////////////////////////////////////////////// +bool Multifile::Memfile:: +write(void) { + ofstream write_stream; + _name.set_binary(); + if (!_name.open_write(write_stream)) { + express_cat.error() + << "Multifile::Memfile::write() - Failed to open output file: " + << _name << endl; + return false; + } + + express_cat.debug() + << "Writing to file: " << _name << endl; + + write_stream.write(_buffer, _buffer_length); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::Memfile::write +// Access: Public +// Description: Writes a Memfile to an open Multifile ofstream +//////////////////////////////////////////////////////////////////// +void Multifile::Memfile:: +write_to_multifile(ofstream &write_stream) { + express_cat.debug() + << "Writing: " << _name << " to multifile" << endl; + + _datagram.clear(); + _datagram.add_int32(_header_length); + string path_name = _name.get_fullpath(); + _datagram.append_data(path_name.c_str(), path_name.length()); + _datagram.add_int32(_buffer_length); + + string msg = _datagram.get_message(); + write_stream.write((char *)msg.data(), msg.length()); + write_stream.write(_buffer, _buffer_length); +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::Memfile::write +// Access: Public +// Description: Returns true when the memfile has been parsed and +// written to disk. +// Advances the start pointer as it writes. +//////////////////////////////////////////////////////////////////// +bool Multifile::Memfile:: +write(char *&start, int &size) { + // Make sure we've got a complete header first + if (parse_header(start, size) == false) + return false; + + // Try to open the file for writing + if (_file_open == false) { + _name.set_binary(); + if ((_file_open = _name.open_write(_write_stream)) == false) { + express_cat.error() + << "Multfile::Memfile::write() - Couldn't open file: " + << _name << endl; + return false; + } + _file_open = true; + } + + // Don't write more than the buffer length + bool done = false; + int tsize = size; + int missing_bytes = _buffer_length - _bytes_written; + if (size >= missing_bytes) { + tsize = missing_bytes; + done = true; + } + + _write_stream.write(start, tsize); + start += tsize; + _bytes_written += tsize; + size -= tsize; + + if (done == true) + _write_stream.close(); + + return done; +} + + + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Multifile:: +Multifile(void) { + reset(); + _header_length = sizeof(_magic_number) + sizeof(_num_mfiles); +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Multifile:: +~Multifile(void) { + _files.erase(_files.begin(), _files.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::evaluate +// Access: Public +// Description: Checks for a valid compressed or uncompressed +// Multifile. +//////////////////////////////////////////////////////////////////// +int Multifile:: +evaluate(const char *start, int size) { + if (size < (int)sizeof(_magic_number)) { + express_cat.debug() + << "Multifile::evaluate() - not enough bytes to determine" << endl; + return T_unknown; + } + + Datagram dgram; + dgram.append_data(start, sizeof(_magic_number)); + DatagramIterator di(dgram); + PN_uint32 magic_number = di.get_uint32(); + if (magic_number == _magic_number) + return T_valid; + + express_cat.debug() + << "Invalid magic number: " << magic_number << " (" + << _magic_number << ")" << endl; + + return T_invalid; +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::parse_header +// Access: Public +// Description: Returns true when a complete header has been parsed. +//////////////////////////////////////////////////////////////////// +bool Multifile:: +parse_header(char *&start, int &size) { + if (_header_parsed == true) + return true; + + int dgramsize = _datagram.get_length(); + int tsize = size; + + // Make sure we don't exceed the length of the header + nassertr(_header_length >= dgramsize, false); + int missing_bytes = _header_length - dgramsize; + if (size >= missing_bytes) { + tsize = missing_bytes; + _header_parsed = true; + } + + // Accumulate bytes collected so far + _datagram.append_data(start, tsize); + + // Verify magic number + if (_header_parsed == true) { + DatagramIterator di(_datagram); + PN_uint32 magic_number = di.get_uint32(); + if (magic_number != _magic_number) { + express_cat.error() + << "Multifile::parse_header() - Invalid magic number: " + << magic_number << " (" << _magic_number << ")" << endl; + return false; + } + _num_mfiles = di.get_int32(); + + // Advance start pointer to the end of the header + start += tsize; + nassertr(size >= tsize, false); + size -= tsize; + _datagram.clear(); + } + + return _header_parsed; +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::add +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool Multifile:: +add(const Filename &name) { + Memfile *mfile = new Memfile; + if (mfile->read(name)) { + _files.push_back(mfile); + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::remove +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool Multifile:: +remove(const Filename &name) { + MemfileList::iterator found; + found = find_if(_files.begin(), _files.end(), MemfileMatch(name)); + if (found != _files.end()) { + _files.erase(found); + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::has_file +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool Multifile:: +has_file(const Filename &name) { + MemfileList::iterator found; + found = find_if(_files.begin(), _files.end(), MemfileMatch(name)); + return (found != _files.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::read +// Access: Public +// Description: Reads a multifile from disk +//////////////////////////////////////////////////////////////////// +bool Multifile:: +read(Filename &name) { + + // Open the multifile for reading + ifstream read_stream; + name.set_binary(); + if (!name.open_read(read_stream)) { + express_cat.error() + << "Multifile::read() - Failed to open input file: " + << name << endl; + return false; + } + + // Check for a valid header + char *buffer = new char[_header_length]; + read_stream.read(buffer, _header_length); + char *start = buffer; + int len = _header_length; + bool ret = parse_header(start, len); + delete buffer; + if (ret == false) + return false; + + if (_num_mfiles <= 0) { + express_cat.error() + << "Multfile::read() - No files in Multfile: " << name << endl; + return false; + } + + // Read all the Memfiles + for (int i = 0; i < _num_mfiles; i++) { + Memfile *mfile = new Memfile; + mfile->read_from_multifile(read_stream); + _files.push_back(mfile); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool Multifile:: +write(Filename name) { + + ofstream write_stream; + name.set_binary(); + if (!name.open_write(write_stream)) { + express_cat.error() + << "Multifile::write() - Error opening file: " << name << endl; + return false; + } + + _num_mfiles = _files.size(); + if (_num_mfiles == 0) { + express_cat.debug() + << "No files in Multifile to write" << endl; + return false; + } + + express_cat.debug() + << "Multifile::write() - Writing multifile: " << name << endl; + + write_header(write_stream); + + MemfileList::iterator i; + for (i = _files.begin(); i != _files.end(); ++i) + (*i)->write_to_multifile(write_stream); + + write_stream.close(); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::write +// Access: Public +// Description: Returns true when all the memfiles have been +// written. +// Advances the start pointer as it writes. +//////////////////////////////////////////////////////////////////// +bool Multifile:: +write(char *&start, int &size) { + // Make sure we have a complete header first + if (parse_header(start, size) == false) + return false; + + while (_num_mfiles > 0) { + if (_current_mfile == (Memfile *)0L) + _current_mfile = new Memfile; + if (_current_mfile->write(start, size) == true) { + _num_mfiles--; + delete _current_mfile; + } else + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::write_extract +// Access: Public +// Description: Returns true when entire Multifile has been +// extracted to disk files. +//////////////////////////////////////////////////////////////////// +bool Multifile:: +write_extract(char *&start, int &size) { + if (parse_header(start, size) == false) + return false; + if (_current_mfile == (Memfile *)0L) + _current_mfile = new Memfile; + for (;;) { + if (_current_mfile->write(start, size) == false) + return false; + if (++_mfiles_written == _num_mfiles) + return true; + _current_mfile->reset(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::reset +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Multifile:: +reset(void) { + _header_parsed = false; + _num_mfiles = 0; + _current_mfile = (Memfile *)0L; + _datagram.clear(); + _files.erase(_files.begin(), _files.end()); + _mfiles_written = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::extract +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool Multifile:: +extract(const Filename &name) { + MemfileList::iterator found; + found = find_if(_files.begin(), _files.end(), MemfileMatch(name)); + if (found != _files.end()) { + (*found)->write(); + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: Multifile::extract_all +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Multifile:: +extract_all(void) { + express_cat.debug() + << "Multifile::extract_all() - Extracting all files" << endl; + + MemfileList::iterator i; + for (i = _files.begin(); i != _files.end(); ++i) + (*i)->write(); +} diff --git a/panda/src/express/multifile.h b/panda/src/express/multifile.h new file mode 100644 index 0000000000..bf1e794379 --- /dev/null +++ b/panda/src/express/multifile.h @@ -0,0 +1,116 @@ +// Filename: multifile.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef MULTIFILE_H +#define MULTIFILE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : Multifile +// Description : A file that contains a set of files. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS Multifile { +public: + enum Type { + T_unknown, + T_valid, + T_invalid, + }; + + Multifile(void); + ~Multifile(void); + + INLINE int get_header_length(void) const; + + static int evaluate(const char *start, int size); + + bool add(const Filename &name); + bool remove(const Filename &name); + bool has_file(const Filename &name); + + bool read(Filename &name); + bool write(Filename name); + bool write(char *&start, int &size); + bool write_extract(char *&start, int &size); + bool extract(const Filename &name); + void extract_all(void); + + void reset(void); + bool parse_header(char *&start, int &size); + +private: + + INLINE void write_header(ofstream &write_stream); + + class Memfile { + public: + Memfile(void); + ~Memfile(void); + bool parse_header_length(char *&start, int &size); + bool parse_header(char *&start, int &size); + + bool read(const Filename &name); + bool read_from_multifile(ifstream &read_stream); + bool write(void); + void write_to_multifile(ofstream &write_stream); + bool write(char *&start, int &size); + void reset(void); + + public: + bool _header_length_parsed; + bool _header_parsed; + Datagram _datagram; + char *_header_length_buf; + int _header_length_buf_length; + + PN_int32 _header_length; + Filename _name; + PN_int32 _buffer_length; + char* _buffer; + + bool _file_open; + ofstream _write_stream; + int _bytes_written; + }; + + typedef list MemfileList; + + class MemfileMatch { + public: + MemfileMatch(const Filename &name) { + _want_name = name; + } + bool operator()(Memfile *mfile) const { + return mfile->_name == _want_name; + } + Filename _want_name; + }; + +private: + MemfileList _files; + PN_int32 _num_mfiles; + bool _header_parsed; + Memfile *_current_mfile; + Datagram _datagram; + int _header_length; + int _mfiles_written; + + static PN_uint32 _magic_number; +}; + +#include "multifile.I" + +#endif diff --git a/panda/src/express/namable.I b/panda/src/express/namable.I new file mode 100644 index 0000000000..c1f2748a19 --- /dev/null +++ b/panda/src/express/namable.I @@ -0,0 +1,106 @@ +// Filename: namable.I +// Created by: drose (16Feb00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Namable::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Namable:: +Namable(const string &initial_name) : + _name(initial_name) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: Namable::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Namable:: +Namable(const Namable ©) : + _name(copy._name) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: Namable::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Namable &Namable:: +operator = (const Namable &other) { + _name = other._name; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: Namable::set_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Namable:: +set_name(const string &name) { + _name = name; +} + +//////////////////////////////////////////////////////////////////// +// Function: Namable::clear_name +// Access: Public +// Description: Resets the Namable's name to empty. +//////////////////////////////////////////////////////////////////// +INLINE void Namable:: +clear_name() { + _name = ""; +} + +//////////////////////////////////////////////////////////////////// +// Function: Namable::has_name +// Access: Public +// Description: Returns true if the Namable has a nonempty name set, +// false if the name is empty. +//////////////////////////////////////////////////////////////////// +INLINE bool Namable:: +has_name() const { + return !_name.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Namable::get_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const string &Namable:: +get_name() const { + return _name; +} + +//////////////////////////////////////////////////////////////////// +// Function: Namable::output +// Access: Public +// Description: Outputs the Namable. This function simply writes the +// name to the output stream; most Namable derivatives +// will probably redefine this. +//////////////////////////////////////////////////////////////////// +INLINE void Namable:: +output(ostream &out) const { + out << get_name(); +} + + +INLINE ostream &operator << (ostream &out, const Namable &n) { + n.output(out); + return out; +} + +//////////////////////////////////////////////////////////////////// +// Function: NamableOrderByName::Function operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NamableOrderByName:: +operator ()(const Namable *n1, const Namable *n2) const { + return (n1->get_name() < n2->get_name()); +} diff --git a/panda/src/express/namable.cxx b/panda/src/express/namable.cxx new file mode 100644 index 0000000000..2c7f626e2e --- /dev/null +++ b/panda/src/express/namable.cxx @@ -0,0 +1,8 @@ +// Filename: namable.cxx +// Created by: drose (15Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "namable.h" + +TypeHandle Namable::_type_handle; diff --git a/panda/src/express/namable.h b/panda/src/express/namable.h new file mode 100644 index 0000000000..52864c7016 --- /dev/null +++ b/panda/src/express/namable.h @@ -0,0 +1,67 @@ +// Filename: namable.h +// Created by: drose (15Jan99) +// + +#ifndef NAMABLE_H +#define NAMABLE_H + +#include + +#include "typeHandle.h" +#include + +/////////////////////////////////////////////////////////////////// +// Class : Namable +// Description : A base class for all things which can have a name. +// The name is either empty or nonempty, but it is never +// NULL. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS Namable { +public: + INLINE Namable(const string &initial_name = ""); + INLINE Namable(const Namable ©); + INLINE Namable &operator = (const Namable &other); + + INLINE void set_name(const string &name); + INLINE void clear_name(); + INLINE bool has_name() const; + INLINE const string &get_name() const; + + // In the absence of any definition to the contrary, outputting a + // Namable will write out its name. + INLINE void output(ostream &out) const; + +private: + string _name; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + register_type(_type_handle, "Namable"); + } + +private: + static TypeHandle _type_handle; +}; + +INLINE ostream &operator << (ostream &out, const Namable &n); + +/////////////////////////////////////////////////////////////////// +// Class : NamableOrderByName +// Description : An STL function object for sorting an array of +// pointers to Namables into order by name. Returns +// true if the objects are in sorted order, false +// otherwise. +//////////////////////////////////////////////////////////////////// +class NamableOrderByName { +public: + INLINE bool operator ()(const Namable *n1, const Namable *n2) const; +}; + +#include "namable.I" + +#endif + + diff --git a/panda/src/express/numeric_types.h b/panda/src/express/numeric_types.h new file mode 100644 index 0000000000..aab4b0f686 --- /dev/null +++ b/panda/src/express/numeric_types.h @@ -0,0 +1,73 @@ +// Filename: numeric_types.h +// Created by: drose (06Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NUMERIC_TYPES_H +#define NUMERIC_TYPES_H + +// This header file defines a number of typedefs that correspond to +// the various numeric types for unsigned and signed numbers of +// various widths. + +// At the present, we use the logic in NSPR to determine this for us. +// Later (especially for non-NSPR platforms) we'll have to do the work +// ourselves. + +// This file also defines one of the macros IS_BIG_ENDIAN or +// IS_LITTLE_ENDIAN, as appropriate. (In the case of NSPR, these are +// defined automatically.) + + +#ifdef HAVE_NSPR + +#include + +typedef PRInt8 PN_int8; +typedef PRInt16 PN_int16; +typedef PRInt32 PN_int32; +typedef PRInt64 PN_int64; + +typedef PRUint8 PN_uint8; +typedef PRUint16 PN_uint16; +typedef PRUint32 PN_uint32; +typedef PRUint64 PN_uint64; + +typedef PRFloat64 PN_float64; + +#else // HAVE_NSPR + +// Without NSPR, and without any other information, we need some +// fallback. For now, we'll just assume a typical 32-bit environment. + +typedef signed char PN_int8; +typedef short PN_int16; +typedef long PN_int32; + +#ifdef WIN32_VC +typedef __int64 PN_int64; +typedef unsigned __int64 PN_uint64; +#else +typedef long long PN_int64; +typedef unsigned long long PN_uint64; +#endif + +typedef unsigned char PN_uint8; +typedef unsigned short PN_uint16; +typedef unsigned long PN_uint32; + +typedef double PN_float64; + +#ifdef WORDS_BIGENDIAN +#undef IS_LITTLE_ENDIAN +#else +#define IS_LITTLE_ENDIAN 1 +#endif + +#endif // HAVE_NSPR + +#endif + + + + diff --git a/panda/src/express/patchfile.I b/panda/src/express/patchfile.I new file mode 100644 index 0000000000..f2359d787f --- /dev/null +++ b/panda/src/express/patchfile.I @@ -0,0 +1,41 @@ +// Filename: patchfile.I +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Patchfile::write_header +// Access: Private +// Description: Writes the header with platform-independent +// byte ordering +//////////////////////////////////////////////////////////////////// +INLINE void Patchfile:: +write_header(ofstream &write_stream, const string &name) { + _datagram.clear(); + _datagram.add_uint32(_magic_number); + _datagram.add_int32(name.length()); + _datagram.append_data(name.c_str(), name.length()); + string msg = _datagram.get_message(); + write_stream.write((char *)msg.data(), msg.length()); +} + +//////////////////////////////////////////////////////////////////// +// Function: Patchfile::write_entry +// Access: Private +// Description: Writes an entry consisting of 1. byte position +// 2. number of bytes being replaced 3. number of +// bytes being inserted with platform-independent +// byte ordering. +//////////////////////////////////////////////////////////////////// +INLINE void Patchfile:: +write_entry(ofstream &write_stream, int pos, int n, int m) { + express_cat.debug() + << "write_entry(" << pos << ", " << n << ", " << m << ")" << endl; + + _datagram.clear(); + _datagram.add_int32(pos); + _datagram.add_int32(n); + _datagram.add_int32(m); + string msg = _datagram.get_message(); + write_stream.write((char *)msg.data(), msg.length()); +} diff --git a/panda/src/express/patchfile.cxx b/panda/src/express/patchfile.cxx new file mode 100644 index 0000000000..38e4097d2c --- /dev/null +++ b/panda/src/express/patchfile.cxx @@ -0,0 +1,635 @@ +// Filename: patchfile.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// Copyright (C) 1992,93,94,95,96,97,98 +// Walt Disney Imagineering, Inc. +// +// These coded instructions, statements, data structures and +// computer programs contain unpublished proprietary information of +// Walt Disney Imagineering and are protected by Federal copyright +// law. They may not be disclosed to third parties or copied or +// duplicated in any form, in whole or in part, without the prior +// written consent of Walt Disney Imagineering Inc. +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "patchfile.h" +#include "config_express.h" + +#include // for tempnam + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +PN_uint32 Patchfile::_magic_number = 0xfeebfaab; + +//////////////////////////////////////////////////////////////////// +// Function: Patchfile::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Patchfile:: +Patchfile(void) { + PT(Buffer) buffer = new Buffer(patchfile_buffer_size); + init(buffer); +} + +//////////////////////////////////////////////////////////////////// +// Function: Patchfile::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Patchfile:: +Patchfile(PT(Buffer) buffer) { + init(buffer); +} + +//////////////////////////////////////////////////////////////////// +// Function: Patchfile::init +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Patchfile:: +init(PT(Buffer) buffer) { + nassertv(!buffer.is_null()); + _win_len = patchfile_window_size; + _zone_len = patchfile_zone_size; + _increment = patchfile_increment_size; + _name.set_binary(); + _buffer = buffer; + _header_length_length = sizeof(PN_uint32) + sizeof(PN_int32); + char *temp_name = tempnam(NULL, "pf"); + _temp_file_name = temp_name; + _temp_file_name.set_binary(); + delete temp_name; + reset(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Patchfile::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Patchfile:: +~Patchfile(void) { + _temp_file_name.unlink(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Patchfile::find_next_difference +// Access: Private +// Description: Finds the first byte that differs between buf_a and +// buf_b. Returns -1 if buf_a == buf_b. +//////////////////////////////////////////////////////////////////// +int Patchfile:: +find_next_difference(const char *buf_a, int size_a, + const char *buf_b, int size_b) { + int i; + for (i = 0; i < size_a && i < size_b; i++) { + if (buf_a[i] != buf_b[i]) + return i; + } + + // buf_a == buf_b + if (size_a == size_b) + return -1; + + return i + 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: Patchfile::is_match +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +bool Patchfile:: +is_match(const char *buf_a, const char *buf_b, int size) const { + if (size < _win_len) + return false; + + for (int i = 0; i < size; i++) { + if (buf_a[i] != buf_b[i]) + return false; + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Patchfile::find_match +// Access: Private +// Description: Run a window along a buffer in specified increments +// until a match is found. +// Returns -1 on failure. +//////////////////////////////////////////////////////////////////// +int Patchfile:: +find_match(const char *win, int win_len, const char *buf, int buf_len) { + if (win_len < _win_len) + return -1; + + char *bufptr = (char *)buf; + int bytes_left = buf_len; + int sample_len; + for (int i = 0; i < buf_len; i++) { + sample_len = (win_len <= bytes_left) ? win_len : bytes_left; + if (is_match(win, bufptr, sample_len) == true) { + return i; + } + bufptr++; + bytes_left--; + } + + return -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: Patchfile::find_next_zone_match +// Access: Private +// Description: Advance a window down the entire length of a buffer +// looking for a complete match. If one is not found, +// advance the window by _win_len and try again until +// a match is found or we reach the end of the buffer. +// Returns false on failure. +//////////////////////////////////////////////////////////////////// +bool Patchfile:: +find_next_zone_match(const char *buf_a, int size_a, int &pos_a, + const char *buf_b, int size_b, int &pos_b) { + // Handle boundary conditions + if (size_a == 0 || size_b == 0) { + express_cat.debug() + << "Patchfile::find_next_zone_match() - size a = " << size_a + << " size b = " << size_b << endl; + return false; + } + + char *winptr = (char *)buf_a; + int bytes_left = size_a; + int win_len, pos; + int i, j, k; + pos_a = 0; + for (i = 0; i < size_a; i += _increment) { + win_len = (_win_len <= bytes_left) ? _win_len : bytes_left; + pos = find_match(winptr, win_len, buf_b, size_b); + if (pos != -1) { +cerr << "found a match at: " << pos << endl; + // Back up the window in case we missed part of an earlier match + if (pos_a >= win_len) { + int prev_pos_a = pos_a - _increment; + for (j = pos_a, k = pos; + j > prev_pos_a && k > 0; + j--, k--) { + if (buf_a[j] != buf_b[k]) + break; + } + pos_a = j+1; + pos_b = k+1; + } else + pos_b = pos; + return true; + } + pos_a += _increment; + winptr += _increment; + bytes_left -= _increment; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: Patchfile::find_next_z_match +// Access: Public +// Description: Starts with a zone in stream b and searches for +// a match. If a match is not found the zone is +// shifted down and we try again. +//////////////////////////////////////////////////////////////////// +bool Patchfile:: +find_next_z_match(const char *buf_a, int size_a, int &pos_a, + const char *buf_b, int size_b, int &pos_b) { + // Handle boundary conditions + if (size_a == 0 || size_b == 0) { + express_cat.debug() + << "Patchfile::find_next_z_match() - size a = " << size_a + << " size b = " << size_b << endl; + return false; + } + + char *bufbptr = (char *)buf_b; + int sizeb; + int bytes_left = size_b; + int starting_pos = 0; + for (int i = 0; i < size_b; i += _zone_len) { + sizeb = (_zone_len <= bytes_left) ? _zone_len : bytes_left; + if (find_next_zone_match(buf_a, size_a, pos_a, bufbptr, sizeb, pos_b) + == true) { + pos_b += starting_pos; + return true; + } + bytes_left -= sizeb; + bufbptr += sizeb; + starting_pos += sizeb; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: Patchfile::find_next_match +// Access: Public +// Description: Starts with a zone in stream a and searches for a +// match. If a match is not found, expands the zone +// in stream a and starts again. +//////////////////////////////////////////////////////////////////// +bool Patchfile:: +find_next_match(const char *buf_a, int size_a, int &pos_a, + const char *buf_b, int size_b, int &pos_b) { + // Handle boundary conditions + if (size_a == 0 || size_b == 0) { + express_cat.debug() + << "Patchfile::find_next_match() - size a = " << size_a + << " size b = " << size_b << endl; + return false; + } + + int sizea; + int bytes_left = size_a; + for (int i = 0; i < size_a; i += _zone_len) { + sizea = (_zone_len <= bytes_left) ? _zone_len : bytes_left; + if (find_next_z_match(buf_a, sizea, pos_a, buf_b, size_b, pos_b) + == true) { + return true; + } + bytes_left -= sizea; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: Patchfile::build +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool Patchfile:: +build(Filename &file_a, Filename &file_b) { + + if (express_cat.is_debug()) { + int pos = 0; + int len = 0; + int seq = find_longest_sequence(file_a, pos, len); + express_cat.debug() + << "File: " << file_a << " longest sequence = " << seq << " at " + << "position: " << pos << " of " << len << endl; + pos = 0; + len = 0; + seq = find_longest_sequence(file_b, pos, len); + express_cat.debug() + << "File: " << file_b << " longest sequence = " << seq << " at " + << "position: " << pos << " of " << len << endl; + } + + // Open the file for read + ifstream stream_a; + file_a.set_binary(); + if (!file_a.open_read(stream_a)) { + express_cat.error() + << "Patchfile::build() - Failed to open file: " << file_a << endl; + return false; + } + + // Open the file for read + ifstream stream_b; + file_b.set_binary(); + if (!file_b.open_read(stream_b)) { + express_cat.error() + << "Patchfile::build() - Failed to open file: " << file_b << endl; + return false; + } + + // Open patch file for write + ofstream write_stream; + _name = file_a.get_fullpath() + ".pch"; + if (!_name.open_write(write_stream)) { + express_cat.error() + << "Patchfile::build() - Failed to open file: " << _name << endl; + return false; + } + + // Determine file length + stream_a.seekg(0, ios::end); + int length_a = stream_a.tellg(); + char *buffer_a = new char[length_a]; + stream_a.seekg(0, ios::beg); + stream_a.read(buffer_a, length_a); + + // Determine file length + stream_b.seekg(0, ios::end); + int length_b = stream_b.tellg(); + char *buffer_b = new char[length_b]; + stream_b.seekg(0, ios::beg); + stream_b.read(buffer_b, length_b); + + char *next_a = buffer_a; + int remaining_a = length_a; + char *next_b = buffer_b; + int remaining_b = length_b; + + // Strip the v# out of the filename + // Save the original extension + string ext = file_a.get_extension(); + // Strip out the extension + Filename tfile = file_a.get_basename_wo_extension(); + // Now strip out the .v# + string fname = tfile.get_basename_wo_extension(); + fname += "."; + fname += ext; + write_header(write_stream, fname); + + bool done = false; + int starting_pos = 0; + int pos_a, pos_b; +cerr << "remaining_a: " << remaining_a << " remaining_b: " << remaining_b << endl; + while (done == false) { + int diff_pos = find_next_difference(next_a, remaining_a, + next_b, remaining_b); + if (diff_pos == -1) + done = true; + else if (diff_pos >= remaining_a || diff_pos >= remaining_b) { +cerr << "madeit" << endl; + done = true; + } else { +cerr << "found a diff at: " << starting_pos + diff_pos << endl; + starting_pos += diff_pos; + next_a += diff_pos; + remaining_a -= diff_pos; + next_b += diff_pos; + remaining_b -= diff_pos; + bool match = find_next_match(next_a, remaining_a, pos_a, + next_b, remaining_b, pos_b); + if (match == false) { + // Files differ from here on out - replace remaining_a bytes + // with remaining_b bytes + write_entry(write_stream, starting_pos, remaining_a, remaining_b); + if (remaining_b > 0) + write_stream.write(next_b, remaining_b); + done = true; + } else { + // Write an entry and start again + write_entry(write_stream, starting_pos, pos_a, pos_b); + if (pos_b > 0) + write_stream.write(next_b, pos_b); + next_a += pos_a; + remaining_a -= pos_a; + next_b += pos_b; + remaining_b -= pos_b; + starting_pos += pos_a; + } + } + } + + stream_a.close(); + stream_b.close(); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Patchfile::apply +// Access: Public +// Description: Apply the patch to the file (original file and +// patch are destroyed in the process). +//////////////////////////////////////////////////////////////////// +bool Patchfile:: +apply(Filename &patch, Filename &file) { + // Open the patch file for read + ifstream patch_stream; + patch.set_binary(); + if (!patch.open_read(patch_stream)) { + express_cat.error() + << "Patchfile::apply() - Failed to open file: " << patch << endl; + return false; + } + + // Open the file for read + ifstream file_stream; + file.set_binary(); + if (!file.open_read(file_stream)) { + express_cat.error() + << "Patchfile::apply() - Failed to open file: " << file << endl; + return false; + } + + // Determine the size of the orig file + file_stream.seekg(0, ios::end); + int orig_file_length = file_stream.tellg(); + file_stream.seekg(0, ios::beg); + +#if 0 + // Open the file for write + ofstream write_stream; + if (!_temp_file_name.open_write(write_stream)) { + express_cat.error() + << "Patchfile::apply() - Failed to open file: " << _temp_file_name + << endl; + return false; + } +#endif + + // Open the file for write + ofstream write_stream; + Filename mofile = "patcher_temp_file"; + mofile.set_binary(); + if (!mofile.open_write(write_stream)) { + express_cat.error() + << "Patchfile::apply() - Failed to open file: " << mofile << endl; + return false; + } + + // Determine the size of the patch file + patch_stream.seekg(0, ios::end); + int patch_file_length = patch_stream.tellg(); + patch_stream.seekg(0, ios::beg); + + // Make sure the patch file is valid + nassertr(_buffer->get_length() >= _header_length_length, false); + patch_stream.read(_buffer->_buffer, _header_length_length); + _datagram.clear(); + _datagram.append_data(_buffer->_buffer, _header_length_length); + DatagramIterator di(_datagram); + uint magic_number = di.get_uint32(); + if (magic_number != _magic_number) { + express_cat.error() + << "Patchfile::apply() - invalid patch file: " << patch << endl; + return false; + } + int name_length = di.get_int32(); + nassertr(_buffer->get_length() >= name_length, false); + patch_stream.read(_buffer->_buffer, name_length); + _datagram.clear(); + _datagram.append_data(_buffer->_buffer, name_length); + DatagramIterator di2(_datagram); + string name = di2.extract_bytes(name_length); + if (name != file.get_basename()) { + express_cat.error() + << "Patchfile::apply() - patch intended for file: " << name + << ", not file: " << file << endl; + return false; + } + patch_file_length -= (_header_length_length + name_length); + + express_cat.debug() + << "Patchfile::apply() - valid patchfile for file: " << name << endl; + + // Now patch the file using the given buffer + int buflen = _buffer->get_length(); + int total_orig_bytes = 0; + int total_patch_bytes = 0; + int read_bytes; + while (total_orig_bytes < orig_file_length) { + + // Read an entry + Entry entry; + nassertr(buflen >= entry._len, false); + patch_stream.read(_buffer->_buffer, entry._len); + _datagram.clear(); + _datagram.append_data(_buffer->_buffer, entry._len); + DatagramIterator di3(_datagram); + entry._pos = di3.get_int32(); + entry._n = di3.get_int32(); + entry._m = di3.get_int32(); + + express_cat.debug() + << "Patchfile::apply() - read an entry: pos: " << entry._pos + << " n: " << entry._n << " m: " << entry._m << endl; + + total_patch_bytes += entry._len; + + // Write original file until we hit a difference + while (total_orig_bytes < entry._pos) { + int next_diff = entry._pos - total_orig_bytes; + read_bytes = (next_diff < buflen) ? next_diff : buflen; + file_stream.read(_buffer->_buffer, read_bytes); + total_orig_bytes += read_bytes; + write_stream.write(_buffer->_buffer, read_bytes); + } + + // Now skip the next "n" bytes of the orig file + file_stream.seekg(total_orig_bytes + entry._n, ios::beg); + total_orig_bytes += entry._n; + + // Now write the patch file until we're done with the current entry + int patch_bytes = 0; + while (patch_bytes < entry._m) { + int patch_end = entry._m - patch_bytes; + read_bytes = (patch_end < buflen) ? patch_end : buflen; + patch_stream.read(_buffer->_buffer, read_bytes); + patch_bytes += read_bytes; + write_stream.write(_buffer->_buffer, read_bytes); + } + + total_patch_bytes += patch_bytes; + + // If the patch file is empty, write any remaining original file + // bytes to the out file + if (total_patch_bytes >= patch_file_length) { + while (total_orig_bytes < orig_file_length) { + int file_end = orig_file_length - total_orig_bytes; + read_bytes = (file_end < buflen) ? file_end : buflen; + file_stream.read(_buffer->_buffer, read_bytes); + total_orig_bytes += read_bytes; + write_stream.write(_buffer->_buffer, read_bytes); + } + } + + } + + patch_stream.close(); + file_stream.close(); + write_stream.close(); + patch.unlink(); + file.unlink(); + if (!mofile.rename_to(file)) { + express_cat.error() + << "Patchfile::apply() failed to rename temp file to: " << file + << endl; + return false; + } + +#if 0 + if (!_temp_file_name.rename_to(file)) { + express_cat.error() + << "Patchfile::apply() failed to rename temp file to: " << file + << endl; + return false; + } +#endif + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Patchfile::reset +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Patchfile:: +reset(void) { + _datagram.clear(); + _current_entry = (Entry *)0L; +} + +//////////////////////////////////////////////////////////////////// +// Function: Patchfile::find_longest_sequence +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int Patchfile:: +find_longest_sequence(Filename &infile, int &pos, int &len) const { + // Open the file for read + ifstream read_stream; + infile.set_binary(); + if (!infile.open_read(read_stream)) { + express_cat.error() + << "Patchfile::find_longest_sequence() - Failed to open file: " + << infile << endl; + return 0; + } + + // Determine file length + read_stream.seekg(0, ios::end); + len = read_stream.tellg(); + char *buffer = new char[len]; + read_stream.seekg(0, ios::beg); + read_stream.read(buffer, len); + + pos = 0; + char holder = 0; + int seq_len; + int longest_seq_len = 0; + for (int i = 0; i < len; i++) { + if (buffer[i] != holder) { + holder = buffer[i]; + seq_len = 0; + } else { + if (++seq_len > longest_seq_len) { + longest_seq_len = seq_len; + pos = i; + } + } + } + + read_stream.close(); + + return longest_seq_len; +} + +//////////////////////////////////////////////////////////////////// +// Function: Patchfile::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Patchfile::Entry:: +Entry(void) { + _len = 3 * sizeof(PN_int32); + _buffer = (char *)0L; +} diff --git a/panda/src/express/patchfile.h b/panda/src/express/patchfile.h new file mode 100644 index 0000000000..2855c1d674 --- /dev/null +++ b/panda/src/express/patchfile.h @@ -0,0 +1,86 @@ +// Filename: patchfile.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef PATCHFILE_H +#define PATCHFILE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "typedef.h" +#include +#include +#include +#include "datagram.h" +#include "datagramIterator.h" +#include "buffer.h" +#include "pointerTo.h" + +//////////////////////////////////////////////////////////////////// +// Class : Patchfile +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS Patchfile { +public: + Patchfile(void); + Patchfile(PT(Buffer) buffer); + ~Patchfile(void); + + bool build(Filename &file_a, Filename &file_b); + bool apply(Filename &patch, Filename &file); + + int find_longest_sequence(Filename &infile, int &pos, int &len) const; + + void reset(void); + +private: + void init(PT(Buffer) buffer); + INLINE void write_header(ofstream &write_stream, const string &name); + INLINE void write_entry(ofstream &write_stream, int pos, int n, int m); + int find_next_difference(const char *buf_a, int size_a, + const char *buf_b, int size_b); + int find_match(const char *win, int win_len, const char *buf, + int buf_len); + bool find_next_match(const char *buf_a, int size_a, int &pos_a, + const char *buf_b, int size_b, int &pos_b); + bool find_next_zone_match(const char *buf_a, int size_a, int &pos_a, + const char *buf_b, int size_b, int &pos_b); + bool find_next_z_match(const char *buf_a, int size_a, int &pos_a, + const char *buf_b, int size_b, int &pos_b); + bool is_match(const char *buf_a, const char *buf_b, + int size) const; + + class Entry { + public: + Entry(void); + + public: + int _pos; + int _n; + int _m; + char *_buffer; + int _len; + }; + +private: + Datagram _datagram; + int _win_len; + int _zone_len; + int _increment; + Filename _name; + int _min_sample_size; + PT(Buffer) _buffer; + int _header_length_length; + Entry *_current_entry; + Filename _temp_file_name; + + static PN_uint32 _magic_number; +}; + +#include "patchfile.I" + +#endif diff --git a/panda/src/express/pointerTo.I b/panda/src/express/pointerTo.I new file mode 100644 index 0000000000..7090da6584 --- /dev/null +++ b/panda/src/express/pointerTo.I @@ -0,0 +1,487 @@ +// Filename: pointerTo.I +// Created by: drose (10Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToBase:: +PointerToBase(To *ptr) { + _ptr = (To *)NULL; + reassign(ptr); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::Copy Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToBase:: +PointerToBase(const PointerToBase ©) { + _ptr = (To *)NULL; + reassign(copy); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::Destructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToBase:: +~PointerToBase() { + reassign((To *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::reassign +// Access: Protected +// Description: This is the main work of the PointerTo family. When +// the pointer is reassigned, decrement the old +// reference count and increment the new one. +//////////////////////////////////////////////////////////////////// +template +void PointerToBase:: +reassign(To *ptr) { + if (ptr != _ptr) { + if (_ptr != (To *)NULL) { + unref_delete(_ptr); + } + + _ptr = ptr; + if (_ptr != (To *)NULL) { + _ptr->ref(); +#ifndef NDEBUG + if (MemoryUsage::get_track_memory_usage()) { + // Make sure the MemoryUsage record knows what the TypeHandle + // is, if we know it ourselves. + TypeHandle type = get_type_handle(To); + if (type == TypeHandle::none()) { + do_init_type(To); + type = get_type_handle(To); + } + if (type != TypeHandle::none()) { + MemoryUsage::update_type(_ptr, type); + } + } +#endif + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::reassign +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void PointerToBase:: +reassign(const PointerToBase ©) { + reassign(copy._ptr); +} + +#ifndef CPPPARSER +#ifndef WIN32_VC +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::Equivalence operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool PointerToBase:: +operator == (const To *other) const { + return _ptr == other; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::Nonequivalence operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool PointerToBase:: +operator != (const To *other) const { + return _ptr != other; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::Greater-than operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool PointerToBase:: +operator > (const To *other) const { + return _ptr > other; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::Less-than-or-equal operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool PointerToBase:: +operator <= (const To *other) const { + return _ptr <= other; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::Greater-than-or-equal operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool PointerToBase:: +operator >= (const To *other) const { + return _ptr >= other; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::Equivalence operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool PointerToBase:: +operator == (const PointerToBase &other) const { + return _ptr == other._ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::Nonequivalence operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool PointerToBase:: +operator != (const PointerToBase &other) const { + return _ptr != other._ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::Greater-than operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool PointerToBase:: +operator > (const PointerToBase &other) const { + return _ptr > other._ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::Less-than-or-equal operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool PointerToBase:: +operator <= (const PointerToBase &other) const { + return _ptr <= other._ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::Greater-than-or-equal operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool PointerToBase:: +operator >= (const PointerToBase &other) const { + return _ptr >= other._ptr; +} +#endif // WIN32_VC + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::Less-than operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool PointerToBase:: +operator < (const To *other) const { + return _ptr < other; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::Less-than operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool PointerToBase:: +operator < (const PointerToBase &other) const { + return _ptr < other._ptr; +} + +#endif // CPPPARSER + + + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::is_null +// Access: Public +// Description: Returns true if the PointerTo is a NULL pointer, +// false otherwise. (Direct comparison to a NULL +// pointer also works.) +//////////////////////////////////////////////////////////////////// +template +INLINE bool PointerToBase:: +is_null() const { + return (_ptr == NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::clear +// Access: Public +// Description: A convenient way to set the PointerTo object to NULL. +// (Assignment to a NULL pointer also works, of course.) +//////////////////////////////////////////////////////////////////// +template +INLINE void PointerToBase:: +clear() { + reassign((To *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToBase::output +// Access: Public +// Description: A handy function to output PointerTo's as a hex +// pointer followed by a reference count. +//////////////////////////////////////////////////////////////////// +template +INLINE void PointerToBase:: +output(ostream &out) const { + out << (void *)_ptr; + if (_ptr != (To *)NULL) { + out << ":" << _ptr->get_count(); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: PointerTo::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerTo:: +PointerTo(To *ptr) : PointerToBase(ptr) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerTo::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerTo:: +PointerTo(const PointerTo ©) : + PointerToBase((const PointerToBase &)copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerTo::Dereference operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerTo::To &PointerTo:: +operator *() const { + return *_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerTo::Member access operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerTo::To *PointerTo:: +operator -> () const { + return _ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerTo::Typecast operator +// Access: Public +// Description: We also have the typecast operator to automatically +// convert PointerTo's to the required kind of actual +// pointer. This introduces ambiguities which the +// compiler will resolve one way or the other, but we +// don't care which way it goes because either will be +// correct. +//////////////////////////////////////////////////////////////////// +template +INLINE PointerTo:: +operator PointerToBase::To *() const { + return _ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerTo::p +// Access: Public +// Description: Returns an ordinary pointer instead of a PointerTo. +// Useful to work around compiler problems, particularly +// for implicit upcasts. +//////////////////////////////////////////////////////////////////// +template +INLINE PointerTo::To *PointerTo:: +p() const { + return _ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerTo::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerTo &PointerTo:: +operator = (To *ptr) { + reassign(ptr); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerTo::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerTo &PointerTo:: +operator = (const PointerTo ©) { + reassign((const PointerToBase &)copy); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerTo::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerTo:: +ConstPointerTo(const To *ptr) : + PointerToBase((ConstPointerTo::To *)ptr) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerTo::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerTo:: +ConstPointerTo(const PointerTo ©) : + PointerToBase((const PointerToBase &)copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerTo::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerTo:: +ConstPointerTo(const ConstPointerTo ©) : + PointerToBase((const PointerToBase &)copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerTo::Dereference operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE const ConstPointerTo::To &ConstPointerTo:: +operator *() const { + return *_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerTo::Member access operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE const ConstPointerTo::To *ConstPointerTo:: +operator -> () const { + return _ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerTo::Typecast operator +// Access: Public +// Description: We also have the typecast operator to automatically +// convert ConstPointerTo's to the required kind of actual +// pointer. This introduces ambiguities which the +// compiler will resolve one way or the other, but we +// don't care which way it goes because either will be +// correct. +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerTo:: +operator const PointerToBase::To *() const { + return _ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerTo::p +// Access: Public +// Description: Returns an ordinary pointer instead of a ConstPointerTo. +// Useful to work around compiler problems, particularly +// for implicit upcasts. +//////////////////////////////////////////////////////////////////// +template +INLINE const ConstPointerTo::To *ConstPointerTo:: +p() const { + return _ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerTo::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerTo &ConstPointerTo:: +operator = (const To *ptr) { + reassign((To *)ptr); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerTo::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerTo &ConstPointerTo:: +operator = (const ConstPointerTo ©) { + reassign((const PointerToBase &)copy); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerTo::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerTo &ConstPointerTo:: +operator = (const PointerTo ©) { + reassign((const PointerToBase &)copy); + return *this; +} diff --git a/panda/src/express/pointerTo.h b/panda/src/express/pointerTo.h new file mode 100644 index 0000000000..1233be0fa0 --- /dev/null +++ b/panda/src/express/pointerTo.h @@ -0,0 +1,218 @@ +// Filename: pointerTo.h +// Created by: drose (23Oct98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef POINTERTO_H +#define POINTERTO_H + +//////////////////////////////////////////////////////////////////// +// +// This file defines the classes PointerTo and ConstPointerTo (and +// their abbreviations, PT and CPT). These should be used in place of +// traditional C-style pointers wherever implicit reference counting +// is desired. +// +// The syntax is: instead of: +// +// PointerTo p; MyClass *p; +// PT(MyClass) p; +// +// ConstPointerTo p; const MyClass *p; +// CPT(MyClass) p; +// +// PointerTo and ConstPointerTo will automatically increment the +// object's reference count while the pointer is kept. When the +// PointerTo object is reassigned or goes out of scope, the reference +// count is automatically decremented. If the reference count reaches +// zero, the object is freed. +// +// Note that const PointerTo is different from +// ConstPointerTo. A const PointerTo may not reassign its +// pointer, but it may still modify the contents at that address. On +// the other hand, a ConstPointerTo may reassign its pointer at will, +// but may not modify the contents. It is like the difference between +// (MyClass * const) and (const MyClass *). +// +// In order to use PointerTo, it is necessary that the thing pointed +// to--MyClass in the above example--either inherits from +// ReferenceCount, or is a proxy built with RefCountProxy or +// RefCountObj (see referenceCount.h). However, also see +// PointerToArray, which does not have this restriction. +// +// It is crucial that the PointerTo object is only used to refer to +// objects allocated from the free store, for which delete is a +// sensible thing to do. If you assign a PointerTo to an automatic +// variable (allocated from the stack, for instance), bad things will +// certainly happen when the reference count reaches zero and it tries +// to delete it. +// +// It's also important to remember that, as always, a virtual +// destructor is required if you plan to support polymorphism. That +// is, if you define a PointerTo to some base type, and assign to it +// instances of a class derived from that base class, the base class +// must have a virtual destructor in order to properly destruct the +// derived object when it is deleted. +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "referenceCount.h" +#include "typedef.h" +#include "memoryUsage.h" +#include "config_express.h" + + +//////////////////////////////////////////////////////////////////// +// Class : PointerToBase +// Description : This is the base class for PointerTo and +// ConstPointerTo. Don't try to use it directly; use +// either derived class instead. +//////////////////////////////////////////////////////////////////// +template +class PointerToBase { +public: + typedef T To; + +protected: + INLINE PointerToBase(To *ptr); + INLINE PointerToBase(const PointerToBase ©); + INLINE ~PointerToBase(); + + void reassign(To *ptr); + + INLINE void reassign(const PointerToBase ©); + + To *_ptr; + + // No assignment or retrieval functions are declared in + // PointerToBase, because we will have to specialize on const + // vs. non-const later. + +public: + // These comparison functions are common to all things PointerTo, so + // they're defined up here. +#ifndef CPPPARSER +#ifndef WIN32_VC + INLINE bool operator == (const To *other) const; + INLINE bool operator != (const To *other) const; + INLINE bool operator > (const To *other) const; + INLINE bool operator <= (const To *other) const; + INLINE bool operator >= (const To *other) const; + + INLINE bool operator == (const PointerToBase &other) const; + INLINE bool operator != (const PointerToBase &other) const; + INLINE bool operator > (const PointerToBase &other) const; + INLINE bool operator <= (const PointerToBase &other) const; + INLINE bool operator >= (const PointerToBase &other) const; +#endif // WIN32_VC + INLINE bool operator < (const To *other) const; + INLINE bool operator < (const PointerToBase &other) const; +#endif // CPPPARSER + + INLINE bool is_null() const; + INLINE void clear(); + + void output(ostream &out) const; +}; + +template +INLINE ostream &operator <<(ostream &out, const PointerToBase &pointer) { + pointer.output(out); + return out; +} + +//////////////////////////////////////////////////////////////////// +// Class : PointerTo +// Description : PointerTo is a template class which implements a +// smart pointer to an object derived from +// ReferenceCount. +//////////////////////////////////////////////////////////////////// +template +class PointerTo : public PointerToBase { +public: + INLINE PointerTo(To *ptr = (To *)NULL); + INLINE PointerTo(const PointerTo ©); + + INLINE To &operator *() const; + INLINE To *operator -> () const; + INLINE operator PointerToBase::To *() const; + + // When downcasting to a derived class from a PointerTo, + // C++ would normally require you to cast twice: once to an actual + // BaseClass pointer, and then again to your desired pointer. You + // can use the handy function p() to avoid this first cast and make + // your code look a bit cleaner. + + // e.g. instead of (MyType *)(BaseClass *)ptr, use (MyType *)ptr.p() + + // If your base class is a derivative of TypedObject, you might want + // to use the DCAST macro defined in typeHandle.h instead, + // e.g. DCAST(MyType, ptr). This provides a clean downcast that + // doesn't require .p() or any double-casting, and it can be + // run-time checked for correctness. + INLINE To *p() const; + + INLINE PointerTo &operator = (To *ptr); + INLINE PointerTo &operator = (const PointerTo ©); + + // These functions normally wouldn't need to be redefined here, but + // we do so anyway just to help out interrogate (which doesn't seem + // to want to automatically export the PointerToBase class). When + // this works again in interrogate, we can remove these. + INLINE bool is_null() const { return PointerToBase::is_null(); } + INLINE void clear() { PointerToBase::clear(); } +}; + + +//////////////////////////////////////////////////////////////////// +// Class : ConstPointerTo +// Description : A ConstPointerTo is similar to a PointerTo, except it +// keeps a const pointer to the thing. +// +// (Actually, it keeps a non-const pointer, because it +// must be allowed to adjust the reference counts, and +// it must be able to delete it when the reference count +// goes to zero. But it presents only a const pointer +// to the outside world.) +// +// Notice that a PointerTo may be assigned to a +// ConstPointerTo, but a ConstPointerTo may not be +// assigned to a PointerTo. +//////////////////////////////////////////////////////////////////// +template +class ConstPointerTo : public PointerToBase { +public: + INLINE ConstPointerTo(const To *ptr = (const To *)NULL); + INLINE ConstPointerTo(const PointerTo ©); + INLINE ConstPointerTo(const ConstPointerTo ©); + + INLINE const To &operator *() const; + INLINE const To *operator -> () const; + INLINE operator const PointerToBase::To *() const; + + INLINE const To *p() const; + + INLINE ConstPointerTo &operator = (const To *ptr); + INLINE ConstPointerTo &operator = (const ConstPointerTo ©); + INLINE ConstPointerTo &operator = (const PointerTo ©); + + // These functions normally wouldn't need to be redefined here, but + // we do so anyway just to help out interrogate (which doesn't seem + // to want to automatically export the PointerToBase class). When + // this works again in interrogate, we can remove these. + INLINE bool is_null() const { return PointerToBase::is_null(); } + INLINE void clear() { PointerToBase::clear(); } +}; + + +// Finally, we'll define a couple of handy abbreviations to save on +// all that wasted typing time. + +#define PT(type) PointerTo< type > +#define CPT(type) ConstPointerTo< type > + +#include "pointerTo.I" + +#endif diff --git a/panda/src/express/referenceCount.I b/panda/src/express/referenceCount.I new file mode 100644 index 0000000000..386fed45e1 --- /dev/null +++ b/panda/src/express/referenceCount.I @@ -0,0 +1,384 @@ +// Filename: referenceCount.I +// Created by: drose (23Oct98) +// +//////////////////////////////////////////////////////////////////// + +template +TypeHandle RefCountProxy::_type_handle; + +template +TypeHandle RefCountObj::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ReferenceCount::Constructor +// Access: Protected +// Description: The ReferenceCount constructor is protected because +// you almost never want to create just a ReferenceCount +// object by itself, and it's probably a mistake if you +// try. +// +// ReferenceCount doesn't store any useful information +// in its own right; its only purpose is to add +// reference-counting to some other class via +// inheritance. +//////////////////////////////////////////////////////////////////// +INLINE ReferenceCount:: +ReferenceCount() { + _ref_count = 0; + MemoryUsage::record_pointer(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ReferenceCount::Copy Constructor +// Access: Protected +// Description: The copies of reference-counted objects do not +// themselves inherit the reference count! +// +// This copy constructor is protected because you almost +// never want to create just a ReferenceCount object by +// itself, and it's probably a mistake if you try. +//////////////////////////////////////////////////////////////////// +INLINE ReferenceCount:: +ReferenceCount(const ReferenceCount &) { + _ref_count = 0; + MemoryUsage::record_pointer(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ReferenceCount::Copy Assignment Operator +// Access: Protected +// Description: The copies of reference-counted objects do not +// themselves inherit the reference count! +// +// This copy assignment operator is protected because +// you almost never want to copy just a ReferenceCount +// object by itself, and it's probably a mistake if you +// try. +//////////////////////////////////////////////////////////////////// +INLINE void ReferenceCount:: +operator = (const ReferenceCount &) { + nassertv(this != NULL); + + // If this assertion fails, our own pointer was recently deleted. + // Yikes! + nassertv(_ref_count != -100); +} + +//////////////////////////////////////////////////////////////////// +// Function: ReferenceCount::Destructor +// Access: Protected +// Description: The ReferenceCount destructor is protected to +// discourage users from accidentally trying to delete a +// ReferenceCount pointer directly. This is almost +// always a bad idea, since the destructor is not +// virtual, and you've almost certainly got some pointer +// to something that inherits from ReferenceCount, not +// just a plain old ReferenceCount object. +//////////////////////////////////////////////////////////////////// +INLINE ReferenceCount:: +~ReferenceCount() { + prepare_delete(); + MemoryUsage::remove_pointer(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ReferenceCount::prepare_delete +// Access: Public +// Description: This function is called by the ReferenceCount +// destructor to check on all of the numbers and make +// sure it's all right to delete the object. Normally +// it will never be called explicitly elsewhere, except +// maybe in unref_delete() to simulate deleting the node +// without actually deleting it, when we want to test +// ref count integrities. +//////////////////////////////////////////////////////////////////// +INLINE void ReferenceCount:: +prepare_delete() { + nassertv(this != NULL); + + // If this assertion fails, we're trying to delete an object that + // was just deleted. Probably you've accidentally made a bitwise + // copy of a PointerTo, by forgetting to write a copy constructor + // for a class that contains PointerTo's. + nassertv(_ref_count != -100); + + // If this assertion fails, the reference counts are all screwed + // up altogether. Maybe some errant code stomped all over memory + // somewhere. + nassertv(_ref_count >= 0); + + // If this assertion fails, someone tried to delete this object + // while its reference count was still positive. Maybe you tried + // to point a PointerTo at a static object (a local variable, + // instead of one allocated via new)? The test below against 0x7f + // is supposed to check for that, but it's a pretty hokey test. + + // Another possibility is you inadvertently omitted a copy + // constructor for a ReferenceCount object, and then bitwise + // copied a dynamically allocated value--reference count and + // all--onto a locally allocated one. + nassertv(_ref_count == 0); + + // Ok, all clear to delete. Now set the reference count to -100, + // so we'll have a better chance of noticing if we happen to have + // a stray pointer to it still out there. + _ref_count = -100; +} + +//////////////////////////////////////////////////////////////////// +// Function: ReferenceCount::get_count +// Access: Public +// Description: Returns the current reference count. +//////////////////////////////////////////////////////////////////// +INLINE int ReferenceCount:: +get_count() const { + test_ref_count_integrity(); + return _ref_count; +} + +//////////////////////////////////////////////////////////////////// +// Function: ReferenceCount::ref +// Access: Public +// Description: Explicitly increments the reference count. User code +// should avoid using ref() and unref() directly, which +// can result in missed reference counts. Instead, let +// a PointerTo object manage the reference counting +// automatically. +// +// This function is const, even though it changes the +// object, because generally fiddling with an object's +// reference count isn't considered part of fiddling +// with the object. An object might be const in other +// ways, but we still need to accurately count the +// number of references to it. +//////////////////////////////////////////////////////////////////// +INLINE void ReferenceCount:: +ref() const { + nassertv(this != NULL); + + // If this assertion fails, we're trying to ref a pointer that was + // just deleted. Probably you used a real pointer instead of a + // PointerTo at some point, and the object was deleted when the + // PointerTo went out of scope. Either that, or you forgot to + // define a copy constructor for a class that contains + // PointerTo's. + nassertv(_ref_count != -100); + + // If this assertion fails, the reference counts are all screwed + // up altogether. Maybe some errant code stomped all over memory + // somewhere. + nassertv(_ref_count >= 0); + +#ifdef PENV_SGI + // This is an SGI-specific hack. Maybe this isn't a valid test at + // all. This is supposed to fail if the 'this' pointer is on the + // stack, instead of on the heap--I assume if the first two hex + // digits of the pointer of 0x7f it's on the stack, possibly a bad + // assumption. In any case, if you ref-count a pointer on the + // stack, you just goofed. Don't assign a PointerTo to refer to + // any local variables! + nassertv(((long)this & 0xff000000) != 0x7f000000); +#endif + + ((ReferenceCount *)this)->_ref_count++; + + // If this assertion fails, we have just wrapped! Either there + // are actually 2^32 pointers to this thing somewhere, or + // something is really screwy. + nassertv(_ref_count > 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: ReferenceCount::unref +// Access: Public +// Description: Explicitly decrements the reference count. Note that +// the object will not be implicitly deleted by unref() +// simply because the reference count drops to zero. +// However, see the helper function unref_delete(). +// +// User code should avoid using ref() and unref() +// directly, which can result in missed reference +// counts. Instead, let a PointerTo object manage the +// reference counting automatically. +// +// This function is const, even though it changes the +// object, because generally fiddling with an object's +// reference count isn't considered part of fiddling +// with the object. An object might be const in other +// ways, but we still need to accurately count the +// number of references to it. +//////////////////////////////////////////////////////////////////// +INLINE void ReferenceCount:: +unref() const { + nassertv(this != NULL); + + // If this assertion fails, we're trying to unref a pointer that + // was just deleted. Probably you used a real pointer instead of + // a PointerTo at some point, and the object was deleted when the + // PointerTo went out of scope. Either that, or you forgot to + // define a copy constructor for a class that contains + // PointerTo's. + nassertv(_ref_count != -100); + + // If this assertion fails, the reference counts are all screwed + // up altogether. Maybe some errant code stomped all over memory + // somewhere. + nassertv(_ref_count >= 0); + + // If this assertion fails, you tried to unref an object with a + // zero reference count. Are you using ref() and unref() + // directly? Are you sure you can't use PointerTo's? + nassertv(_ref_count > 0); + ((ReferenceCount *)this)->_ref_count--; +} + + +//////////////////////////////////////////////////////////////////// +// Function: ReferenceCount::test_ref_count_integrity +// Access: Public +// Description: Does some easy checks to make sure that the reference +// count isn't completely bogus. +//////////////////////////////////////////////////////////////////// +INLINE void ReferenceCount:: +test_ref_count_integrity() const { + nassertv(this != NULL); + + // If this assertion fails, we're trying to access a pointer that + // was just deleted. Probably you used a real pointer instead of + // a PointerTo at some point, and the object was deleted when the + // PointerTo went out of scope. Either that, or you forgot to + // define a copy constructor for a class that contains + // PointerTo's. + nassertv(_ref_count != -100); + + // If this assertion fails, the reference counts are all screwed + // up altogether. Maybe some errant code stomped all over memory + // somewhere. + nassertv(_ref_count >= 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: unref_delete +// Description: This global helper function will unref the given +// ReferenceCount object, and if the reference count +// reaches zero, automatically delete it. It can't be a +// member function because it's usually a bad idea to +// delete an object from within its own member function. +// It's a template function so the destructor doesn't +// have to be virtual. +//////////////////////////////////////////////////////////////////// +template +INLINE void +unref_delete(RefCountType *ptr) { + ptr->unref(); + if (ptr->get_count() == 0) { + delete ptr; + + // If we're testing reference counts, it's sometimes useful to do + // the following instead of actually deleting the pointer: + + // ptr->prepare_delete(); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: RefCountProxy::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE RefCountProxy:: +RefCountProxy() { +} + +//////////////////////////////////////////////////////////////////// +// Function: RefCountProxy::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE RefCountProxy:: +RefCountProxy(const Base ©) : _base(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function: RefCountProxy::Base Typecast Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE RefCountProxy:: +operator Base &() { + return _base; +} + +//////////////////////////////////////////////////////////////////// +// Function: RefCountProxy::Base Typecast Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE RefCountProxy:: +operator const Base &() const { + return _base; +} + +//////////////////////////////////////////////////////////////////// +// Function: RefCountProxy::init_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void RefCountProxy:: +init_type() { + do_init_type(Base); + register_type(_type_handle, + "RefCountProxy<" + get_type_handle(Base).get_name() + ">", + get_type_handle(Base)); +} + + +//////////////////////////////////////////////////////////////////// +// Function: RefCountObj::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE RefCountObj:: +RefCountObj() { +} + +//////////////////////////////////////////////////////////////////// +// Function: RefCountObj::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE RefCountObj:: +RefCountObj(const Base ©) : Base(copy) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: RefCountObj::init_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void RefCountObj:: +init_type() { +#ifdef RTTI + // If we have RTTI, we can determine the name of the base type. + string base_name = typeid(Base).name(); +#else + string base_name = "unknown"; +#endif + + TypeHandle base_type = register_dynamic_type(base_name); + + ReferenceCount::init_type(); + _type_handle = + register_dynamic_type("RefCountObj<" + base_name + ">", + base_type, ReferenceCount::get_class_type()); +} diff --git a/panda/src/express/referenceCount.cxx b/panda/src/express/referenceCount.cxx new file mode 100644 index 0000000000..b2aa1d4e90 --- /dev/null +++ b/panda/src/express/referenceCount.cxx @@ -0,0 +1,8 @@ +// Filename: referenceCount.cxx +// Created by: drose (23Oct98) +// +//////////////////////////////////////////////////////////////////// + +#include "referenceCount.h" + +TypeHandle ReferenceCount::_type_handle; diff --git a/panda/src/express/referenceCount.h b/panda/src/express/referenceCount.h new file mode 100644 index 0000000000..9021e24170 --- /dev/null +++ b/panda/src/express/referenceCount.h @@ -0,0 +1,129 @@ +// Filename: referenceCount.h +// Created by: drose (23Oct98) +// +/////////////////////////////////////////////////////////////////////// +// Copyright (C) 1992,93,94,95,96,97,98 Walt Disney Imagineering, Inc. +// +// These coded instructions, statements, data structures and +// computer programs contain unpublished proprietary information of +// Walt Disney Imagineering and are protected by Federal copyright +// law. They may not be disclosed to third parties or copied or +// duplicated in any form, in whole or in part, without the prior +// written consent of Walt Disney Imagineering Inc. +/////////////////////////////////////////////////////////////////////// + +#ifndef REFERENCECOUNT_H +#define REFERENCECOUNT_H + +#include + +#include "typeHandle.h" +#include "memoryUsage.h" + +#include + +#ifdef HAVE_RTTI +#include +#endif + +/////////////////////////////////////////////////////////////////// +// Class : ReferenceCount +// Description : A base class for all things that want to be +// reference-counted. ReferenceCount works in +// conjunction with PointerTo to automatically delete +// objects when the last pointer to them goes away. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS ReferenceCount { +protected: + INLINE ReferenceCount(); + INLINE ReferenceCount(const ReferenceCount &); + INLINE void operator = (const ReferenceCount &); + INLINE ~ReferenceCount(); + +public: + INLINE void prepare_delete(); + + INLINE int get_count() const; + INLINE void ref() const; + INLINE void unref() const; + + INLINE void test_ref_count_integrity() const; + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + register_type(_type_handle, "ReferenceCount"); + } + +private: + int _ref_count; + static TypeHandle _type_handle; +}; + +template +INLINE void unref_delete(RefCountType *ptr); + +/////////////////////////////////////////////////////////////////// +// Class : RefCountProxy +// Description : A "proxy" to use to make a reference-countable object +// whenever the object cannot inherit from +// ReferenceCount for some reason. RefCountPr +// can be treated as an instance of MyClass directly, +// for the most part, except that it can be reference +// counted. +// +// If you want to declare a RefCountProxy to something +// that does not have get_class_type(), you will have to +// define a template specialization on +// _get_type_handle() and _do_init_type(), as in +// typeHandle.h. +//////////////////////////////////////////////////////////////////// +template +class EXPCL_PANDAEXPRESS RefCountProxy : public ReferenceCount { +public: + INLINE RefCountProxy(); + INLINE RefCountProxy(const Base ©); + + INLINE operator Base &(); + INLINE operator const Base &() const; + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + Base _base; + static TypeHandle _type_handle; +}; + + +/////////////////////////////////////////////////////////////////// +// Class : RefCountObj +// Description : Another kind of proxy, similar to RefCountProxy. +// This one works by inheriting from the indicated base +// type, giving it an is-a relation instead of a has-a +// relation. As such, it's a little more robust, but +// only works when the base type is, in fact, a class. +//////////////////////////////////////////////////////////////////// +template +class EXPCL_PANDAEXPRESS RefCountObj : public Base, public ReferenceCount { +public: + INLINE RefCountObj(); + INLINE RefCountObj(const Base ©); + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + + + +#include "referenceCount.I" + +#endif diff --git a/panda/src/express/tokenBoard.I b/panda/src/express/tokenBoard.I new file mode 100644 index 0000000000..1afa4e9751 --- /dev/null +++ b/panda/src/express/tokenBoard.I @@ -0,0 +1,73 @@ +// Filename: tokenBoard.I +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// Copyright (C) 1992,93,94,95,96,97,98 +// Walt Disney Imagineering, Inc. +// +// These coded instructions, statements, data structures and +// computer programs contain unpublished proprietary information of +// Walt Disney Imagineering and are protected by Federal copyright +// law. They may not be disclosed to third parties or copied or +// duplicated in any form, in whole or in part, without the prior +// written consent of Walt Disney Imagineering Inc. +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TokenBoard::is_done_token() +// Access: Public +// Description: Returns true if the indicated token id is on the +// done queue, false otherwise. +//////////////////////////////////////////////////////////////////// +template +bool TokenBoard:: +is_done_token(int id) { + // First, empty the done list, copying them to really_done. We have + // to do this since we can only examine tokens on the head of the + // done list, and the token we're looking for might not be at the + // head. + while (!_done.is_empty()) { + _really_done.push_back(_done.extract()); + } + + // Now we can search really_done for our desired id. + list::iterator found; + found = find_if(_really_done.begin(), _really_done.end(), + TokenMatch(id)); + + return (found != _really_done.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: TokenBoard::get_done_token +// Access: Public +// Description: Locates the token by the given id in the list of done +// tokens, removes it from the list, and returns its +// pointer (which should be deleted by the calling +// function). Returns NULL if the token was not on the +// done list. +//////////////////////////////////////////////////////////////////// +template +PT(TokenType) TokenBoard:: +get_done_token(int id) { + // First, empty the done list, copying them to really_done. We have + // to do this since we can only examine tokens on the head of the + // done list, and the token we're looking for might not be at the + // head. + while (!_done.is_empty()) { + _really_done.push_back(_done.extract()); + } + + // Now we can search really_done for our desired id. + list::iterator found; + found = find_if(_really_done.begin(), _really_done.end(), + TokenMatch(id)); + + if (found == _really_done.end()) { + return NULL; + } else { + PT(TokenType) tok = *found; + _really_done.erase(found); + return tok; + } +} diff --git a/panda/src/express/tokenBoard.h b/panda/src/express/tokenBoard.h new file mode 100644 index 0000000000..356030fec2 --- /dev/null +++ b/panda/src/express/tokenBoard.h @@ -0,0 +1,66 @@ +// Filename: tokenBoard.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef TOKENBOARD_H +#define TOKENBOARD_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include +#include +#include "circBuffer.h" +#include "pointerTo.h" + +//////////////////////////////////////////////////////////////////// +// Struct : TokenMatch +// Description : This is an STL predicate function object that is used +// to find a particular id in a list of token pointers. +// It returns true when the token's id matches the +// numeric id supplied to the constructor. +//////////////////////////////////////////////////////////////////// +template +class TokenMatch { +public: + TokenMatch(int id) { + _want_id = id; + } + bool operator()(PT(TokenType) tok) const { + return tok->_id == _want_id; + } + int _want_id; +}; + +const int MAX_TOKENBOARD_REQUESTS = 100; + +//////////////////////////////////////////////////////////////////// +// Class : TokenBoard +// Description : +//////////////////////////////////////////////////////////////////// +template +class TokenBoard { +public: + bool is_done_token(int id); + PT(TokenType) get_done_token(int id); + + // waiting holds the list of requests sent to the DBASE process, not + // yet handled. + CircBuffer _waiting; + + // done holds the list of requests handled by the DBASE process, but + // not yet discovered by APP. Probably this queue will only have + // one item at a time on it. + CircBuffer _done; + + // really_done holds the requests extracted from done. These are + // extracted into the local list so we can safely search for and + // remove a particular token from the middle of the list (we can + // only remove from the head of a circular buffer). + list _really_done; +}; + +#include "tokenBoard.I" + +#endif diff --git a/panda/src/express/trueClock.I b/panda/src/express/trueClock.I new file mode 100644 index 0000000000..2f2b44a731 --- /dev/null +++ b/panda/src/express/trueClock.I @@ -0,0 +1,28 @@ +// Filename: trueClock.I +// Created by: drose (04Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TrueClock::get_ptr +// Access: Public +// Description: Returns a pointer to the one TrueClock object in +// the world. +//////////////////////////////////////////////////////////////////// +INLINE TrueClock *TrueClock:: +get_ptr() { + if (_global_ptr == (TrueClock *)NULL) { + _global_ptr = new TrueClock; + } + return _global_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: TrueClock::Destructor +// Access: Protected +// Description: A protected destructor because no one should try to +// delete the global TrueClock. +//////////////////////////////////////////////////////////////////// +INLINE TrueClock:: +~TrueClock() { +} diff --git a/panda/src/express/trueClock.cxx b/panda/src/express/trueClock.cxx new file mode 100644 index 0000000000..ff63fed473 --- /dev/null +++ b/panda/src/express/trueClock.cxx @@ -0,0 +1,190 @@ +// Filename: trueClock.cxx +// Created by: drose (04Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "trueClock.h" +#include "config_express.h" +#include "numeric_types.h" + +TrueClock *TrueClock::_global_ptr = NULL; + + +#if defined(WIN32_VC) + +//////////////////////////////////////////////////////////////////// +// +// The Win32 implementation. +// +//////////////////////////////////////////////////////////////////// + +#include + +#define WINDOWS_LEAN_AND_MEAN +#include +#include +#undef WINDOWS_LEAN_AND_MEAN + +static BOOL _has_high_res; +static PN_int64 _frequency; +static PN_int64 _init_count; +static long _init_sec; + + +double TrueClock:: +get_real_time() const { + if (_has_high_res) { + LARGE_INTEGER count; + QueryPerformanceCounter(&count); + + if (_init_count > count.QuadPart) + _init_count = count.QuadPart; + + if (_frequency < 0) { + express_cat.error() + << "TrueClock::get_real_time() - frequency is negative!" << endl; + QueryPerformanceFrequency((LARGE_INTEGER *)&_frequency); + } + return (double)(count.QuadPart - _init_count) / (double)_frequency; + + } else { + // No high-resolution clock; return the best information we have. + struct timeb tb; + ftime(&tb); + return (double)(tb.time - _init_sec) + (double)tb.millitm / 1000.0; + } +} + +TrueClock:: +TrueClock() { + _has_high_res = QueryPerformanceFrequency((LARGE_INTEGER *)&_frequency); + + if (_has_high_res) { + QueryPerformanceCounter((LARGE_INTEGER *)&_init_count); + + } else { + express_cat.warning() + << "No high resolution clock available." << endl; + + struct timeb tb; + ftime(&tb); + _init_sec = tb.time; + } +} + + + +#elif defined(PENV_PS2) + +//////////////////////////////////////////////////////////////////// +// +// The PS2 implementation. +// +//////////////////////////////////////////////////////////////////// + +#include +#include + +static unsigned int _msec; +static unsigned int _sec; + +// PS2 timer interrupt, as the RTC routines don't exist unless you're +// using the .irx iop compiler, which scares us. A lot. +static int +timer_handler(int) { + _msec++; + + if (_msec >= 1000) { + _msec = 0; + _sec++; + } + + return -1; +} + +double TrueClock:: +get_real_time() const { + return (double) _sec + ((double) _msec / 1000.0); +} + +TrueClock:: +TrueClock() { + _init_sec = 0; + _msec = 0; + _sec = 0; + + tT_MODE timer_mode; + *(unsigned int *) &timer_mode = 0; + + timer_mode.cxxLKS = 1; + timer_mode.ZRET = 1; + timer_mode.cxxUE = 1; + timer_mode.cxxMPE = 1; + timer_mode.EQUF = 1; + + *T0_COMP = 9375; + *T0_MODE = *(unsigned int *) &timer_mode; + + EnableIntc(INTC_TIM0); + AddIntcHandler(INTC_TIM0, timer_handler, -1); +} + + +#elif !defined(WIN32) + +//////////////////////////////////////////////////////////////////// +// +// The Posix implementation. +// +//////////////////////////////////////////////////////////////////// + +#include +#include // for perror + +static long _init_sec; + + +double TrueClock:: +get_real_time() const { + struct timeval tv; + + int result; + +#ifdef GETTIMEOFDAY_ONE_PARAM + result = gettimeofday(&tv); +#else + result = gettimeofday(&tv, (struct timezone *)NULL); +#endif + + if (result < 0) { + // Error in gettimeofday(). + return 0.0; + } + + // We subtract out the time at which the clock was initialized, + // because we don't care about the number of seconds all the way + // back to 1970, and we want to leave the double with as much + // precision as it can get. + return (double)(tv.tv_sec - _init_sec) + (double)tv.tv_usec / 1000000.0; +} + +TrueClock:: +TrueClock() { + struct timeval tv; + + int result; +#ifdef GETTIMEOFDAY_ONE_PARAM + result = gettimeofday(&tv); +#else + result = gettimeofday(&tv, (struct timezone *)NULL); +#endif + + if (result < 0) { + perror("gettimeofday"); + _init_sec = 0; + } else { + _init_sec = tv.tv_sec; + } +} + +#endif diff --git a/panda/src/express/trueClock.h b/panda/src/express/trueClock.h new file mode 100644 index 0000000000..0db2d6d346 --- /dev/null +++ b/panda/src/express/trueClock.h @@ -0,0 +1,39 @@ +// Filename: trueClock.h +// Created by: drose (04Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TRUECLOCK_H +#define TRUECLOCK_H + +#include + +//////////////////////////////////////////////////////////////////// +// Class : TrueClock +// Description : An interface to whatever real-time clock we might +// have available in the current environment. There is +// only one TrueClock in existence, and it constructs +// itself. +// +// The TrueClock returns elapsed real time in seconds +// since some undefined epoch. Since it is not defined +// at what time precisely the clock indicates zero, this +// value can only be meaningfully used to measure +// elapsed time, by sampling it at two different times +// and subtracting. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS TrueClock { +public: + INLINE static TrueClock *get_ptr(); + double get_real_time() const; + +protected: + TrueClock(); + INLINE ~TrueClock(); + + static TrueClock *_global_ptr; +}; + +#include "trueClock.I" + +#endif diff --git a/panda/src/express/typeHandle.I b/panda/src/express/typeHandle.I new file mode 100644 index 0000000000..66ee3fcd3f --- /dev/null +++ b/panda/src/express/typeHandle.I @@ -0,0 +1,473 @@ +// Filename: typeHandle.I +// Created by: drose (22Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_express.h" + +// In general, we use the express_cat->info() syntax in this file +// (instead of express_cat.info()), because much of this work is done at +// static init time, and we must use the arrow syntax to force +// initialization of the express_cat category. + +//////////////////////////////////////////////////////////////////// +// Function: TypeHandle::Constructor +// Access: Public +// Description: The default constructor must do nothing, because we +// can't guarantee ordering of static initializers. If +// the constructor tried to initialize its value, it +// might happen after the value had already been set +// previously by another static initializer! +//////////////////////////////////////////////////////////////////// +INLINE TypeHandle:: +TypeHandle() { +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeHandle::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypeHandle:: +TypeHandle(const TypeHandle ©) : _index(copy._index) { +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeHandle::Equality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool TypeHandle:: +operator == (const TypeHandle &other) const { + return (_index == other._index); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeHandle::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool TypeHandle:: +operator != (const TypeHandle &other) const { + return (_index != other._index); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeHandle::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool TypeHandle:: +operator < (const TypeHandle &other) const { + return (_index < other._index); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeHandle::get_name +// Access: Public +// Description: Returns the name of the type. +// +// The "object" pointer is an optional pointer to the +// TypedObject class that owns this TypeHandle. It is +// only used in case the TypeHandle is inadvertantly +// undefined. +//////////////////////////////////////////////////////////////////// +INLINE string TypeHandle:: +get_name(TypedObject *object) const { + if ((*this) == TypeHandle::none()) { + return "none"; + } else { + return TypeRegistry::ptr()->get_name(*this, object); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeHandle::is_derived_from +// Access: Public +// Description: Returns true if this type is derived from the +// indicated type, false otherwise. +// +// The "object" pointer is an optional pointer to the +// TypedObject class that owns this TypeHandle. It is +// only used in case the TypeHandle is inadvertantly +// undefined. +//////////////////////////////////////////////////////////////////// +INLINE bool TypeHandle:: +is_derived_from(TypeHandle parent, TypedObject *object) const { + return TypeRegistry::ptr()->is_derived_from(*this, parent, object); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeHandle::get_num_parent_classes +// Access: Public +// Description: Returns the number of parent classes that this +// type is known to have. This may then be used to +// index into get_parent_class(). The result will be 0 +// if this class does not inherit from any other +// classes, 1 if normal, single inheritance is in +// effect, or greater than one if multiple inheritance +// is in effect. +// +// The "object" pointer is an optional pointer to the +// TypedObject class that owns this TypeHandle. It is +// only used in case the TypeHandle is inadvertantly +// undefined. +//////////////////////////////////////////////////////////////////// +INLINE int TypeHandle:: +get_num_parent_classes(TypedObject *object) const { + return TypeRegistry::ptr()->get_num_parent_classes(*this, object); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeHandle::get_num_parent_classes +// Access: Public +// Description: Returns the nth parent class of this type. The index +// should be in the range 0 <= index < +// get_num_parent_classes(). +//////////////////////////////////////////////////////////////////// +INLINE TypeHandle TypeHandle:: +get_parent_class(int index) const { + return TypeRegistry::ptr()->get_parent_class(*this, index); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeHandle::get_num_child_classes +// Access: Public +// Description: Returns the number of child classes that this +// type is known to have. This may then be used to +// index into get_child_class(). +// +// The "object" pointer is an optional pointer to the +// TypedObject class that owns this TypeHandle. It is +// only used in case the TypeHandle is inadvertantly +// undefined. +//////////////////////////////////////////////////////////////////// +INLINE int TypeHandle:: +get_num_child_classes(TypedObject *object) const { + return TypeRegistry::ptr()->get_num_child_classes(*this, object); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeHandle::get_num_child_classes +// Access: Public +// Description: Returns the nth child class of this type. The index +// should be in the range 0 <= index < +// get_num_child_classes(). +//////////////////////////////////////////////////////////////////// +INLINE TypeHandle TypeHandle:: +get_child_class(int index) const { + return TypeRegistry::ptr()->get_child_class(*this, index); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeHandle::get_parent_towards +// Access: Public +// Description: Returns the parent class that is in a direct line of +// inheritance to the indicated ancestor class. This is +// useful in the presence of multiple inheritance to try +// to determine what properties an unknown type may +// have. +// +// The return value is TypeHandle::none() if the type +// does not inherit from the ancestor. If ancestor is +// the same as this type, the return value is this type. +// +// The "object" pointer is an optional pointer to the +// TypedObject class that owns this TypeHandle. It is +// only used in case the TypeHandle is inadvertantly +// undefined. +//////////////////////////////////////////////////////////////////// +INLINE TypeHandle TypeHandle:: +get_parent_towards(TypeHandle ancestor, TypedObject *object) const { + return TypeRegistry::ptr()->get_parent_towards(*this, ancestor, object); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeHandle::get_index +// Access: Public +// Description: Returns the integer index associated with this +// TypeHandle. Each different TypeHandle will have a +// different index. However, you probably shouldn't be +// using this method; you should just treat the +// TypeHandles as opaque classes. This is provided for +// the convenience of non-C++ scripting languages to +// build a hashtable of TypeHandles. +//////////////////////////////////////////////////////////////////// +INLINE int TypeHandle:: +get_index() const { + return _index; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeHandle::none +// Access: Public, Static +// Description: Returns a special zero-valued TypeHandle that is used +// to indicate no type. +//////////////////////////////////////////////////////////////////// +INLINE TypeHandle TypeHandle:: +none() { + return _none; +} + + +//////////////////////////////////////////////////////////////////// +// Function: register_type +// Description: This inline function is just a convenient way to call +// TypeRegistry::register_type(), along with zero to four +// record_derivation()s. If for some reason you have a +// class that has more than four base classes (you're +// insane!), then you will need to call Register() and +// record_derivation() yourself. +//////////////////////////////////////////////////////////////////// +INLINE void +register_type(TypeHandle &type_handle, const string &name) { + TypeRegistry::ptr()->register_type(type_handle, name); +} +INLINE void +register_type(TypeHandle &type_handle, const string &name, + TypeHandle parent1) { + if (TypeRegistry::ptr()->register_type(type_handle, name)) { + TypeRegistry::ptr()->record_derivation(type_handle, parent1); + } +} +INLINE void +register_type(TypeHandle &type_handle, const string &name, + TypeHandle parent1, TypeHandle parent2) { + if (TypeRegistry::ptr()->register_type(type_handle, name)) { + TypeRegistry::ptr()->record_derivation(type_handle, parent1); + TypeRegistry::ptr()->record_derivation(type_handle, parent2); + } +} +INLINE void +register_type(TypeHandle &type_handle, const string &name, + TypeHandle parent1, TypeHandle parent2, + TypeHandle parent3) { + if (TypeRegistry::ptr()->register_type(type_handle, name)) { + TypeRegistry::ptr()->record_derivation(type_handle, parent1); + TypeRegistry::ptr()->record_derivation(type_handle, parent2); + TypeRegistry::ptr()->record_derivation(type_handle, parent3); + } +} +INLINE void +register_type(TypeHandle &type_handle, const string &name, + TypeHandle parent1, TypeHandle parent2, + TypeHandle parent3, TypeHandle parent4) { + if (TypeRegistry::ptr()->register_type(type_handle, name)) { + TypeRegistry::ptr()->record_derivation(type_handle, parent1); + TypeRegistry::ptr()->record_derivation(type_handle, parent2); + TypeRegistry::ptr()->record_derivation(type_handle, parent3); + TypeRegistry::ptr()->record_derivation(type_handle, parent4); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: register_dynamic_type +// Description: This is essentially similar to register_type(), +// except that it doesn't store a reference to any +// TypeHandle passed in and it therefore doesn't +// complain if the type is registered more than once to +// different TypeHandle reference. +//////////////////////////////////////////////////////////////////// +INLINE TypeHandle +register_dynamic_type(const string &name) { + return TypeRegistry::ptr()->register_dynamic_type(name); +} +INLINE TypeHandle +register_dynamic_type(const string &name, TypeHandle parent1) { + TypeHandle type_handle = + TypeRegistry::ptr()->register_dynamic_type(name); + TypeRegistry::ptr()->record_derivation(type_handle, parent1); + return type_handle; +} +INLINE TypeHandle +register_dynamic_type(const string &name, + TypeHandle parent1, TypeHandle parent2) { + TypeHandle type_handle = + TypeRegistry::ptr()->register_dynamic_type(name); + TypeRegistry::ptr()->record_derivation(type_handle, parent1); + TypeRegistry::ptr()->record_derivation(type_handle, parent2); + return type_handle; +} +INLINE TypeHandle +register_dynamic_type(const string &name, + TypeHandle parent1, TypeHandle parent2, + TypeHandle parent3) { + TypeHandle type_handle = + TypeRegistry::ptr()->register_dynamic_type(name); + TypeRegistry::ptr()->record_derivation(type_handle, parent1); + TypeRegistry::ptr()->record_derivation(type_handle, parent2); + TypeRegistry::ptr()->record_derivation(type_handle, parent3); + return type_handle; +} +INLINE TypeHandle +register_dynamic_type(const string &name, + TypeHandle parent1, TypeHandle parent2, + TypeHandle parent3, TypeHandle parent4) { + TypeHandle type_handle = + TypeRegistry::ptr()->register_dynamic_type(name); + TypeRegistry::ptr()->record_derivation(type_handle, parent1); + TypeRegistry::ptr()->record_derivation(type_handle, parent2); + TypeRegistry::ptr()->record_derivation(type_handle, parent3); + TypeRegistry::ptr()->record_derivation(type_handle, parent4); + return type_handle; +} + + +//////////////////////////////////////////////////////////////////// +// Function: TypedObject::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypedObject:: +TypedObject() { +} + +//////////////////////////////////////////////////////////////////// +// Function: TypedObject::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypedObject:: +TypedObject(const TypedObject &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: TypedObject::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TypedObject:: +operator = (const TypedObject &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: TypedObject::is_of_type +// Access: Public +// Description: Returns true if the current object is or derives from +// the indicated type. +//////////////////////////////////////////////////////////////////// +INLINE bool TypedObject:: +is_of_type(TypeHandle handle) const { + return get_type().is_derived_from(handle, (TypedObject *)this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypedObject::is_exact_type +// Access: Public +// Description: Returns true if the current object is the indicated +// type exactly. +//////////////////////////////////////////////////////////////////// +INLINE bool TypedObject:: +is_exact_type(TypeHandle handle) const { +#ifndef NDEBUG + // Call get_name() to force the type to look itself up if necessary. + get_type().get_name((TypedObject *)this); +#endif + return get_type() == handle; +} + + +//////////////////////////////////////////////////////////////////// +// Function: _dcast_get_typehandle +// Description: Returns the TypeHandle associated with the type of +// the parameter, if it can be determined. This is a +// support function for _dcast, below. +//////////////////////////////////////////////////////////////////// +template +INLINE TypeHandle +_dcast_get_typehandle(WantType *) { + TypeHandle handle = WantType::get_class_type(); + if (handle == TypeHandle::none()) { + // This type handle is unregistered. Oops! + WantType::init_type(); + handle = WantType::get_class_type(); + express_cat->warning() + << "Type " << handle << " was unregistered!\n"; + } + return handle; +} + + +//////////////////////////////////////////////////////////////////// +// Function: _dcast +// Description: The implementation of the DCAST macro, this checks +// the actual type of the pointer before performing a +// downcast operation. In NDEBUG mode, it simply +// downcasts. +// +// This flavor of _dcast works on non-const pointers. +//////////////////////////////////////////////////////////////////// +template +INLINE WantType * +_dcast(WantType *, TypedObject *ptr) { +#ifndef NDEBUG + TypeHandle want_handle = _dcast_get_typehandle((WantType *)0); + if (ptr == (TypedObject *)NULL) { + express_cat->warning() + << "Attempt to cast NULL pointer to " << want_handle << "\n"; + return (WantType *)NULL; + } + if (!ptr->is_of_type(want_handle)) { + express_cat->error() + << "Attempt to cast pointer from " << ptr->get_type() + << " to " << want_handle << "\n"; + if (ptr->get_type() == TypedObject::get_class_type()) { + express_cat->error(false) + << "Perhaps pointer was inadvertently deleted?\n"; + } + return (WantType *)NULL; + } +#endif + return (WantType *)ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: _dcast +// Description: The implementation of the DCAST macro, this checks +// the actual type of the pointer before performing a +// downcast operation. In NDEBUG mode, it simply +// downcasts. +// +// This flavor of _dcast works on const pointers. +//////////////////////////////////////////////////////////////////// +template +INLINE const WantType * +_dcast(WantType *, const TypedObject *ptr) { +#ifndef NDEBUG + TypeHandle want_handle = _dcast_get_typehandle((WantType *)0); + if (ptr == (const TypedObject *)NULL) { + express_cat->warning() + << "Attempt to cast NULL pointer to " << want_handle << "\n"; + return (const WantType *)NULL; + } + if (!ptr->is_of_type(want_handle)) { + express_cat->error() + << "Attempt to cast pointer from " << ptr->get_type() + << " to " << want_handle << "\n"; + if (ptr->get_type() == TypedObject::get_class_type()) { + express_cat->error(false) + << "Perhaps pointer was inadvertently deleted?\n"; + } + return (const WantType *)NULL; + } +#endif + return (const WantType *)ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: _dcast_ref +// Description: Similar to the above, with a pointer reference as the +// first parameter. Just for fiddly compiler reasons; +// the reference isn't used. +//////////////////////////////////////////////////////////////////// +template +INLINE WantType * +_dcast_ref(WantType *&, TypedObject *ptr) { + return _dcast((WantType *)NULL, ptr); +} + +template +INLINE const WantType * +_dcast_ref(WantType *&, const TypedObject *ptr) { + return _dcast((WantType *)NULL, ptr); +} diff --git a/panda/src/express/typeHandle.cxx b/panda/src/express/typeHandle.cxx new file mode 100644 index 0000000000..d15d5fcec8 --- /dev/null +++ b/panda/src/express/typeHandle.cxx @@ -0,0 +1,669 @@ +// Filename: typeHandle.cxx +// Created by: drose (23Oct98) +// +//////////////////////////////////////////////////////////////////// + +#include "typeHandle.h" +#include "indent.h" +#include "config_express.h" + +#include + +// In general, we use the express_cat->info() syntax in this file +// (instead of express_cat.info()), because much of this work is done at +// static init time, and we must use the arrow syntax to force +// initialization of the express_cat category. + +TypeRegistry *TypeRegistry::_global_pointer = NULL; + +TypeHandle TypedObject::_type_handle; + +// This is initialized to zero by static initialization. +TypeHandle TypeHandle::_none; + + +TypeHandle long_type_handle; +TypeHandle int_type_handle; +TypeHandle short_type_handle; +TypeHandle char_type_handle; +TypeHandle bool_type_handle; +TypeHandle double_type_handle; +TypeHandle float_type_handle; + +TypeHandle long_p_type_handle; +TypeHandle int_p_type_handle; +TypeHandle short_p_type_handle; +TypeHandle char_p_type_handle; +TypeHandle bool_p_type_handle; +TypeHandle double_p_type_handle; +TypeHandle float_p_type_handle; +TypeHandle void_p_type_handle; + +void init_system_type_handles() { + static bool done = false; + if (!done) { + done = true; + register_type(long_type_handle, "long"); + register_type(int_type_handle, "int"); + register_type(short_type_handle, "short"); + register_type(char_type_handle, "char"); + register_type(bool_type_handle, "bool"); + register_type(double_type_handle, "double"); + register_type(float_type_handle, "float"); + + register_type(int_p_type_handle, "int*"); + register_type(short_p_type_handle, "short*"); + register_type(char_p_type_handle, "char*"); + register_type(bool_p_type_handle, "bool*"); + register_type(double_p_type_handle, "double*"); + register_type(float_p_type_handle, "float*"); + register_type(void_p_type_handle, "void*"); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::RegistryNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +TypeRegistry::RegistryNode:: +RegistryNode(TypeHandle handle, const string &name, TypeHandle &ref) : + _handle(handle), _name(name), _ref(ref) { +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::register_type +// Access: Public +// Description: Creates a new Type of the given name and assigns a +// unique value to the type_handle. All type names must +// be unique. If the type name has already been used, +// the supplied type_handle value must match the name's +// assigned type_handle or an error is triggered. +// Returns true if the name wasn't defined before, false +// if it was. +//////////////////////////////////////////////////////////////////// +bool TypeRegistry:: +register_type(TypeHandle &type_handle, const string &name) { + if (type_handle != TypeHandle::none()) { + // Here's a type that was already registered. Just make sure + // everything's still kosher. + RegistryNode *rnode = look_up(type_handle, NULL); + if (&type_handle == &rnode->_ref) { + // No problem. + nassertr(rnode->_name == name, false); + return false; + } + } + + NameRegistry::iterator ri; + ri = _name_registry.find(name); + + if (ri == _name_registry.end()) { + // The name was not already used; this is the first time this + // class has been defined. + + if (express_cat->is_spam()) { + express_cat->spam() << "Registering type " << name << "\n"; + } + + TypeHandle new_handle; + new_handle._index = _handle_registry.size(); + + RegistryNode *rnode = new RegistryNode(new_handle, name, type_handle); + _handle_registry.push_back(rnode); + _name_registry[name] = rnode; + + type_handle = new_handle; + return true; + } + RegistryNode *rnode = (*ri).second; + nassertr(rnode->_name == (*ri).first, false); + nassertr(rnode->_handle._index >= 0 && + rnode->_handle._index < _handle_registry.size(), false); + nassertr(_handle_registry[rnode->_handle._index] == rnode, false); + nassertr(rnode->_handle._index != 0, false); + + // The name was previously used; make sure the type_handle matches. + if (&type_handle == &rnode->_ref) { + // Ok, this was just a repeated attempt to register the same type. + + if (type_handle == rnode->_handle) { + // No problem. + return false; + } + // But wait--the type_handle has changed! We kept a reference to + // the static _type_handle member in the class that was passed in + // at the first call to register_type(), and we got the same + // reference passed in this time, but now it's different! Bad + // juju. + express_cat->error() + << "Reregistering " << name << "\n"; + type_handle == rnode->_handle; + return false; + } + + if (type_handle != rnode->_handle) { + // Hmm, we seem to have a contradictory type registration! + express_cat->warning() + << "Attempt to register type " << name << " more than once!\n"; + + // This is invalid, but we'll allow it anyway. It seems to happen + // for some reason under GNU libc5 that we occasionally end up + // with two legitimate copies of the same class object in + // memory--each with its own static _type_handle member. + + type_handle = rnode->_handle; + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::register_dynamic_type +// Access: Public +// Description: Registers a new type on-the-fly, presumably at +// runtime. A new TypeHandle is returned if the +// typename was not seen before; otherwise the same +// TypeHandle that was last used for this typename is +// returned. +//////////////////////////////////////////////////////////////////// +TypeHandle TypeRegistry:: +register_dynamic_type(const string &name) { + NameRegistry::iterator ri; + ri = _name_registry.find(name); + + if (ri == _name_registry.end()) { + // The name was not already used; this is the first time this + // class has been defined. + + if (express_cat->is_spam()) { + express_cat->spam() << "Registering type " << name << "\n"; + } + + // We must dynamically allocate a new handle so the RegistryNode + // has something unique to point to. This doesn't really mean + // anything, though. + TypeHandle *new_handle = new TypeHandle; + new_handle->_index = _handle_registry.size(); + + RegistryNode *rnode = new RegistryNode(*new_handle, name, *new_handle); + _handle_registry.push_back(rnode); + _name_registry[name] = rnode; + + return *new_handle; + } + + // Return the TypeHandle previously obtained. + RegistryNode *rnode = (*ri).second; + return rnode->_handle; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::record_derivation +// Access: Public +// Description: Records that the type referenced by child inherits +// directly from the type referenced by parent. In the +// event of multiple inheritance, this should be called +// once for each parent class. +//////////////////////////////////////////////////////////////////// +void TypeRegistry:: +record_derivation(TypeHandle child, TypeHandle parent) { + RegistryNode *cnode = look_up(child, NULL); + nassertv(cnode != (RegistryNode *)NULL); + RegistryNode *pnode = look_up(parent, NULL); + nassertv(pnode != (RegistryNode *)NULL); + + // First, we'll just run through the list to make sure we hadn't + // already made this connection. + RegistryNode::Classes::iterator ni; + ni = find(cnode->_parent_classes.begin(), cnode->_parent_classes.end(), + pnode); + + if (ni == cnode->_parent_classes.end()) { + cnode->_parent_classes.push_back(pnode); + pnode->_child_classes.push_back(cnode); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::find_type +// Access: Public +// Description: Looks for a previously-registered type of the given +// name. Returns its TypeHandle if it exists, or +// TypeHandle::none() if there is no such type. +//////////////////////////////////////////////////////////////////// +TypeHandle TypeRegistry:: +find_type(const string &name) const { + NameRegistry::const_iterator ri; + ri = _name_registry.find(name); + if (ri == _name_registry.end()) { + return TypeHandle::none(); + } else { + return (*ri).second->_handle; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::get_name +// Access: Public +// Description: Returns the name of the indicated type. +// +// The "object" pointer is an optional pointer to the +// TypedObject class that owns this TypeHandle. It is +// only used in case the TypeHandle is inadvertantly +// undefined. +//////////////////////////////////////////////////////////////////// +string TypeRegistry:: +get_name(TypeHandle type, TypedObject *object) const { + RegistryNode *rnode = look_up(type, object); + nassertr(rnode != (RegistryNode *)NULL, ""); + return rnode->_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::is_derived_from +// Access: Public +// Description: Returns true if the first type is derived from the +// second type, false otherwise. +// +// The "object" pointer is an optional pointer to the +// TypedObject class that owns this TypeHandle. It is +// only used in case the TypeHandle is inadvertantly +// undefined. +//////////////////////////////////////////////////////////////////// +bool TypeRegistry:: +is_derived_from(TypeHandle child, TypeHandle parent, + TypedObject *child_object) const { + RegistryNode *rnode = look_up(child, child_object); + nassertr(rnode != (RegistryNode *)NULL, false); + return rnode->is_derived_from(parent); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::get_num_root_classes +// Access: Public +// Description: Returns the number of root classes--that is, classes +// that do not inherit from any other classes--known in +// the system. +//////////////////////////////////////////////////////////////////// +int TypeRegistry:: +get_num_root_classes() { + freshen_root_classes(); + return _root_classes.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::get_root_class +// Access: Public +// Description: Returns the nth root class in the system. See +// get_num_root_classes(). +//////////////////////////////////////////////////////////////////// +TypeHandle TypeRegistry:: +get_root_class(int n) { + freshen_root_classes(); + nassertr(n >= 0 && n < get_num_root_classes(), TypeHandle::none()); + return _root_classes[n]->_handle; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::get_num_parent_classes +// Access: Public +// Description: Returns the number of parent classes that the +// indicated type is known to have. This may then be +// used to index into get_parent_class(). The result +// will be 0 if this class does not inherit from any +// other classes, 1 if normal, single inheritance is in +// effect, or greater than one if multiple inheritance +// is in effect. +// +// The "object" pointer is an optional pointer to the +// TypedObject class that owns this TypeHandle. It is +// only used in case the TypeHandle is inadvertantly +// undefined. +//////////////////////////////////////////////////////////////////// +int TypeRegistry:: +get_num_parent_classes(TypeHandle child, TypedObject *child_object) const { + RegistryNode *rnode = look_up(child, child_object); + nassertr(rnode != (RegistryNode *)NULL, 0); + return rnode->_parent_classes.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::get_parent_class +// Access: Public +// Description: Returns the nth parent class of this type. The index +// should be in the range 0 <= index < +// get_num_parent_classes(). +//////////////////////////////////////////////////////////////////// +TypeHandle TypeRegistry:: +get_parent_class(TypeHandle child, int index) const { + RegistryNode *rnode = look_up(child, (TypedObject *)NULL); + nassertr(rnode != (RegistryNode *)NULL, TypeHandle::none()); + nassertr(index >= 0 && index < rnode->_parent_classes.size(), + TypeHandle::none()); + return rnode->_parent_classes[index]->_handle; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::get_num_child_classes +// Access: Public +// Description: Returns the number of child classes that the +// indicated type is known to have. This may then be +// used to index into get_child_class(). +// +// The "object" pointer is an optional pointer to the +// TypedObject class that owns this TypeHandle. It is +// only used in case the TypeHandle is inadvertantly +// undefined. +//////////////////////////////////////////////////////////////////// +int TypeRegistry:: +get_num_child_classes(TypeHandle child, TypedObject *child_object) const { + RegistryNode *rnode = look_up(child, child_object); + nassertr(rnode != (RegistryNode *)NULL, 0); + return rnode->_child_classes.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::get_child_class +// Access: Public +// Description: Returns the nth child class of this type. The index +// should be in the range 0 <= index < +// get_num_child_classes(). +//////////////////////////////////////////////////////////////////// +TypeHandle TypeRegistry:: +get_child_class(TypeHandle child, int index) const { + RegistryNode *rnode = look_up(child, (TypedObject *)NULL); + nassertr(rnode != (RegistryNode *)NULL, TypeHandle::none()); + nassertr(index >= 0 && index < rnode->_child_classes.size(), + TypeHandle::none()); + return rnode->_child_classes[index]->_handle; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::get_parent_towards +// Access: Public +// Description: Returns the parent of the indicated child class that +// is in a direct line of inheritance to the indicated +// ancestor class. This is useful in the presence of +// multiple inheritance to try to determine what +// properties an unknown type may have. +// +// The "object" pointer is an optional pointer to the +// TypedObject class that owns this TypeHandle. It is +// only used in case the TypeHandle is inadvertantly +// undefined. +//////////////////////////////////////////////////////////////////// +TypeHandle TypeRegistry:: +get_parent_towards(TypeHandle child, TypeHandle ancestor, + TypedObject *child_object) const { + if (child_object != (TypedObject *)NULL) { + // First, guarantee that the ancestor type is defined. + look_up(ancestor, child_object); + } + RegistryNode *rnode = look_up(child, child_object); + nassertr(rnode != (RegistryNode *)NULL, TypeHandle::none()); + return rnode->get_parent_towards(ancestor); +} + + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::reregister_types +// Access: Public, Static +// Description: Walks through the TypeRegistry tree and makes sure +// that each type that was previously registered is +// *still* registered. This seems to get broken in +// certain circumstances when compiled against libc5--it +// is as if the static initializer stomps on the +// _type_handle values of each class after they've been +// registered. +//////////////////////////////////////////////////////////////////// +void TypeRegistry:: +reregister_types() { + HandleRegistry::iterator ri; + TypeRegistry *reg = ptr(); + for (ri = reg->_handle_registry.begin(); + ri != reg->_handle_registry.end(); + ++ri) { + RegistryNode *rnode = (*ri); + if (rnode != NULL && rnode->_handle != rnode->_ref) { + express_cat->warning() + << "Reregistering " << rnode->_name << "\n"; + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::write +// Access: Public +// Description: Makes an attempt to format the entire TypeRegistry in +// a nice way that shows the derivation tree as +// intelligently as possible. +//////////////////////////////////////////////////////////////////// +void TypeRegistry:: +write(ostream &out) const { + // Recursively write out the tree, starting from each node that has + // no parent. + HandleRegistry::const_iterator hi; + for (hi = _handle_registry.begin(); + hi != _handle_registry.end(); + ++hi) { + const RegistryNode *root = *hi; + if (root != NULL && root->_parent_classes.empty()) { + write_node(out, 2, root); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::ptr +// Access: Public, Static +// Description: Returns the pointer to the global TypeRegistry +// object. +//////////////////////////////////////////////////////////////////// +TypeRegistry *TypeRegistry:: +ptr() { + if (_global_pointer == NULL) { + init_global_pointer(); + } + return _global_pointer; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::Constructor +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +TypeRegistry:: +TypeRegistry() { + // We'll start out our handle_registry with a default entry for the + // TypeHandles whose index number is zero, and are therefore + // (probably) uninitialized. + _handle_registry.push_back(NULL); + + _root_classes_fresh = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::init_global_pointer +// Access: Private, Static +// Description: Constructs the TypeRegistry object for the first +// time. It is initially created on the local heap, +// then as soon as shared memory becomes available, it +// should be moved into shared memory. +//////////////////////////////////////////////////////////////////// +void TypeRegistry:: +init_global_pointer() { + _global_pointer = new TypeRegistry; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::freshen_root_classes +// Access: Private +// Description: Walks through the list of types registered, and adds +// any type known to be a root (that is, that derives +// from no other types) to the set of known root +// classes. This must be done from time to time because +// we don't record this information when we initially +// record the types. +//////////////////////////////////////////////////////////////////// +void TypeRegistry:: +freshen_root_classes() { + if (!_root_classes_fresh) { + _root_classes.clear(); + + HandleRegistry::iterator hi; + for (hi = _handle_registry.begin(); + hi != _handle_registry.end(); + ++hi) { + RegistryNode *root = *hi; + if (root != NULL && root->_parent_classes.empty()) { + _root_classes.push_back(root); + } + } + _root_classes_fresh = true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::write_node +// Access: Private +// Description: Writes a single RegistryNode out, along with all of +// its descendants. +//////////////////////////////////////////////////////////////////// +void TypeRegistry:: +write_node(ostream &out, int indent_level, const RegistryNode *node) const { + indent(out, indent_level) << node->_handle.get_index() << " " << node->_name; + if (!node->_parent_classes.empty()) { + out << " : " << node->_parent_classes[0]->_name; + for (int pi = 1; pi < node->_parent_classes.size(); pi++) { + out << ", " << node->_parent_classes[pi]->_name; + } + } + out << "\n"; + + for (int i = 0; i < node->_child_classes.size(); i++) { + write_node(out, indent_level + 2, node->_child_classes[i]); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::look_up +// Access: Private +// Description: Returns the RegistryNode associated with the +// indicated TypeHandle. If there is no associated +// RegistryNode, reports an error condition and aborts. +// +// The associated TypedObject pointer is the pointer to +// the object that owns the handle, if available. It is +// only used in an error condition, if for some reason +// the handle was uninitialized. +//////////////////////////////////////////////////////////////////// +TypeRegistry::RegistryNode *TypeRegistry:: +look_up(TypeHandle handle, TypedObject *object) const { + if (handle._index == 0) { + // The TypeHandle is unregistered. This is an error condition. + + if (object != NULL) { + // But we're lucky enough to have a TypedObject pointer handy! + // Maybe we can use it to resolve the error. + handle = object->force_init_type(); + if (handle._index == 0) { + // Strange. + express_cat->error() + << "Unable to force_init_type() on unregistered TypeHandle.\n"; + nassertr(false, NULL); + } + if (handle == object->get_type()) { + // Problem solved! + express_cat->warning() + << "Type " << handle << " was unregistered!\n"; + } else { + // No good; it looks like the TypeHandle belongs to a class + // that defined get_type(), but didn't define + // force_init_type(). + express_cat->error() + << "Attempt to reference unregistered TypeHandle. Type is of some\n" + << "class derived from " << handle << " that doesn't define a good\n" + << "force_init_type() method.\n"; + nassertr(false, NULL); + } + + } else { + // We don't have a TypedObject pointer, so there's nothing we + // can do about it. + express_cat->error() + << "Attempt to reference unregistered TypeHandle!\n" + << "Registered TypeHandles are:\n"; + write(express_cat->error(false)); + nassertr(false, NULL); + } + } + + if (handle._index < 0 || + handle._index >= _handle_registry.size()) { + express_cat->fatal() + << "Invalid TypeHandle index " << handle._index + << "! Is memory corrupt?\n"; + nassertr(false, NULL); + } + + return _handle_registry[handle._index]; +} + + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::RegistryNode::is_derived_from +// Access: Public +// Description: Returns true if the current RegistryNode represents a +// class of type TypeHandle, or any of its ancestors do. +//////////////////////////////////////////////////////////////////// +bool TypeRegistry::RegistryNode:: +is_derived_from(TypeHandle type) const { + if (_handle == type) { + return true; + } + + Classes::const_iterator ni; + for (ni = _parent_classes.begin(); ni != _parent_classes.end(); ++ni) { + if ((*ni)->is_derived_from(type)) { + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeRegistry::RegistryNode::get_parent_towards +// Access: Public +// Description: Returns the first derived class that is an descendant +// of the indicated ancestor class. +//////////////////////////////////////////////////////////////////// +TypeHandle TypeRegistry::RegistryNode:: +get_parent_towards(TypeHandle type) const { + if (_handle == type) { + return type; + } + + Classes::const_iterator ni; + for (ni = _parent_classes.begin(); ni != _parent_classes.end(); ++ni) { + if ((*ni)->is_derived_from(type)) { + return (*ni)->_handle; + } + } + + return TypeHandle::none(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: TypedObject::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +TypedObject:: +~TypedObject() { +} diff --git a/panda/src/express/typeHandle.h b/panda/src/express/typeHandle.h new file mode 100644 index 0000000000..a696e1ccb7 --- /dev/null +++ b/panda/src/express/typeHandle.h @@ -0,0 +1,571 @@ +// Filename: typeHandle.h +// Created by: drose (23Oct98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TYPEHANDLE_H +#define TYPEHANDLE_H + +#include + +#include + +#include +#include +#include +#include +#include + +// The following illustrates the convention for declaring a type that +// uses TypeHandle. In this example, ThisThingie inherits from +// TypedObject, which automatically supplies some type-differentiation +// functions at the cost of one virtual function, get_type(); however, +// this inheritance is optional, and may be omitted to avoid the +// virtual function pointer overhead. (If you do use TypedObject, be +// sure to consider whether your destructor should also be virtual.) + +// +// class ThatThingie : public SimpleTypedObject { +// public: +// static TypeHandle get_class_type() { +// return _type_handle; +// } +// static void init_type() { +// register_type(_type_handle, "ThatThingie"); +// } +// +// private: +// static TypeHandle _type_handle; +// }; +// +// class ThisThingie : public ThatThingie, publid TypedObject { +// public: +// static TypeHandle get_class_type() { +// return _type_handle; +// } +// static void init_type() { +// ThatThingie::init_type(); +// TypedObject::init_type(); +// register_type(_type_handle, "ThisThingie", +// ThatThingie::get_class_type(), +// TypedObject::get_class_type()); +// } +// virtual TypeHandle get_type() const { +// return get_class_type(); +// } +// +// private: +// static TypeHandle _type_handle; +// }; +// + +class TypedObject; + +//////////////////////////////////////////////////////////////////// +// Class : TypeHandle +// Description : TypeHandle is the identifier used to differentiate +// C++ class types. Any C++ classes that inherit from +// some base class, and must be differentiated at run +// time, should store a static TypeHandle object that +// can be queried through a static member function +// named get_class_type(). Most of the time, it is also +// desirable to inherit from TypedObject (defined +// below), which provides some virtual functions to +// return the TypeHandle for a particular instance. +// +// At its essence, a TypeHandle is simply a unique +// identifier that is assigned by the TypeRegistry. The +// TypeRegistry stores a tree of TypeHandles, so that +// ancestry of a particular type may be queried, and the +// type name may be retrieved for run-time display. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS TypeHandle { +public: + INLINE TypeHandle(); + INLINE TypeHandle(const TypeHandle ©); + + INLINE bool operator == (const TypeHandle &other) const; + INLINE bool operator != (const TypeHandle &other) const; + INLINE bool operator < (const TypeHandle &other) const; + + INLINE string get_name(TypedObject *object = (TypedObject *)NULL) const; + INLINE bool is_derived_from(TypeHandle parent, + TypedObject *object = (TypedObject *)NULL) const; + + INLINE int get_num_parent_classes(TypedObject *object = (TypedObject *)NULL) const; + INLINE TypeHandle get_parent_class(int index) const; + + INLINE int get_num_child_classes(TypedObject *object = (TypedObject *)NULL) const; + INLINE TypeHandle get_child_class(int index) const; + + INLINE TypeHandle get_parent_towards(TypeHandle ancestor, + TypedObject *object = (TypedObject *)NULL) const; + + INLINE int get_index() const; + + INLINE static TypeHandle none(); + +private: + int _index; + static TypeHandle _none; + +friend class TypeRegistry; +}; + + +// It's handy to be able to output a TypeHandle directly, and see the +// type name. +INLINE ostream &operator << (ostream &out, TypeHandle type) { + return out << type.get_name(); +} + +//////////////////////////////////////////////////////////////////// +// Class : TypeRegistry +// Description : The TypeRegistry class maintains all the assigned +// TypeHandles in a given system. There should be only +// one TypeRegistry class during the lifetime of the +// application. It will be created on the local heap +// initially, and it should be migrated to shared memory +// as soon as shared memory becomes available. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS TypeRegistry { +protected: + class RegistryNode { + public: + RegistryNode(TypeHandle handle, const string &name, TypeHandle &ref); + bool is_derived_from(TypeHandle type) const; + TypeHandle get_parent_towards(TypeHandle type) const; + + TypeHandle _handle; + string _name; + TypeHandle &_ref; + typedef vector Classes; + Classes _parent_classes; + Classes _child_classes; + }; + +public: + // User code shouldn't generally need to call + // TypeRegistry::register_type() or record_derivation() directly; + // instead, use the register_type convenience function, defined + // below. + bool register_type(TypeHandle &type_handle, const string &name); + TypeHandle register_dynamic_type(const string &name); + + void record_derivation(TypeHandle child, TypeHandle parent); + + TypeHandle find_type(const string &name) const; + + string get_name(TypeHandle type, TypedObject *object) const; + bool is_derived_from(TypeHandle child, TypeHandle parent, + TypedObject *child_object) const; + + int get_num_root_classes(); + TypeHandle get_root_class(int n); + + int get_num_parent_classes(TypeHandle child, + TypedObject *child_object) const; + TypeHandle get_parent_class(TypeHandle child, int index) const; + + int get_num_child_classes(TypeHandle child, + TypedObject *child_object) const; + TypeHandle get_child_class(TypeHandle child, int index) const; + + TypeHandle get_parent_towards(TypeHandle child, TypeHandle ancestor, + TypedObject *child_object) const; + + static void reregister_types(); + + void write(ostream &out) const; + + // ptr() returns the pointer to the global TypeRegistry object. + static TypeRegistry *ptr(); + +private: + // The TypeRegistry class should never be constructed by user code. + // There is only one in the universe, and it constructs itself! + TypeRegistry(); + + static void init_global_pointer(); + + RegistryNode *look_up(TypeHandle type, TypedObject *object) const; + + void freshen_root_classes(); + void write_node(ostream &out, int indent_level, + const RegistryNode *node) const; + + typedef vector HandleRegistry; + HandleRegistry _handle_registry; + + typedef map NameRegistry; + NameRegistry _name_registry; + + typedef vector RootClasses; + bool _root_classes_fresh; + RootClasses _root_classes; + + static TypeRegistry *_global_pointer; +}; + + +//////////////////////////////////////////////////////////////////// +// Function: register_type +// Description: This inline function is just a convenient way to call +// TypeRegistry::register_type(), along with zero to four +// record_derivation()s. If for some reason you have a +// class that has more than four base classes (you're +// insane!), then you will need to call Register() and +// record_derivation() yourself. +//////////////////////////////////////////////////////////////////// +INLINE void +register_type(TypeHandle &type_handle, const string &name); + +INLINE void +register_type(TypeHandle &type_handle, const string &name, + TypeHandle parent1); + +INLINE void +register_type(TypeHandle &type_handle, const string &name, + TypeHandle parent1, TypeHandle parent2); + +INLINE void +register_type(TypeHandle &type_handle, const string &name, + TypeHandle parent1, TypeHandle parent2, + TypeHandle parent3); + +INLINE void +register_type(TypeHandle &type_handle, const string &name, + TypeHandle parent1, TypeHandle parent2, + TypeHandle parent3, TypeHandle parent4); + + +//////////////////////////////////////////////////////////////////// +// Function: register_dynamic_type +// Description: This is essentially similar to register_type(), +// except that it doesn't store a reference to any +// TypeHandle passed in and it therefore doesn't +// complain if the type is registered more than once to +// different TypeHandle reference. +//////////////////////////////////////////////////////////////////// +INLINE TypeHandle +register_dynamic_type(const string &name); + +INLINE TypeHandle +register_dynamic_type(const string &name, TypeHandle parent1); + +INLINE TypeHandle +register_dynamic_type(const string &name, + TypeHandle parent1, TypeHandle parent2); + +INLINE TypeHandle +register_dynamic_type(const string &name, + TypeHandle parent1, TypeHandle parent2, + TypeHandle parent3); + +INLINE TypeHandle +register_dynamic_type(const string &name, + TypeHandle parent1, TypeHandle parent2, + TypeHandle parent3, TypeHandle parent4); + + + +//////////////////////////////////////////////////////////////////// +// Class : TypedObject +// Description : This is an abstract class that all classes which +// use TypeHandle, and also provide virtual functions to +// support polymorphism, should inherit from. Each +// derived class should define get_type(), which should +// return the specific type of the derived class. +// Inheriting from this automatically provides support +// for is_of_type() and is_exact_type(). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS TypedObject { +public: + INLINE TypedObject(); + INLINE TypedObject(const TypedObject ©); + INLINE void operator = (const TypedObject ©); + + // A virtual destructor is just a good idea. + virtual ~TypedObject(); + + // Derived classes should override this function to return + // get_class_type(). + virtual TypeHandle get_type() const=0; + + // Derived classes should override this function to call + // init_type(). It will only be called in error situations when the + // type was for some reason not properly initialized. + virtual TypeHandle force_init_type()=0; + + INLINE bool is_of_type(TypeHandle handle) const; + INLINE bool is_exact_type(TypeHandle handle) const; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + register_type(_type_handle, "TypedObject"); + } + +private: + static TypeHandle _type_handle; +}; + +// The DCAST (downcast) macro is defined as a convenience for +// downcasting from some TypedObject pointer (or a PointerTo). It's +// just a normal C++-style downcast, except it first checks get_type() +// to make sure the downcasting is safe. If you compile with NDEBUG, +// this check is removed. + +// DCAST will return NULL if the downcasting is unsafe. If you'd +// rather it abort out of the function (ala nassertv/nassertr), then +// see DCAST_INTO_V and DCAST_INTO_R, below. + +template +INLINE WantType *_dcast(WantType *, TypedObject *ptr); +template +INLINE const WantType *_dcast(WantType *, const TypedObject *ptr); + +// Note: it is important that DCAST not repeat the pointer parameter, +// since many users of DCAST may want to use the result of a function +// as the pointer parameter, and it could be terribly confusing and +// difficult to trace if the function was executed twice. This +// happened! +#define DCAST(want_type, pointer) _dcast((want_type*)0, pointer) + +// DCAST_INTO_V and DCAST_INTO_R are similar in purpose to DCAST, +// except they: (a) automatically assign a variable instead of +// returning the downcasted pointer, and (b) they immediately return +// out of the function if the downcasting fails. DCAST_INTO_V is for +// use in a void function and returns nothing; DCAST_INTO_R is for use +// in a non-void function and returns the indicated value. + +// Both DCAST_INTO_V and DCAST_INTO_R accept as the first parameter a +// variable of type (want_type *) or (const want_type *), instead of +// the name of the type. This variable will be filled with the new +// pointer. + + +// _dcast_ref is used to implement DCAST_INTO_V and DCAST_INTO_R. Its +// difference from _dcast is that it takes a reference to a pointer as +// a first parameter. The main point of this is to shut up the +// compiler about pointers used before their value is assigned. +template +INLINE WantType *_dcast_ref(WantType *&, TypedObject *ptr); +template +INLINE const WantType *_dcast_ref(WantType *&, const TypedObject *ptr); + + +#define DCAST_INTO_V(to_pointer, from_pointer) \ + { \ + (to_pointer) = _dcast_ref(to_pointer, from_pointer); \ + nassertv((void *)(to_pointer) != (void *)NULL); \ + } + +#define DCAST_INTO_R(to_pointer, from_pointer, return_value) \ + { \ + (to_pointer) = _dcast_ref(to_pointer, from_pointer); \ + nassertr((void *)(to_pointer) != (void *)NULL, return_value); \ + } + +// A few system-wide TypeHandles are defined for some basic types. +extern TypeHandle EXPCL_PANDAEXPRESS long_type_handle; +extern TypeHandle EXPCL_PANDAEXPRESS int_type_handle; +extern TypeHandle EXPCL_PANDAEXPRESS short_type_handle; +extern TypeHandle EXPCL_PANDAEXPRESS char_type_handle; +extern TypeHandle EXPCL_PANDAEXPRESS bool_type_handle; +extern TypeHandle EXPCL_PANDAEXPRESS double_type_handle; +extern TypeHandle EXPCL_PANDAEXPRESS float_type_handle; + +extern TypeHandle long_p_type_handle; +extern TypeHandle int_p_type_handle; +extern TypeHandle short_p_type_handle; +extern TypeHandle char_p_type_handle; +extern TypeHandle bool_p_type_handle; +extern TypeHandle double_p_type_handle; +extern TypeHandle float_p_type_handle; +extern TypeHandle void_p_type_handle; + +void EXPCL_PANDAEXPRESS init_system_type_handles(); + +// The following template function and its specializations will return +// a TypeHandle for any type in the world, from a pointer to that +// type. + +template +INLINE TypeHandle _get_type_handle(const T *) { + return T::get_class_type(); +} + +template<> +INLINE TypeHandle _get_type_handle(const long *) { + return long_type_handle; +} + +template<> +INLINE TypeHandle _get_type_handle(const int *) { + return int_type_handle; +} + +template<> +INLINE TypeHandle _get_type_handle(const short *) { + return short_type_handle; +} + +template<> +INLINE TypeHandle _get_type_handle(const char *) { + return char_type_handle; +} + +template<> +INLINE TypeHandle _get_type_handle(const bool *) { + return bool_type_handle; +} + +template<> +INLINE TypeHandle _get_type_handle(const double *) { + return double_type_handle; +} + +template<> +INLINE TypeHandle _get_type_handle(const float *) { + return float_type_handle; +} + +template<> +INLINE TypeHandle _get_type_handle(const long * const *) { + return long_p_type_handle; +} + +template<> +INLINE TypeHandle _get_type_handle(const int * const *) { + return int_p_type_handle; +} + +template<> +INLINE TypeHandle _get_type_handle(const short * const *) { + return short_p_type_handle; +} + +template<> +INLINE TypeHandle _get_type_handle(const char * const *) { + return char_p_type_handle; +} + +template<> +INLINE TypeHandle _get_type_handle(const bool * const *) { + return bool_p_type_handle; +} + +template<> +INLINE TypeHandle _get_type_handle(const double * const *) { + return double_p_type_handle; +} + +template<> +INLINE TypeHandle _get_type_handle(const float * const *) { + return float_p_type_handle; +} + +template<> +INLINE TypeHandle _get_type_handle(const void * const *) { + return void_p_type_handle; +} + + +// The macro get_type_handle(type) is defined to make getting the type +// handle associated with a particular type a bit cleaner. +#define get_type_handle(type) _get_type_handle((const type *)0) + + +// The following template function and its specializations can be used +// to call init() on any unknown type. Handy for use within a +// template class. + +template +INLINE void _do_init_type(const T *) { + T::init_type(); +} + +template<> +INLINE void _do_init_type(const long *) { + init_system_type_handles(); +} + +template<> +INLINE void _do_init_type(const int *) { + init_system_type_handles(); +} + +template<> +INLINE void _do_init_type(const short *) { + init_system_type_handles(); +} + +template<> +INLINE void _do_init_type(const char *) { + init_system_type_handles(); +} + +template<> +INLINE void _do_init_type(const bool *) { + init_system_type_handles(); +} + +template<> +INLINE void _do_init_type(const double *) { + init_system_type_handles(); +} + +template<> +INLINE void _do_init_type(const float *) { + init_system_type_handles(); +} + +template<> +INLINE void _do_init_type(const long * const *) { + init_system_type_handles(); +} + +template<> +INLINE void _do_init_type(const int * const *) { + init_system_type_handles(); +} + +template<> +INLINE void _do_init_type(const short * const *) { + init_system_type_handles(); +} + +template<> +INLINE void _do_init_type(const char * const *) { + init_system_type_handles(); +} + +template<> +INLINE void _do_init_type(const bool * const *) { + init_system_type_handles(); +} + +template<> +INLINE void _do_init_type(const double * const *) { + init_system_type_handles(); +} + +template<> +INLINE void _do_init_type(const float * const *) { + init_system_type_handles(); +} + +template<> +INLINE void _do_init_type(const void * const *) { + init_system_type_handles(); +} + +#define do_init_type(type) _do_init_type((const type *)0) + +#include "typeHandle.I" + +#endif + diff --git a/panda/src/express/typedReferenceCount.I b/panda/src/express/typedReferenceCount.I new file mode 100644 index 0000000000..ab681a0271 --- /dev/null +++ b/panda/src/express/typedReferenceCount.I @@ -0,0 +1,39 @@ +// Filename: typedReferenceCount.I +// Created by: drose (25May00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: TypedReferenceCount::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypedReferenceCount:: +TypedReferenceCount() { + MemoryUsage::update_type(this, TypedReferenceCount::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypedReferenceCount::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypedReferenceCount:: +TypedReferenceCount(const TypedReferenceCount ©) : + TypedObject(copy), + ReferenceCount(copy) +{ + MemoryUsage::update_type(this, TypedReferenceCount::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypedReferenceCount::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TypedReferenceCount:: +operator = (const TypedReferenceCount ©) { + TypedObject::operator = (copy); + ReferenceCount::operator = (copy); +} diff --git a/panda/src/express/typedReferenceCount.cxx b/panda/src/express/typedReferenceCount.cxx new file mode 100644 index 0000000000..885fb747fa --- /dev/null +++ b/panda/src/express/typedReferenceCount.cxx @@ -0,0 +1,7 @@ +// Filename: typedReferenceCount.cxx +// Created by: drose (08Feb99) +// + +#include "typedReferenceCount.h" + +TypeHandle TypedReferenceCount::_type_handle; diff --git a/panda/src/express/typedReferenceCount.h b/panda/src/express/typedReferenceCount.h new file mode 100644 index 0000000000..be2c5b6548 --- /dev/null +++ b/panda/src/express/typedReferenceCount.h @@ -0,0 +1,52 @@ +// Filename: typedReferenceCount.h +// Created by: drose (08Feb99) +// + +#ifndef TYPEDREFERENCECOUNT_H +#define TYPEDREFERENCECOUNT_H + +#include + +#include "typeHandle.h" +#include "referenceCount.h" + +//////////////////////////////////////////////////////////////////// +// Class : TypedReferenceCount +// Description : A base class for things which need to inherit from +// both TypedObject and from ReferenceCount. It's +// convenient to define this intermediate base class +// instead of multiply inheriting from the two classes +// each time they are needed, so that we can sensibly +// pass around pointers to things which are both +// TypedObjects and ReferenceCounters. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS TypedReferenceCount : public TypedObject, public ReferenceCount { +public: + INLINE TypedReferenceCount(); + INLINE TypedReferenceCount(const TypedReferenceCount ©); + INLINE void operator = (const TypedReferenceCount ©); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedObject::init_type(); + ReferenceCount::init_type(); + register_type(_type_handle, "TypedReferenceCount", + TypedObject::get_class_type(), + ReferenceCount::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "typedReferenceCount.I" + +#endif + diff --git a/panda/src/express/typedef.h b/panda/src/express/typedef.h new file mode 100644 index 0000000000..2a482c2f72 --- /dev/null +++ b/panda/src/express/typedef.h @@ -0,0 +1,29 @@ +// Filename: typedef.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef TYPEDEF_H +#define TYPEDEF_H +// +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +#ifndef NPOS + #define NPOS string::npos +#endif + +// Declare the namespace std in case it's not already. +namespace std { +}; + +// Let's always be in the std namespace unless we specify otherwise. +using namespace std; + + +#endif diff --git a/panda/src/framework/Sources.pp b/panda/src/framework/Sources.pp new file mode 100644 index 0000000000..90c7f37e7c --- /dev/null +++ b/panda/src/framework/Sources.pp @@ -0,0 +1,16 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin static_lib_target + #define TARGET framework + #define LOCAL_LIBS \ + putil collide loader sgmanip chan text chancfg cull \ + pnmimage pnmimagetypes event + + #define SOURCES \ + config_framework.cxx config_framework.h framework.cxx framework.h + + #define INSTALL_HEADERS \ + framework.h + +#end static_lib_target + diff --git a/panda/src/framework/config_framework.cxx b/panda/src/framework/config_framework.cxx new file mode 100644 index 0000000000..5d8f54926f --- /dev/null +++ b/panda/src/framework/config_framework.cxx @@ -0,0 +1,19 @@ +// Filename: config_framework.cxx +// Created by: drose (06Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_framework.h" + +#include + +Configure(config_framework); +NotifyCategoryDef(framework, ""); + +ConfigureFn(config_framework) { +} + +// This is the height above the ground your eye should maintain while +// driving using the "D" interface. +const double drive_height = config_framework.GetDouble("drive-height", 6.0); +const CollideMask drive_mask = config_framework.GetInt("drive-mask", ~0); diff --git a/panda/src/framework/config_framework.h b/panda/src/framework/config_framework.h new file mode 100644 index 0000000000..de10c5f272 --- /dev/null +++ b/panda/src/framework/config_framework.h @@ -0,0 +1,19 @@ +// Filename: config_framework.h +// Created by: drose (06Sep00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_FRAMEWORK_H +#define CONFIG_FRAMEWORK_H + +#include +#include +#include + +NotifyCategoryDecl(framework, EXPCL_FRAMEWORK, EXPTP_FRAMEWORK); + +// Configure variables for framework package. +extern const double EXPCL_FRAMEWORK drive_height; +extern const CollideMask EXPCL_FRAMEWORK drive_mask; + +#endif diff --git a/panda/src/framework/framework.cxx b/panda/src/framework/framework.cxx new file mode 100644 index 0000000000..09565c45a5 --- /dev/null +++ b/panda/src/framework/framework.cxx @@ -0,0 +1,1177 @@ +// Filename: framework.cxx +// Created by: cary (25Mar99) +// +//////////////////////////////////////////////////////////////////// + +// We need to include bitMask.h first to avoid a VC++ compiler bug +// related to 2 parameter templates +#include + +#include "framework.h" +#include "config_framework.h" + +#include +// Since framework.cxx includes pystub.h, no program that links with +// framework needs to do so. No Python code should attempt to link +// with libframework.so. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_IPC +#include +#include +#endif + +/* +#ifdef PENV_WIN32 +#include +#else +#include +#endif +*/ + +Configure(framework); + +ConfigureFn(framework) { +} + +AppTraverser *app_traverser; +PT_NamedNode data_root; +PT_NamedNode root; +PT(GeomNode) geomnode; +PT_NamedNode render; +PT_NamedNode cameras; +PT(MouseAndKeyboard) mak; +PT(MouseWatcher) mouse_watcher; +PT(Trackball) trackball; +PT(DriveInterface) drive_interface; + +static Node *current_trackball = NULL; +static Node *alt_trackball = NULL; + +NodeAttributes initial_state; +Texture* ttex; +PT(GraphicsPipe) main_pipe; +PT(GraphicsPipe) rib_pipe; +PT(GraphicsWindow) main_win; +PT(GraphicsWindow) rib_win; +RenderRelation* first_arc; + +PT_NamedNode lights; + +PT(AmbientLight) light; +PT(DirectionalLight) dlight; +bool have_dlight = false; +PT(PointLight) plight; +PT(Spotlight) slight; + +PT(Material) material; + +PT(Fog) fog; + +// Framerate vars + +PT_NamedNode framerate_top; +RenderRelation *framerate_arc = (RenderRelation*)0L; +PT_NamedNode framerate_node; + +PT(GraphicsLayer) framerate_layer; +PT_Node framerate_font; +PT(TextNode) framerate_text; + +Loader loader; + +EventHandler event_handler(EventQueue::get_global_event_queue()); + +std::string chan_config = "single"; + +static double start_time = 0.0; +static int start_frame_count = 0; + +void (*extra_display_func)() = NULL; +void (*define_keys)(EventHandler&) = NULL; +void (*extra_overrides_func)(ChanCfgOverrides&, std::string&) = NULL; +void (*first_init)() = NULL; +void (*additional_idle)() = NULL; + +#ifdef USE_IPC +static bool forked_draw = framework.GetBool("fork-draw", false); +static mutex run_render; +static bool render_running = true; +static bool quit_draw = false; +static thread* draw_thread; +#endif + +static CollisionTraverser *col_trans = NULL; +static CollisionHandlerFloor *col_handler = NULL; +static CollisionNode *ray_node = NULL; + +class GeomNorms : public GeomLine +{ +public: + GeomNorms(void) : GeomLine() {} + virtual Geom *explode() const { + return new GeomNorms(*this); + } + + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + GeomLine::init_type(); + register_type(_type_handle, "GeomNorms", + GeomLine::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +TypeHandle GeomNorms::_type_handle; + +// Since the Normal*Traversers don't care about state, we don't need +// to accumulate the RenderTransitions, so it will template on +// NullTransition. +class NormalAddTraverser : + public TraverserVisitor { +public: + NormalAddTraverser(GraphicsStateGuardian *gsg) : _gsg(gsg) {} + bool reached_node(Node*, NullAttributeWrapper&, NullLevelState&); + + // No need to declare a forward_arc() function that simply returns + // true; this is the default behavior. + +public: + GraphicsStateGuardian *_gsg; +}; + +bool NormalAddTraverser:: +reached_node(Node *node, NullAttributeWrapper &, NullLevelState &) { + if (node->is_of_type(GeomNode::get_class_type())) { + GeomNorms *gn = new GeomNorms; + GeomNode *geom = DCAST(GeomNode, node); + int vert_count = 0; + int i; + for (i = 0; i < geom->get_num_geoms(); i++) { + dDrawable *d = geom->get_geom(i); + if (d->is_of_type(Geom::get_class_type())) { + Geom *g = DCAST(Geom, d); + for (int j=0; jget_num_prims(); ++j) + vert_count += g->get_length(j); + } + } + if (vert_count > 0) { + PTA_Vertexf verts(2 * vert_count); + for (i = 0; i < geom->get_num_geoms(); i++) { + dDrawable *d = geom->get_geom(i); + if (d->is_of_type(Geom::get_class_type())) { + PTA_Vertexf lverts; + PTA_ushort iverts; + GeomBindType vbond; + Geom *g = DCAST(Geom, d); + g->get_coords(lverts, vbond, iverts); + int vert_idx = 0; + if (g->get_binding(G_NORMAL) == G_OFF) { + for (int j=0; jget_num_prims(); ++j) { + for (int k=0; kget_length(j); ++k, ++vert_idx) { + verts[2 * vert_idx] = lverts[vert_idx]; + verts[(2 * vert_idx) + 1] = lverts[vert_idx]; + } + } + } else { + PTA_Normalf lnorms; + PTA_ushort inorms; + GeomBindType nbond; + g->get_normals(lnorms, nbond, inorms); + for (int j=0; jget_num_prims(); ++j) { + for (int k=0; kget_length(j); ++k, ++vert_idx) { + verts[2 * vert_idx] = lverts[vert_idx]; + verts[(2 * vert_idx) + 1] = lverts[vert_idx] + + lnorms[vert_idx]; + } + } + } + } + } + gn->set_num_prims(vert_count); + gn->set_coords(verts, G_PER_VERTEX); + } + geom->add_geom(gn); + } + return true; +} + +class NormalDelTraverser : + public TraverserVisitor { +public: + NormalDelTraverser(GraphicsStateGuardian *gsg) : _gsg(gsg) {} + bool reached_node(Node*, NullAttributeWrapper&, NullLevelState&); +public: + GraphicsStateGuardian *_gsg; +}; + +bool NormalDelTraverser:: +reached_node(Node *node, NullAttributeWrapper &, NullLevelState &) { + if (node->is_of_type(GeomNode::get_class_type())) { + GeomNode *geom = DCAST(GeomNode, node); + int i, j; + do { + for (i = 0, j = -1; + i < geom->get_num_geoms(); + ++i) { + if (geom->get_geom(i)->is_of_type(GeomNorms::get_class_type())) { + j = i; + } + } + if (j != -1) { + geom->remove_geom(j); + } + } while (j != -1); + } + return true; +} + +void render_frame(GraphicsPipe *pipe) { + app_traverser->traverse(render); + int num_windows = pipe->get_num_windows(); + for (int w = 0; w < num_windows; w++) { + GraphicsWindow *win = pipe->get_window(w); + win->get_gsg()->render_frame(initial_state); + } + ClockObject::get_global_clock()->tick(); + throw_event("NewFrame"); +} + +// to be used with new display callback system +class DisplayCallback : public GraphicsWindow::Callback { + public: + virtual void draw(bool) { + render_frame(main_pipe); + if (extra_display_func != NULL) + extra_display_func(); + } +}; + +// to be used with old GLUT callback system +void display_func( void ) { + render_frame(main_pipe); + if (extra_display_func != NULL) + extra_display_func(); +} + +void set_lighting(bool enabled) { + if (enabled) { + // Enable the lights on the initial state. + LightAttribute *la = new LightAttribute; + la->set_on(light.p()); + + if (have_dlight) { + la->set_on(dlight.p()); + } + initial_state.set_attribute(LightTransition::get_class_type(), la); + + } else { + // Remove the lights from the initial state. + initial_state.clear_attribute(LightTransition::get_class_type()); + } +} + +// to be used with new display callback system +class IdleCallback : public GraphicsWindow::Callback { + public: + virtual void idle(void) { + // Initiate the data traversal, to send device data down its + // respective pipelines. + traverse_data_graph(data_root); + + // Perform the collision traversal, if we have a collision + // traverser standing by. + if (col_trans != (CollisionTraverser *)NULL) { + col_trans->traverse(render); + } + + // Throw any events generated recently. + event_handler.process_events(); + + if (additional_idle != NULL) { + (*additional_idle)(); + } + } +}; + +// to be used with old GLUT callback system +void idle_func( void ) +{ + // Initiate the data traversal, to send device data down its + // respective pipelines. + traverse_data_graph(data_root); + + // Throw any events generated recently. + event_handler.process_events(); +} + +void resize_func(int w, int h) +{ + main_win->resized(w, h); +} + +void unpause_draw(void); + +void event_esc(CPT_Event) { +#ifdef USE_IPC + if (forked_draw) { + quit_draw = true; + unpause_draw(); + mutex_lock m(run_render); + } +#endif + + double now = ClockObject::get_global_clock()->get_time(); + double delta = now - start_time; + + int frame_count = ClockObject::get_global_clock()->get_frame_count(); + int num_frames = frame_count - start_frame_count; + if (num_frames > 0) { + nout << endl << num_frames << " frames in " << delta << " seconds" << endl; + double x = ((double)num_frames) / delta; + nout << x << " fps average (" << 1000.0 / x << "ms)" << endl; + } + + // The Escape key was pressed. Exit the application. + main_pipe = NULL; + main_win = NULL; + + rib_pipe = NULL; + rib_win = NULL; + + if (PStatClient::get_global_pstats()->is_connected()) { + nout << "Disconnecting from stats host" << endl; + PStatClient::get_global_pstats()->disconnect(); + } + + exit(0); +} + +void event_f(CPT_Event) { + double now = ClockObject::get_global_clock()->get_time(); + double delta = now - start_time; + + int frame_count = ClockObject::get_global_clock()->get_frame_count(); + int num_frames = frame_count - start_frame_count; + if (num_frames > 0) { + nout << endl << num_frames << " frames in " << delta << " seconds" << endl; + double x = ((double)num_frames) / delta; + nout << x << " fps average (" << 1000.0 / x << "ms)" << endl; + + // Reset the frame rate counter for the next press of 'f'. + start_time = now; + start_frame_count = frame_count; + } +} + +void event_S(CPT_Event) { + nout << "Connecting to stats host" << endl; + PStatClient::get_global_pstats()->connect(); +} + +void event_A(CPT_Event) { + if (PStatClient::get_global_pstats()->is_connected()) { + nout << "Disconnecting from stats host" << endl; + PStatClient::get_global_pstats()->disconnect(); + } else { + nout << "Stats host is already disconnected." << endl; + } +} + +void setup_framerate(void) { + if (framerate_top != (NamedNode*)0L) + return; + + framerate_top = new NamedNode("framerate_top"); + framerate_node = new NamedNode("framerate"); + framerate_arc = new RenderRelation(framerate_top, framerate_node); + + // Setup some overrides to turn off certain properties which we probably + // won't need for 2-d objects. + framerate_arc->set_transition(new DepthTestTransition(DepthTestProperty::M_none), 1); + framerate_arc->set_transition(new DepthWriteTransition(DepthWriteTransition::off()), 1); + framerate_arc->set_transition(new LightTransition(LightTransition::all_off()), 1); + framerate_arc->set_transition(new MaterialTransition(MaterialTransition::off()), 1); + framerate_arc->set_transition(new CullFaceTransition(CullFaceProperty::M_cull_none), 1); + + // create a 2-d camera. + PT(Camera) cam2d = new Camera("framerate_cam"); + new RenderRelation(framerate_node, cam2d); + cam2d->set_scene(framerate_top); + + Frustumf frustum2d; + frustum2d.make_ortho_2D(); + cam2d->set_projection(OrthoProjection(frustum2d)); + + // Now create a new layer + // eventually this should be done through chanconfig' + GraphicsChannel *chan = main_win->get_channel(0); + nassertv(chan != (GraphicsChannel*)0L); + + framerate_layer = chan->make_layer(); + nassertv(framerate_layer != (GraphicsLayer *)0L); + framerate_layer->set_active(true); + + DisplayRegion *dr = framerate_layer->make_display_region(); + nassertv(dr != (DisplayRegion *)0L); + dr->set_camera(cam2d); + + // load the font + framerate_font = loader.load_sync("cmtt12"); + + if (framerate_font != (NamedNode *)0L) { + framerate_text = new TextNode("framerate_text"); + RenderRelation *text_arc = new RenderRelation(framerate_node, + framerate_text); + + LMatrix4f mat = LMatrix4f::scale_mat(0.05) * + LMatrix4f::translate_mat(-0.95, 0.0, 0.95); + + framerate_text->set_transform(mat); + framerate_text->set_font(framerate_font.p()); + framerate_text->set_card_color(0.5, 0.5, 0.5, 0.5); + framerate_text->set_card_as_margin(0.5, 0.5, 0.2, 0.2); + framerate_text->set_frame_color(1., 0., 0., 1.); + framerate_text->set_frame_as_margin(0.5, 0.5, 0.2, 0.2); + framerate_text->set_align(TM_ALIGN_LEFT); + framerate_text->set_text_color(1., 1., 1., 1.); + framerate_text->set_text("blah"); + } +} + +void handle_framerate(void) { + static bool first_time = true; + static int buffer_count; + static int buffer_size = framework.GetInt("framerate-buffer", 60); + static double *prev_times = (double*)0L; + static double *deltas = (double*)0L; + + if (framerate_layer == (GraphicsLayer*)0L) + return; + + if (!framerate_layer->is_active()) { + first_time = true; + return; + } + + double now = ClockObject::get_global_clock()->get_time(); + + if (first_time) { + if (prev_times == (double*)0L) { + prev_times = new double[buffer_size]; + deltas = new double[buffer_size]; + } + buffer_count = 0; + prev_times[buffer_count++] = now; + first_time = false; + } else if (buffer_count < buffer_size) { + deltas[buffer_count-1] = now - prev_times[buffer_count-1]; + prev_times[buffer_count++] = now; + } else { + deltas[buffer_size-1] = now - prev_times[buffer_size-1]; + double delta = 0.; + for (int i=0; iset_text(os.str()); + + // now roll everything down one + for (int j=0; jset_active(is_on); + is_on = !is_on; +} + +void event_t(CPT_Event) { + // The "t" key was pressed. Toggle the showing of textures. + static bool textures_enabled = true; + + textures_enabled = !textures_enabled; + if (textures_enabled) { + // Remove the override from the initial state. + initial_state.clear_attribute(TextureTransition::get_class_type()); + } else { + // Set an override on the initial state to disable texturing. + TextureAttribute *ta = new TextureAttribute; + ta->set_priority(100); + initial_state.set_attribute(TextureTransition::get_class_type(), ta); + } +} + +void event_l(CPT_Event) { + // The "l" key was pressed. Toggle lighting. + static bool lighting_enabled = false; + + lighting_enabled = !lighting_enabled; + set_lighting(lighting_enabled); +} + +void event_w(CPT_Event) { + // The "w" key was pressed. Toggle wireframe mode. + static bool wireframe_mode = false; + + wireframe_mode = !wireframe_mode; + if (!wireframe_mode) { + // Set the normal, filled mode on the render arc. + RenderModeAttribute *rma = new RenderModeAttribute; + rma->set_mode(RenderModeProperty::M_filled); + CullFaceAttribute *cfa = new CullFaceAttribute; + cfa->set_mode(CullFaceProperty::M_cull_clockwise); + initial_state.set_attribute(RenderModeTransition::get_class_type(), rma); + initial_state.set_attribute(CullFaceTransition::get_class_type(), cfa); + + } else { + // Set the initial state up for wireframe mode. + RenderModeAttribute *rma = new RenderModeAttribute; + rma->set_mode(RenderModeProperty::M_wireframe); + CullFaceAttribute *cfa = new CullFaceAttribute; + cfa->set_mode(CullFaceProperty::M_cull_none); + initial_state.set_attribute(RenderModeTransition::get_class_type(), rma); + initial_state.set_attribute(CullFaceTransition::get_class_type(), cfa); + } +} + +void event_b(CPT_Event) { + // The 'b' key was pressed. Toggle backface culling. + static bool backface_mode = false; + + backface_mode = !backface_mode; + if (backface_mode) { + material->set_twoside(true); + CullFaceAttribute *cfa = new CullFaceAttribute; + cfa->set_mode(CullFaceProperty::M_cull_none); + initial_state.set_attribute(CullFaceTransition::get_class_type(), cfa); + } else { + material->set_twoside(false); + CullFaceAttribute *cfa = new CullFaceAttribute; + cfa->set_mode(CullFaceProperty::M_cull_clockwise); + initial_state.set_attribute(CullFaceTransition::get_class_type(), cfa); + } +} + +void event_R(CPT_Event) { + // The "R" key was pressed. Dump a RIB file. + if (rib_win == (GraphicsWindow*)0L) + return; + nout << "Writing RIB frame " << rib_win->get_frame_number() << "\n"; + render_frame(rib_pipe); +} + +void event_grave(CPT_Event) { + PixelBuffer p; + GraphicsStateGuardian* g = main_win->get_gsg(); + const RenderBuffer& r = g->get_render_buffer(RenderBuffer::T_front); + + p.set_xsize(main_win->get_width()); + p.set_ysize(main_win->get_height()); + p._image = PTA_uchar(main_win->get_width() * main_win->get_height() * 3); + + p.copy(main_win->get_gsg(), + main_win->get_gsg()->get_current_display_region(),r); + ostringstream s; + s << "frame" << main_win->get_frame_number() << ".pnm"; + p.write(s.str()); +} + +void event_n(CPT_Event) { + static bool normals_on = false; + + normals_on = !normals_on; + if (normals_on) { + NormalAddTraverser trav(main_win->get_gsg()); + df_traverse(render, trav, NullAttributeWrapper(), NullLevelState(), + RenderRelation::get_class_type()); + } else { + NormalDelTraverser trav(main_win->get_gsg()); + df_traverse(render, trav, NullAttributeWrapper(), NullLevelState(), + RenderRelation::get_class_type()); + } +} + +void event_C(CPT_Event) { + static bool showing_collision_solids = false; + + showing_collision_solids = !showing_collision_solids; + if (showing_collision_solids) { + // So we'll break down and use the NodePath interface in + // framework. We haven't used it here before, but it's such a + // splendid interface; why not use it? + NodePath render_path(render); + render_path.show_collision_solids(); + + } else { + NodePath render_path(render); + render_path.hide_collision_solids(); + } +} + +void event_N(CPT_Event) { + nout << "Reducing scene graph.\n"; + SceneGraphReducer gr(RenderRelation::get_class_type()); + gr.apply_transitions(root); + int num_reduced = gr.flatten(root, true); + nout << "Removed " << num_reduced << " arcs.\n"; +} + +// switch_trackball() is a local function to fiddle with the dgraph +// arcs to make a different trackball be in control of the mouse. +static void +switch_trackball(Node *trackball) { + if (current_trackball != NULL) { + remove_child(mouse_watcher, current_trackball, + DataRelation::get_class_type()); + } + current_trackball = trackball; + if (current_trackball != NULL) { + new DataRelation(mouse_watcher, current_trackball); + } +} + +// set_alt_trackball() should be called by user code to change the +// alternate trackball that is in effect when the user presses "c". +void +set_alt_trackball(Node *tb) { + if (tb == NULL) { + switch_trackball(trackball); + } else { + alt_trackball = tb; + switch_trackball(alt_trackball); + } +} + + +static void +start_drive() { + // Extract the current position from the trackball. + LMatrix4f mat = trackball->get_trans_mat(); + LPoint3f scale, hpr, xyz; + decompose_matrix(mat, scale, hpr, xyz); + if (hpr[2] > 90) { + hpr[0] += 180.0; + } + hpr[1] = 0.0; + hpr[2] = 0.0; + + drive_interface->set_pos(xyz); + drive_interface->set_hpr(hpr); + + // Make sure the ray-downcaster is set, so we maintain a constant + // height above the ground. + if (col_trans == (CollisionTraverser *)NULL) { + ray_node = new CollisionNode("ray"); + ray_node->set_into_collide_mask(0); + ray_node->set_from_collide_mask(drive_mask); + + NodeRelation *arc = new RenderRelation(cameras, ray_node); + + ray_node->add_solid(new CollisionRay(LPoint3f(0.0, 0.0, 0.0), + LVector3f::down())); + arc->set_transition(new PruneTransition); + + col_trans = new CollisionTraverser; + col_handler = new CollisionHandlerFloor; + col_handler->set_offset(drive_height); + col_trans->add_collider(ray_node, col_handler); + col_handler->add_collider(ray_node, drive_interface); + } +} + +static void +stop_drive() { + // Extract the current position from the drive interface and + // restore it to the trackball. + LPoint3f xyz = drive_interface->get_pos(); + LPoint3f hpr = drive_interface->get_hpr(); + LPoint3f scale(1.0, 1.0, 1.0); + LMatrix4f mat; + compose_matrix(mat, scale, hpr, xyz); + trackball->set_mat(invert(mat)); + trackball->reset_origin_here(); +} + +void event_c(CPT_Event) { + // "c" key pressed: change to alternate controls. + + if (current_trackball == trackball) { + if (alt_trackball != NULL) { + switch_trackball(alt_trackball); + } + } else { + if (current_trackball == drive_interface) { + stop_drive(); + } + switch_trackball(trackball); + } +} + +void event_D(CPT_Event) { + // "D" key pressed: toggle drive controls. + if (current_trackball == drive_interface) { + stop_drive(); + switch_trackball(trackball); + + } else { + start_drive(); + set_alt_trackball(drive_interface); + } +} + +void event_g(CPT_Event) { + // "g" key pressed: toggle fog. + static bool fog_mode = false; + + fog_mode = !fog_mode; + if (fog_mode) { + FogAttribute *fa = new FogAttribute; + fa->set_on(fog); + initial_state.set_attribute(FogTransition::get_class_type(), fa); + } else { + FogAttribute *fa = new FogAttribute; + initial_state.set_attribute(FogTransition::get_class_type(), fa); + } +} + +#ifdef USE_IPC +void pause_draw(void) { + if (!render_running) + return; + run_render.lock(); + render_running = false; + nout << "draw thread paused" << endl; +} + +void unpause_draw(void) { + if (render_running) + return; + run_render.unlock(); + render_running = true; + nout << "draw thread continuing" << endl; +} + +void draw_loop(void*) { + for (;!quit_draw;) { + mutex_lock m(run_render); + main_win->update(); + handle_framerate(); + } + nout << "draw thread exiting" << endl; +} + +void event_x(CPT_Event) { + // "x" key pressed: pause/unpause draw + if (!forked_draw) + return; + if (render_running) + pause_draw(); + else + unpause_draw(); +} +#endif + +int framework_main(int argc, char *argv[]) { + pystub(); + + GeomNorms::init_type(); + +#ifndef DEBUG + // This just makes sure that no one changed the value of a + // _type_handle member after the type was registered. It shouldn't + // ever happen. If it did, most likely two classes are sharing the + // same _type_handle variable for some reason. + TypeRegistry::reregister_types(); +#endif + + app_traverser = new AppTraverser(RenderRelation::get_class_type()); + + // Allow the specification of multiple files on the command + // line. This is handy, for instance, to load up both a character + // and its animation file. + + typedef vector Files; + Files files; + + if (first_init != NULL) + first_init(); + + for (int a = 1; a < argc; a++) + if ((argv[a] != (char*)0L) && ((argv[a])[0] != '-') && + ((argv[a])[0] != '+') && ((argv[a])[0] != '#')) + files.push_back(argv[a]); + + // load display modules + GraphicsPipe::resolve_modules(); + + nout << "Known pipe types:" << endl; + GraphicsPipe::_factory.write_types(nout, 2); + + // Create a window + main_pipe = GraphicsPipe::_factory. + make_instance(InteractiveGraphicsPipe::get_class_type()); + + if (main_pipe == (GraphicsPipe*)0L) { + nout << "No interactive pipe is available! Check your Configrc!\n"; + exit(1); + } + + cout << "Opened a '" << main_pipe->get_type().get_name() + << "' interactive graphics pipe." << endl; + + rib_pipe = GraphicsPipe::_factory. + make_instance(NoninteractiveGraphicsPipe::get_class_type()); + + if (rib_pipe == (GraphicsPipe*)0L) + cout << "Did not open a non-interactive graphics pipe, features related" + << " to that will\nbe disabled." << endl; + else + cout << "Opened a '" << rib_pipe->get_type().get_name() + << "' non-interactive graphics pipe." << endl; + + ChanCfgOverrides override; + + // need to find a better way to differentiate unsigned int from regular + override.setField(ChanCfgOverrides::Mask, + ((unsigned int)(W_DOUBLE|W_DEPTH|W_MULTISAMPLE))); + override.setField(ChanCfgOverrides::Title, "Demo"); + + std::string conf = framework.GetString("chan-config", chan_config); + if (extra_overrides_func != NULL) + extra_overrides_func(override, conf); + + // Create the render node + render = new NamedNode("render"); + + // make a node for the cameras to live under + cameras = new NamedNode("cameras"); + RenderRelation* arc1 = new RenderRelation(render, cameras); + + main_win = ChanConfig(main_pipe, conf, cameras, render, override); + assert(main_win != (GraphicsWindow*)0L); + + // is ok if this doesn't work or returns NULL + if (rib_pipe != (GraphicsPipe*)0L) { + Node *rib_cameras = new NamedNode("rib_cameras"); + new RenderRelation(render, rib_cameras); + rib_win = ChanConfig(rib_pipe, "single", rib_cameras, render, override); + } + + // Make a node for the lights to live under. We put the lights in + // with the cameras, so they'll stay locked to our point-of-view. + + lights = new NamedNode("lights"); + new RenderRelation(cameras, lights); + + light = new AmbientLight( "ambient" ); + dlight = new DirectionalLight( "directional" ); + plight = new PointLight( "point" ); + plight->set_constant_attenuation( 2.0 ); + plight->set_linear_attenuation( 1.0 ); + plight->set_quadratic_attenuation( 0.5 ); + slight = new Spotlight( "spot" ); + new RenderRelation( lights, light ); +#if 0 + new RenderRelation( lights, dlight ); + new RenderRelation( lights, plight ); + new RenderRelation( lights, slight ); +#endif + + // Turn on culling. + CullFaceAttribute *cfa = new CullFaceAttribute; + cfa->set_mode(CullFaceProperty::M_cull_clockwise); + initial_state.set_attribute(CullFaceTransition::get_class_type(), cfa); + + // Set up a default material + material = new Material; + material->set_ambient( Colorf( 1, 1, 1, 1 ) ); + MaterialAttribute *ma = new MaterialAttribute; + ma->set_on(material); + initial_state.set_attribute(MaterialTransition::get_class_type(), ma); + + // Set up a default fog + fog = new Fog; + + // Create the data graph root. + data_root = new NamedNode( "data" ); + + // Create a mouse and put it in the data graph. + mak = new MouseAndKeyboard( main_win, 0 ); + new DataRelation(data_root, mak); + + // Create a MouseWatcher underneath the mouse, so we can have some + // 2-d control effects. + mouse_watcher = new MouseWatcher("mouse_watcher"); + new DataRelation(mak, mouse_watcher); + mouse_watcher->set_button_down_pattern("mw-%r-%b"); + mouse_watcher->set_button_up_pattern("mw-%r-%b-up"); + mouse_watcher->set_enter_pattern("mw-in-%r"); + mouse_watcher->set_leave_pattern("mw-out-%r"); + + // Create a trackball to handle the mouse input. + trackball = new Trackball("trackball"); + trackball->set_pos(LVector3f::forward() * 50.0); + + // Also create a drive interface. The user might switch to this + // later. + drive_interface = new DriveInterface("drive_interface"); + + new DataRelation(mouse_watcher, trackball); + current_trackball = trackball; + + // Connect the trackball output to the camera's transform. + PT(Transform2SG) tball2cam = new Transform2SG("tball2cam"); + tball2cam->set_arc(arc1); + new DataRelation(trackball, tball2cam); + new DataRelation(drive_interface, tball2cam); + + // Create a ButtonThrower to throw events from the keyboard. + PT(ButtonThrower) et = new ButtonThrower("kb-events"); + new DataRelation(mouse_watcher, et); + + root = new NamedNode("root"); + first_arc = new RenderRelation(render, root, 100); + + if (files.empty() && framework.GetBool("have-omnitriangle", true)) { + // The user did not specify an file. Create some default + // geometry. + + PTA_Vertexf coords; + PTA_TexCoordf uvs; + PTA_Normalf norms; + PTA_Colorf colors; + PTA_ushort cindex; + + coords.push_back(Vertexf::rfu(0.0, 0.0, 0.0)); + coords.push_back(Vertexf::rfu(1.0, 0.0, 0.0)); + coords.push_back(Vertexf::rfu(0.0, 0.0, 1.0)); + uvs.push_back(TexCoordf(0.0, 0.0)); + uvs.push_back(TexCoordf(1.0, 0.0)); + uvs.push_back(TexCoordf(0.0, 1.0)); + norms.push_back(Normalf::back()); + colors.push_back(Colorf(0.5, 0.5, 1.0, 1.0)); + cindex.push_back(0); + cindex.push_back(0); + cindex.push_back(0); + + PT(GeomTri) geom = new GeomTri; + geom->set_num_prims(1); + geom->set_coords(coords, G_PER_VERTEX); + geom->set_texcoords(uvs, G_PER_VERTEX); + geom->set_normals(norms, G_PER_PRIM); + geom->set_colors(colors, G_PER_VERTEX, cindex); + + geomnode = new GeomNode; + geomnode->add_geom(geom.p()); + RenderRelation *arc = new RenderRelation(root, geomnode, 10); + first_arc = arc; + + Texture *tex = TexturePool::load_texture("rock-floor.rgb"); + if (tex != (Texture *)NULL) { + tex->set_minfilter(Texture::FT_linear); + tex->set_magfilter(Texture::FT_linear); + arc->set_transition(new TextureTransition(tex)); + } + + } else { + // Load up some geometry from one or more files. + + Files::const_iterator fi; + for (fi = files.begin(); + fi != files.end(); + ++fi) { + const Filename &filename = (*fi); + PT_Node node = loader.load_sync(filename); + + if (node == (Node *)NULL) { + nout << "Unable to load file " << filename << "\n"; + } else { + new RenderRelation(root, node); + } + } + + // If we happened to load up both a character file and its + // matching animation file, attempt to bind them together now. + AnimControlCollection anim_controls; + auto_bind(root, anim_controls, ~0); + + // And start looping any animations we successfully bound. + anim_controls.loop_all(true); + } + + // Set up keyboard events. + event_handler.add_hook("escape", event_esc); + event_handler.add_hook("q", event_esc); + event_handler.add_hook("f", event_f); + event_handler.add_hook("F", event_f_full); + event_handler.add_hook("t", event_t); + event_handler.add_hook("l", event_l); + event_handler.add_hook("w", event_w); + event_handler.add_hook("b", event_b); + event_handler.add_hook("R", event_R); + event_handler.add_hook("grave", event_grave); + event_handler.add_hook("n", event_n); + event_handler.add_hook("c", event_c); + event_handler.add_hook("D", event_D); + event_handler.add_hook("g", event_g); + event_handler.add_hook("C", event_C); + event_handler.add_hook("N", event_N); + event_handler.add_hook("S", event_S); + event_handler.add_hook("A", event_A); + +#ifdef USE_IPC + event_handler.add_hook("x", event_x); +#endif + + if (define_keys != NULL) + define_keys(event_handler); + + // Now that we've called define_keys() (which might or might not + // set have_dlight), we can turn on lighting. + set_lighting(false); + + // Tick the clock once so we won't count the time spent loading up + // files, above, in our frame rate average. + ClockObject::get_global_clock()->tick(); + start_time = ClockObject::get_global_clock()->get_time(); + start_frame_count = ClockObject::get_global_clock()->get_frame_count(); + + if (framework.Defined("clear-value")) { + float cf = framework.GetFloat("clear-value", 0.); + Colorf c(cf, cf, cf, 1.); + main_win->get_gsg()->set_color_clear_value(c); + } + + if (!main_win->supports_update()) { + nout + << "Window type " << main_win->get_type() + << " supports only the glut-style main loop interface.\n"; + + main_win->register_draw_function(display_func); + main_win->register_idle_function(idle_func); + main_win->register_resize_function(resize_func); + main_win->main_loop(); + + } else { + DisplayCallback dcb; + main_win->set_draw_callback(&dcb); + IdleCallback icb; + +#ifdef USE_IPC + if (forked_draw) { + nout << "forking draw thread" << endl; + draw_thread = thread::create(draw_loop); + for (;;) + icb.idle(); + } else +#endif + { + main_win->set_idle_callback(&icb); + for (;;) { + main_win->update(); + handle_framerate(); + } + } + } + return 1; +} diff --git a/panda/src/framework/framework.h b/panda/src/framework/framework.h new file mode 100644 index 0000000000..f35a3353bf --- /dev/null +++ b/panda/src/framework/framework.h @@ -0,0 +1,44 @@ +// Filename: framework.h +// Created by: drose (04May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FRAMEWORK_H +#define FRAMEWORK_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +EXPCL_FRAMEWORK extern EventHandler event_handler; +EXPCL_FRAMEWORK extern PT_NamedNode lights; +EXPCL_FRAMEWORK extern PT_NamedNode root; +EXPCL_FRAMEWORK extern PT_NamedNode data_root; +EXPCL_FRAMEWORK extern PT_NamedNode render; +EXPCL_FRAMEWORK extern PT_NamedNode cameras; +EXPCL_FRAMEWORK extern PT(MouseAndKeyboard) mak; +EXPCL_FRAMEWORK extern PT(MouseWatcher) mouse_watcher; +EXPCL_FRAMEWORK extern PT(Trackball) trackball; +EXPCL_FRAMEWORK extern PT(GraphicsWindow) main_win; +EXPCL_FRAMEWORK extern PT(DirectionalLight) dlight; +EXPCL_FRAMEWORK extern bool have_dlight; +EXPCL_FRAMEWORK extern Loader loader; + +EXPCL_FRAMEWORK extern void set_alt_trackball(Node*); +EXPCL_FRAMEWORK extern int framework_main(int argc, char *argv[]); +EXPCL_FRAMEWORK extern void (*define_keys)(EventHandler&); +EXPCL_FRAMEWORK extern void (*additional_idle)(); +EXPCL_FRAMEWORK extern void (*extra_overrides_func)(ChanCfgOverrides&, + std::string&); + +#endif diff --git a/panda/src/glgsg/Sources.pp b/panda/src/glgsg/Sources.pp new file mode 100644 index 0000000000..034633b859 --- /dev/null +++ b/panda/src/glgsg/Sources.pp @@ -0,0 +1,22 @@ +#define DIRECTORY_IF_GL yes + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase +#define USE_GL yes + +#begin lib_target + #define TARGET glgsg + #define LOCAL_LIBS \ + cull gsgmisc gsgbase gobj sgattrib sgraphutil graph display light \ + putil linmath sgraph mathutil pnmimage + + #define SOURCES \ + config_glgsg.cxx config_glgsg.h glGraphicsStateGuardian.I \ + glGraphicsStateGuardian.cxx glGraphicsStateGuardian.h \ + glSavedFrameBuffer.I glSavedFrameBuffer.cxx glSavedFrameBuffer.h \ + glTextureContext.I glTextureContext.cxx glTextureContext.h + + #define INSTALL_HEADERS \ + config_glgsg.h glGraphicsStateGuardian.I glGraphicsStateGuardian.h + +#end lib_target + diff --git a/panda/src/glgsg/config_glgsg.cxx b/panda/src/glgsg/config_glgsg.cxx new file mode 100644 index 0000000000..352c3a41ef --- /dev/null +++ b/panda/src/glgsg/config_glgsg.cxx @@ -0,0 +1,63 @@ +// Filename: config_glgsg.cxx +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_glgsg.h" +#include "glGraphicsStateGuardian.h" +#include "glSavedFrameBuffer.h" +#include "glTextureContext.h" + +#include + +Configure(config_glgsg); +NotifyCategoryDef(glgsg, ":display:gsg"); + +// Configure this variable true to cause the GLGSG to show each +// transform space it renders by drawing a little unit axis. This +// cannot be enabled when the player is compiled in NDEBUG mode. +bool gl_show_transforms = config_glgsg.GetBool("gl-show-transforms", false); + +// Configure this true to glHint the textures into the cheapest +// possible mode. +bool gl_cheap_textures = config_glgsg.GetBool("gl-cheap-textures", false); + +// Configure this true to perform a cull traversal over the geometry +// by default, false otherwise. The cull traversal provides support +// for state-sorting, z-sorting, and binning. +bool gl_cull_traversal = config_glgsg.GetBool("gl-cull-traversal", true); + +// Configure this true to disable the use of mipmapping in the +// renderer. +bool gl_ignore_mipmaps = config_glgsg.GetBool("gl-ignore-mipmaps", false); + +GLDecalType gl_decal_type = GDT_offset; + +static GLDecalType +parse_decal_type(const string &type) { + if (type == "mask") { + return GDT_mask; + } else if (type == "blend") { + return GDT_blend; + } else if (type == "offset") { + return GDT_offset; + } + glgsg_cat.error() << "Invalid gl-decal-type: " << type << "\n"; + return GDT_offset; +} + + +ConfigureFn(config_glgsg) { + string decal_type = config_glgsg.GetString("gl-decal-type", ""); + if (!decal_type.empty()) { + gl_decal_type = parse_decal_type(decal_type); + } + + GLGraphicsStateGuardian::init_type(); + GLSavedFrameBuffer::init_type(); + GLTextureContext::init_type(); + + GraphicsStateGuardian::_factory.register_factory + (GLGraphicsStateGuardian::get_class_type(), + GLGraphicsStateGuardian::make_GlGraphicsStateGuardian); +} diff --git a/panda/src/glgsg/config_glgsg.h b/panda/src/glgsg/config_glgsg.h new file mode 100644 index 0000000000..2ecb91f5d8 --- /dev/null +++ b/panda/src/glgsg/config_glgsg.h @@ -0,0 +1,27 @@ +// Filename: config_glgsg.h +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_GLGSG_H +#define CONFIG_GLGSG_H + +#include +#include + +NotifyCategoryDecl(glgsg, EXPCL_PANDAGL, EXPTP_PANDAGL); + +extern bool gl_show_transforms; +extern bool gl_cheap_textures; +extern bool gl_cull_traversal; +extern bool gl_ignore_mipmaps; + +// Ways to implement decals. +enum GLDecalType { + GDT_mask, // GL 1.0 style, involving three steps + GDT_blend, // As above, but slower; a hack for broken nVidia driver + GDT_offset // The fastest, using GL 1.1 style glPolygonOffset +}; +extern GLDecalType gl_decal_type; + +#endif diff --git a/panda/src/glgsg/glGraphicsStateGuardian.I b/panda/src/glgsg/glGraphicsStateGuardian.I new file mode 100644 index 0000000000..f12618e297 --- /dev/null +++ b/panda/src/glgsg/glGraphicsStateGuardian.I @@ -0,0 +1,1305 @@ +// Filename: glGraphicsStateGuardian.I +// Created by: drose (02Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_glgsg.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::activate +// Access: Public +// Description: Sets this context to be the active context for future +// GL commands. +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +activate() { + // This operation seems to be incredibly expensive on some + // platforms--particularly for the NVidia drivers under Linux. It + // doesn't seem to check if we already had the context current + // before we switch contexts. For now, we'll limit our use of this + // function. + + _win->make_current(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glClearColor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glClearColor(GLclampf red, GLclampf green, GLclampf blue, + GLclampf alpha) { + if (red != _clear_color_red || + green != _clear_color_green || + blue != _clear_color_blue || + alpha != _clear_color_alpha) { + glClearColor(red, green, blue, alpha); + _clear_color_red = red; + _clear_color_green = green; + _clear_color_blue = blue; + _clear_color_alpha = alpha; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glClearDepth +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glClearDepth(GLclampd depth) { + if (depth != _clear_depth) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glClearDepth(" << (double)depth << ")" << endl; +#endif + glClearDepth(depth); + _clear_depth = depth; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glClearStencil +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glClearStencil(GLint s) { + if (s != _clear_stencil) { + glClearStencil(s); + _clear_stencil = s; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glClearAccum +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glClearAccum(GLclampf red, GLclampf green, GLclampf blue, + GLclampf alpha) { + if (red != _clear_accum_red || + green != _clear_accum_green || + blue != _clear_accum_blue || + alpha != _clear_accum_alpha) { + glClearAccum(red, green, blue, alpha); + _clear_accum_red = red; + _clear_accum_green = green; + _clear_accum_blue = blue; + _clear_accum_alpha = alpha; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glDrawBuffer +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glDrawBuffer(GLenum mode) { + if (mode != _draw_buffer_mode) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "glDrawBuffer("; + switch (mode) { + case GL_FRONT: + glgsg_cat.debug(false) << "GL_FRONT)"; + break; + case GL_BACK: + glgsg_cat.debug(false) << "GL_BACK)"; + break; + case GL_RIGHT: + glgsg_cat.debug(false) << "GL_RIGHT)"; + break; + case GL_LEFT: + glgsg_cat.debug(false) << "GL_LEFT)"; + break; + case GL_FRONT_RIGHT: + glgsg_cat.debug(false) << "GL_FRONT_RIGHT)"; + break; + case GL_FRONT_LEFT: + glgsg_cat.debug(false) << "GL_FRONT_LEFT)"; + break; + case GL_BACK_RIGHT: + glgsg_cat.debug(false) << "GL_BACK_RIGHT)"; + break; + case GL_BACK_LEFT: + glgsg_cat.debug(false) << "GL_BACK_LEFT)"; + break; + case GL_FRONT_AND_BACK: + glgsg_cat.debug(false) << "GL_FRONT_AND_BACK)"; + break; + } + glgsg_cat.debug(false) << endl; +#endif + glDrawBuffer(mode); + _draw_buffer_mode = mode; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glReadBuffer +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glReadBuffer(GLenum mode) { + if (mode != _read_buffer_mode) { + glReadBuffer(mode); + _read_buffer_mode = mode; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glShadeModel +// Access: +// Description: Set the shading model to be either GL_FLAT or GL_SMOOTH +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glShadeModel(GLenum mode) { + if (_shade_model_mode != mode) { + glShadeModel(mode); + _shade_model_mode = mode; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glScissor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glScissor(GLint x, GLint y, GLsizei width, GLsizei height) +{ + if ( _scissor_x != x || _scissor_y != y || + _scissor_width != width || _scissor_height != height ) + { + _scissor_x = x; _scissor_y = y; + _scissor_width = width; _scissor_height = height; + glScissor( x, y, width, height ); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glViewport +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glViewport(GLint x, GLint y, GLsizei width, GLsizei height) +{ + if ( _viewport_x != x || _viewport_y != y || + _viewport_width != width || _viewport_height != height ) + { + _viewport_x = x; _viewport_y = y; + _viewport_width = width; _viewport_height = height; + glViewport( x, y, width, height ); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glLightModelAmbient +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glLightModelAmbient( const Colorf& color ) +{ + if (_lmodel_ambient != color) { + _lmodel_ambient = color; +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glLightModel(GL_LIGHT_MODEL_AMBIENT, " << color << ")" << endl; +#endif + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, color.get_data()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glLightModelLocal +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glLightModelLocal(GLboolean local) +{ + if ( _lmodel_local != local ) + { + _lmodel_local = local; + glLightModeli( GL_LIGHT_MODEL_LOCAL_VIEWER, local ); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glLightModelLocal +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glLightModelTwoSide(GLboolean twoside) +{ + if (_lmodel_twoside != twoside) { + _lmodel_twoside = twoside; +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glLightModel(GL_LIGHT_MODEL_TWO_SIDE, " << (int)twoside << ")" << endl; +#endif + glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, twoside); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glMaterialAmbient +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glMaterialAmbient( bool twoside, const Colorf& color ) +{ + if (_material_ambient != color) { + _material_ambient = color; + if (twoside) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glMaterial(GL_FRONT_AND_BACK, GL_AMBIENT, " + << color << ")" << endl; +#endif + glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, color.get_data() ); + } else { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glMaterial(GL_FRONT, GL_AMBIENT, " << color << ")" << endl; +#endif + glMaterialfv( GL_FRONT, GL_AMBIENT, color.get_data() ); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glMaterialDiffuse +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glMaterialDiffuse( bool twoside, const Colorf& color ) +{ + if ( _material_diffuse != color ) + { + _material_diffuse = color; + if ( twoside ) + glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, color.get_data() ); + else + glMaterialfv( GL_FRONT, GL_DIFFUSE, color.get_data() ); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glMaterialAmbientDiffuse +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glMaterialAmbientDiffuse( bool twoside, const Colorf& color ) +{ + if ( _material_ambient != color || _material_diffuse != color ) + { + _material_ambient = color; + _material_diffuse = color; + if ( twoside ) + glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, + color.get_data() ); + else + glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color.get_data() ); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glMaterialSpecular +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glMaterialSpecular( bool twoside, const Colorf& color ) +{ + if ( _material_specular != color ) + { + _material_specular = color; + if ( twoside ) + glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, color.get_data() ); + else + glMaterialfv( GL_FRONT, GL_SPECULAR, color.get_data() ); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glMaterialShininess +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glMaterialShininess( bool twoside, float shininess ) +{ + if ( _material_shininess != shininess ) + { + _material_shininess = shininess; + if ( twoside ) + glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, shininess ); + else + glMaterialf( GL_FRONT, GL_SHININESS, shininess ); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glMaterialEmision +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glMaterialEmission( bool twoside, const Colorf& color ) +{ + if ( _material_emission != color ) + { + _material_emission = color; + if ( twoside ) + glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, color.get_data() ); + else + glMaterialfv( GL_FRONT, GL_EMISSION, color.get_data() ); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glStencilFunc +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glStencilFunc(GLenum func) { + if (_stencil_func != func) { + _stencil_func = func; +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "glStencilFunc("; + switch (func) { + case GL_NEVER: + glgsg_cat.debug(false) << "GL_NEVER, "; + break; + case GL_LESS: + glgsg_cat.debug(false) << "GL_LESS, "; + break; + case GL_EQUAL: + glgsg_cat.debug(false) << "GL_EQUAL, "; + break; + case GL_LEQUAL: + glgsg_cat.debug(false) << "GL_LEQUAL, "; + break; + case GL_GREATER: + glgsg_cat.debug(false) << "GL_GREATER, "; + break; + case GL_NOTEQUAL: + glgsg_cat.debug(false) << "GL_NOTEQUAL, "; + break; + case GL_GEQUAL: + glgsg_cat.debug(false) << "GL_GEQUAL, "; + break; + case GL_ALWAYS: + glgsg_cat.debug(false) << "GL_ALWAYS, "; + break; + default: + glgsg_cat.debug(false) << "unknown, "; + break; + } + glgsg_cat.debug(false) << "1, 1)" << endl; +#endif + glStencilFunc(func, 1, 1); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glStencilOp +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glStencilOp(GLenum op) { + if (_stencil_op != op) { + _stencil_op = op; +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "glStencilOp(GL_KEEP, GL_KEEP, "; + switch (op) { + case GL_KEEP: + glgsg_cat.debug(false) << "GL_KEEP)"; + break; + case GL_ZERO: + glgsg_cat.debug(false) << "GL_ZERO)"; + break; + case GL_REPLACE: + glgsg_cat.debug(false) << "GL_REPLACE)"; + break; + case GL_INCR: + glgsg_cat.debug(false) << "GL_INCR)"; + break; + case GL_DECR: + glgsg_cat.debug(false) << "GL_DECR)"; + break; + case GL_INVERT: + glgsg_cat.debug(false) << "GL_INVERT)"; + break; + default: + glgsg_cat.debug(false) << "unknown)"; + break; + } + glgsg_cat.debug(false) << endl; +#endif + glStencilOp(GL_KEEP, GL_KEEP, op); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glLineWidth +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glLineWidth(GLfloat width) { + if (_line_width != width) { + _line_width = width; +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glLineWidth(" << width << ")" << endl; +#endif + glLineWidth(width); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glPointSize +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glPointSize(GLfloat size) { + if (_point_size != size) { + _point_size = size; +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glPointSize(" << size << ")" << endl; +#endif + glPointSize(size); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glBlendFunc +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glBlendFunc(GLenum sfunc, GLenum dfunc) { + if (_blend_source_func != sfunc || _blend_dest_func != dfunc) { + _blend_source_func = sfunc; + _blend_dest_func = dfunc; +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "glBlendFunc("; + switch (sfunc) { + case GL_ZERO: + glgsg_cat.debug(false) << "GL_ZERO, "; + break; + case GL_ONE: + glgsg_cat.debug(false) << "GL_ONE, "; + break; + case GL_DST_COLOR: + glgsg_cat.debug(false) << "GL_DST_COLOR, "; + break; + case GL_ONE_MINUS_DST_COLOR: + glgsg_cat.debug(false) << "GL_ONE_MINUS_DST_COLOR, "; + break; + case GL_SRC_ALPHA: + glgsg_cat.debug(false) << "GL_SRC_ALPHA, "; + break; + case GL_ONE_MINUS_SRC_ALPHA: + glgsg_cat.debug(false) << "GL_ONE_MINUS_SRC_ALPHA, "; + break; + case GL_DST_ALPHA: + glgsg_cat.debug(false) << "GL_DST_ALPHA, "; + break; + case GL_ONE_MINUS_DST_ALPHA: + glgsg_cat.debug(false) << "GL_ONE_MINUS_DST_ALPHA, "; + break; + case GL_SRC_ALPHA_SATURATE: + + glgsg_cat.debug(false) << "GL_SRC_ALPHA_SATURATE, "; + break; + default: + glgsg_cat.debug(false) << "unknown, "; + break; + } + switch (dfunc) { + case GL_ZERO: + glgsg_cat.debug(false) << "GL_ZERO)"; + break; + case GL_ONE: + glgsg_cat.debug(false) << "GL_ONE)"; + break; + case GL_SRC_COLOR: + glgsg_cat.debug(false) << "GL_SRC_COLOR)"; + break; + case GL_ONE_MINUS_SRC_COLOR: + glgsg_cat.debug(false) << "GL_ONE_MINUS_SRC_COLOR)"; + break; + case GL_SRC_ALPHA: + glgsg_cat.debug(false) << "GL_SRC_ALPHA)"; + break; + case GL_ONE_MINUS_SRC_ALPHA: + glgsg_cat.debug(false) << "GL_ONE_MINUS_SRC_ALPHA)"; + break; + case GL_DST_ALPHA: + glgsg_cat.debug(false) << "GL_DST_ALPHA)"; + break; + case GL_ONE_MINUS_DST_ALPHA: + glgsg_cat.debug(false) << "GL_ONE_MINUS_DST_ALPHA)"; + break; + default: + glgsg_cat.debug(false) << "unknown)"; + break; + } + glgsg_cat.debug(false) << endl; +#endif + glBlendFunc(sfunc, dfunc); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glDepthMask +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glDepthMask(GLboolean mask) { + if (_depth_mask != mask) { + _depth_mask = mask; +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glDepthMask(" << (int)mask << ")" << endl; +#endif + glDepthMask(mask); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glFogMode +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glFogMode(GLint mode) { + if (_fog_mode != mode) { + _fog_mode = mode; +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "glFog(GL_FOG_MODE, "; + switch(mode) { + case GL_LINEAR: + glgsg_cat.debug(false) << "GL_LINEAR)" << endl; + break; + case GL_EXP: + glgsg_cat.debug(false) << "GL_EXP)" << endl; + break; + case GL_EXP2: + glgsg_cat.debug(false) << "GL_EXP2)" << endl; + break; +#ifdef GL_FOG_FUNC_SGIS + case GL_FOG_FUNC_SGIS: + glgsg_cat.debug(false) << "GL_FOG_FUNC_SGIS)" << endl; + break; +#endif + default: + glgsg_cat.debug(false) << "unknown)" << endl; + break; + } +#endif + glFogi(GL_FOG_MODE, mode); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glFogStart +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glFogStart(GLfloat start) { + if (_fog_start != start) { + _fog_start = start; +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glFog(GL_FOG_START, " << start << ")" << endl; +#endif + glFogf(GL_FOG_START, start); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glFogEnd +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glFogEnd(GLfloat end) { + if (_fog_end != end) { + _fog_end = end; +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glFog(GL_FOG_END, " << end << ")" << endl; +#endif + glFogf(GL_FOG_END, end); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glFogDensity +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glFogDensity(GLfloat density) { + if (_fog_density != density) { + _fog_density = density; +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glFog(GL_FOG_DENSITY, " << density << ")" << endl; +#endif + glFogf(GL_FOG_DENSITY, density); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glFogColor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glFogColor(const Colorf &color) { + if (_fog_color != color) { + _fog_color = color; +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glFog(GL_FOG_COLOR, " << color << ")" << endl; +#endif + glFogfv(GL_FOG_COLOR, color.get_data()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glAlphaFunc +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glAlphaFunc(GLenum func, GLclampf ref) { + if (_alpha_func != func || _alpha_func_ref != ref) { + _alpha_func = func; + _alpha_func_ref = ref; +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "glAlphaFunc("; + switch (func) { + case GL_NEVER: + glgsg_cat.debug(false) << "GL_NEVER, "; + break; + case GL_LESS: + glgsg_cat.debug(false) << "GL_LESS, "; + break; + case GL_EQUAL: + glgsg_cat.debug(false) << "GL_EQUAL, "; + break; + case GL_LEQUAL: + glgsg_cat.debug(false) << "GL_LEQUAL, "; + break; + case GL_GREATER: + glgsg_cat.debug(false) << "GL_GREATER, "; + break; + case GL_NOTEQUAL: + glgsg_cat.debug(false) << "GL_NOTEQUAL, "; + break; + case GL_GEQUAL: + glgsg_cat.debug(false) << "GL_GEQUAL, "; + break; + case GL_ALWAYS: + glgsg_cat.debug(false) << "GL_ALWAYS, "; + break; + } + glgsg_cat.debug() << ref << ")" << endl; +#endif + glAlphaFunc(func, ref); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::call_glPolygonMode +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +call_glPolygonMode(GLenum mode) { + if (_polygon_mode != mode) { + _polygon_mode = mode; +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "glPolygonMode(GL_BACK_AND_FRONT, "; + switch (mode) { + case GL_POINT: + glgsg_cat.debug(false) << "GL_POINT)" << endl; + break; + case GL_LINE: + glgsg_cat.debug(false) << "GL_LINE)" << endl; + break; + case GL_FILL: + glgsg_cat.debug(false) << "GL_FILL)" << endl; + break; + } +#endif + glPolygonMode(GL_FRONT_AND_BACK, mode); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::set_pack_alignment +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +set_pack_alignment(GLint alignment) { + if (_pack_alignment != alignment) { + glPixelStorei(GL_PACK_ALIGNMENT, alignment); + _pack_alignment = alignment; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::set_unpack_alignment +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +set_unpack_alignment(GLint alignment) { + if (_unpack_alignment != alignment) { + glPixelStorei(GL_UNPACK_ALIGNMENT, alignment); + _unpack_alignment = alignment; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_multisample +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +enable_multisample(bool val) { + if (_multisample_enabled != val) { + _multisample_enabled = val; + if (val) { +#ifdef GL_MULTISAMPLE_SGIS + glEnable(GL_MULTISAMPLE_SGIS); +#endif + glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + } else { +#ifdef GL_MULTISAMPLE_SGIS + glDisable(GL_MULTISAMPLE_SGIS); +#endif + glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_line_smooth +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +enable_line_smooth(bool val) { + if (_line_smooth_enabled != val) { + _line_smooth_enabled = val; + if (val) { + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST); + } else { + glDisable(GL_LINE_SMOOTH); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_point_smooth +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +enable_point_smooth(bool val) { + if (_point_smooth_enabled != val) { + _point_smooth_enabled = val; + if (val) { + glEnable(GL_POINT_SMOOTH); + glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + } else { + glDisable(GL_POINT_SMOOTH); + glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_lighting +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +enable_lighting(bool val) { + if (_lighting_enabled != val) { + _lighting_enabled = val; + if (val) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glEnable(GL_LIGHTING)" << endl; +#endif + glEnable(GL_LIGHTING); + } else { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glDisable(GL_LIGHTING)" << endl; +#endif + glDisable(GL_LIGHTING); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_dither +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +enable_dither(bool val) { + if (_dither_enabled != val) { + _dither_enabled = val; + if (val) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glEnable(GL_DITHER)" << endl; +#endif + glEnable(GL_DITHER); + } else { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glDisable(GL_DITHER)" << endl; +#endif + glDisable(GL_DITHER); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_stencil_test +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +enable_stencil_test(bool val) { + if (_stencil_test_enabled != val) { + _stencil_test_enabled = val; + if (val) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glEnable(GL_STENCIL_TEST)" << endl; +#endif + glEnable(GL_STENCIL_TEST); + } else { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glDisable(GL_STENCIL_TEST)" << endl; +#endif + glDisable(GL_STENCIL_TEST); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::get_light_id +// Access: Public +// Description: Convert index to gl light id +//////////////////////////////////////////////////////////////////// +INLINE GLenum GLGraphicsStateGuardian::get_light_id(int index) const +{ + switch( index ) + { + case 0: return GL_LIGHT0; + case 1: return GL_LIGHT1; + case 2: return GL_LIGHT2; + case 3: return GL_LIGHT3; + case 4: return GL_LIGHT4; + case 5: return GL_LIGHT5; + case 6: return GL_LIGHT6; + case 7: return GL_LIGHT7; + default: + glgsg_cat.error() + << "get_light_id() - we don't currently support ids " + << "> 8" << endl; + break; + } + return GL_LIGHT0; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::get_clip_plane_id +// Access: Public +// Description: Convert index to gl clip plane id +//////////////////////////////////////////////////////////////////// +INLINE GLenum GLGraphicsStateGuardian:: +get_clip_plane_id(int index) const { + switch(index) { + case 0: return GL_CLIP_PLANE0; + case 1: return GL_CLIP_PLANE1; + case 2: return GL_CLIP_PLANE2; + case 3: return GL_CLIP_PLANE3; + case 4: return GL_CLIP_PLANE4; + case 5: return GL_CLIP_PLANE5; + default: + glgsg_cat.error() + << "get_clip_plane_id() - we don't currently support ids " + << "> 5" << endl; + break; + } + return GL_CLIP_PLANE0; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_light +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian::enable_light(int light, bool val) +{ + if ( _light_enabled[light] != val ) + { + _light_enabled[light] = val; + if ( val ) + { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glEnable(GL_LIGHT_" << light << ")" << endl; +#endif + glEnable( get_light_id( light ) ); + } + else + { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glDisable(GL_LIGHT_" << light << ")" << endl; +#endif + glDisable( get_light_id( light ) ); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_color_material +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +enable_color_material(bool val) { + if (_color_material_enabled != val) { + _color_material_enabled = val; + if (val) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glEnable(GL_COLOR_MATERIAL)" << endl; +#endif + glEnable(GL_COLOR_MATERIAL); + } else { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glDisable(GL_COLOR_MATERIAL)" << endl; +#endif + glDisable(GL_COLOR_MATERIAL); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_texturing +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +enable_texturing(bool val) { + if (_texturing_enabled != val) { + _texturing_enabled = val; + if (val) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glEnable(GL_TEXTURE_2D)" << endl; +#endif + glEnable(GL_TEXTURE_2D); + } else { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glDisable(GL_TEXTURE_2D)" << endl; +#endif + glDisable(GL_TEXTURE_2D); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_scissor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +enable_scissor(bool val) +{ + if ( _scissor_enabled != val ) { + _scissor_enabled = val; + if ( val ) + glEnable( GL_SCISSOR_TEST ); + else + glDisable( GL_SCISSOR_TEST ); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_clip_plane +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +enable_clip_plane(int clip_plane, bool val) { + if (_clip_plane_enabled[clip_plane] != val) { + _clip_plane_enabled[clip_plane] = val; + if (val) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glEnable(GL_CLIP_PLANE_" << clip_plane << ")" << endl; +#endif + glEnable(get_clip_plane_id(clip_plane)); + } else { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glDisable(GL_CLIP_PLANE_" << clip_plane << ")" << endl; +#endif + glDisable(get_clip_plane_id(clip_plane)); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_multisample_alpha_one +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +enable_multisample_alpha_one(bool val) { + if (_multisample_alpha_one_enabled != val) { + _multisample_alpha_one_enabled = val; +#ifdef GL_SAMPLE_ALPHA_TO_ONE_SGIS + if (val) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glEnable(GL_SAMPLE_ALPHA_TO_ONE_SGIS)" << endl; +#endif + glEnable(GL_SAMPLE_ALPHA_TO_ONE_SGIS); + } else { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glDisable(GL_SAMPLE_ALPHA_TO_ONE_SGIS)" << endl; +#endif + glDisable(GL_SAMPLE_ALPHA_TO_ONE_SGIS); + } +#endif // GL_SAMPLE_ALPHA_TO_ONE_SGIS + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_multisample_alpha_mask +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +enable_multisample_alpha_mask(bool val) { + if (_multisample_alpha_mask_enabled != val) { + _multisample_alpha_mask_enabled = val; +#ifdef GL_SAMPLE_ALPHA_TO_MASK_SGIS + if (val) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glEnable(GL_SAMPLE_ALPHA_TO_MASK_SGIS)" << endl; +#endif + glEnable(GL_SAMPLE_ALPHA_TO_MASK_SGIS); + } else { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glDisable(GL_SAMPLE_ALPHA_TO_MASK_SGIS)" << endl; +#endif + glDisable(GL_SAMPLE_ALPHA_TO_MASK_SGIS); + } +#endif // GL_SAMPLE_ALPHA_TO_MASK_SGIS + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_blend +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +enable_blend(bool val) { + if (_blend_enabled != val) { + _blend_enabled = val; + if (val) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glEnable(GL_BLEND)" << endl; +#endif + glEnable(GL_BLEND); + } else { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glDisable(GL_BLEND)" << endl; +#endif + glDisable(GL_BLEND); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_depth_test +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +enable_depth_test(bool val) { + if (_depth_test_enabled != val) { + _depth_test_enabled = val; + if (val) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glEnable(GL_DEPTH_TEST)" << endl; +#endif + glEnable(GL_DEPTH_TEST); + } else { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glDisable(GL_DEPTH_TEST)" << endl; +#endif + glDisable(GL_DEPTH_TEST); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_fog +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +enable_fog(bool val) { + if (_fog_enabled != val) { + _fog_enabled = val; + if (val) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glEnable(GL_FOG)" << endl; +#endif + glEnable(GL_FOG); + } else { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glDisable(GL_FOG)" << endl; +#endif + glDisable(GL_FOG); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_alpha_test +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +enable_alpha_test(bool val) { + if (_alpha_test_enabled != val) { + _alpha_test_enabled = val; + if (val) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glEnable(GL_ALPHA_TEST)" << endl; +#endif + glEnable(GL_ALPHA_TEST); + } else { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glDisable(GL_ALPHA_TEST)" << endl; +#endif + glDisable(GL_ALPHA_TEST); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::enable_polygon_offset +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GLGraphicsStateGuardian:: +enable_polygon_offset(bool val) { + if (_polygon_offset_enabled != val) { + _polygon_offset_enabled = val; + if (val) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glEnable(GL_POLYGON_OFFSET_*)" << endl; +#endif + glEnable(GL_POLYGON_OFFSET_FILL); + } else { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glDisable(GL_POLYGON_OFFSET_*)" << endl; +#endif + glDisable(GL_POLYGON_OFFSET_FILL); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::get_current_color_mat +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const LMatrix4f &GLGraphicsStateGuardian:: +get_current_color_mat() const { + return _current_color_mat; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::get_current_alpha_offset +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const float &GLGraphicsStateGuardian:: +get_current_alpha_offset() const { + return _current_alpha_offset; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::get_current_alpha_scale +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const float &GLGraphicsStateGuardian:: +get_current_alpha_scale() const { + return _current_alpha_scale; +} diff --git a/panda/src/glgsg/glGraphicsStateGuardian.cxx b/panda/src/glgsg/glGraphicsStateGuardian.cxx new file mode 100644 index 0000000000..d2f9853aa3 --- /dev/null +++ b/panda/src/glgsg/glGraphicsStateGuardian.cxx @@ -0,0 +1,5025 @@ +// Filename: glGraphicsStateGuardian.cxx +// Created by: drose (02Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "glGraphicsStateGuardian.h" +#include "glSavedFrameBuffer.h" +#include "glTextureContext.h" +#include "config_glgsg.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#if 0 +#define glGenTextures glGenTexturesEXT +#define glPrioritizeTextures glPrioritizeTexturesEXT +#define glBindTexture glBindTextureEXT +#define glCopyTexImage2D glCopyTexImage2DEXT +#define glDeleteTextures glDeleteTexturesEXT +#endif + +TypeHandle GLGraphicsStateGuardian::_type_handle; + +static void +issue_vertex_gl(const Geom *geom, Geom::VertexIterator &viterator) { + const Vertexf &vertex = geom->get_next_vertex(viterator); + // glgsg_cat.debug() << "Issuing vertex " << vertex << "\n"; + glVertex3fv(vertex.get_data()); +} + +static void +issue_normal_gl(const Geom *geom, Geom::NormalIterator &niterator) { + const Normalf &normal = geom->get_next_normal(niterator); + // glgsg_cat.debug() << "Issuing normal " << normal << "\n"; + glNormal3fv(normal.get_data()); +} + +static void +issue_texcoord_gl(const Geom *geom, Geom::TexCoordIterator &tciterator) { + const TexCoordf &texcoord = geom->get_next_texcoord(tciterator); + // glgsg_cat.debug() << "Issuing texcoord " << texcoord << "\n"; + glTexCoord3fv(texcoord.get_data()); +} + +static void +issue_color_gl(const Geom *geom, Geom::ColorIterator &citerator, + const GraphicsStateGuardianBase *) { + const Colorf &color = geom->get_next_color(citerator); + // glgsg_cat.debug() << "Issuing color " << color << "\n"; + glColor4fv(color.get_data()); +} + +static void +issue_transformed_color_gl(const Geom *geom, Geom::ColorIterator &citerator, + const GraphicsStateGuardianBase *gsg) { + const GLGraphicsStateGuardian *glgsg = DCAST(GLGraphicsStateGuardian, gsg); + const Colorf &color = geom->get_next_color(citerator); + + //This is a little cheat here just for a slight bit of + //efficiency. We don't want/need to transform the alpha by the color + //matrix, so use a Vertexf (which is only 3 component) and multiply + //this by the matrix, that will by us 1 dot product + Vertexf temp(color[0], color[1], color[2]); + temp = temp * glgsg->get_current_color_mat(); + float alpha = (color[3] * glgsg->get_current_alpha_scale()) + + glgsg->get_current_alpha_offset(); + + Colorf transformed(temp[0], temp[1], temp[2], alpha); + +// glgsg_cat.debug() << "Issuing color " << transformed << "\n"; +// glgsg_cat.debug() << "\tTransformed by " << glgsg->get_current_color_mat() << "\n"; +// glgsg_cat.debug() << "\tAlpha Transformed by " << glgsg->get_current_alpha_offset() << " " +// << glgsg->get_current_alpha_scale() << "\n"; + glColor4fv(transformed.get_data()); +} +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GLGraphicsStateGuardian:: +GLGraphicsStateGuardian(GraphicsWindow *win) : GraphicsStateGuardian(win) { + _light_enabled = (bool *)NULL; + _cur_light_enabled = (bool *)NULL; + _clip_plane_enabled = (bool *)NULL; + _cur_clip_plane_enabled = (bool *)NULL; + + // Create a default RenderTraverser. + if (gl_cull_traversal) { + _render_traverser = + new CullTraverser(this, RenderRelation::get_class_type()); + } else { + _render_traverser = + new DirectRenderTraverser(this, RenderRelation::get_class_type()); + } + + reset(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GLGraphicsStateGuardian:: +~GLGraphicsStateGuardian() { + free_pointers(); + release_all_textures(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::reset +// Access: Public, Virtual +// Description: Resets all internal state as if the gsg were newly +// created. +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +reset() { + free_pointers(); + activate(); + GraphicsStateGuardian::reset(); + + _buffer_mask = 0; + + // All GL implementations have the following buffers. (?) + _buffer_mask = (RenderBuffer::T_color | + RenderBuffer::T_depth | + RenderBuffer::T_stencil | + RenderBuffer::T_accum); + + // Check to see if we have double-buffering. + GLboolean has_back; + glGetBooleanv(GL_DOUBLEBUFFER, &has_back); + if (!has_back) { + _buffer_mask &= ~RenderBuffer::T_back; + } + + // Check to see if we have stereo (and therefore a right buffer). + GLboolean has_stereo; + glGetBooleanv(GL_STEREO, &has_stereo); + if (!has_stereo) { + _buffer_mask &= ~RenderBuffer::T_right; + } + + // Set up our clear values to invalid values, so the glClear* calls + // will be made initially. + _clear_color_red = -1.0; + _clear_color_green = -1.0; + _clear_color_blue = -1.0; + _clear_color_alpha = -1.0; + _clear_depth = -1.0; + _clear_stencil = -1; + _clear_accum_red = -1.0; + _clear_accum_green = -1.0; + _clear_accum_blue = -1.0; + _clear_accum_alpha = -1.0; + + // Set up the specific state values to GL's known initial values. + _draw_buffer_mode = (has_back) ? GL_BACK : GL_FRONT; + _read_buffer_mode = (has_back) ? GL_BACK : GL_FRONT; + _shade_model_mode = GL_SMOOTH; + glFrontFace(GL_CCW); + + _line_width = 1.0; + _point_size = 1.0; + _depth_mask = false; + _fog_mode = GL_EXP; + _alpha_func = GL_ALWAYS; + _alpha_func_ref = 0; + _polygon_mode = GL_FILL; + + _pack_alignment = 4; + _unpack_alignment = 4; + + // Set up all the enabled/disabled flags to GL's known initial + // values: everything off. + _multisample_enabled = false; + _line_smooth_enabled = false; + _point_smooth_enabled = false; + _color_material_enabled = false; + _scissor_enabled = false; + _lighting_enabled = false; + _normals_enabled = false; + _texturing_enabled = false; + _multisample_alpha_one_enabled = false; + _multisample_alpha_mask_enabled = false; + _blend_enabled = false; + _depth_test_enabled = false; + _fog_enabled = false; + _alpha_test_enabled = false; + _polygon_offset_enabled = false; + _decal_level = 0; + + // Dither is on by default in GL, let's turn it off + _dither_enabled = true; + enable_dither(false); + + // Stencil test is off by default + _stencil_test_enabled = false; + _stencil_func = GL_NOTEQUAL; + _stencil_op = GL_REPLACE; + + // Antialiasing. + enable_line_smooth(false); + enable_multisample(true); + + // Set up the light id map + glGetIntegerv( GL_MAX_LIGHTS, &_max_lights ); + _available_light_ids = PTA(Light*)(_max_lights); + _light_enabled = new bool[_max_lights]; + _cur_light_enabled = new bool[_max_lights]; + int i; + for (i = 0; i < _max_lights; i++ ) { + _available_light_ids[i] = NULL; + _light_enabled[i] = false; + } + + // Set up the clip plane id map + glGetIntegerv(GL_MAX_CLIP_PLANES, &_max_clip_planes); + _available_clip_plane_ids = PTA(PlaneNode*)(_max_clip_planes); + _clip_plane_enabled = new bool[_max_clip_planes]; + _cur_clip_plane_enabled = new bool[_max_clip_planes]; + for (i = 0; i < _max_clip_planes; i++) { + _available_clip_plane_ids[i] = NULL; + _clip_plane_enabled[i] = false; + } + + _current_projection_mat = LMatrix4f::ident_mat(); + _projection_mat_stack_count = 0; + + //Color and alpha transform variables + _color_transform_enabled = false; + _alpha_transform_enabled = false; + _current_color_mat = LMatrix4f::ident_mat(); + _current_alpha_offset = 0; + _current_alpha_scale = 1; + + // Make sure the GL state matches all of our initial attribute + // states. + PT(DepthTestAttribute) dta = new DepthTestAttribute; + PT(DepthWriteAttribute) dwa = new DepthWriteAttribute; + PT(CullFaceAttribute) cfa = new CullFaceAttribute; + PT(LightAttribute) la = new LightAttribute; + PT(TextureAttribute) ta = new TextureAttribute; + + dta->issue(this); + dwa->issue(this); + cfa->issue(this); + la->issue(this); + ta->issue(this); + + if (gl_cheap_textures) { + glgsg_cat.info() + << "Setting glHint() for fastest textures.\n"; + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::clear +// Access: Public, Virtual +// Description: Clears all of the indicated buffers to their assigned +// colors. +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +clear(const RenderBuffer &buffer) { + activate(); + + nassertv(buffer._gsg == this); + int buffer_type = buffer._buffer_type; + GLbitfield mask = 0; + NodeAttributes state; + + if (buffer_type & RenderBuffer::T_color) { + call_glClearColor(_color_clear_value[0], + _color_clear_value[1], + _color_clear_value[2], + _color_clear_value[3]); + state.set_attribute(ColorMaskTransition::get_class_type(), + new ColorMaskAttribute); + mask |= GL_COLOR_BUFFER_BIT; + + set_draw_buffer(buffer); + } + + if (buffer_type & RenderBuffer::T_depth) { + call_glClearDepth(_depth_clear_value); + mask |= GL_DEPTH_BUFFER_BIT; + + // In order to clear the depth buffer, the depth mask must enable + // writing to the depth buffer. + if (!_depth_mask) { + state.set_attribute(DepthWriteTransition::get_class_type(), + new DepthWriteAttribute); + } + } + + if (buffer_type & RenderBuffer::T_stencil) { + call_glClearStencil(_stencil_clear_value != false); + mask |= GL_STENCIL_BUFFER_BIT; + } + + if (buffer_type & RenderBuffer::T_accum) { + call_glClearAccum(_accum_clear_value[0], + _accum_clear_value[1], + _accum_clear_value[2], + _accum_clear_value[3]); + mask |= GL_ACCUM_BUFFER_BIT; + } + +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "glClear("; + if (mask & GL_COLOR_BUFFER_BIT) { + glgsg_cat.debug(false) << "GL_COLOR_BUFFER_BIT|"; + } + if (mask & GL_DEPTH_BUFFER_BIT) { + glgsg_cat.debug(false) << "GL_DEPTH_BUFFER_BIT|"; + } + if (mask & GL_STENCIL_BUFFER_BIT) { + glgsg_cat.debug(false) << "GL_STENCIL_BUFFER_BIT|"; + } + if (mask & GL_ACCUM_BUFFER_BIT) { + glgsg_cat.debug(false) << "GL_ACCUM_BUFFER_BIT|"; + } + glgsg_cat.debug(false) << ")" << endl; +#endif + + set_state(state, false); + glClear(mask); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::clear +// Access: Public, Virtual +// Description: Clears all of the indicated buffers to their assigned +// colors. +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +clear(const RenderBuffer &buffer, const DisplayRegion *region) { + DisplayRegionStack old_dr = push_display_region(region); + prepare_display_region(); + clear(buffer); + pop_display_region(old_dr); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::prepare_display_region +// Access: Public, Virtual +// Description: Prepare a display region for rendering (set up +// scissor region and viewport) +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +prepare_display_region() { + if (_current_display_region == (DisplayRegion*)0L) { + glgsg_cat.error() + << "Invalid NULL display region in prepare_display_region()\n"; + enable_scissor(false); + + } else if (_current_display_region != _actual_display_region) { + _actual_display_region = _current_display_region; + + int l, b, w, h; + _actual_display_region->get_region_pixels(l, b, w, h); + GLint x = GLint(l); + GLint y = GLint(b); + GLsizei width = GLsizei(w); + GLsizei height = GLsizei(h); + + enable_scissor( true ); + call_glScissor( x, y, width, height ); + call_glViewport( x, y, width, height ); + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::render_frame +// Access: Public, Virtual +// Description: Renders an entire frame, including all display +// regions within the frame, and includes any necessary +// pre- and post-processing like swapping buffers. +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +render_frame(const AllAttributesWrapper &initial_state) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "begin frame " << ClockObject::get_global_clock()->get_frame_count() + << " --------------------------------------------" << endl; +#endif + + _win->begin_frame(); + _decal_level = 0; + + // First, clear the entire window. + PT(DisplayRegion) win_dr = + _win->make_scratch_display_region(_win->get_width(), _win->get_height()); + if (win_dr == (DisplayRegion*)NULL) + { + cerr << "null scratch " << endl; + exit(0); + } + DisplayRegionStack old_dr = push_display_region(win_dr); + prepare_display_region(); + clear(get_render_buffer(RenderBuffer::T_back | RenderBuffer::T_depth)); + pop_display_region(old_dr); + + // Now render each of our layers in order. + int max_channel_index = _win->get_max_channel_index(); + for (int c = 0; c < max_channel_index; c++) { + if (_win->is_channel_defined(c)) { + GraphicsChannel *chan = _win->get_channel(c); + if (chan->is_active()) { + int num_layers = chan->get_num_layers(); + for (int l = 0; l < num_layers; l++) { + GraphicsLayer *layer = chan->get_layer(l); + if (layer->is_active()) { + int num_drs = layer->get_num_drs(); + for (int d = 0; d < num_drs; d++) { + DisplayRegion *dr = layer->get_dr(d); + if (dr == (DisplayRegion*)NULL) + { + cerr << "null camera layer " << endl; + exit(0); + } + Camera *cam = dr->get_camera(); + + // For each display region, render from the camera's view. + if (dr->is_active() && cam != (Camera *)NULL && + cam->is_active() && cam->get_scene() != (Node *)NULL) { + DisplayRegionStack old_dr = push_display_region(dr); + prepare_display_region(); + render_scene(cam->get_scene(), cam, initial_state); + pop_display_region(old_dr); + } + } + } + } + } + } + } + + // Now we're done with the frame processing. Clean up. + + // Let's turn off all the lights we had on, and clear the light + // cache--to force the lights to be reissued next frame, in case + // their parameters or positions have changed between frames. + + for (int i = 0; i < _max_lights; i++) { + if (_light_enabled[i]) { + enable_light(i, false); + } + _available_light_ids[i] = NULL; + } + + // Also force the lighting state to unlit, so that issue_light() + // will be guaranteed to be called next frame even if we have the + // same set of light pointers we had this frame. + NodeAttributes state; + state.set_attribute(LightTransition::get_class_type(), new LightAttribute); + set_state(state, false); + + // All this work to undo the lighting state each frame doesn't seem + // ideal--there may be a better way. Maybe if the lights were just + // more aware of whether their parameters or positions have changed + // at all? + + _win->end_frame(); + +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "end frame ----------------------------------------------" << endl; +#endif +} + + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::render_scene +// Access: Public, Virtual +// Description: Renders an entire scene, from the root node of the +// scene graph, as seen from a particular ProjectionNode +// and with a given initial state. This initial state +// may be modified during rendering. +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +render_scene(Node *root, const ProjectionNode *projnode, + const AllAttributesWrapper &initial_state) { +#ifdef GSG_VERBOSE + _pass_number = 0; + glgsg_cat.debug() + << "begin scene - - - - - - - - - - - - - - - - - - - - - - - - -" + << endl; +#endif + _current_root_node = root; + + render_subgraph(_render_traverser, root, projnode, initial_state, + AllTransitionsWrapper()); + +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "done scene - - - - - - - - - - - - - - - - - - - - - - - - -" + << endl; +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::render_subgraph +// Access: Public, Virtual +// Description: Renders a subgraph of the scene graph as seen from a +// given projection node, and with a particular initial +// state. This state may be modified by the render +// process. +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +render_subgraph(RenderTraverser *traverser, + Node *subgraph, const ProjectionNode *projnode, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans) { + // Calling activate() frequently seems to be intolerably expensive + // on some platforms. We'll limit ourselves for now to calling it + // only during the clear(). + + // activate(); + + const ProjectionNode *old_projection_node = _current_projection_node; + _current_projection_node = projnode; + LMatrix4f old_projection_mat = _current_projection_mat; + + // The projection matrix must always be right-handed Y-up, even if + // our coordinate system of choice is otherwise, because certain GL + // calls (specifically glTexGen(GL_SPHERE_MAP)) assume this kind of + // a coordinate system. Sigh. In order to implement a Z-up + // coordinate system, we'll store the Z-up conversion in the + // modelview matrix. + LMatrix4f projection_mat = + projnode->get_projection()->get_projection_mat(CS_yup_right); + + _current_projection_mat = projection_mat; + _projection_mat_stack_count++; + + // We load the projection matrix directly. +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glMatrixMode(GL_PROJECTION): " << projection_mat << endl; +#endif + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(_current_projection_mat.get_data()); + + // We infer the modelview matrix by doing a wrt on the projection + // node. + LMatrix4f modelview_mat; + get_rel_mat(subgraph, _current_projection_node, modelview_mat); + + if (_coordinate_system != CS_yup_right) { + // Now we build the coordinate system conversion into the + // modelview matrix (as described in the paragraph above). + modelview_mat = modelview_mat * + LMatrix4f::convert_mat(_coordinate_system, CS_yup_right); + } + + // The modelview matrix will be loaded as each geometry is + // encountered. So we set the supplied modelview matrix as an + // initial value instead of loading it now. + AllTransitionsWrapper sub_trans = net_trans; + sub_trans.set_transition(new TransformTransition(modelview_mat)); + + render_subgraph(traverser, subgraph, initial_state, sub_trans); + + _current_projection_node = old_projection_node; + _current_projection_mat = old_projection_mat; + _projection_mat_stack_count--; + + + // We must now restore the projection matrix from before. We could + // do a push/pop matrix, but OpenGL doesn't promise more than 2 + // levels in the projection matrix stack, so we'd better do it in + // the CPU. + if (_projection_mat_stack_count > 0) { + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(_current_projection_mat.get_data()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::render_subgraph +// Access: Public, Virtual +// Description: Renders a subgraph of the scene graph as seen from the +// current projection node, and with a particular +// initial state. This state may be modified during the +// render process. +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +render_subgraph(RenderTraverser *traverser, Node *subgraph, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans) { +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "begin subgraph (pass " << ++_pass_number + << ") - - - - - - - - - - - - - - - - - - - - - - - - -" << endl; +#endif + // activate(); + + nassertv(traverser != (RenderTraverser *)NULL); + traverser->traverse(subgraph, initial_state, net_trans); + +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "end subgraph (pass " << _pass_number + << ") - - - - - - - - - - - - - - - - - - - - - - - - -" + << endl; +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::draw_point +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +draw_point(const GeomPoint *geom) { + // activate(); + +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "draw_point()" << endl; +#endif + + call_glPointSize(geom->get_size()); + + int nprims = geom->get_num_prims(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::NormalIterator ni = geom->make_normal_iterator(); + Geom::TexCoordIterator ti = geom->make_texcoord_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color_gl); + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + issuer.issue_normal(G_OVERALL, ni); + + glBegin(GL_POINTS); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + issuer.issue_normal(G_PER_PRIM, ni); + + // Draw per vertex, same thing. + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_normal(G_PER_VERTEX, ni); + issuer.issue_texcoord(G_PER_VERTEX, ti); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + + glEnd(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::draw_line +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +draw_line(const GeomLine* geom) { + // activate(); + +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "draw_line()" << endl; +#endif + + call_glLineWidth(geom->get_width()); + + int nprims = geom->get_num_prims(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color_gl); + + if (geom->get_binding(G_COLOR) == G_PER_VERTEX) { + call_glShadeModel(GL_SMOOTH); + } else { + call_glShadeModel(GL_FLAT); + } + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + + glBegin(GL_LINES); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + + for (int j = 0; j < 2; j++) { + // Draw per vertex + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + } + + glEnd(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::draw_linestrip +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +draw_linestrip(const GeomLinestrip* geom) { + // activate(); + +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "draw_linestrip()" << endl; +#endif + + call_glLineWidth(geom->get_width()); + + int nprims = geom->get_num_prims(); + const int *plen = geom->get_lengths(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color_gl); + + if (geom->get_binding(G_COLOR) == G_PER_VERTEX) { + call_glShadeModel(GL_SMOOTH); + } else { + call_glShadeModel(GL_FLAT); + } + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + + int num_verts = *(plen++); + nassertv(num_verts >= 2); + + glBegin(GL_LINE_STRIP); + + // Per-component attributes for the first line segment? + issuer.issue_color(G_PER_COMPONENT, ci); + + // Draw the first 2 vertices + int v; + for (v = 0; v < 2; v++) { + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + + // Now draw each of the remaining vertices. Each vertex from + // this point on defines a new line segment. + for (v = 2; v < num_verts; v++) { + // Per-component attributes? + issuer.issue_color(G_PER_COMPONENT, ci); + + // Per-vertex attributes + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + glEnd(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::draw_sprite +// Access: Public, Virtual +// Description: CSN, 7/11/00 +//////////////////////////////////////////////////////////////////// + +// this class exists because an alpha sort is necessary for correct +// sprite rendering, and we can't simply sort the vertex arrays as +// each vertex may or may not have corresponding information in the +// x/y texel-world-ratio and rotation arrays. +class WrappedSprite { +public: + Vertexf _v; + Colorf _c; + float _x_ratio; + float _y_ratio; + float _theta; +}; + +// this struct exists because the STL can sort faster than i can. +struct draw_sprite_vertex_less { + INLINE bool operator ()(const WrappedSprite& v0, + const WrappedSprite& v1) const { + return v0._v[2] < v1._v[2]; } +}; + +void GLGraphicsStateGuardian:: +draw_sprite(const GeomSprite *geom) { + // this is a little bit of a mess, but it's ok. Here's the deal: + // we want to draw, and draw quickly, an arbitrarily large number + // of sprites all facing the screen. Performing the billboard math + // for ~1000 sprites is way too slow. Ideally, we want one + // matrix transformation that will handle everything, and this is + // just about what ends up happening. We're getting the front-facing + // effect by setting up a new frustum (of the same z-depth as the + // current one) that is very small in x and y. This way regularly + // rendered triangles that might not be EXACTLY facing the camera + // will certainly look close enough. Then, we transform to camera-space + // by hand and apply the inverse frustum to the transformed point. + // For some cracked out reason, this actually works. + +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "draw_sprite()" << endl; +#endif + + Texture *tex = geom->get_texture(); + nassertv(tex != (Texture *) NULL); + + // get the array traversal set up. + int nprims = geom->get_num_prims(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + // save the modelview matrix + LMatrix4f modelview_mat; + + const TransformAttribute *ctatt; + if (!get_attribute_into(ctatt, _state, TransformTransition::get_class_type())) + modelview_mat = LMatrix4f::ident_mat(); + else + modelview_mat = ctatt->get_matrix(); + + // get the camera information + float tnear, tfar, hfov, aspect_ratio; + tnear = _actual_display_region->get_camera()->get_near(); + tfar = _actual_display_region->get_camera()->get_far(); + hfov = _actual_display_region->get_camera()->get_hfov(); + aspect_ratio = _actual_display_region->get_camera()->get_aspect(); + + // to assure that the scale between the two frustra stays the same + // (if they are different, sprites move at different speeds than the world), + // we have to apply the frustum inverse to the point, then render it in our + // own frustum. Since the z values are identical and 1:1, we only need + // concern ourselves with the x and y mappings, which are conveniently linear. + + float x_frustum_scale, y_frustum_scale; + float recip_x_frustum_scale, recip_y_frustum_scale; + + // extract the left and top bounds of the current camera + x_frustum_scale = tanf(hfov * 0.5f * (3.1415926f / 180.0f)) * tnear; + recip_x_frustum_scale = 1.0f / x_frustum_scale; + y_frustum_scale = x_frustum_scale / aspect_ratio; + recip_y_frustum_scale = 1.0f / y_frustum_scale; + + // load up our own matrices + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(LMatrix4f::ident_mat().get_data()); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-1.0f, 1.0f, -1.0f, 1.0f, tnear, tfar); + + // precomputation stuff + float half_width = 0.5f * (float) tex->_pbuffer->get_xsize(); + float half_height = 0.5f * (float) tex->_pbuffer->get_ysize(); + float scaled_width, scaled_height; + + // set up the texture-rendering state + NodeAttributes state; + + TextureAttribute *ta = new TextureAttribute; + ta->set_on(tex); + state.set_attribute(TextureTransition::get_class_type(), ta); + + TextureApplyAttribute *taa = new TextureApplyAttribute; + taa->set_mode(TextureApplyProperty::M_modulate); + state.set_attribute(TextureApplyTransition::get_class_type(), taa); + + set_state(state, false); + + // the user can override alpha sorting if they want + bool alpha = false; + + if (geom->get_alpha_disable() == false) { + // figure out if alpha's enabled (if not, no reason to sort) + const TransparencyAttribute *ctratt; + if (get_attribute_into(ctratt, _state, TransparencyTransition::get_class_type())) + alpha = true; + } + + // sort container and iterator + vector< WrappedSprite > cameraspace_vector; + vector< WrappedSprite >::iterator vec_iter; + + // inner loop vars + int i; + Vertexf source_vert, cameraspace_vert; + float x, y, z, *x_walk, *y_walk, *theta_walk; + float theta; + + nassertv(geom->get_x_bind_type() != G_PER_VERTEX); + nassertv(geom->get_y_bind_type() != G_PER_VERTEX); + + // set up the non-built-in bindings + bool x_overall = (geom->get_x_bind_type() == G_OVERALL); + bool y_overall = (geom->get_y_bind_type() == G_OVERALL); + bool theta_overall = (geom->get_theta_bind_type() == G_OVERALL); + bool color_overall = (geom->get_binding(G_COLOR) == G_OVERALL); + bool theta_on = !(geom->get_theta_bind_type() == G_OFF); + + // x direction + if (x_overall == true) + scaled_width = geom->_x_texel_ratio[0] * half_width; + else { + nassertv((geom->_x_texel_ratio.size() >= geom->get_num_prims())); + x_walk = &geom->_x_texel_ratio[0]; + } + + // y direction + if (y_overall == true) + scaled_height = geom->_y_texel_ratio[0] * half_height * aspect_ratio; + else { + nassertv((geom->_y_texel_ratio.size() >= geom->get_num_prims())); + y_walk = &geom->_y_texel_ratio[0]; + } + + // theta + if (theta_on) { + if (theta_overall == true) + theta = geom->_theta[0]; + else { + nassertv((geom->_theta.size() >= geom->get_num_prims())); + theta_walk = &geom->_theta[0]; + } + } + + ///////////////////////////////////////////////////////////////////// + // INNER LOOP PART 1 STARTS HERE + // Here we transform each point to cameraspace and fill our sort + // vector with the final geometric information. + ///////////////////////////////////////////////////////////////////// + + + // the state is set, start running the prims + for (i = 0; i < nprims; i++) { + source_vert = geom->get_next_vertex(vi); + + // this mult converts to y-up cameraspace. + cameraspace_vert = modelview_mat * source_vert; + z = cameraspace_vert[2]; + + // do the inverse transform on the cameraspace point. + x = cameraspace_vert[0] * recip_x_frustum_scale; + y = cameraspace_vert[1] * recip_y_frustum_scale; + + // build the final object that will go into the vector. + WrappedSprite ws; + ws._v.set(x, y, z); + + if (color_overall == false) + ws._c = geom->get_next_color(ci); + if (x_overall == false) + ws._x_ratio = *x_walk++; + if (y_overall == false) + ws._y_ratio = *y_walk++; + if (theta_on) { + if (theta_overall == false) + ws._theta = *theta_walk++; + } + + cameraspace_vector.push_back(ws); + } + + // now the verts are properly sorted by alpha (if necessary). Of course, + // the sort is only local, not scene-global, so if you look closely you'll + // notice that alphas may be screwy. It's ok though, because this is fast. + // if you want accuracy, use billboards and take the speed hit. + if (alpha) { + sort(cameraspace_vector.begin(), cameraspace_vector.end(), + draw_sprite_vertex_less()); + } + + int tex_bottom = 0, tex_top = 1, tex_right = 1, tex_left = 0; + Vertexf ul, ur, ll, lr; +// float top, bottom, left, right; +// float sin_theta, cos_theta, radians; +// float final_left, final_right, final_top, final_bottom; + + if (color_overall == true) + glColor4fv(geom->get_next_color(ci).get_data()); + + //////////////////////////////////////////////////////////////////////////// + // INNER LOOP PART 2 STARTS HERE + // Now we run through the cameraspace vector and compute the geometry for each + // tristrip. This includes scaling as per the ratio arrays, as well as + // rotating in the z. + //////////////////////////////////////////////////////////////////////////// + + vec_iter = cameraspace_vector.begin(); + for (; vec_iter != cameraspace_vector.end(); vec_iter++) { + WrappedSprite& cur_image = *vec_iter; + + // if not G_OVERALL, calculate the scale factors + if (x_overall == false) + scaled_width = cur_image._x_ratio * half_width; + + if (y_overall == false) + scaled_height = cur_image._y_ratio * half_height * aspect_ratio; + + // if not G_OVERALL, do some trig for this z rotate + if (theta_on) { + if (theta_overall == false) + theta = cur_image._theta; + + // create the rotated points + LMatrix3f xform_mat = LMatrix3f::rotate_mat(theta) * + LMatrix3f::scale_mat(scaled_width, scaled_height); + + ur = (xform_mat * LVector3f(1, 1, 0)) + cur_image._v; + ul = (xform_mat * LVector3f(-1, 1, 0)) + cur_image._v; + lr = (xform_mat * LVector3f(1, -1, 0)) + cur_image._v; + ll = (xform_mat * LVector3f(-1, -1, 0)) + cur_image._v; + } + else { + // create the normal points + ur.set(scaled_width, scaled_height, 0); + ul.set(-scaled_width, scaled_height, 0); + lr.set(scaled_width, -scaled_height, 0); + ll.set(-scaled_width, -scaled_height, 0); + + ur += cur_image._v; + ul += cur_image._v; + lr += cur_image._v; + ll += cur_image._v; + } + + // set the color + if (color_overall == false) + glColor4fv(cur_image._c.get_data()); + + // draw each one as a 2-element tri-strip + glBegin(GL_TRIANGLE_STRIP); + glNormal3f(0.0f, 0.0f, 1.0f); + glTexCoord2i(tex_left, tex_bottom); glVertex3fv(ll.get_data()); + glTexCoord2i(tex_right, tex_bottom); glVertex3fv(lr.get_data()); + glTexCoord2i(tex_left, tex_top); glVertex3fv(ul.get_data()); + glTexCoord2i(tex_right, tex_top); glVertex3fv(ur.get_data()); + glEnd(); + } + + // restore the matrices + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(modelview_mat.get_data()); + + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(_current_projection_mat.get_data()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::draw_polygon +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +draw_polygon(const GeomPolygon *geom) { + // activate(); + +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "draw_polygon()" << endl; +#endif + + int nprims = geom->get_num_prims(); + const int *plen = geom->get_lengths(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::NormalIterator ni = geom->make_normal_iterator(); + Geom::TexCoordIterator ti = geom->make_texcoord_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color_gl); + + // If we have per-vertex colors or normals, we need smooth shading. + // Otherwise we want flat shading for performance reasons. + if (geom->get_binding(G_COLOR) == G_PER_VERTEX || + geom->get_binding(G_NORMAL) == G_PER_VERTEX) { + call_glShadeModel(GL_SMOOTH); + } else { + call_glShadeModel(GL_FLAT); + } + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + issuer.issue_normal(G_OVERALL, ni); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + issuer.issue_normal(G_PER_PRIM, ni); + + int num_verts = *(plen++); + nassertv(num_verts >= 3); + + glBegin(GL_POLYGON); + + // Draw the vertices. + int v; + for (v = 0; v < num_verts; v++) { + // Per-vertex attributes. + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_normal(G_PER_VERTEX, ni); + issuer.issue_texcoord(G_PER_VERTEX, ti); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + glEnd(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::draw_tri +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +draw_tri(const GeomTri *geom) { + // activate(); + +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "draw_tri()" << endl; +#endif + + int nprims = geom->get_num_prims(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::NormalIterator ni = geom->make_normal_iterator(); + Geom::TexCoordIterator ti = geom->make_texcoord_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + GeomIssuer::IssueColor *issue_color; + + if (!_color_transform_enabled && !_alpha_transform_enabled) { + issue_color = issue_color_gl; + } + else { + issue_color = issue_transformed_color_gl; + } + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color); + + // If we have per-vertex colors or normals, we need smooth shading. + // Otherwise we want flat shading for performance reasons. + if (geom->get_binding(G_COLOR) == G_PER_VERTEX || + geom->get_binding(G_NORMAL) == G_PER_VERTEX) { + call_glShadeModel(GL_SMOOTH); + } else { + call_glShadeModel(GL_FLAT); + } + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + issuer.issue_normal(G_OVERALL, ni); + + glBegin(GL_TRIANGLES); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + issuer.issue_normal(G_PER_PRIM, ni); + + for (int j = 0; j < 3; j++) { + // Draw per vertex + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_normal(G_PER_VERTEX, ni); + issuer.issue_texcoord(G_PER_VERTEX, ti); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + } + + glEnd(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::draw_quad +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +draw_quad(const GeomQuad *geom) { + // activate(); + +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "draw_quad()" << endl; +#endif + + int nprims = geom->get_num_prims(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::NormalIterator ni = geom->make_normal_iterator(); + Geom::TexCoordIterator ti = geom->make_texcoord_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + GeomIssuer::IssueColor *issue_color; + + if (!_color_transform_enabled && !_alpha_transform_enabled) { + issue_color = issue_color_gl; + } + else { + issue_color = issue_transformed_color_gl; + } + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color); + + // If we have per-vertex colors or normals, we need smooth shading. + // Otherwise we want flat shading for performance reasons. + if (geom->get_binding(G_COLOR) == G_PER_VERTEX || + geom->get_binding(G_NORMAL) == G_PER_VERTEX) { + call_glShadeModel(GL_SMOOTH); + } else { + call_glShadeModel(GL_FLAT); + } + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + issuer.issue_normal(G_OVERALL, ni); + + glBegin(GL_QUADS); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + issuer.issue_normal(G_PER_PRIM, ni); + + for (int j = 0; j < 4; j++) { + // Draw per vertex + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_normal(G_PER_VERTEX, ni); + issuer.issue_texcoord(G_PER_VERTEX, ti); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + } + + glEnd(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::draw_tristrip +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +draw_tristrip(const GeomTristrip *geom) { + // activate(); + +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "draw_tristrip()" << endl; +#endif + + int nprims = geom->get_num_prims(); + const int *plen = geom->get_lengths(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::NormalIterator ni = geom->make_normal_iterator(); + Geom::TexCoordIterator ti = geom->make_texcoord_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + GeomIssuer::IssueColor *issue_color; + + if (!_color_transform_enabled && !_alpha_transform_enabled) { + issue_color = issue_color_gl; + } + else { + issue_color = issue_transformed_color_gl; + } + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color); + + // If we have per-vertex colors or normals, we need smooth shading. + // Otherwise we want flat shading for performance reasons. + if (geom->get_binding(G_COLOR) == G_PER_VERTEX || + geom->get_binding(G_NORMAL) == G_PER_VERTEX) { + call_glShadeModel(GL_SMOOTH); + } else { + call_glShadeModel(GL_FLAT); + } + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + issuer.issue_normal(G_OVERALL, ni); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + issuer.issue_normal(G_PER_PRIM, ni); + + int num_verts = *(plen++); + nassertv(num_verts >= 3); + + glBegin(GL_TRIANGLE_STRIP); + + // Per-component attributes for the first triangle? + issuer.issue_color(G_PER_COMPONENT, ci); + issuer.issue_normal(G_PER_COMPONENT, ni); + + // Draw the first three vertices. + int v; + for (v = 0; v < 3; v++) { + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_normal(G_PER_VERTEX, ni); + issuer.issue_texcoord(G_PER_VERTEX, ti); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + + // Now draw each of the remaining vertices. Each vertex from + // this point on defines a new triangle. + for (v = 3; v < num_verts; v++) { + // Per-component attributes? + issuer.issue_color(G_PER_COMPONENT, ci); + issuer.issue_normal(G_PER_COMPONENT, ni); + + // Per-vertex attributes. + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_normal(G_PER_VERTEX, ni); + issuer.issue_texcoord(G_PER_VERTEX, ti); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + glEnd(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::draw_trifan +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +draw_trifan(const GeomTrifan *geom) { + // activate(); + +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "draw_trifan()" << endl; +#endif + + int nprims = geom->get_num_prims(); + const int *plen = geom->get_lengths(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::NormalIterator ni = geom->make_normal_iterator(); + Geom::TexCoordIterator ti = geom->make_texcoord_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + GeomIssuer::IssueColor *issue_color; + + if (!_color_transform_enabled && !_alpha_transform_enabled) { + issue_color = issue_color_gl; + } + else { + issue_color = issue_transformed_color_gl; + } + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color); + + // If we have per-vertex colors or normals, we need smooth shading. + // Otherwise we want flat shading for performance reasons. + if (geom->get_binding(G_COLOR) == G_PER_VERTEX || + geom->get_binding(G_NORMAL) == G_PER_VERTEX) { + call_glShadeModel(GL_SMOOTH); + } else { + call_glShadeModel(GL_FLAT); + } + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + issuer.issue_normal(G_OVERALL, ni); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + issuer.issue_normal(G_PER_PRIM, ni); + + int num_verts = *(plen++); + nassertv(num_verts >= 3); + + glBegin(GL_TRIANGLE_FAN); + + // Per-component attributes for the first triangle? + issuer.issue_color(G_PER_COMPONENT, ci); + issuer.issue_normal(G_PER_COMPONENT, ni); + + // Draw the first three vertices. + int v; + for (v = 0; v < 3; v++) { + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_normal(G_PER_VERTEX, ni); + issuer.issue_texcoord(G_PER_VERTEX, ti); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + + // Now draw each of the remaining vertices. Each vertex from + // this point on defines a new triangle. + for (v = 3; v < num_verts; v++) { + // Per-component attributes? + issuer.issue_color(G_PER_COMPONENT, ci); + issuer.issue_normal(G_PER_COMPONENT, ni); + + // Per-vertex attributes. + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_normal(G_PER_VERTEX, ni); + issuer.issue_texcoord(G_PER_VERTEX, ti); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + glEnd(); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::draw_sphere +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +draw_sphere(const GeomSphere *geom) { + // activate(); + +#ifdef GSG_VERBOSE + glgsg_cat.debug() << "draw_sphere()" << endl; +#endif + + int nprims = geom->get_num_prims(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + GeomIssuer::IssueColor *issue_color; + + if (!_color_transform_enabled && !_alpha_transform_enabled) { + issue_color = issue_color_gl; + } + else { + issue_color = issue_transformed_color_gl; + } + + GeomIssuer issuer(geom, this, + issue_vertex_gl, + issue_normal_gl, + issue_texcoord_gl, + issue_color); + + if (wants_normals()) { + call_glShadeModel(GL_SMOOTH); + } else { + call_glShadeModel(GL_FLAT); + } + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + + GLUquadricObj *sph = gluNewQuadric(); + gluQuadricNormals(sph, wants_normals() ? (GLenum)GLU_SMOOTH : (GLenum)GLU_NONE); + gluQuadricTexture(sph, wants_texcoords() ? (GLenum)GL_TRUE : (GLenum)GL_FALSE); + gluQuadricOrientation(sph, (GLenum)GLU_OUTSIDE); + gluQuadricDrawStyle(sph, (GLenum)GLU_FILL); + //gluQuadricDrawStyle(sph, (GLenum)GLU_LINE); + + for (int i = 0; i < nprims; i++) { + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + + for (int j = 0; j < 2; j++) { + // Draw per vertex + issuer.issue_color(G_PER_VERTEX, ci); + } + Vertexf center = geom->get_next_vertex(vi); + Vertexf edge = geom->get_next_vertex(vi); + LVector3f v = edge - center; + float r = sqrt(dot(v, v)); + + // Since gluSphere doesn't have a center parameter, we have to use + // a matrix transform. + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glMultMatrixf(LMatrix4f::translate_mat(center).get_data()); + + // Now render the sphere using GLU calls. + gluSphere(sph, r, 16, 10); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + } + + gluDeleteQuadric(sph); +} + + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::prepare_texture +// Access: Public, Virtual +// Description: Creates a new retained-mode representation of the +// given texture, and returns a newly-allocated +// TextureContext pointer to reference it. It is the +// responsibility of the calling function to later +// call release_texture() with this same pointer (which +// will also delete the pointer). +//////////////////////////////////////////////////////////////////// +TextureContext *GLGraphicsStateGuardian:: +prepare_texture(Texture *tex) { + // activate(); + GLTextureContext *gtc = new GLTextureContext(tex); + glGenTextures(1, >c->_index); + + bind_texture(gtc); + glPrioritizeTextures(1, >c->_index, >c->_priority); + specify_texture(tex); + apply_texture_immediate(tex); + + bool inserted = mark_prepared_texture(gtc); + + // If this assertion fails, the same texture was prepared twice, + // which shouldn't be possible, since the texture itself should + // detect this. + nassertr(inserted, NULL); + + return gtc; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::apply_texture +// Access: Public, Virtual +// Description: Makes the texture the currently available texture for +// rendering. +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +apply_texture(TextureContext *tc) { + // activate(); + bind_texture(tc); + + /* + To render in immediate mode: + specify_texture(tex); + apply_texture_immediate(tex); + */ +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::release_texture +// Access: Public, Virtual +// Description: Frees the GL resources previously allocated for the +// texture. +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +release_texture(TextureContext *tc) { + // activate(); + GLTextureContext *gtc = DCAST(GLTextureContext, tc); + Texture *tex = tc->_texture; + + glDeleteTextures(1, >c->_index); + gtc->_index = 0; + + bool erased = unmark_prepared_texture(gtc); + + // If this assertion fails, a texture was released that hadn't been + // prepared (or a texture was released twice). + nassertv(erased); + + tex->clear_gsg(this); + + delete gtc; +} + +static int logs[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, + 4096, 0 }; + +// This function returns the smallest power of two greater than or +// equal to x. +static int binary_log_cap(const int x) { + int i = 0; + for (; (x > logs[i]) && (logs[i] != 0); ++i); + if (logs[i] == 0) + return 4096; + return logs[i]; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::copy_texture +// Access: Public, Virtual +// Description: Copy the pixel region indicated by the display +// region from the framebuffer into texture memory +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +copy_texture(TextureContext *tc, const DisplayRegion *dr) { + nassertv(tc != NULL && dr != NULL); + // activate(); + + Texture *tex = tc->_texture; + + // Determine the size of the grab from the given display region + // If the requested region is not a power of two, grab a region that is + // a power of two that contains the requested region + int xo, yo, req_w, req_h; + dr->get_region_pixels(xo, yo, req_w, req_h); + int w = binary_log_cap(req_w); + int h = binary_log_cap(req_h); + if (w != req_w || h != req_h) { + tex->_requested_w = req_w; + tex->_requested_h = req_h; + tex->_has_requested_size = true; + } + + PixelBuffer *pb = tex->_pbuffer; + + pb->set_xorg(xo); + pb->set_yorg(yo); + pb->set_xsize(w); + pb->set_ysize(h); + + bind_texture(tc); + + glCopyTexImage2D( GL_TEXTURE_2D, tex->get_level(), + get_internal_image_format(pb->get_format()), + pb->get_xorg(), pb->get_yorg(), + pb->get_xsize(), pb->get_ysize(), pb->get_border() ); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::copy_texture +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +copy_texture(TextureContext *tc, const DisplayRegion *dr, const RenderBuffer &rb) { + // activate(); + set_read_buffer(rb); + copy_texture(tc, dr); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::draw_texture +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +draw_texture(TextureContext *tc, const DisplayRegion *dr) { + nassertv(tc != NULL && dr != NULL); + // activate(); + + Texture *tex = tc->_texture; + + DisplayRegionStack old_dr = push_display_region(dr); + prepare_display_region(); + + NodeAttributes state; + CullFaceAttribute *cfa = new CullFaceAttribute; + cfa->set_mode(CullFaceProperty::M_cull_none); + DepthTestAttribute *dta = new DepthTestAttribute; + dta->set_mode(DepthTestProperty::M_none); + DepthWriteAttribute *dwa = new DepthWriteAttribute; + dwa->set_off(); + TextureAttribute *ta = new TextureAttribute; + ta->set_on(tex); + TextureApplyAttribute *taa = new TextureApplyAttribute; + taa->set_mode(TextureApplyProperty::M_decal); + + state.set_attribute(LightTransition::get_class_type(), + new LightAttribute); + state.set_attribute(ColorMaskTransition::get_class_type(), + new ColorMaskAttribute); + state.set_attribute(RenderModeTransition::get_class_type(), + new RenderModeAttribute); + state.set_attribute(TexMatrixTransition::get_class_type(), + new TexMatrixAttribute); + state.set_attribute(TransformTransition::get_class_type(), + new TransformAttribute); + state.set_attribute(ColorBlendTransition::get_class_type(), + new ColorBlendAttribute); + state.set_attribute(CullFaceTransition::get_class_type(), cfa); + state.set_attribute(DepthTestTransition::get_class_type(), dta); + state.set_attribute(DepthWriteTransition::get_class_type(), dwa); + state.set_attribute(TextureTransition::get_class_type(), ta); + state.set_attribute(TextureApplyTransition::get_class_type(), taa); + set_state(state, false); + + // We set up an orthographic projection that defines our entire + // viewport to the range [0..1] in both dimensions. Then, when we + // create a unit square polygon below, it will exactly fill the + // viewport (and thus exactly fill the display region). + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + gluOrtho2D(0, 1, 0, 1); + + float txl, txr, tyt, tyb; + txl = tyb = 0.; + if (tex->_has_requested_size) { + txr = ((float)(tex->_requested_w)) / ((float)(tex->_pbuffer->get_xsize())); + tyt = ((float)(tex->_requested_h)) / ((float)(tex->_pbuffer->get_ysize())); + } else { + txr = tyt = 1.; + } + + // This two-triangle strip is actually a quad. But it's usually + // better to render quads as tristrips anyway. + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(txl, tyb); glVertex2i(0, 0); + glTexCoord2f(txr, tyb); glVertex2i(1, 0); + glTexCoord2f(txl, tyt); glVertex2i(0, 1); + glTexCoord2f(txr, tyt); glVertex2i(1, 1); + glEnd(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + pop_display_region(old_dr); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::draw_texture +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +draw_texture(TextureContext *tc, const DisplayRegion *dr, const RenderBuffer &rb) { + // activate(); + set_draw_buffer(rb); + draw_texture(tc, dr); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::texture_to_pixel_buffer +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb) { + nassertv(tc != NULL && pb != NULL); + // activate(); + + Texture *tex = tc->_texture; + + int w = tex->_pbuffer->get_xsize(); + int h = tex->_pbuffer->get_ysize(); + + PT(DisplayRegion) dr = _win->make_scratch_display_region(w, h); + + FrameBufferStack old_fb = push_frame_buffer + (get_render_buffer(RenderBuffer::T_back | RenderBuffer::T_depth), + dr); + + texture_to_pixel_buffer(tc, pb, dr); + + pop_frame_buffer(old_fb); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::texture_to_pixel_buffer +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb, + const DisplayRegion *dr) { + nassertv(tc != NULL && pb != NULL && dr != NULL); + // activate(); + + Texture *tex = tc->_texture; + + // Do a deep copy to initialize the pixel buffer + pb->copy(tex->_pbuffer); + + // If the image was empty, we need to render the texture into the frame + // buffer and then copy the results into the pixel buffer's image + if (pb->_image.empty()) { + int w = pb->get_xsize(); + int h = pb->get_ysize(); + draw_texture(tc, dr); + pb->_image = PTA_uchar(w * h * pb->get_num_components()); + copy_pixel_buffer(pb, dr); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::copy_pixel_buffer +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr) { + nassertv(pb != NULL && dr != NULL); + // activate(); + set_pack_alignment(1); + + NodeAttributes state; + + // Bug fix for RE, RE2, and VTX - need to disable texturing in order + // for glReadPixels() to work + // NOTE: reading the depth buffer is *much* slower than reading the + // color buffer + state.set_attribute(TextureTransition::get_class_type(), + new TextureAttribute); + set_state(state, false); + + int xo, yo, w, h; + dr->get_region_pixels(xo, yo, w, h); + +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glReadPixels(" << pb->get_xorg() << ", " << pb->get_yorg() + << ", " << pb->get_xsize() << ", " << pb->get_ysize() + << ", "; + switch (get_external_image_format(pb->get_format())) { + case GL_DEPTH_COMPONENT: + glgsg_cat.debug(false) << "GL_DEPTH_COMPONENT, "; + break; + case GL_RGB: + glgsg_cat.debug(false) << "GL_RGB, "; + break; + case GL_RGBA: + glgsg_cat.debug(false) << "GL_RGBA, "; + break; + default: + glgsg_cat.debug(false) << "unknown, "; + break; + } + switch (get_image_type(pb->get_image_type())) { + case GL_UNSIGNED_BYTE: + glgsg_cat.debug(false) << "GL_UNSIGNED_BYTE, "; + break; + case GL_FLOAT: + glgsg_cat.debug(false) << "GL_FLOAT, "; + break; + default: + glgsg_cat.debug(false) << "unknown, "; + break; + } + glgsg_cat.debug(false) + << (void *)pb->_image.p() << ")" << endl; +#endif + + glReadPixels( pb->get_xorg() + xo, pb->get_yorg() + yo, + pb->get_xsize(), pb->get_ysize(), + get_external_image_format(pb->get_format()), + get_image_type(pb->get_image_type()), + pb->_image.p() ); + + nassertv(!pb->_image.empty()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::copy_pixel_buffer +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const RenderBuffer &rb) { + // activate(); + set_read_buffer(rb); + copy_pixel_buffer(pb, dr); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::draw_pixel_buffer +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const NodeAttributes& na) { + nassertv(pb != NULL && dr != NULL); + nassertv(!pb->_image.empty()); + // activate(); + + DisplayRegionStack old_dr = push_display_region(dr); + prepare_display_region(); + + NodeAttributes state(na); + state.set_attribute(LightTransition::get_class_type(), + new LightAttribute); + state.set_attribute(TextureTransition::get_class_type(), + new TextureAttribute); + state.set_attribute(TransformTransition::get_class_type(), + new TransformAttribute); + //state.set_attribute(ColorBlendTransition::get_class_type(), + // new ColorBlendAttribute); + state.set_attribute(StencilTransition::get_class_type(), + new StencilAttribute); + + switch (pb->get_format()) { + case PixelBuffer::F_depth_component: + { + ColorMaskAttribute *cma = new ColorMaskAttribute; + cma->set_mask(0); + DepthTestAttribute *dta = new DepthTestAttribute; + dta->set_mode(DepthTestProperty::M_always); + DepthWriteAttribute *dwa = new DepthWriteAttribute; + dwa->set_on(); + state.set_attribute(ColorMaskTransition::get_class_type(), cma); + state.set_attribute(DepthTestTransition::get_class_type(), dta); + state.set_attribute(DepthWriteTransition::get_class_type(), dwa); + } + break; + + case PixelBuffer::F_rgb: + case PixelBuffer::F_rgb5: + case PixelBuffer::F_rgb8: + case PixelBuffer::F_rgb12: + case PixelBuffer::F_rgba: + case PixelBuffer::F_rgba4: + case PixelBuffer::F_rgba5: + case PixelBuffer::F_rgba8: + case PixelBuffer::F_rgba12: + { + ColorMaskAttribute *cma = new ColorMaskAttribute; + DepthTestAttribute *dta = new DepthTestAttribute; + dta->set_mode(DepthTestProperty::M_none); + DepthWriteAttribute *dwa = new DepthWriteAttribute; + dwa->set_off(); + state.set_attribute(ColorMaskTransition::get_class_type(), cma); + state.set_attribute(DepthTestTransition::get_class_type(), dta); + state.set_attribute(DepthWriteTransition::get_class_type(), dwa); + } + break; + default: + glgsg_cat.error() + << "draw_pixel_buffer(): unknown buffer format" << endl; + break; + } + + set_state(state, false); + + enable_color_material(false); + set_unpack_alignment(1); + + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); + glLoadIdentity(); + gluOrtho2D(0, _win->get_width(), + 0, _win->get_height()); + +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glDrawPixels(" << pb->get_xsize() << ", " << pb->get_ysize() + << ", "; + switch (get_external_image_format(pb->get_format())) { + case GL_DEPTH_COMPONENT: + glgsg_cat.debug(false) << "GL_DEPTH_COMPONENT, "; + break; + case GL_RGB: + glgsg_cat.debug(false) << "GL_RGB, "; + break; + case GL_RGBA: + glgsg_cat.debug(false) << "GL_RGBA, "; + break; + default: + glgsg_cat.debug(false) << "unknown, "; + break; + } + switch (get_image_type(pb->get_image_type())) { + case GL_UNSIGNED_BYTE: + glgsg_cat.debug(false) << "GL_UNSIGNED_BYTE, "; + break; + case GL_FLOAT: + glgsg_cat.debug(false) << "GL_FLOAT, "; + break; + default: + glgsg_cat.debug(false) << "unknown, "; + break; + } + glgsg_cat.debug(false) + << (void *)pb->_image.p() << ")" << endl; +#endif + + glRasterPos2i( pb->get_xorg(), pb->get_yorg() ); + glDrawPixels( pb->get_xsize(), pb->get_ysize(), + get_external_image_format(pb->get_format()), + get_image_type(pb->get_image_type()), + pb->_image.p() ); + + glMatrixMode( GL_PROJECTION ); + glPopMatrix(); + + pop_display_region(old_dr); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::draw_pixel_buffer +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const RenderBuffer &rb, const NodeAttributes& na) { + // activate(); + set_draw_buffer(rb); + draw_pixel_buffer(pb, dr, na); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::apply_material +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian::apply_material( Material* material ) +{ + call_glMaterialAmbient( material->get_twoside(), material->get_ambient() ); + call_glMaterialDiffuse( material->get_twoside(), material->get_diffuse() ); + call_glMaterialSpecular( material->get_twoside(), + material->get_specular() ); + call_glMaterialShininess( material->get_twoside(), + material->get_shininess() ); + call_glMaterialEmission( material->get_twoside(), + material->get_emission() ); + + call_glLightModelLocal( material->get_local() ); + call_glLightModelTwoSide( material->get_twoside() ); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::apply_fog +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +apply_fog(Fog *fog) { + call_glFogMode(get_fog_mode_type(fog->get_mode())); + switch(fog->get_mode()) { + case Fog::M_linear: + call_glFogStart(fog->get_start()); + call_glFogEnd(fog->get_end()); + break; + case Fog::M_exponential: + case Fog::M_super_exponential: + call_glFogDensity(fog->get_density()); + break; + case Fog::M_spline: + break; + } + call_glFogColor(fog->get_color()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::apply_light +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian::apply_light( PointLight* light ) +{ + // The light position will be relative to the current matrix, so + // we have to know what the current matrix is. Find a better + // solution later. +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glMatrixMode(GL_MODELVIEW)" << endl; + glgsg_cat.debug() + << "glPushMatrix()" << endl; + glgsg_cat.debug() + << "glLoadIdentity()" << endl; +#endif + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + glLoadMatrixf(LMatrix4f::convert_mat(_coordinate_system, CS_yup_right) + .get_data()); + + GLenum id = get_light_id( _cur_light_id ); + Colorf black(0, 0, 0, 1); + glLightfv(id, GL_AMBIENT, black.get_data()); + glLightfv(id, GL_DIFFUSE, light->get_color().get_data()); + glLightfv(id, GL_SPECULAR, light->get_specular().get_data()); + + // Position needs to specify x, y, z, and w + // w == 1 implies non-infinite position + LPoint3f pos = get_rel_pos( light, _current_projection_node ); + LPoint4f fpos( pos[0], pos[1], pos[2], 1 ); + glLightfv( id, GL_POSITION, fpos.get_data() ); + + // GL_SPOT_DIRECTION is not significant when cutoff == 180 + + // Exponent == 0 implies uniform light distribution + glLightf( id, GL_SPOT_EXPONENT, 0 ); + + // Cutoff == 180 means uniform point light source + glLightf( id, GL_SPOT_CUTOFF, 180.0 ); + + glLightf( id, GL_CONSTANT_ATTENUATION, + light->get_constant_attenuation() ); + glLightf( id, GL_LINEAR_ATTENUATION, + light->get_linear_attenuation() ); + glLightf( id, GL_QUADRATIC_ATTENUATION, + light->get_quadratic_attenuation() ); + + glPopMatrix(); + +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glPopMatrix()" << endl; +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::apply_light +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian::apply_light( DirectionalLight* light ) +{ + // The light position will be relative to the current matrix, so + // we have to know what the current matrix is. Find a better + // solution later. +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glMatrixMode(GL_MODELVIEW)" << endl; + glgsg_cat.debug() + << "glPushMatrix()" << endl; + glgsg_cat.debug() + << "glLoadIdentity()" << endl; +#endif + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixf(LMatrix4f::convert_mat(_coordinate_system, CS_yup_right) + .get_data()); + + GLenum id = get_light_id( _cur_light_id ); + Colorf black(0, 0, 0, 1); + glLightfv(id, GL_AMBIENT, black.get_data()); + glLightfv(id, GL_DIFFUSE, light->get_color().get_data()); + glLightfv(id, GL_SPECULAR, light->get_specular().get_data()); + + // Position needs to specify x, y, z, and w + // w == 0 implies light is at infinity + LPoint3f dir = get_rel_forward( light, _current_projection_node, + _coordinate_system ); + LPoint4f pos( -dir[0], -dir[1], -dir[2], 0 ); + glLightfv( id, GL_POSITION, pos.get_data() ); + + // GL_SPOT_DIRECTION is not significant when cutoff == 180 + // In this case, position x, y, z specifies direction + + // Exponent == 0 implies uniform light distribution + glLightf( id, GL_SPOT_EXPONENT, 0 ); + + // Cutoff == 180 means uniform point light source + glLightf( id, GL_SPOT_CUTOFF, 180.0 ); + + // Default attenuation values (only spotlight can modify these) + glLightf( id, GL_CONSTANT_ATTENUATION, 1 ); + glLightf( id, GL_LINEAR_ATTENUATION, 0 ); + glLightf( id, GL_QUADRATIC_ATTENUATION, 0 ); + + glPopMatrix(); +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glPopMatrix()" << endl; +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::apply_light +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian::apply_light( Spotlight* light ) +{ + // The light position will be relative to the current matrix, so + // we have to know what the current matrix is. Find a better + // solution later. +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glMatrixMode(GL_MODELVIEW)" << endl; + glgsg_cat.debug() + << "glPushMatrix()" << endl; + glgsg_cat.debug() + << "glLoadIdentity()" << endl; +#endif + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixf(LMatrix4f::convert_mat(_coordinate_system, CS_yup_right) + .get_data()); + + GLenum id = get_light_id( _cur_light_id ); + Colorf black(0, 0, 0, 1); + glLightfv(id, GL_AMBIENT, black.get_data()); + glLightfv(id, GL_DIFFUSE, light->get_color().get_data()); + glLightfv(id, GL_SPECULAR, light->get_specular().get_data()); + + // Position needs to specify x, y, z, and w + // w == 1 implies non-infinite position + LPoint3f pos = get_rel_pos( light, _current_projection_node ); + LPoint4f fpos( pos[0], pos[1], pos[2], 1 ); + glLightfv( id, GL_POSITION, fpos.get_data() ); + + glLightfv( id, GL_SPOT_DIRECTION, + get_rel_forward( light, _current_projection_node, + _coordinate_system ).get_data() ); + glLightf( id, GL_SPOT_EXPONENT, light->get_exponent() ); + glLightf( id, GL_SPOT_CUTOFF, + light->get_cutoff_angle() ); + glLightf( id, GL_CONSTANT_ATTENUATION, + light->get_constant_attenuation() ); + glLightf( id, GL_LINEAR_ATTENUATION, + light->get_linear_attenuation() ); + glLightf( id, GL_QUADRATIC_ATTENUATION, + light->get_quadratic_attenuation() ); + + glPopMatrix(); +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glPopMatrix()" << endl; +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::apply_light +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian::apply_light( AmbientLight* light ) +{ + _cur_ambient_light = _cur_ambient_light + light->get_color(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_transform +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_transform(const TransformAttribute *attrib) { + // activate(); +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glLoadMatrix(GL_MODELVIEW): " << attrib->get_matrix() << endl; +#endif + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(attrib->get_matrix().get_data()); + +#ifndef NDEBUG + if (gl_show_transforms) { + bool lighting_was_enabled = _lighting_enabled; + bool texturing_was_enabled = _texturing_enabled; + enable_lighting(false); + enable_texturing(false); + + glBegin(GL_LINES); + + // X axis in red + glColor3f(1.0, 0.0, 0.0); + glVertex3f(0.0, 0.0, 0.0); + glVertex3f(1.0, 0.0, 0.0); + + // Y axis in green + glColor3f(0.0, 1.0, 0.0); + glVertex3f(0.0, 0.0, 0.0); + glVertex3f(0.0, 1.0, 0.0); + + // Z axis in blue + glColor3f(0.0, 0.0, 1.0); + glVertex3f(0.0, 0.0, 0.0); + glVertex3f(0.0, 0.0, 1.0); + + glEnd(); + enable_lighting(lighting_was_enabled); + enable_texturing(texturing_was_enabled); + } +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_color_transform +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_color_transform(const ColorMatrixAttribute *attrib) { + _current_color_mat = attrib->get_matrix(); + + if (_current_color_mat == LMatrix4f::ident_mat()) { + _color_transform_enabled = false; + } + else { + _color_transform_enabled = true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_alpha_transform +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_alpha_transform(const AlphaTransformAttribute *attrib) { + _current_alpha_offset= attrib->get_offset(); + _current_alpha_scale = attrib->get_scale(); + + if (_current_alpha_offset == 0 && _current_alpha_scale == 1) { + _alpha_transform_enabled = false; + } + else { + _alpha_transform_enabled = true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_matrix +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_tex_matrix(const TexMatrixAttribute *attrib) { + // activate(); +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glLoadMatrix(GL_TEXTURE): " << attrib->get_matrix() << endl; +#endif + glMatrixMode(GL_TEXTURE); + glLoadMatrixf(attrib->get_matrix().get_data()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_color +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_color(const ColorAttribute *attrib) { + // activate(); + if (attrib->is_on() && attrib->is_real()) { + const Colorf c = attrib->get_color(); + glColor4f(c[0], c[1], c[2], c[3]); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_texture +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_texture(const TextureAttribute *attrib) { + // activate(); + + if (attrib->is_on()) { + enable_texturing(true); + Texture *tex = attrib->get_texture(); + nassertv(tex != (Texture *)NULL); + tex->apply(this); + } else { + enable_texturing(false); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_tex_gen +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_tex_gen(const TexGenAttribute *attrib) { + // activate(); + TexGenProperty::Mode mode = attrib->get_mode(); + + switch (mode) { + case TexGenProperty::M_none: + glDisable(GL_TEXTURE_GEN_S); + glDisable(GL_TEXTURE_GEN_T); + glDisable(GL_TEXTURE_GEN_Q); + glDisable(GL_TEXTURE_GEN_R); + break; + + case TexGenProperty::M_texture_projector: + { + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + glEnable(GL_TEXTURE_GEN_Q); + glEnable(GL_TEXTURE_GEN_R); + const LMatrix4f &plane = attrib->get_plane(); + glTexGenfv(GL_S, GL_OBJECT_PLANE, plane.get_row(0).get_data()); + glTexGenfv(GL_T, GL_OBJECT_PLANE, plane.get_row(1).get_data()); + glTexGenfv(GL_R, GL_OBJECT_PLANE, plane.get_row(2).get_data()); + glTexGenfv(GL_Q, GL_OBJECT_PLANE, plane.get_row(3).get_data()); + glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + } + break; + + case TexGenProperty::M_sphere_map: + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + glDisable(GL_TEXTURE_GEN_Q); + glDisable(GL_TEXTURE_GEN_R); + glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); + glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); + break; + + default: + glgsg_cat.error() + << "Unknown texgen mode " << (int)mode << endl; + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_material +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_material(const MaterialAttribute *attrib) { + // activate(); + if (attrib->is_on()) { + Material *material = attrib->get_material(); + nassertv(material != (Material *)NULL); + material->apply(this); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_fog +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_fog(const FogAttribute *attrib) { + // activate(); + + if (attrib->is_on()) { + enable_fog(true); + Fog *fog = attrib->get_fog(); + nassertv(fog != (Fog *)NULL); + fog->apply(this); + } else { + enable_fog(false); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_render_mode +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_render_mode(const RenderModeAttribute *attrib) { + // activate(); + + RenderModeProperty::Mode mode = attrib->get_mode(); + + switch (mode) { + case RenderModeProperty::M_filled: + call_glPolygonMode(GL_FILL); + break; + + case RenderModeProperty::M_wireframe: + // Make sure line width is back to default (1 pixel) + call_glLineWidth(attrib->get_line_width()); + call_glPolygonMode(GL_LINE); + break; + + default: + glgsg_cat.error() + << "Unknown render mode " << (int)mode << endl; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_light +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian::issue_light(const LightAttribute *attrib ) +{ + nassertv(attrib->get_properties_is_on()); + // activate(); + + // Initialize the current ambient light total and currently enabled + // light list + _cur_ambient_light.set(0, 0, 0, 1); + int i; + for (i = 0; i < _max_lights; i++) + _cur_light_enabled[i] = false; + + int num_enabled = 0; + LightAttribute::const_iterator li; + for (li = attrib->begin(); li != attrib->end(); ++li) { + _cur_light_id = -1; + num_enabled++; + enable_lighting(true); + Light *light = (*li); + nassertv(light != (Light *)NULL); + + // Ambient lights don't require specific light ids + // Simply add in the ambient contribution to the current total + if (light->get_light_type() == AmbientLight::get_class_type()) { + light->apply(this); + // We need to indicate that no light id is necessary because + // it's an ambient light + _cur_light_id = -2; + } + + // Check to see if this light has already been bound to an id + for (i = 0; i < _max_lights; i++) { + if (_available_light_ids[i] == light) { + // Light has already been bound to an id, we only need + // to enable the light, not apply it + _cur_light_id = -2; + enable_light(i, true); + _cur_light_enabled[i] = true; + break; + } + } + + // See if there are any unbound light ids + if (_cur_light_id == -1) { + for (i = 0; i < _max_lights; i++) { + if (_available_light_ids[i] == NULL) { + _available_light_ids[i] = light; + _cur_light_id = i; + break; + } + } + } + + // If there were no unbound light ids, see if we can replace + // a currently unused but previously bound id + if (_cur_light_id == -1) { + for (i = 0; i < _max_lights; i++) { + if (attrib->is_off(_available_light_ids[i])) { + _available_light_ids[i] = light; + _cur_light_id = i; + break; + } + } + } + + if (_cur_light_id >= 0) { + enable_light(_cur_light_id, true); + _cur_light_enabled[_cur_light_id] = true; + + // We need to do something different for each type of light + light->apply(this); + } else if (_cur_light_id == -1) { + glgsg_cat.error() + << "issue_light() - failed to bind light to id" << endl; + } + } + + // Disable all unused lights + for (i = 0; i < _max_lights; i++) { + if (_cur_light_enabled[i] == false) + enable_light(i, false); + } + + // If no lights were enabled, disable lighting + if (num_enabled == 0) { + enable_lighting(false); + enable_color_material(false); + } else { + call_glLightModelAmbient(_cur_ambient_light); + enable_color_material(true); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_color_blend +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_color_blend(const ColorBlendAttribute *attrib) { + // activate(); + ColorBlendProperty::Mode mode = attrib->get_mode(); + + switch (mode) { + case ColorBlendProperty::M_none: + enable_blend(false); + break; + case ColorBlendProperty::M_multiply: + enable_blend(true); + call_glBlendFunc(GL_DST_COLOR, GL_ZERO); + break; + case ColorBlendProperty::M_add: + enable_blend(true); + call_glBlendFunc(GL_ONE, GL_ONE); + break; + case ColorBlendProperty::M_multiply_add: + enable_blend(true); + call_glBlendFunc(GL_DST_COLOR, GL_ONE); + break; + case ColorBlendProperty::M_alpha: + enable_blend(true); + call_glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + default: + glgsg_cat.error() + << "Unknown color blend mode " << (int)mode << endl; + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_texture_apply +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_texture_apply(const TextureApplyAttribute *attrib) { + // activate(); + GLint glmode = get_texture_apply_mode_type(attrib->get_mode()); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, glmode); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_color_mask +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_color_mask(const ColorMaskAttribute *attrib) { + // activate(); + glColorMask(attrib->is_write_r(), + attrib->is_write_g(), + attrib->is_write_b(), + attrib->is_write_a()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_depth_test +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_depth_test(const DepthTestAttribute *attrib) { + // activate(); + + DepthTestProperty::Mode mode = attrib->get_mode(); + if (mode == DepthTestProperty::M_none) { + enable_depth_test(false); + } else { + enable_depth_test(true); + glDepthFunc(get_depth_func_type(mode)); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_depth_write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_depth_write(const DepthWriteAttribute *attrib) { + // activate(); + + call_glDepthMask(attrib->is_on()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_stencil +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_stencil(const StencilAttribute *attrib) { + // activate(); + + StencilProperty::Mode mode = attrib->get_mode(); + if (mode == StencilProperty::M_none) { + enable_stencil_test(false); + + } else { + enable_stencil_test(true); + call_glStencilFunc(get_stencil_func_type(mode)); + call_glStencilOp(get_stencil_action_type(attrib->get_action())); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_cull_attribute +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_cull_face(const CullFaceAttribute *attrib) { + // activate(); + + CullFaceProperty::Mode mode = attrib->get_mode(); + + switch (mode) { + case CullFaceProperty::M_cull_none: + glDisable(GL_CULL_FACE); + break; + case CullFaceProperty::M_cull_clockwise: + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + break; + case CullFaceProperty::M_cull_counter_clockwise: + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + break; + case CullFaceProperty::M_cull_all: + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT_AND_BACK); + break; + default: + glgsg_cat.error() + << "invalid cull face mode " << (int)mode << endl; + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_clip_plane +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_clip_plane(const ClipPlaneAttribute *attrib) +{ + // activate(); + + // Initialize the currently enabled clip plane list + int i; + for (i = 0; i < _max_clip_planes; i++) + _cur_clip_plane_enabled[i] = false; + + int num_enabled = 0; + ClipPlaneAttribute::const_iterator pi; + for (pi = attrib->begin(); pi != attrib->end(); ++pi) { + PlaneNode *plane_node; + DCAST_INTO_V(plane_node, (*pi)); + nassertv(plane_node != (PlaneNode *)NULL); + + _cur_clip_plane_id = -1; + num_enabled++; + + // Check to see if this clip plane has already been bound to an id + for (i = 0; i < _max_clip_planes; i++) { + if (_available_clip_plane_ids[i] == plane_node) { + // Clip plane has already been bound to an id, we only need + // to enable the clip plane, not apply it + _cur_clip_plane_id = -2; + enable_clip_plane(i, true); + _cur_clip_plane_enabled[i] = true; + break; + } + } + + // See if there are any unbound clip plane ids + if (_cur_clip_plane_id == -1) { + for (i = 0; i < _max_clip_planes; i++) { + if (_available_clip_plane_ids[i] == NULL) { + _available_clip_plane_ids[i] = plane_node; + _cur_clip_plane_id = i; + break; + } + } + } + + // If there were no unbound clip plane ids, see if we can replace + // a currently unused but previously bound id + if (_cur_clip_plane_id == -1) { + for (i = 0; i < _max_clip_planes; i++) { + if (attrib->is_off(_available_clip_plane_ids[i])) { + _available_clip_plane_ids[i] = plane_node; + _cur_clip_plane_id = i; + break; + } + } + } + + if (_cur_clip_plane_id >= 0) { + enable_clip_plane(_cur_clip_plane_id, true); + _cur_clip_plane_enabled[_cur_clip_plane_id] = true; + double equation[4]; + const Planef clip_plane = plane_node->get_plane(); + // Move us into the coordinate space of the plane + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixf(clip_plane.get_reflection_mat().get_data()); + equation[0] = clip_plane._a; + equation[1] = clip_plane._b; + equation[2] = clip_plane._c; + equation[3] = clip_plane._d; + glClipPlane(get_clip_plane_id(_cur_clip_plane_id), equation); + glPopMatrix(); + } else if (_cur_clip_plane_id == -1) { + glgsg_cat.error() + << "issue_clip_plane() - failed to bind clip plane to id" << endl; + } + } + + // Disable all unused clip planes + for (i = 0; i < _max_clip_planes; i++) { + if (_cur_clip_plane_enabled[i] == false) + enable_clip_plane(i, false); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_transparency +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_transparency(const TransparencyAttribute *attrib ) +{ + // activate(); + + TransparencyProperty::Mode mode = attrib->get_mode(); + + switch (mode) { + case TransparencyProperty::M_none: + enable_multisample_alpha_one(false); + enable_multisample_alpha_mask(false); + enable_blend(false); + enable_alpha_test(false); + break; + case TransparencyProperty::M_alpha: + case TransparencyProperty::M_alpha_sorted: + // Should we really have an "alpha" and an "alpha_sorted" mode, + // like Performer does? (The difference is that "alpha" is with + // the write to the depth buffer disabled.) Or should we just use + // the separate depth write transition to control this? Doing it + // implicitly requires a bit more logic here and in the state + // management; for now we require the user to explicitly turn off + // the depth write. + enable_multisample_alpha_one(false); + enable_multisample_alpha_mask(false); + enable_blend(true); + enable_alpha_test(false); + call_glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + case TransparencyProperty::M_multisample: + enable_multisample_alpha_one(true); + enable_multisample_alpha_mask(true); + enable_blend(false); + enable_alpha_test(false); + break; + case TransparencyProperty::M_multisample_mask: + enable_multisample_alpha_one(false); + enable_multisample_alpha_mask(true); + enable_blend(false); + enable_alpha_test(false); + break; + case TransparencyProperty::M_binary: + enable_multisample_alpha_one(false); + enable_multisample_alpha_mask(false); + enable_blend(false); + enable_alpha_test(true); + call_glAlphaFunc(GL_EQUAL, 1); + break; + default: + glgsg_cat.error() + << "invalid transparency mode " << (int)mode << endl; + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_linesmooth +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_linesmooth(const LinesmoothAttribute *attrib) { + // activate(); + enable_line_smooth(attrib->is_on()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_point_shape +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_point_shape(const PointShapeAttribute *attrib) { + // activate(); + + if (attrib->get_mode() == PointShapeProperty::M_square) + glDisable(GL_POINT_SMOOTH); + else + glEnable(GL_POINT_SMOOTH); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_polygon_offset +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +issue_polygon_offset(const PolygonOffsetAttribute *attrib) { + // activate(); + //GL really wants to do a enable/disable of PolygonOffset, but it + //seems more intuitive to have a PolygonOffset "on" all the time, + //and have zero mean nothing is being done. So check for a zero + //offset to decide whether to enable or disable PolygonOffset + if(attrib->get_units() != 0 || attrib->get_factor() != 0) + { + glPolygonOffset(attrib->get_factor(), attrib->get_units()); + enable_polygon_offset(true); + } + else + { + enable_polygon_offset(false); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::wants_normals +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool GLGraphicsStateGuardian:: +wants_normals() const { + return (_lighting_enabled || _normals_enabled); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::wants_texcoords +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool GLGraphicsStateGuardian:: +wants_texcoords() const { + return _texturing_enabled; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::wants_colors +// Access: Public, Virtual +// Description: Returns true if the GSG should issue geometry color +// commands, false otherwise. +//////////////////////////////////////////////////////////////////// +bool GLGraphicsStateGuardian:: +wants_colors() const { + // If we have scene graph color enabled, return false to indicate we + // shouldn't bother issuing geometry color commands. + + const ColorAttribute *catt; + if (!get_attribute_into(catt, _state, ColorTransition::get_class_type())) { + // No scene graph color at all. + return true; + } + + // We should issue geometry colors only if the scene graph color is + // off. + return catt->is_off(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::begin_decal +// Access: Public, Virtual +// Description: This will be called to initiate decaling mode. It is +// passed the pointer to the GeomNode that will be the +// destination of the decals, which it is expected that +// the GSG will render normally; subsequent geometry +// rendered up until the next call of end_decal() should +// be rendered as decals of the base_geom. +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +begin_decal(GeomNode *base_geom) { + nassertv(base_geom != (GeomNode *)NULL); + _decal_level++; + + if (gl_decal_type == GDT_offset) { + // GL 1.1-style: use glPolygonOffset to do decals. + + // Just draw the base geometry normally. + base_geom->draw(this); + + // And now draw the decal geoms with a polygon offset specified. + NodeAttributes state; + PolygonOffsetAttribute *po = new PolygonOffsetAttribute; + po->set_units(-2 * _decal_level); + state.set_attribute(PolygonOffsetTransition::get_class_type(), po); + set_state(state, false); + } else { + // GL 1.0-style: use three-step rendering to do decals. + + if (_decal_level > 1) { + // If we're already decaling, just draw the geometry. + base_geom->draw(this); + + } else { + // Turn off writing the depth buffer to render the base geometry. + call_glDepthMask(false); + + // Now render the base geometry. + base_geom->draw(this); + + // Render all of the decal geometry, too. We'll keep the depth + // buffer write off during this. + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::end_decal +// Access: Public, Virtual +// Description: This will be called to terminate decaling mode. It +// is passed the same base_geom that was passed to +// begin_decal(). +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +end_decal(GeomNode *base_geom) { + nassertv(base_geom != (GeomNode *)NULL); + + _decal_level--; + + if (gl_decal_type == GDT_offset) { + // GL 1.1-style: use glPolygonOffset to do decals. + + NodeAttributes state; + PolygonOffsetAttribute *po = new PolygonOffsetAttribute; + if (_decal_level == 0) { + po->set_units(0); + } + else { + po->set_units(-2 * _decal_level); + } + state.set_attribute(PolygonOffsetTransition::get_class_type(), po); + set_state(state, false); + + } else { + // GL 1.0-style: use three-step rendering to do decals. + + if (_decal_level == 0) { + // Now we need to re-render the base geometry with the depth write + // on and the color mask off, so we update the depth buffer + // properly. + bool was_textured = _texturing_enabled; + bool was_blend = _blend_enabled; + GLenum old_blend_source_func = _blend_source_func; + GLenum old_blend_dest_func = _blend_dest_func; + + // Enable the writing to the depth buffer. + call_glDepthMask(true); + + // Disable the writing to the color buffer, however we have to + // do this. + if (gl_decal_type == GDT_blend) { + // For the early nVidia Linux driver, at least, we don't seem + // to have a working glColorMask. So we have to disable the + // color writes through the use of a blend function. + // Expensive. + enable_blend(true); + call_glBlendFunc(GL_ZERO, GL_ONE); + } else { + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + } + + // No need to have texturing on for this. + enable_texturing(false); + + base_geom->draw(this); + + // Finally, restore the depth write and color mask states to the + // way they're supposed to be. + DepthWriteAttribute *depth_write; + if (get_attribute_into(depth_write, _state, + DepthWriteTransition::get_class_type())) { + issue_depth_write(depth_write); + } + + if (gl_decal_type == GDT_blend) { + enable_blend(was_blend); + if (was_blend) { + call_glBlendFunc(old_blend_source_func, old_blend_dest_func); + } + } else { + ColorMaskAttribute *color_mask; + if (get_attribute_into(color_mask, _state, + ColorMaskTransition::get_class_type())) { + issue_color_mask(color_mask); + } else { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + } + + enable_texturing(was_textured); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::compute_distance_to +// Access: Public, Virtual +// Description: This function may only be called during a render +// traversal; it will compute the distance to the +// indicated point, assumed to be in modelview +// coordinates, from the camera plane. +//////////////////////////////////////////////////////////////////// +float GLGraphicsStateGuardian:: +compute_distance_to(const LPoint3f &point) const { + // In the case of a GLGraphicsStateGuardian, we know that the + // modelview matrix already includes the relative transform from the + // camera, as well as a to-y-up conversion. Thus, the distance to + // the camera plane is simply the -z distance. + + return -point[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::set_draw_buffer +// Access: Protected +// Description: Sets up the glDrawBuffer to render into the buffer +// indicated by the RenderBuffer object. This only sets +// up the color bits; it does not affect the depth, +// stencil, accum layers. +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +set_draw_buffer(const RenderBuffer &rb) { + switch (rb._buffer_type & RenderBuffer::T_color) { + case RenderBuffer::T_front: + call_glDrawBuffer(GL_FRONT); + break; + + case RenderBuffer::T_back: + call_glDrawBuffer(GL_BACK); + break; + + case RenderBuffer::T_right: + call_glDrawBuffer(GL_RIGHT); + break; + + case RenderBuffer::T_left: + call_glDrawBuffer(GL_LEFT); + break; + + case RenderBuffer::T_front_right: + call_glDrawBuffer(GL_FRONT_RIGHT); + break; + + case RenderBuffer::T_front_left: + call_glDrawBuffer(GL_FRONT_LEFT); + break; + + case RenderBuffer::T_back_right: + call_glDrawBuffer(GL_BACK_RIGHT); + break; + + case RenderBuffer::T_back_left: + call_glDrawBuffer(GL_BACK_LEFT); + break; + + default: + call_glDrawBuffer(GL_FRONT_AND_BACK); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::set_read_buffer +// Access: Protected +// Description: Sets up the glReadBuffer to render into the buffer +// indicated by the RenderBuffer object. This only sets +// up the color bits; it does not affect the depth, +// stencil, accum layers. +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +set_read_buffer(const RenderBuffer &rb) { + switch (rb._buffer_type & RenderBuffer::T_color) { + case RenderBuffer::T_front: + call_glReadBuffer(GL_FRONT); + break; + + case RenderBuffer::T_back: + call_glReadBuffer(GL_BACK); + break; + + case RenderBuffer::T_right: + call_glReadBuffer(GL_RIGHT); + break; + + case RenderBuffer::T_left: + call_glReadBuffer(GL_LEFT); + break; + + case RenderBuffer::T_front_right: + call_glReadBuffer(GL_FRONT_RIGHT); + break; + + case RenderBuffer::T_front_left: + call_glReadBuffer(GL_FRONT_LEFT); + break; + + case RenderBuffer::T_back_right: + call_glReadBuffer(GL_BACK_RIGHT); + break; + + case RenderBuffer::T_back_left: + call_glReadBuffer(GL_BACK_LEFT); + break; + + default: + call_glReadBuffer(GL_FRONT_AND_BACK); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::bind_texture +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +bind_texture(TextureContext *tc) { + // activate(); + GLTextureContext *gtc = DCAST(GLTextureContext, tc); + +#ifdef GSG_VERBOSE + Texture *tex = tc->_texture; + glgsg_cat.debug() + << "glBindTexture(): " << tex->get_name() << "(" << (int)gtc->_index + << ")" << endl; +#endif + glBindTexture(GL_TEXTURE_2D, gtc->_index); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::specify_texture +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +specify_texture(Texture *tex) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + get_texture_wrap_mode(tex->get_wrapu())); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + get_texture_wrap_mode(tex->get_wrapv())); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + get_texture_filter_type(tex->get_minfilter())); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + get_texture_filter_type(tex->get_magfilter())); +} + + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::apply_texture_immediate +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +apply_texture_immediate(Texture *tex) { + PixelBuffer *pb = tex->_pbuffer; + + GLenum internal_format = get_internal_image_format(pb->get_format()); + GLenum external_format = get_external_image_format(pb->get_format()); + GLenum type = get_image_type(pb->get_image_type()); + +#ifdef GSG_VERBOSE + glgsg_cat.debug() + << "glTexImage2D(GL_TEXTURE_2D, " + << tex->get_level() << ", " << (int)internal_format << ", " + << pb->get_xsize() << ", " << pb->get_ysize() << ", " + << pb->get_border() << ", " << (int)external_format << ", " + << (int)type << ", " << tex->get_name() << ")\n"; +#endif + glTexImage2D( GL_TEXTURE_2D, tex->get_level(), internal_format, + pb->get_xsize(), pb->get_ysize(), pb->get_border(), + external_format, type, pb->_image ); +} + + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::get_texture_wrap_mode +// Access: Protected +// Description: Maps from the Texture's internal wrap mode symbols to +// GL's. +//////////////////////////////////////////////////////////////////// +GLenum GLGraphicsStateGuardian:: +get_texture_wrap_mode(Texture::WrapMode wm) { + switch (wm) { + case Texture::WM_clamp: + return GL_CLAMP; + case Texture::WM_repeat: + return GL_REPEAT; + } + glgsg_cat.error() << "Invalid Texture::WrapMode value!\n"; + return GL_CLAMP; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::get_texture_filter_type +// Access: Protected +// Description: Maps from the Texture's internal filter type symbols +// to GL's. +//////////////////////////////////////////////////////////////////// +GLenum GLGraphicsStateGuardian:: +get_texture_filter_type(Texture::FilterType ft) { + if (gl_ignore_mipmaps) { + switch (ft) { + case Texture::FT_nearest_mipmap_nearest: + case Texture::FT_nearest: + return GL_NEAREST; + case Texture::FT_linear: + case Texture::FT_linear_mipmap_nearest: + case Texture::FT_nearest_mipmap_linear: + case Texture::FT_linear_mipmap_linear: + return GL_LINEAR; + } + } else { + switch (ft) { + case Texture::FT_nearest: + return GL_NEAREST; + case Texture::FT_linear: + return GL_LINEAR; + case Texture::FT_nearest_mipmap_nearest: + return GL_NEAREST_MIPMAP_NEAREST; + case Texture::FT_linear_mipmap_nearest: + return GL_LINEAR_MIPMAP_NEAREST; + case Texture::FT_nearest_mipmap_linear: + return GL_NEAREST_MIPMAP_LINEAR; + case Texture::FT_linear_mipmap_linear: + return GL_LINEAR_MIPMAP_LINEAR; + } + } + glgsg_cat.error() << "Invalid Texture::FilterType value!\n"; + return GL_NEAREST; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::get_image_type +// Access: Protected +// Description: Maps from the PixelBuffer's internal Type symbols +// to GL's. +//////////////////////////////////////////////////////////////////// +GLenum GLGraphicsStateGuardian:: +get_image_type(PixelBuffer::Type type) { + switch (type) { + case PixelBuffer::T_unsigned_byte: + return GL_UNSIGNED_BYTE; + case PixelBuffer::T_unsigned_short: + return GL_UNSIGNED_SHORT; +#ifdef GL_UNSIGNED_BYTE_3_3_2_EXT + case PixelBuffer::T_unsigned_byte_332: + return GL_UNSIGNED_BYTE_3_3_2_EXT; +#endif + case PixelBuffer::T_float: + return GL_FLOAT; + } + glgsg_cat.error() << "Invalid PixelBuffer::Type value!\n"; + return GL_UNSIGNED_BYTE; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::get_external_image_format +// Access: Protected +// Description: Maps from the PixelBuffer's Format symbols +// to GL's. +//////////////////////////////////////////////////////////////////// +GLenum GLGraphicsStateGuardian:: +get_external_image_format(PixelBuffer::Format format) { + switch (format) { + case PixelBuffer::F_color_index: + return GL_COLOR_INDEX; + case PixelBuffer::F_stencil_index: + return GL_STENCIL_INDEX; + case PixelBuffer::F_depth_component: + return GL_DEPTH_COMPONENT; + case PixelBuffer::F_red: + return GL_RED; + case PixelBuffer::F_green: + return GL_GREEN; + case PixelBuffer::F_blue: + return GL_BLUE; + case PixelBuffer::F_alpha: + return GL_ALPHA; + case PixelBuffer::F_rgb: + case PixelBuffer::F_rgb5: + case PixelBuffer::F_rgb8: + case PixelBuffer::F_rgb12: + case PixelBuffer::F_rgb332: + return GL_RGB; + case PixelBuffer::F_rgba: + case PixelBuffer::F_rgba4: + case PixelBuffer::F_rgba5: + case PixelBuffer::F_rgba8: + case PixelBuffer::F_rgba12: + return GL_RGBA; + case PixelBuffer::F_luminance: + return GL_LUMINANCE; + case PixelBuffer::F_luminance_alpha: + return GL_LUMINANCE_ALPHA; + } + glgsg_cat.error() + << "Invalid PixelBuffer::Format value in get_external_image_format(): " + << (int)format << "\n"; + return GL_RGB; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::get_internal_image_format +// Access: Protected +// Description: Maps from the PixelBuffer's Format symbols to a +// suitable internal format for GL textures. +//////////////////////////////////////////////////////////////////// +GLenum GLGraphicsStateGuardian:: +get_internal_image_format(PixelBuffer::Format format) { + switch (format) { + case PixelBuffer::F_rgba: + return GL_RGBA; + case PixelBuffer::F_rgba4: + return GL_RGBA4; + case PixelBuffer::F_rgba8: + return GL_RGBA8; + case PixelBuffer::F_rgba12: + return GL_RGBA12; + + case PixelBuffer::F_rgb: + return GL_RGB; + case PixelBuffer::F_rgb5: + return GL_RGB5; + case PixelBuffer::F_rgba5: + return GL_RGB5_A1; + case PixelBuffer::F_rgb8: + return GL_RGB8; + case PixelBuffer::F_rgb12: + return GL_RGB12; + case PixelBuffer::F_rgb332: + return GL_R3_G3_B2; + + case PixelBuffer::F_luminance_alpha: + return GL_LUMINANCE_ALPHA; + + case PixelBuffer::F_alpha: + return GL_ALPHA; + + case PixelBuffer::F_red: + case PixelBuffer::F_green: + case PixelBuffer::F_blue: + case PixelBuffer::F_luminance: + return GL_LUMINANCE; + } + + glgsg_cat.error() + << "Invalid image format in get_internal_image_format(): " + << (int)format << "\n"; + return GL_RGB; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::get_texture_apply_mode_type +// Access: Protected +// Description: Maps from the texture environment's mode types +// to the corresponding OpenGL ids +//////////////////////////////////////////////////////////////////// +GLint GLGraphicsStateGuardian:: +get_texture_apply_mode_type( TextureApplyProperty::Mode am ) const +{ + switch( am ) + { + case TextureApplyProperty::M_modulate: return GL_MODULATE; + case TextureApplyProperty::M_decal: return GL_DECAL; + case TextureApplyProperty::M_blend: return GL_BLEND; + case TextureApplyProperty::M_replace: return GL_REPLACE; + case TextureApplyProperty::M_add: return GL_ADD; + } + glgsg_cat.error() + << "Invalid TextureApplyProperty::Mode value" << endl; + return GL_MODULATE; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::get_depth_func_type +// Access: Protected +// Description: Maps from the depth func modes to gl version +//////////////////////////////////////////////////////////////////// +GLenum GLGraphicsStateGuardian:: +get_depth_func_type(DepthTestProperty::Mode m) const +{ + switch(m) + { + case DepthTestProperty::M_never: return GL_NEVER; + case DepthTestProperty::M_less: return GL_LESS; + case DepthTestProperty::M_equal: return GL_EQUAL; + case DepthTestProperty::M_less_equal: return GL_LEQUAL; + case DepthTestProperty::M_greater: return GL_GREATER; + case DepthTestProperty::M_not_equal: return GL_NOTEQUAL; + case DepthTestProperty::M_greater_equal: return GL_GEQUAL; + case DepthTestProperty::M_always: return GL_ALWAYS; + } + glgsg_cat.error() + << "Invalid DepthTestProperty::Mode value" << endl; + return GL_LESS; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::get_stencil_func_type +// Access: Protected +// Description: Maps from the stencil func modes to gl version +//////////////////////////////////////////////////////////////////// +GLenum GLGraphicsStateGuardian:: +get_stencil_func_type(StencilProperty::Mode m) const +{ + switch(m) { + case StencilProperty::M_never: return GL_NEVER; + case StencilProperty::M_less: return GL_LESS; + case StencilProperty::M_equal: return GL_EQUAL; + case StencilProperty::M_less_equal: return GL_LEQUAL; + case StencilProperty::M_greater: return GL_GREATER; + case StencilProperty::M_not_equal: return GL_NOTEQUAL; + case StencilProperty::M_greater_equal: return GL_GEQUAL; + case StencilProperty::M_always: return GL_ALWAYS; + } + glgsg_cat.error() + << "Invalid StencilProperty::Mode value" << endl; + return GL_LESS; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::get_stencil_action_type +// Access: Protected +// Description: Maps from the stencil action modes to gl version +//////////////////////////////////////////////////////////////////// +GLenum GLGraphicsStateGuardian:: +get_stencil_action_type(StencilProperty::Action a) const +{ + switch(a) { + case StencilProperty::A_keep: return GL_KEEP; + case StencilProperty::A_zero: return GL_ZERO; + case StencilProperty::A_replace: return GL_REPLACE; + case StencilProperty::A_increment: return GL_INCR; + case StencilProperty::A_decrement: return GL_DECR; + case StencilProperty::A_invert: return GL_INVERT; + } + glgsg_cat.error() + << "Invalid StencilProperty::Action value" << endl; + return GL_KEEP; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::get_fog_mode_type +// Access: Protected +// Description: Maps from the fog types to gl version +//////////////////////////////////////////////////////////////////// +GLenum GLGraphicsStateGuardian:: +get_fog_mode_type(Fog::Mode m) const { + switch(m) { + case Fog::M_linear: return GL_LINEAR; + case Fog::M_exponential: return GL_EXP; + case Fog::M_super_exponential: return GL_EXP2; +#ifdef PENV_SGI + case Fog::M_spline: return GL_FOG_FUNC_SGIS; +#endif /* PENV_SGI */ + } + glgsg_cat.error() << "Invalid Fog::Mode value" << endl; + return GL_EXP; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::print_gfx_visual +// Access: Public +// Description: Prints a description of the current visual selected. +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +print_gfx_visual() { + GLint i; + GLboolean j; + // activate(); + + cout << "Graphics Visual Info (# bits of each):" << endl; + + cout << "RGBA: "; + glGetIntegerv( GL_RED_BITS, &i ); cout << i << " "; + glGetIntegerv( GL_GREEN_BITS, &i ); cout << i << " "; + glGetIntegerv( GL_BLUE_BITS, &i ); cout << i << " "; + glGetIntegerv( GL_ALPHA_BITS, &i ); cout << i << endl; + + cout << "Accum RGBA: "; + glGetIntegerv( GL_ACCUM_RED_BITS, &i ); cout << i << " "; + glGetIntegerv( GL_ACCUM_GREEN_BITS, &i ); cout << i << " "; + glGetIntegerv( GL_ACCUM_BLUE_BITS, &i ); cout << i << " "; + glGetIntegerv( GL_ACCUM_ALPHA_BITS, &i ); cout << i << endl; + + glGetIntegerv( GL_INDEX_BITS, &i ); cout << "Color Index: " << i << endl; + + glGetIntegerv( GL_DEPTH_BITS, &i ); cout << "Depth: " << i << endl; + glGetIntegerv( GL_ALPHA_BITS, &i ); cout << "Alpha: " << i << endl; + glGetIntegerv( GL_STENCIL_BITS, &i ); cout << "Stencil: " << i << endl; + + glGetBooleanv( GL_DOUBLEBUFFER, &j ); cout << "DoubleBuffer? " + << (int)j << endl; + + glGetBooleanv( GL_STEREO, &j ); cout << "Stereo? " << (int)j << endl; + +#ifdef PENV_SGI + glGetBooleanv( GL_MULTISAMPLE_SGIS, &j ); cout << "Multisample? " + << (int)j << endl; + glGetIntegerv( GL_SAMPLES_SGIS, &i ); cout << "Samples: " << i << endl; +#endif + + glGetBooleanv( GL_BLEND, &j ); cout << "Blend? " << (int)j << endl; + glGetBooleanv( GL_POINT_SMOOTH, &j ); cout << "Point Smooth? " + << (int)j << endl; + glGetBooleanv( GL_LINE_SMOOTH, &j ); cout << "Line Smooth? " + << (int)j << endl; + + glGetIntegerv( GL_AUX_BUFFERS, &i ); cout << "Aux Buffers: " << i << endl; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::free_pointers +// Access: Public +// Description: Frees some memory that was explicitly allocated +// within the glgsg. +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +free_pointers() { + if (_light_enabled != (bool *)NULL) { + delete[] _light_enabled; + _light_enabled = (bool *)NULL; + } + if (_cur_light_enabled != (bool *)NULL) { + delete[] _cur_light_enabled; + _cur_light_enabled = (bool *)NULL; + } + if (_clip_plane_enabled != (bool *)NULL) { + delete[] _clip_plane_enabled; + _clip_plane_enabled = (bool *)NULL; + } + if (_cur_clip_plane_enabled != (bool *)NULL) { + delete[] _cur_clip_plane_enabled; + _cur_clip_plane_enabled = (bool *)NULL; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::save_frame_buffer +// Access: Public +// Description: Saves the indicated planes of the frame buffer +// (within the indicated display region) and returns it +// in some meaningful form that can be restored later +// via restore_frame_buffer(). This is a helper +// function for push_frame_buffer() and +// pop_frame_buffer(). +//////////////////////////////////////////////////////////////////// +PT(SavedFrameBuffer) GLGraphicsStateGuardian:: +save_frame_buffer(const RenderBuffer &buffer, + CPT(DisplayRegion) dr) { + GLSavedFrameBuffer *sfb = new GLSavedFrameBuffer(buffer, dr); + + if (buffer._buffer_type & RenderBuffer::T_depth) { + // Save the depth buffer. + sfb->_depth = + new PixelBuffer(PixelBuffer::depth_buffer(dr->get_pixel_width(), + dr->get_pixel_height())); + copy_pixel_buffer(sfb->_depth, dr, buffer); + } + + if (buffer._buffer_type & RenderBuffer::T_back) { + // Save the color buffer. + sfb->_back_rgba = new Texture; + copy_texture(sfb->_back_rgba->prepare(this), dr, buffer); + } + + return sfb; +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::restore_frame_buffer +// Access: Public +// Description: Restores the frame buffer that was previously saved. +//////////////////////////////////////////////////////////////////// +void GLGraphicsStateGuardian:: +restore_frame_buffer(SavedFrameBuffer *frame_buffer) { + GLSavedFrameBuffer *sfb = DCAST(GLSavedFrameBuffer, frame_buffer); + + if (sfb->_back_rgba != (Texture *)NULL && + (sfb->_buffer._buffer_type & RenderBuffer::T_back) != 0) { + // Restore the color buffer. + draw_texture(sfb->_back_rgba->prepare(this), + sfb->_display_region, sfb->_buffer); + } + + if (sfb->_depth != (PixelBuffer *)NULL && + (sfb->_buffer._buffer_type & RenderBuffer::T_depth) != 0) { + // Restore the depth buffer. + draw_pixel_buffer(sfb->_depth, sfb->_display_region, sfb->_buffer); + } +} + +// factory and type stuff + +GraphicsStateGuardian *GLGraphicsStateGuardian:: +make_GlGraphicsStateGuardian(const FactoryParams ¶ms) { + GraphicsStateGuardian::GsgWindow *win_param; + if (!get_param_into(win_param, params)) { + glgsg_cat.error() + << "No window specified for gsg creation!" << endl; + return NULL; + } + + GraphicsWindow *win = win_param->get_window(); + return new GLGraphicsStateGuardian(win); +} + +TypeHandle GLGraphicsStateGuardian::get_type(void) const { + return get_class_type(); +} + +TypeHandle GLGraphicsStateGuardian::get_class_type(void) { + return _type_handle; +} + +void GLGraphicsStateGuardian::init_type(void) { + GraphicsStateGuardian::init_type(); + register_type(_type_handle, "GLGraphicsStateGuardian", + GraphicsStateGuardian::get_class_type()); +} + + +#ifdef GSG_VERBOSE + +void GLGraphicsStateGuardian:: +dump_state(void) +{ + if (glgsg_cat.is_debug()) + { + int i; + ostream &dump = glgsg_cat.debug(false); + glgsg_cat.debug() << "Dumping GL State" << endl; + + dump << "\t\t" << "GL_LINE_SMOOTH " << _line_smooth_enabled << " " << (bool)glIsEnabled(GL_LINE_SMOOTH) << "\n"; + dump << "\t\t" << "GL_POINT_SMOOTH " << _point_smooth_enabled << " " << (bool)glIsEnabled(GL_POINT_SMOOTH) << "\n"; + dump << "\t\t" << "GL_LIGHTING " << _lighting_enabled << " " << (bool)glIsEnabled(GL_LIGHTING) << "\n"; + for(i = 0; i < _max_lights; i++) + { + dump << "\t\t\t\t" << "GL_LIGHT" << i << " " << _light_enabled[i] << " " << (bool)glIsEnabled(GL_LIGHT0+i) << "\n"; + } + dump << "\t\t" << "GL_COLOR_MATERIAL " << _color_material_enabled << " " << (bool)glIsEnabled(GL_COLOR_MATERIAL) << "\n"; + dump << "\t\t" << "GL_SCISSOR_TEST " << _scissor_enabled << " " << (bool)glIsEnabled(GL_SCISSOR_TEST) << "\n"; + dump << "\t\t" << "GL_TEXTURE_2D " << _texturing_enabled << " " << (bool)glIsEnabled(GL_TEXTURE_2D) << "\n"; + dump << "\t\t" << "GL_DITHER " << _dither_enabled << " " << (bool)glIsEnabled(GL_DITHER) << "\n"; + dump << "\t\t" << "GL_STENCIL_TEST " << " " << (bool)glIsEnabled(GL_STENCIL_TEST) << "\n"; + for(i = 0; i < _max_clip_planes; i++) + { + dump << "\t\t\t\t" << "GL_CLIP_PLANE" << i << " " << _clip_plane_enabled[i] << " " << (bool)glIsEnabled(GL_CLIP_PLANE0+i) << "\n"; + } + dump << "\t\t" << "GL_BLEND " << _blend_enabled << " " << (bool)glIsEnabled(GL_BLEND) << "\n"; + dump << "\t\t" << "GL_DEPTH_TEST " << _depth_test_enabled << " " << (bool)glIsEnabled(GL_DEPTH_TEST) << "\n"; + dump << "\t\t" << "GL_FOG " << _fog_enabled << " " << (bool)glIsEnabled(GL_FOG) << "\n"; + dump << "\t\t" << "GL_ALPHA_TEST " << _alpha_test_enabled << " " << (bool)glIsEnabled(GL_ALPHA_TEST) << "\n"; + dump << "\t\t" << "GL_POLYGON_OFFSET_FILL " << _polygon_offset_enabled << " " << (bool)glIsEnabled(GL_POLYGON_OFFSET_FILL) << "\n"; + + dump << endl; + } +} + +#else // GSG_VERBOSE + +// This function does nothing unless GSG_VERBOSE is compiled in. +void GLGraphicsStateGuardian:: +dump_state(void) +{ +} + +#endif // GSG_VERBOSE + + +#ifdef GSG_VERBOSE + +// This is a handy function to output a GLenum value as a string, for +// debugging. +ostream &output_gl_enum(ostream &out, GLenum v) { + switch (v) { + case GL_FALSE: + return out << "GL_FALSE"; + case GL_TRUE: + return out << "GL_TRUE"; + + /* Data types */ + case GL_BYTE: + return out << "GL_BYTE"; + case GL_UNSIGNED_BYTE: + return out << "GL_UNSIGNED_BYTE"; + case GL_SHORT: + return out << "GL_SHORT"; + case GL_UNSIGNED_SHORT: + return out << "GL_UNSIGNED_SHORT"; + case GL_INT: + return out << "GL_INT"; + case GL_UNSIGNED_INT: + return out << "GL_UNSIGNED_INT"; + case GL_FLOAT: + return out << "GL_FLOAT"; + case GL_DOUBLE: + return out << "GL_DOUBLE"; + case GL_2_BYTES: + return out << "GL_2_BYTES"; + case GL_3_BYTES: + return out << "GL_3_BYTES"; + case GL_4_BYTES: + return out << "GL_4_BYTES"; + + /* Primitives */ + /* + case GL_LINES: + return out << "GL_LINES"; + case GL_POINTS: + return out << "GL_POINTS"; + */ + case GL_LINE_STRIP: + return out << "GL_LINE_STRIP"; + case GL_LINE_LOOP: + return out << "GL_LINE_LOOP"; + case GL_TRIANGLES: + return out << "GL_TRIANGLES"; + case GL_TRIANGLE_STRIP: + return out << "GL_TRIANGLE_STRIP"; + case GL_TRIANGLE_FAN: + return out << "GL_TRIANGLE_FAN"; + case GL_QUADS: + return out << "GL_QUADS"; + case GL_QUAD_STRIP: + return out << "GL_QUAD_STRIP"; + case GL_POLYGON: + return out << "GL_POLYGON"; + case GL_EDGE_FLAG: + return out << "GL_EDGE_FLAG"; + + /* Vertex Arrays */ + case GL_VERTEX_ARRAY: + return out << "GL_VERTEX_ARRAY"; + case GL_NORMAL_ARRAY: + return out << "GL_NORMAL_ARRAY"; + case GL_COLOR_ARRAY: + return out << "GL_COLOR_ARRAY"; + case GL_INDEX_ARRAY: + return out << "GL_INDEX_ARRAY"; + case GL_TEXTURE_COORD_ARRAY: + return out << "GL_TEXTURE_COORD_ARRAY"; + case GL_EDGE_FLAG_ARRAY: + return out << "GL_EDGE_FLAG_ARRAY"; + case GL_VERTEX_ARRAY_SIZE: + return out << "GL_VERTEX_ARRAY_SIZE"; + case GL_VERTEX_ARRAY_TYPE: + return out << "GL_VERTEX_ARRAY_TYPE"; + case GL_VERTEX_ARRAY_STRIDE: + return out << "GL_VERTEX_ARRAY_STRIDE"; + case GL_NORMAL_ARRAY_TYPE: + return out << "GL_NORMAL_ARRAY_TYPE"; + case GL_NORMAL_ARRAY_STRIDE: + return out << "GL_NORMAL_ARRAY_STRIDE"; + case GL_COLOR_ARRAY_SIZE: + return out << "GL_COLOR_ARRAY_SIZE"; + case GL_COLOR_ARRAY_TYPE: + return out << "GL_COLOR_ARRAY_TYPE"; + case GL_COLOR_ARRAY_STRIDE: + return out << "GL_COLOR_ARRAY_STRIDE"; + case GL_INDEX_ARRAY_TYPE: + return out << "GL_INDEX_ARRAY_TYPE"; + case GL_INDEX_ARRAY_STRIDE: + return out << "GL_INDEX_ARRAY_STRIDE"; + case GL_TEXTURE_COORD_ARRAY_SIZE: + return out << "GL_TEXTURE_COORD_ARRAY_SIZE"; + case GL_TEXTURE_COORD_ARRAY_TYPE: + return out << "GL_TEXTURE_COORD_ARRAY_TYPE"; + case GL_TEXTURE_COORD_ARRAY_STRIDE: + return out << "GL_TEXTURE_COORD_ARRAY_STRIDE"; + case GL_EDGE_FLAG_ARRAY_STRIDE: + return out << "GL_EDGE_FLAG_ARRAY_STRIDE"; + case GL_VERTEX_ARRAY_POINTER: + return out << "GL_VERTEX_ARRAY_POINTER"; + case GL_NORMAL_ARRAY_POINTER: + return out << "GL_NORMAL_ARRAY_POINTER"; + case GL_COLOR_ARRAY_POINTER: + return out << "GL_COLOR_ARRAY_POINTER"; + case GL_INDEX_ARRAY_POINTER: + return out << "GL_INDEX_ARRAY_POINTER"; + case GL_TEXTURE_COORD_ARRAY_POINTER: + return out << "GL_TEXTURE_COORD_ARRAY_POINTER"; + case GL_EDGE_FLAG_ARRAY_POINTER: + return out << "GL_EDGE_FLAG_ARRAY_POINTER"; + case GL_V2F: + return out << "GL_V2F"; + case GL_V3F: + return out << "GL_V3F"; + case GL_C4UB_V2F: + return out << "GL_C4UB_V2F"; + case GL_C4UB_V3F: + return out << "GL_C4UB_V3F"; + case GL_C3F_V3F: + return out << "GL_C3F_V3F"; + case GL_N3F_V3F: + return out << "GL_N3F_V3F"; + case GL_C4F_N3F_V3F: + return out << "GL_C4F_N3F_V3F"; + case GL_T2F_V3F: + return out << "GL_T2F_V3F"; + case GL_T4F_V4F: + return out << "GL_T4F_V4F"; + case GL_T2F_C4UB_V3F: + return out << "GL_T2F_C4UB_V3F"; + case GL_T2F_C3F_V3F: + return out << "GL_T2F_C3F_V3F"; + case GL_T2F_N3F_V3F: + return out << "GL_T2F_N3F_V3F"; + case GL_T2F_C4F_N3F_V3F: + return out << "GL_T2F_C4F_N3F_V3F"; + case GL_T4F_C4F_N3F_V4F: + return out << "GL_T4F_C4F_N3F_V4F"; + + /* Matrix Mode */ + case GL_MATRIX_MODE: + return out << "GL_MATRIX_MODE"; + case GL_MODELVIEW: + return out << "GL_MODELVIEW"; + case GL_PROJECTION: + return out << "GL_PROJECTION"; + case GL_TEXTURE: + return out << "GL_TEXTURE"; + + /* Points */ + case GL_POINT_SMOOTH: + return out << "GL_POINT_SMOOTH"; + case GL_POINT_SIZE: + return out << "GL_POINT_SIZE"; + case GL_POINT_SIZE_GRANULARITY: + return out << "GL_POINT_SIZE_GRANULARITY"; + case GL_POINT_SIZE_RANGE: + return out << "GL_POINT_SIZE_RANGE"; + + /* Lines */ + case GL_LINE_SMOOTH: + return out << "GL_LINE_SMOOTH"; + case GL_LINE_STIPPLE: + return out << "GL_LINE_STIPPLE"; + case GL_LINE_STIPPLE_PATTERN: + return out << "GL_LINE_STIPPLE_PATTERN"; + case GL_LINE_STIPPLE_REPEAT: + return out << "GL_LINE_STIPPLE_REPEAT"; + case GL_LINE_WIDTH: + return out << "GL_LINE_WIDTH"; + case GL_LINE_WIDTH_GRANULARITY: + return out << "GL_LINE_WIDTH_GRANULARITY"; + case GL_LINE_WIDTH_RANGE: + return out << "GL_LINE_WIDTH_RANGE"; + + /* Polygons */ + case GL_POINT: + return out << "GL_POINT"; + case GL_LINE: + return out << "GL_LINE"; + case GL_FILL: + return out << "GL_FILL"; + case GL_CCW: + return out << "GL_CCW"; + case GL_CW: + return out << "GL_CW"; + case GL_FRONT: + return out << "GL_FRONT"; + case GL_BACK: + return out << "GL_BACK"; + case GL_CULL_FACE: + return out << "GL_CULL_FACE"; + case GL_CULL_FACE_MODE: + return out << "GL_CULL_FACE_MODE"; + case GL_POLYGON_SMOOTH: + return out << "GL_POLYGON_SMOOTH"; + case GL_POLYGON_STIPPLE: + return out << "GL_POLYGON_STIPPLE"; + case GL_FRONT_FACE: + return out << "GL_FRONT_FACE"; + case GL_POLYGON_MODE: + return out << "GL_POLYGON_MODE"; + case GL_POLYGON_OFFSET_FACTOR: + return out << "GL_POLYGON_OFFSET_FACTOR"; + case GL_POLYGON_OFFSET_UNITS: + return out << "GL_POLYGON_OFFSET_UNITS"; + case GL_POLYGON_OFFSET_POINT: + return out << "GL_POLYGON_OFFSET_POINT"; + case GL_POLYGON_OFFSET_LINE: + return out << "GL_POLYGON_OFFSET_LINE"; + case GL_POLYGON_OFFSET_FILL: + return out << "GL_POLYGON_OFFSET_FILL"; + + /* Display Lists */ + case GL_COMPILE: + return out << "GL_COMPILE"; + case GL_COMPILE_AND_EXECUTE: + return out << "GL_COMPILE_AND_EXECUTE"; + case GL_LIST_BASE: + return out << "GL_LIST_BASE"; + case GL_LIST_INDEX: + return out << "GL_LIST_INDEX"; + case GL_LIST_MODE: + return out << "GL_LIST_MODE"; + + /* Depth buffer */ + case GL_NEVER: + return out << "GL_NEVER"; + case GL_LESS: + return out << "GL_LESS"; + case GL_GEQUAL: + return out << "GL_GEQUAL"; + case GL_LEQUAL: + return out << "GL_LEQUAL"; + case GL_GREATER: + return out << "GL_GREATER"; + case GL_NOTEQUAL: + return out << "GL_NOTEQUAL"; + case GL_EQUAL: + return out << "GL_EQUAL"; + case GL_ALWAYS: + return out << "GL_ALWAYS"; + case GL_DEPTH_TEST: + return out << "GL_DEPTH_TEST"; + case GL_DEPTH_BITS: + return out << "GL_DEPTH_BITS"; + case GL_DEPTH_CLEAR_VALUE: + return out << "GL_DEPTH_CLEAR_VALUE"; + case GL_DEPTH_FUNC: + return out << "GL_DEPTH_FUNC"; + case GL_DEPTH_RANGE: + return out << "GL_DEPTH_RANGE"; + case GL_DEPTH_WRITEMASK: + return out << "GL_DEPTH_WRITEMASK"; + case GL_DEPTH_COMPONENT: + return out << "GL_DEPTH_COMPONENT"; + + /* Lighting */ + case GL_LIGHTING: + return out << "GL_LIGHTING"; + case GL_LIGHT0: + return out << "GL_LIGHT0"; + case GL_LIGHT1: + return out << "GL_LIGHT1"; + case GL_LIGHT2: + return out << "GL_LIGHT2"; + case GL_LIGHT3: + return out << "GL_LIGHT3"; + case GL_LIGHT4: + return out << "GL_LIGHT4"; + case GL_LIGHT5: + return out << "GL_LIGHT5"; + case GL_LIGHT6: + return out << "GL_LIGHT6"; + case GL_LIGHT7: + return out << "GL_LIGHT7"; + case GL_SPOT_EXPONENT: + return out << "GL_SPOT_EXPONENT"; + case GL_SPOT_CUTOFF: + return out << "GL_SPOT_CUTOFF"; + case GL_CONSTANT_ATTENUATION: + return out << "GL_CONSTANT_ATTENUATION"; + case GL_LINEAR_ATTENUATION: + return out << "GL_LINEAR_ATTENUATION"; + case GL_QUADRATIC_ATTENUATION: + return out << "GL_QUADRATIC_ATTENUATION"; + case GL_AMBIENT: + return out << "GL_AMBIENT"; + case GL_DIFFUSE: + return out << "GL_DIFFUSE"; + case GL_SPECULAR: + return out << "GL_SPECULAR"; + case GL_SHININESS: + return out << "GL_SHININESS"; + case GL_EMISSION: + return out << "GL_EMISSION"; + case GL_POSITION: + return out << "GL_POSITION"; + case GL_SPOT_DIRECTION: + return out << "GL_SPOT_DIRECTION"; + case GL_AMBIENT_AND_DIFFUSE: + return out << "GL_AMBIENT_AND_DIFFUSE"; + case GL_COLOR_INDEXES: + return out << "GL_COLOR_INDEXES"; + case GL_LIGHT_MODEL_TWO_SIDE: + return out << "GL_LIGHT_MODEL_TWO_SIDE"; + case GL_LIGHT_MODEL_LOCAL_VIEWER: + return out << "GL_LIGHT_MODEL_LOCAL_VIEWER"; + case GL_LIGHT_MODEL_AMBIENT: + return out << "GL_LIGHT_MODEL_AMBIENT"; + case GL_FRONT_AND_BACK: + return out << "GL_FRONT_AND_BACK"; + case GL_SHADE_MODEL: + return out << "GL_SHADE_MODEL"; + case GL_FLAT: + return out << "GL_FLAT"; + case GL_SMOOTH: + return out << "GL_SMOOTH"; + case GL_COLOR_MATERIAL: + return out << "GL_COLOR_MATERIAL"; + case GL_COLOR_MATERIAL_FACE: + return out << "GL_COLOR_MATERIAL_FACE"; + case GL_COLOR_MATERIAL_PARAMETER: + return out << "GL_COLOR_MATERIAL_PARAMETER"; + case GL_NORMALIZE: + return out << "GL_NORMALIZE"; + + /* User clipping planes */ + case GL_CLIP_PLANE0: + return out << "GL_CLIP_PLANE0"; + case GL_CLIP_PLANE1: + return out << "GL_CLIP_PLANE1"; + case GL_CLIP_PLANE2: + return out << "GL_CLIP_PLANE2"; + case GL_CLIP_PLANE3: + return out << "GL_CLIP_PLANE3"; + case GL_CLIP_PLANE4: + return out << "GL_CLIP_PLANE4"; + case GL_CLIP_PLANE5: + return out << "GL_CLIP_PLANE5"; + + /* Accumulation buffer */ + case GL_ACCUM_RED_BITS: + return out << "GL_ACCUM_RED_BITS"; + case GL_ACCUM_GREEN_BITS: + return out << "GL_ACCUM_GREEN_BITS"; + case GL_ACCUM_BLUE_BITS: + return out << "GL_ACCUM_BLUE_BITS"; + case GL_ACCUM_ALPHA_BITS: + return out << "GL_ACCUM_ALPHA_BITS"; + case GL_ACCUM_CLEAR_VALUE: + return out << "GL_ACCUM_CLEAR_VALUE"; + case GL_ACCUM: + return out << "GL_ACCUM"; + case GL_ADD: + return out << "GL_ADD"; + case GL_LOAD: + return out << "GL_LOAD"; + case GL_MULT: + return out << "GL_MULT"; + + /* Alpha testing */ + case GL_ALPHA_TEST: + return out << "GL_ALPHA_TEST"; + case GL_ALPHA_TEST_REF: + return out << "GL_ALPHA_TEST_REF"; + case GL_ALPHA_TEST_FUNC: + return out << "GL_ALPHA_TEST_FUNC"; + + /* Blending */ + case GL_BLEND: + return out << "GL_BLEND"; + case GL_BLEND_SRC: + return out << "GL_BLEND_SRC"; + case GL_BLEND_DST: + return out << "GL_BLEND_DST"; + /* + case GL_ZERO: + return out << "GL_ZERO"; + case GL_ONE: + return out << "GL_ONE"; + */ + case GL_SRC_COLOR: + return out << "GL_SRC_COLOR"; + case GL_ONE_MINUS_SRC_COLOR: + return out << "GL_ONE_MINUS_SRC_COLOR"; + case GL_DST_COLOR: + return out << "GL_DST_COLOR"; + case GL_ONE_MINUS_DST_COLOR: + return out << "GL_ONE_MINUS_DST_COLOR"; + case GL_SRC_ALPHA: + return out << "GL_SRC_ALPHA"; + case GL_ONE_MINUS_SRC_ALPHA: + return out << "GL_ONE_MINUS_SRC_ALPHA"; + case GL_DST_ALPHA: + return out << "GL_DST_ALPHA"; + case GL_ONE_MINUS_DST_ALPHA: + return out << "GL_ONE_MINUS_DST_ALPHA"; + case GL_SRC_ALPHA_SATURATE: + return out << "GL_SRC_ALPHA_SATURATE"; + case GL_CONSTANT_COLOR: + return out << "GL_CONSTANT_COLOR"; + case GL_ONE_MINUS_CONSTANT_COLOR: + return out << "GL_ONE_MINUS_CONSTANT_COLOR"; + case GL_CONSTANT_ALPHA: + return out << "GL_CONSTANT_ALPHA"; + case GL_ONE_MINUS_CONSTANT_ALPHA: + return out << "GL_ONE_MINUS_CONSTANT_ALPHA"; + + /* Render Mode */ + case GL_FEEDBACK: + return out << "GL_FEEDBACK"; + case GL_RENDER: + return out << "GL_RENDER"; + case GL_SELECT: + return out << "GL_SELECT"; + + /* Feedback */ + case GL_2D: + return out << "GL_2D"; + case GL_3D: + return out << "GL_3D"; + case GL_3D_COLOR: + return out << "GL_3D_COLOR"; + case GL_3D_COLOR_TEXTURE: + return out << "GL_3D_COLOR_TEXTURE"; + case GL_4D_COLOR_TEXTURE: + return out << "GL_4D_COLOR_TEXTURE"; + case GL_POINT_TOKEN: + return out << "GL_POINT_TOKEN"; + case GL_LINE_TOKEN: + return out << "GL_LINE_TOKEN"; + case GL_LINE_RESET_TOKEN: + return out << "GL_LINE_RESET_TOKEN"; + case GL_POLYGON_TOKEN: + return out << "GL_POLYGON_TOKEN"; + case GL_BITMAP_TOKEN: + return out << "GL_BITMAP_TOKEN"; + case GL_DRAW_PIXEL_TOKEN: + return out << "GL_DRAW_PIXEL_TOKEN"; + case GL_COPY_PIXEL_TOKEN: + return out << "GL_COPY_PIXEL_TOKEN"; + case GL_PASS_THROUGH_TOKEN: + return out << "GL_PASS_THROUGH_TOKEN"; + case GL_FEEDBACK_BUFFER_POINTER: + return out << "GL_FEEDBACK_BUFFER_POINTER"; + case GL_FEEDBACK_BUFFER_SIZE: + return out << "GL_FEEDBACK_BUFFER_SIZE"; + case GL_FEEDBACK_BUFFER_TYPE: + return out << "GL_FEEDBACK_BUFFER_TYPE"; + + /* Selection */ + case GL_SELECTION_BUFFER_POINTER: + return out << "GL_SELECTION_BUFFER_POINTER"; + case GL_SELECTION_BUFFER_SIZE: + return out << "GL_SELECTION_BUFFER_SIZE"; + + /* Fog */ + case GL_FOG: + return out << "GL_FOG"; + case GL_FOG_MODE: + return out << "GL_FOG_MODE"; + case GL_FOG_DENSITY: + return out << "GL_FOG_DENSITY"; + case GL_FOG_COLOR: + return out << "GL_FOG_COLOR"; + case GL_FOG_INDEX: + return out << "GL_FOG_INDEX"; + case GL_FOG_START: + return out << "GL_FOG_START"; + case GL_FOG_END: + return out << "GL_FOG_END"; + case GL_LINEAR: + return out << "GL_LINEAR"; + case GL_EXP: + return out << "GL_EXP"; + case GL_EXP2: + return out << "GL_EXP2"; + + /* Logic Ops */ + case GL_LOGIC_OP: + return out << "GL_LOGIC_OP"; + /* + case GL_INDEX_LOGIC_OP: + return out << "GL_INDEX_LOGIC_OP"; + */ + case GL_COLOR_LOGIC_OP: + return out << "GL_COLOR_LOGIC_OP"; + case GL_LOGIC_OP_MODE: + return out << "GL_LOGIC_OP_MODE"; + case GL_CLEAR: + return out << "GL_CLEAR"; + case GL_SET: + return out << "GL_SET"; + case GL_COPY: + return out << "GL_COPY"; + case GL_COPY_INVERTED: + return out << "GL_COPY_INVERTED"; + case GL_NOOP: + return out << "GL_NOOP"; + case GL_INVERT: + return out << "GL_INVERT"; + case GL_AND: + return out << "GL_AND"; + case GL_NAND: + return out << "GL_NAND"; + case GL_OR: + return out << "GL_OR"; + case GL_NOR: + return out << "GL_NOR"; + case GL_XOR: + return out << "GL_XOR"; + case GL_EQUIV: + return out << "GL_EQUIV"; + case GL_AND_REVERSE: + return out << "GL_AND_REVERSE"; + case GL_AND_INVERTED: + return out << "GL_AND_INVERTED"; + case GL_OR_REVERSE: + return out << "GL_OR_REVERSE"; + case GL_OR_INVERTED: + return out << "GL_OR_INVERTED"; + + /* Stencil */ + case GL_STENCIL_TEST: + return out << "GL_STENCIL_TEST"; + case GL_STENCIL_WRITEMASK: + return out << "GL_STENCIL_WRITEMASK"; + case GL_STENCIL_BITS: + return out << "GL_STENCIL_BITS"; + case GL_STENCIL_FUNC: + return out << "GL_STENCIL_FUNC"; + case GL_STENCIL_VALUE_MASK: + return out << "GL_STENCIL_VALUE_MASK"; + case GL_STENCIL_REF: + return out << "GL_STENCIL_REF"; + case GL_STENCIL_FAIL: + return out << "GL_STENCIL_FAIL"; + case GL_STENCIL_PASS_DEPTH_PASS: + return out << "GL_STENCIL_PASS_DEPTH_PASS"; + case GL_STENCIL_PASS_DEPTH_FAIL: + return out << "GL_STENCIL_PASS_DEPTH_FAIL"; + case GL_STENCIL_CLEAR_VALUE: + return out << "GL_STENCIL_CLEAR_VALUE"; + case GL_STENCIL_INDEX: + return out << "GL_STENCIL_INDEX"; + case GL_KEEP: + return out << "GL_KEEP"; + case GL_REPLACE: + return out << "GL_REPLACE"; + case GL_INCR: + return out << "GL_INCR"; + case GL_DECR: + return out << "GL_DECR"; + + /* Buffers, Pixel Drawing/Reading */ + /* + case GL_NONE: + return out << "GL_NONE"; + */ + case GL_LEFT: + return out << "GL_LEFT"; + case GL_RIGHT: + return out << "GL_RIGHT"; + case GL_FRONT_LEFT: + return out << "GL_FRONT_LEFT"; + case GL_FRONT_RIGHT: + return out << "GL_FRONT_RIGHT"; + case GL_BACK_LEFT: + return out << "GL_BACK_LEFT"; + case GL_BACK_RIGHT: + return out << "GL_BACK_RIGHT"; + case GL_AUX0: + return out << "GL_AUX0"; + case GL_AUX1: + return out << "GL_AUX1"; + case GL_AUX2: + return out << "GL_AUX2"; + case GL_AUX3: + return out << "GL_AUX3"; + case GL_COLOR_INDEX: + return out << "GL_COLOR_INDEX"; + case GL_RED: + return out << "GL_RED"; + case GL_GREEN: + return out << "GL_GREEN"; + case GL_BLUE: + return out << "GL_BLUE"; + case GL_ALPHA: + return out << "GL_ALPHA"; + case GL_LUMINANCE: + return out << "GL_LUMINANCE"; + case GL_LUMINANCE_ALPHA: + return out << "GL_LUMINANCE_ALPHA"; + case GL_ALPHA_BITS: + return out << "GL_ALPHA_BITS"; + case GL_RED_BITS: + return out << "GL_RED_BITS"; + case GL_GREEN_BITS: + return out << "GL_GREEN_BITS"; + case GL_BLUE_BITS: + return out << "GL_BLUE_BITS"; + case GL_INDEX_BITS: + return out << "GL_INDEX_BITS"; + case GL_SUBPIXEL_BITS: + return out << "GL_SUBPIXEL_BITS"; + case GL_AUX_BUFFERS: + return out << "GL_AUX_BUFFERS"; + case GL_READ_BUFFER: + return out << "GL_READ_BUFFER"; + case GL_DRAW_BUFFER: + return out << "GL_DRAW_BUFFER"; + case GL_DOUBLEBUFFER: + return out << "GL_DOUBLEBUFFER"; + case GL_STEREO: + return out << "GL_STEREO"; + case GL_BITMAP: + return out << "GL_BITMAP"; + case GL_COLOR: + return out << "GL_COLOR"; + case GL_DEPTH: + return out << "GL_DEPTH"; + case GL_STENCIL: + return out << "GL_STENCIL"; + case GL_DITHER: + return out << "GL_DITHER"; + case GL_RGB: + return out << "GL_RGB"; + case GL_RGBA: + return out << "GL_RGBA"; + + /* Implementation limits */ + case GL_MAX_LIST_NESTING: + return out << "GL_MAX_LIST_NESTING"; + case GL_MAX_ATTRIB_STACK_DEPTH: + return out << "GL_MAX_ATTRIB_STACK_DEPTH"; + case GL_MAX_MODELVIEW_STACK_DEPTH: + return out << "GL_MAX_MODELVIEW_STACK_DEPTH"; + case GL_MAX_NAME_STACK_DEPTH: + return out << "GL_MAX_NAME_STACK_DEPTH"; + case GL_MAX_PROJECTION_STACK_DEPTH: + return out << "GL_MAX_PROJECTION_STACK_DEPTH"; + case GL_MAX_TEXTURE_STACK_DEPTH: + return out << "GL_MAX_TEXTURE_STACK_DEPTH"; + case GL_MAX_EVAL_ORDER: + return out << "GL_MAX_EVAL_ORDER"; + case GL_MAX_LIGHTS: + return out << "GL_MAX_LIGHTS"; + case GL_MAX_CLIP_PLANES: + return out << "GL_MAX_CLIP_PLANES"; + case GL_MAX_TEXTURE_SIZE: + return out << "GL_MAX_TEXTURE_SIZE"; + case GL_MAX_PIXEL_MAP_TABLE: + return out << "GL_MAX_PIXEL_MAP_TABLE"; + case GL_MAX_VIEWPORT_DIMS: + return out << "GL_MAX_VIEWPORT_DIMS"; + case GL_MAX_CLIENT_ATTRIB_STACK_DEPTH: + return out << "GL_MAX_CLIENT_ATTRIB_STACK_DEPTH"; + + /* Gets */ + case GL_ATTRIB_STACK_DEPTH: + return out << "GL_ATTRIB_STACK_DEPTH"; + case GL_CLIENT_ATTRIB_STACK_DEPTH: + return out << "GL_CLIENT_ATTRIB_STACK_DEPTH"; + case GL_COLOR_CLEAR_VALUE: + return out << "GL_COLOR_CLEAR_VALUE"; + case GL_COLOR_WRITEMASK: + return out << "GL_COLOR_WRITEMASK"; + case GL_CURRENT_INDEX: + return out << "GL_CURRENT_INDEX"; + case GL_CURRENT_COLOR: + return out << "GL_CURRENT_COLOR"; + case GL_CURRENT_NORMAL: + return out << "GL_CURRENT_NORMAL"; + case GL_CURRENT_RASTER_COLOR: + return out << "GL_CURRENT_RASTER_COLOR"; + case GL_CURRENT_RASTER_DISTANCE: + return out << "GL_CURRENT_RASTER_DISTANCE"; + case GL_CURRENT_RASTER_INDEX: + return out << "GL_CURRENT_RASTER_INDEX"; + case GL_CURRENT_RASTER_POSITION: + return out << "GL_CURRENT_RASTER_POSITION"; + case GL_CURRENT_RASTER_TEXTURE_COORDS: + return out << "GL_CURRENT_RASTER_TEXTURE_COORDS"; + case GL_CURRENT_RASTER_POSITION_VALID: + return out << "GL_CURRENT_RASTER_POSITION_VALID"; + case GL_CURRENT_TEXTURE_COORDS: + return out << "GL_CURRENT_TEXTURE_COORDS"; + case GL_INDEX_CLEAR_VALUE: + return out << "GL_INDEX_CLEAR_VALUE"; + case GL_INDEX_MODE: + return out << "GL_INDEX_MODE"; + case GL_INDEX_WRITEMASK: + return out << "GL_INDEX_WRITEMASK"; + case GL_MODELVIEW_MATRIX: + return out << "GL_MODELVIEW_MATRIX"; + case GL_MODELVIEW_STACK_DEPTH: + return out << "GL_MODELVIEW_STACK_DEPTH"; + case GL_NAME_STACK_DEPTH: + return out << "GL_NAME_STACK_DEPTH"; + case GL_PROJECTION_MATRIX: + return out << "GL_PROJECTION_MATRIX"; + case GL_PROJECTION_STACK_DEPTH: + return out << "GL_PROJECTION_STACK_DEPTH"; + case GL_RENDER_MODE: + return out << "GL_RENDER_MODE"; + case GL_RGBA_MODE: + return out << "GL_RGBA_MODE"; + case GL_TEXTURE_MATRIX: + return out << "GL_TEXTURE_MATRIX"; + case GL_TEXTURE_STACK_DEPTH: + return out << "GL_TEXTURE_STACK_DEPTH"; + case GL_VIEWPORT: + return out << "GL_VIEWPORT"; + + + /* Evaluators */ + case GL_AUTO_NORMAL: + return out << "GL_AUTO_NORMAL"; + case GL_MAP1_COLOR_4: + return out << "GL_MAP1_COLOR_4"; + case GL_MAP1_GRID_DOMAIN: + return out << "GL_MAP1_GRID_DOMAIN"; + case GL_MAP1_GRID_SEGMENTS: + return out << "GL_MAP1_GRID_SEGMENTS"; + case GL_MAP1_INDEX: + return out << "GL_MAP1_INDEX"; + case GL_MAP1_NORMAL: + return out << "GL_MAP1_NORMAL"; + case GL_MAP1_TEXTURE_COORD_1: + return out << "GL_MAP1_TEXTURE_COORD_1"; + case GL_MAP1_TEXTURE_COORD_2: + return out << "GL_MAP1_TEXTURE_COORD_2"; + case GL_MAP1_TEXTURE_COORD_3: + return out << "GL_MAP1_TEXTURE_COORD_3"; + case GL_MAP1_TEXTURE_COORD_4: + return out << "GL_MAP1_TEXTURE_COORD_4"; + case GL_MAP1_VERTEX_3: + return out << "GL_MAP1_VERTEX_3"; + case GL_MAP1_VERTEX_4: + return out << "GL_MAP1_VERTEX_4"; + case GL_MAP2_COLOR_4: + return out << "GL_MAP2_COLOR_4"; + case GL_MAP2_GRID_DOMAIN: + return out << "GL_MAP2_GRID_DOMAIN"; + case GL_MAP2_GRID_SEGMENTS: + return out << "GL_MAP2_GRID_SEGMENTS"; + case GL_MAP2_INDEX: + return out << "GL_MAP2_INDEX"; + case GL_MAP2_NORMAL: + return out << "GL_MAP2_NORMAL"; + case GL_MAP2_TEXTURE_COORD_1: + return out << "GL_MAP2_TEXTURE_COORD_1"; + case GL_MAP2_TEXTURE_COORD_2: + return out << "GL_MAP2_TEXTURE_COORD_2"; + case GL_MAP2_TEXTURE_COORD_3: + return out << "GL_MAP2_TEXTURE_COORD_3"; + case GL_MAP2_TEXTURE_COORD_4: + return out << "GL_MAP2_TEXTURE_COORD_4"; + case GL_MAP2_VERTEX_3: + return out << "GL_MAP2_VERTEX_3"; + case GL_MAP2_VERTEX_4: + return out << "GL_MAP2_VERTEX_4"; + case GL_COEFF: + return out << "GL_COEFF"; + case GL_DOMAIN: + return out << "GL_DOMAIN"; + case GL_ORDER: + return out << "GL_ORDER"; + + /* Hints */ + case GL_FOG_HINT: + return out << "GL_FOG_HINT"; + case GL_LINE_SMOOTH_HINT: + return out << "GL_LINE_SMOOTH_HINT"; + case GL_PERSPECTIVE_CORRECTION_HINT: + return out << "GL_PERSPECTIVE_CORRECTION_HINT"; + case GL_POINT_SMOOTH_HINT: + return out << "GL_POINT_SMOOTH_HINT"; + case GL_POLYGON_SMOOTH_HINT: + return out << "GL_POLYGON_SMOOTH_HINT"; + case GL_DONT_CARE: + return out << "GL_DONT_CARE"; + case GL_FASTEST: + return out << "GL_FASTEST"; + case GL_NICEST: + return out << "GL_NICEST"; + + /* Scissor box */ + case GL_SCISSOR_TEST: + return out << "GL_SCISSOR_TEST"; + case GL_SCISSOR_BOX: + return out << "GL_SCISSOR_BOX"; + + /* Pixel Mode / Transfer */ + case GL_MAP_COLOR: + return out << "GL_MAP_COLOR"; + case GL_MAP_STENCIL: + return out << "GL_MAP_STENCIL"; + case GL_INDEX_SHIFT: + return out << "GL_INDEX_SHIFT"; + case GL_INDEX_OFFSET: + return out << "GL_INDEX_OFFSET"; + case GL_RED_SCALE: + return out << "GL_RED_SCALE"; + case GL_RED_BIAS: + return out << "GL_RED_BIAS"; + case GL_GREEN_SCALE: + return out << "GL_GREEN_SCALE"; + case GL_GREEN_BIAS: + return out << "GL_GREEN_BIAS"; + case GL_BLUE_SCALE: + return out << "GL_BLUE_SCALE"; + case GL_BLUE_BIAS: + return out << "GL_BLUE_BIAS"; + case GL_ALPHA_SCALE: + return out << "GL_ALPHA_SCALE"; + case GL_ALPHA_BIAS: + return out << "GL_ALPHA_BIAS"; + case GL_DEPTH_SCALE: + return out << "GL_DEPTH_SCALE"; + case GL_DEPTH_BIAS: + return out << "GL_DEPTH_BIAS"; + case GL_PIXEL_MAP_S_TO_S_SIZE: + return out << "GL_PIXEL_MAP_S_TO_S_SIZE"; + case GL_PIXEL_MAP_I_TO_I_SIZE: + return out << "GL_PIXEL_MAP_I_TO_I_SIZE"; + case GL_PIXEL_MAP_I_TO_R_SIZE: + return out << "GL_PIXEL_MAP_I_TO_R_SIZE"; + case GL_PIXEL_MAP_I_TO_G_SIZE: + return out << "GL_PIXEL_MAP_I_TO_G_SIZE"; + case GL_PIXEL_MAP_I_TO_B_SIZE: + return out << "GL_PIXEL_MAP_I_TO_B_SIZE"; + case GL_PIXEL_MAP_I_TO_A_SIZE: + return out << "GL_PIXEL_MAP_I_TO_A_SIZE"; + case GL_PIXEL_MAP_R_TO_R_SIZE: + return out << "GL_PIXEL_MAP_R_TO_R_SIZE"; + case GL_PIXEL_MAP_G_TO_G_SIZE: + return out << "GL_PIXEL_MAP_G_TO_G_SIZE"; + case GL_PIXEL_MAP_B_TO_B_SIZE: + return out << "GL_PIXEL_MAP_B_TO_B_SIZE"; + case GL_PIXEL_MAP_A_TO_A_SIZE: + return out << "GL_PIXEL_MAP_A_TO_A_SIZE"; + case GL_PIXEL_MAP_S_TO_S: + return out << "GL_PIXEL_MAP_S_TO_S"; + case GL_PIXEL_MAP_I_TO_I: + return out << "GL_PIXEL_MAP_I_TO_I"; + case GL_PIXEL_MAP_I_TO_R: + return out << "GL_PIXEL_MAP_I_TO_R"; + case GL_PIXEL_MAP_I_TO_G: + return out << "GL_PIXEL_MAP_I_TO_G"; + case GL_PIXEL_MAP_I_TO_B: + return out << "GL_PIXEL_MAP_I_TO_B"; + case GL_PIXEL_MAP_I_TO_A: + return out << "GL_PIXEL_MAP_I_TO_A"; + case GL_PIXEL_MAP_R_TO_R: + return out << "GL_PIXEL_MAP_R_TO_R"; + case GL_PIXEL_MAP_G_TO_G: + return out << "GL_PIXEL_MAP_G_TO_G"; + case GL_PIXEL_MAP_B_TO_B: + return out << "GL_PIXEL_MAP_B_TO_B"; + case GL_PIXEL_MAP_A_TO_A: + return out << "GL_PIXEL_MAP_A_TO_A"; + case GL_PACK_ALIGNMENT: + return out << "GL_PACK_ALIGNMENT"; + case GL_PACK_LSB_FIRST: + return out << "GL_PACK_LSB_FIRST"; + case GL_PACK_ROW_LENGTH: + return out << "GL_PACK_ROW_LENGTH"; + case GL_PACK_SKIP_PIXELS: + return out << "GL_PACK_SKIP_PIXELS"; + case GL_PACK_SKIP_ROWS: + return out << "GL_PACK_SKIP_ROWS"; + case GL_PACK_SWAP_BYTES: + return out << "GL_PACK_SWAP_BYTES"; + case GL_UNPACK_ALIGNMENT: + return out << "GL_UNPACK_ALIGNMENT"; + case GL_UNPACK_LSB_FIRST: + return out << "GL_UNPACK_LSB_FIRST"; + case GL_UNPACK_ROW_LENGTH: + return out << "GL_UNPACK_ROW_LENGTH"; + case GL_UNPACK_SKIP_PIXELS: + return out << "GL_UNPACK_SKIP_PIXELS"; + case GL_UNPACK_SKIP_ROWS: + return out << "GL_UNPACK_SKIP_ROWS"; + case GL_UNPACK_SWAP_BYTES: + return out << "GL_UNPACK_SWAP_BYTES"; + case GL_ZOOM_X: + return out << "GL_ZOOM_X"; + case GL_ZOOM_Y: + return out << "GL_ZOOM_Y"; + + /* Texture mapping */ + case GL_TEXTURE_ENV: + return out << "GL_TEXTURE_ENV"; + case GL_TEXTURE_ENV_MODE: + return out << "GL_TEXTURE_ENV_MODE"; + case GL_TEXTURE_1D: + return out << "GL_TEXTURE_1D"; + case GL_TEXTURE_2D: + return out << "GL_TEXTURE_2D"; + case GL_TEXTURE_WRAP_S: + return out << "GL_TEXTURE_WRAP_S"; + case GL_TEXTURE_WRAP_T: + return out << "GL_TEXTURE_WRAP_T"; + case GL_TEXTURE_MAG_FILTER: + return out << "GL_TEXTURE_MAG_FILTER"; + case GL_TEXTURE_MIN_FILTER: + return out << "GL_TEXTURE_MIN_FILTER"; + case GL_TEXTURE_ENV_COLOR: + return out << "GL_TEXTURE_ENV_COLOR"; + case GL_TEXTURE_GEN_S: + return out << "GL_TEXTURE_GEN_S"; + case GL_TEXTURE_GEN_T: + return out << "GL_TEXTURE_GEN_T"; + case GL_TEXTURE_GEN_MODE: + return out << "GL_TEXTURE_GEN_MODE"; + case GL_TEXTURE_BORDER_COLOR: + return out << "GL_TEXTURE_BORDER_COLOR"; + case GL_TEXTURE_WIDTH: + return out << "GL_TEXTURE_WIDTH"; + case GL_TEXTURE_HEIGHT: + return out << "GL_TEXTURE_HEIGHT"; + case GL_TEXTURE_BORDER: + return out << "GL_TEXTURE_BORDER"; + case GL_TEXTURE_COMPONENTS: + return out << "GL_TEXTURE_COMPONENTS"; + case GL_TEXTURE_RED_SIZE: + return out << "GL_TEXTURE_RED_SIZE"; + case GL_TEXTURE_GREEN_SIZE: + return out << "GL_TEXTURE_GREEN_SIZE"; + case GL_TEXTURE_BLUE_SIZE: + return out << "GL_TEXTURE_BLUE_SIZE"; + case GL_TEXTURE_ALPHA_SIZE: + return out << "GL_TEXTURE_ALPHA_SIZE"; + case GL_TEXTURE_LUMINANCE_SIZE: + return out << "GL_TEXTURE_LUMINANCE_SIZE"; + case GL_TEXTURE_INTENSITY_SIZE: + return out << "GL_TEXTURE_INTENSITY_SIZE"; + case GL_NEAREST_MIPMAP_NEAREST: + return out << "GL_NEAREST_MIPMAP_NEAREST"; + case GL_NEAREST_MIPMAP_LINEAR: + return out << "GL_NEAREST_MIPMAP_LINEAR"; + case GL_LINEAR_MIPMAP_NEAREST: + return out << "GL_LINEAR_MIPMAP_NEAREST"; + case GL_LINEAR_MIPMAP_LINEAR: + return out << "GL_LINEAR_MIPMAP_LINEAR"; + case GL_OBJECT_LINEAR: + return out << "GL_OBJECT_LINEAR"; + case GL_OBJECT_PLANE: + return out << "GL_OBJECT_PLANE"; + case GL_EYE_LINEAR: + return out << "GL_EYE_LINEAR"; + case GL_EYE_PLANE: + return out << "GL_EYE_PLANE"; + case GL_SPHERE_MAP: + return out << "GL_SPHERE_MAP"; + case GL_DECAL: + return out << "GL_DECAL"; + case GL_MODULATE: + return out << "GL_MODULATE"; + case GL_NEAREST: + return out << "GL_NEAREST"; + case GL_REPEAT: + return out << "GL_REPEAT"; + case GL_CLAMP: + return out << "GL_CLAMP"; + case GL_S: + return out << "GL_S"; + case GL_T: + return out << "GL_T"; + case GL_R: + return out << "GL_R"; + case GL_Q: + return out << "GL_Q"; + case GL_TEXTURE_GEN_R: + return out << "GL_TEXTURE_GEN_R"; + case GL_TEXTURE_GEN_Q: + return out << "GL_TEXTURE_GEN_Q"; + + /* GL 1.1 texturing */ + case GL_PROXY_TEXTURE_1D: + return out << "GL_PROXY_TEXTURE_1D"; + case GL_PROXY_TEXTURE_2D: + return out << "GL_PROXY_TEXTURE_2D"; + case GL_TEXTURE_PRIORITY: + return out << "GL_TEXTURE_PRIORITY"; + case GL_TEXTURE_RESIDENT: + return out << "GL_TEXTURE_RESIDENT"; + case GL_TEXTURE_BINDING_1D: + return out << "GL_TEXTURE_BINDING_1D"; + case GL_TEXTURE_BINDING_2D: + return out << "GL_TEXTURE_BINDING_2D"; + /* + case GL_TEXTURE_INTERNAL_FORMAT: + return out << "GL_TEXTURE_INTERNAL_FORMAT"; + */ + + /* GL 1.2 texturing */ + case GL_PACK_SKIP_IMAGES: + return out << "GL_PACK_SKIP_IMAGES"; + case GL_PACK_IMAGE_HEIGHT: + return out << "GL_PACK_IMAGE_HEIGHT"; + case GL_UNPACK_SKIP_IMAGES: + return out << "GL_UNPACK_SKIP_IMAGES"; + case GL_UNPACK_IMAGE_HEIGHT: + return out << "GL_UNPACK_IMAGE_HEIGHT"; + case GL_TEXTURE_3D: + return out << "GL_TEXTURE_3D"; + case GL_PROXY_TEXTURE_3D: + return out << "GL_PROXY_TEXTURE_3D"; + case GL_TEXTURE_DEPTH: + return out << "GL_TEXTURE_DEPTH"; + case GL_TEXTURE_WRAP_R: + return out << "GL_TEXTURE_WRAP_R"; + case GL_MAX_3D_TEXTURE_SIZE: + return out << "GL_MAX_3D_TEXTURE_SIZE"; +#ifdef GL_TEXTURE_BINDING_3D + case GL_TEXTURE_BINDING_3D: + return out << "GL_TEXTURE_BINDING_3D"; +#endif + + /* Internal texture formats (GL 1.1) */ + case GL_ALPHA4: + return out << "GL_ALPHA4"; + case GL_ALPHA8: + return out << "GL_ALPHA8"; + case GL_ALPHA12: + return out << "GL_ALPHA12"; + case GL_ALPHA16: + return out << "GL_ALPHA16"; + case GL_LUMINANCE4: + return out << "GL_LUMINANCE4"; + case GL_LUMINANCE8: + return out << "GL_LUMINANCE8"; + case GL_LUMINANCE12: + return out << "GL_LUMINANCE12"; + case GL_LUMINANCE16: + return out << "GL_LUMINANCE16"; + case GL_LUMINANCE4_ALPHA4: + return out << "GL_LUMINANCE4_ALPHA4"; + case GL_LUMINANCE6_ALPHA2: + return out << "GL_LUMINANCE6_ALPHA2"; + case GL_LUMINANCE8_ALPHA8: + return out << "GL_LUMINANCE8_ALPHA8"; + case GL_LUMINANCE12_ALPHA4: + return out << "GL_LUMINANCE12_ALPHA4"; + case GL_LUMINANCE12_ALPHA12: + return out << "GL_LUMINANCE12_ALPHA12"; + case GL_LUMINANCE16_ALPHA16: + return out << "GL_LUMINANCE16_ALPHA16"; + case GL_INTENSITY: + return out << "GL_INTENSITY"; + case GL_INTENSITY4: + return out << "GL_INTENSITY4"; + case GL_INTENSITY8: + return out << "GL_INTENSITY8"; + case GL_INTENSITY12: + return out << "GL_INTENSITY12"; + case GL_INTENSITY16: + return out << "GL_INTENSITY16"; + case GL_R3_G3_B2: + return out << "GL_R3_G3_B2"; + case GL_RGB4: + return out << "GL_RGB4"; + case GL_RGB5: + return out << "GL_RGB5"; + case GL_RGB8: + return out << "GL_RGB8"; + case GL_RGB10: + return out << "GL_RGB10"; + case GL_RGB12: + return out << "GL_RGB12"; + case GL_RGB16: + return out << "GL_RGB16"; + case GL_RGBA2: + return out << "GL_RGBA2"; + case GL_RGBA4: + return out << "GL_RGBA4"; + case GL_RGB5_A1: + return out << "GL_RGB5_A1"; + case GL_RGBA8: + return out << "GL_RGBA8"; + case GL_RGB10_A2: + return out << "GL_RGB10_A2"; + case GL_RGBA12: + return out << "GL_RGBA12"; + case GL_RGBA16: + return out << "GL_RGBA16"; + + /* Utility */ + case GL_VENDOR: + return out << "GL_VENDOR"; + case GL_RENDERER: + return out << "GL_RENDERER"; + case GL_VERSION: + return out << "GL_VERSION"; + case GL_EXTENSIONS: + return out << "GL_EXTENSIONS"; + + /* Errors */ + case GL_INVALID_VALUE: + return out << "GL_INVALID_VALUE"; + case GL_INVALID_ENUM: + return out << "GL_INVALID_ENUM"; + case GL_INVALID_OPERATION: + return out << "GL_INVALID_OPERATION"; + case GL_STACK_OVERFLOW: + return out << "GL_STACK_OVERFLOW"; + case GL_STACK_UNDERFLOW: + return out << "GL_STACK_UNDERFLOW"; + case GL_OUT_OF_MEMORY: + return out << "GL_OUT_OF_MEMORY"; + + /* OpenGL 1.2 */ + case GL_RESCALE_NORMAL: + return out << "GL_RESCALE_NORMAL"; + case GL_CLAMP_TO_EDGE: + return out << "GL_CLAMP_TO_EDGE"; + case GL_MAX_ELEMENTS_VERTICES: + return out << "GL_MAX_ELEMENTS_VERTICES"; + case GL_MAX_ELEMENTS_INDICES: + return out << "GL_MAX_ELEMENTS_INDICES"; + case GL_BGR: + return out << "GL_BGR"; + case GL_BGRA: + return out << "GL_BGRA"; + case GL_UNSIGNED_BYTE_3_3_2: + return out << "GL_UNSIGNED_BYTE_3_3_2"; + case GL_UNSIGNED_BYTE_2_3_3_REV: + return out << "GL_UNSIGNED_BYTE_2_3_3_REV"; + case GL_UNSIGNED_SHORT_5_6_5: + return out << "GL_UNSIGNED_SHORT_5_6_5"; + case GL_UNSIGNED_SHORT_5_6_5_REV: + return out << "GL_UNSIGNED_SHORT_5_6_5_REV"; + case GL_UNSIGNED_SHORT_4_4_4_4: + return out << "GL_UNSIGNED_SHORT_4_4_4_4"; + case GL_UNSIGNED_SHORT_4_4_4_4_REV: + return out << "GL_UNSIGNED_SHORT_4_4_4_4_REV"; + case GL_UNSIGNED_SHORT_5_5_5_1: + return out << "GL_UNSIGNED_SHORT_5_5_5_1"; + case GL_UNSIGNED_SHORT_1_5_5_5_REV: + return out << "GL_UNSIGNED_SHORT_1_5_5_5_REV"; + case GL_UNSIGNED_INT_8_8_8_8: + return out << "GL_UNSIGNED_INT_8_8_8_8"; + case GL_UNSIGNED_INT_8_8_8_8_REV: + return out << "GL_UNSIGNED_INT_8_8_8_8_REV"; + case GL_UNSIGNED_INT_10_10_10_2: + return out << "GL_UNSIGNED_INT_10_10_10_2"; + case GL_UNSIGNED_INT_2_10_10_10_REV: + return out << "GL_UNSIGNED_INT_2_10_10_10_REV"; + case GL_LIGHT_MODEL_COLOR_CONTROL: + return out << "GL_LIGHT_MODEL_COLOR_CONTROL"; + case GL_SINGLE_COLOR: + return out << "GL_SINGLE_COLOR"; + case GL_SEPARATE_SPECULAR_COLOR: + return out << "GL_SEPARATE_SPECULAR_COLOR"; + case GL_TEXTURE_MIN_LOD: + return out << "GL_TEXTURE_MIN_LOD"; + case GL_TEXTURE_MAX_LOD: + return out << "GL_TEXTURE_MAX_LOD"; + case GL_TEXTURE_BASE_LEVEL: + return out << "GL_TEXTURE_BASE_LEVEL"; + case GL_TEXTURE_MAX_LEVEL: + return out << "GL_TEXTURE_MAX_LEVEL"; + } + + return out << (int)v; +} +#endif diff --git a/panda/src/glgsg/glGraphicsStateGuardian.h b/panda/src/glgsg/glGraphicsStateGuardian.h new file mode 100644 index 0000000000..fde7ae711c --- /dev/null +++ b/panda/src/glgsg/glGraphicsStateGuardian.h @@ -0,0 +1,352 @@ +// Filename: glGraphicsStateGuardian.h +// Created by: drose (02Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GLGRAPHICSSTATEGUARDIAN_H +#define GLGRAPHICSSTATEGUARDIAN_H + +//#define GSG_VERBOSE + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef PENV_WIN32 +// Must include windows.h before gl.h on NT +#define WIN32_LEAN_AND_MEAN +#include +#undef WIN32_LEAN_AND_MEAN +#endif +#include + +#include + +class PlaneNode; +class Light; + +#ifdef GSG_VERBOSE +ostream &output_gl_enum(ostream &out, GLenum v); +INLINE ostream &operator << (ostream &out, GLenum v) { + return output_gl_enum(out, v); +} +#endif + + +//////////////////////////////////////////////////////////////////// +// Class : GLGraphicsStateGuardian +// Description : A GraphicsStateGuardian specialized for rendering +// into OpenGL contexts. There should be no GL calls +// outside of this object. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAGL GLGraphicsStateGuardian : public GraphicsStateGuardian { +public: + GLGraphicsStateGuardian(GraphicsWindow *win); + ~GLGraphicsStateGuardian(); + + virtual void reset(); + + virtual void clear(const RenderBuffer &buffer); + virtual void clear(const RenderBuffer &buffer, const DisplayRegion* region); + + virtual void prepare_display_region(); + + virtual void render_frame(const AllAttributesWrapper &initial_state); + virtual void render_scene(Node *root, const ProjectionNode *projnode, + const AllAttributesWrapper &initial_state); + virtual void render_subgraph(RenderTraverser *traverser, + Node *subgraph, const ProjectionNode *projnode, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans); + virtual void render_subgraph(RenderTraverser *traverser, + Node *subgraph, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans); + + virtual void draw_point(const GeomPoint *geom); + virtual void draw_line(const GeomLine *geom); + virtual void draw_linestrip(const GeomLinestrip *geom); + virtual void draw_sprite(const GeomSprite *geom); + virtual void draw_polygon(const GeomPolygon *geom); + virtual void draw_quad(const GeomQuad *geom); + virtual void draw_tri(const GeomTri *geom); + virtual void draw_tristrip(const GeomTristrip *geom); + virtual void draw_trifan(const GeomTrifan *geom); + virtual void draw_sphere(const GeomSphere *geom); + + virtual TextureContext *prepare_texture(Texture *tex); + virtual void apply_texture(TextureContext *tc); + virtual void release_texture(TextureContext *tc); + + virtual void copy_texture(TextureContext *tc, const DisplayRegion *dr); + virtual void copy_texture(TextureContext *tc, const DisplayRegion *dr, + const RenderBuffer &rb); + virtual void draw_texture(TextureContext *tc, const DisplayRegion *dr); + virtual void draw_texture(TextureContext *tc, const DisplayRegion *dr, + const RenderBuffer &rb); + + virtual void texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb); + virtual void texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb, + const DisplayRegion *dr); + + virtual void copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr); + virtual void copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const RenderBuffer &rb); + virtual void draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const NodeAttributes& na=NodeAttributes()); + virtual void draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const RenderBuffer &rb, + const NodeAttributes& na=NodeAttributes()); + + virtual void apply_material(Material* material); + virtual void apply_fog(Fog *fog); + + virtual void apply_light(PointLight* light); + virtual void apply_light(DirectionalLight* light); + virtual void apply_light(Spotlight* light); + virtual void apply_light(AmbientLight* light); + + virtual void issue_transform(const TransformAttribute *attrib); + virtual void issue_color_transform(const ColorMatrixAttribute *attrib); + virtual void issue_alpha_transform(const AlphaTransformAttribute *attrib); + virtual void issue_tex_matrix(const TexMatrixAttribute *attrib); + virtual void issue_color(const ColorAttribute *attrib); + virtual void issue_texture(const TextureAttribute *attrib); + virtual void issue_light(const LightAttribute *attrib); + virtual void issue_material(const MaterialAttribute *attrib); + virtual void issue_render_mode(const RenderModeAttribute *attrib); + virtual void issue_color_blend(const ColorBlendAttribute *attrib); + virtual void issue_texture_apply(const TextureApplyAttribute *attrib); + virtual void issue_color_mask(const ColorMaskAttribute *attrib); + virtual void issue_depth_test(const DepthTestAttribute *attrib); + virtual void issue_depth_write(const DepthWriteAttribute *attrib); + virtual void issue_tex_gen(const TexGenAttribute *attrib); + virtual void issue_cull_face(const CullFaceAttribute *attrib); + virtual void issue_stencil(const StencilAttribute *attrib); + virtual void issue_clip_plane(const ClipPlaneAttribute *attrib); + virtual void issue_transparency(const TransparencyAttribute *attrib); + virtual void issue_fog(const FogAttribute *attrib); + virtual void issue_linesmooth(const LinesmoothAttribute *attrib); + virtual void issue_point_shape(const PointShapeAttribute *attrib); + virtual void issue_polygon_offset(const PolygonOffsetAttribute *attrib); + + virtual bool wants_normals(void) const; + virtual bool wants_texcoords(void) const; + virtual bool wants_colors(void) const; + + virtual void begin_decal(GeomNode *base_geom); + virtual void end_decal(GeomNode *base_geom); + + virtual float compute_distance_to(const LPoint3f &point) const; + + void print_gfx_visual(); + + //For those interested in what the guardian thinks is the current + //enabled/disable GL State compared to what GL says it is + void dump_state(void); + + //Methods for extracting the current color and alpha transformations + INLINE const LMatrix4f &get_current_color_mat() const; + INLINE const float &get_current_alpha_offset() const; + INLINE const float &get_current_alpha_scale() const; + +protected: + void free_pointers(); + virtual PT(SavedFrameBuffer) save_frame_buffer(const RenderBuffer &buffer, + CPT(DisplayRegion) dr); + virtual void restore_frame_buffer(SavedFrameBuffer *frame_buffer); + + INLINE void activate(); + + INLINE void call_glClearColor(GLclampf red, GLclampf green, GLclampf blue, + GLclampf alpha); + INLINE void call_glClearDepth(GLclampd depth); + INLINE void call_glClearStencil(GLint s); + INLINE void call_glClearAccum(GLclampf red, GLclampf green, GLclampf blue, + GLclampf alpha); + INLINE void call_glDrawBuffer(GLenum mode); + INLINE void call_glReadBuffer(GLenum mode); + INLINE void call_glShadeModel(GLenum mode); + INLINE void call_glBlendFunc(GLenum sfunc, GLenum dfunc); + INLINE void call_glCullFace(GLenum mode); + INLINE void call_glScissor(GLint x, GLint y, GLsizei width, GLsizei height); + INLINE void call_glViewport(GLint x, GLint y, GLsizei width, GLsizei height); + INLINE void call_glLightModelAmbient(const Colorf& color); + INLINE void call_glLightModelLocal(GLboolean local); + INLINE void call_glLightModelTwoSide(GLboolean twoside); + INLINE void call_glMaterialAmbient(bool twoside, const Colorf& color); + INLINE void call_glMaterialDiffuse(bool twoside, const Colorf& color); + INLINE void call_glMaterialAmbientDiffuse(bool twoside, const Colorf& color); + INLINE void call_glMaterialSpecular(bool twoside, const Colorf& color); + INLINE void call_glMaterialShininess(bool twoside, float shininess); + INLINE void call_glMaterialEmission(bool twoside, const Colorf& color); + INLINE void call_glStencilFunc(GLenum func); + INLINE void call_glStencilOp(GLenum op); + INLINE void call_glClipPlane(GLenum plane, const double equation[4]); + INLINE void call_glLineWidth(GLfloat width); + INLINE void call_glPointSize(GLfloat size); + INLINE void call_glDepthMask(GLboolean mask); + INLINE void call_glFogMode(GLint mode); + INLINE void call_glFogStart(GLfloat start); + INLINE void call_glFogEnd(GLfloat end); + INLINE void call_glFogDensity(GLfloat density); + INLINE void call_glFogColor(const Colorf &color); + INLINE void call_glAlphaFunc(GLenum func, GLclampf ref); + INLINE void call_glPolygonMode(GLenum mode); + + INLINE void set_pack_alignment(GLint alignment); + INLINE void set_unpack_alignment(GLint alignment); + + INLINE void enable_multisample(bool val); + INLINE void enable_line_smooth(bool val); + INLINE void enable_point_smooth(bool val); + INLINE void enable_lighting(bool val); + INLINE void enable_light(int light, bool val); + INLINE void enable_color_material(bool val); + INLINE void enable_texturing(bool val); + INLINE void enable_scissor(bool val); + INLINE void enable_dither(bool val); + INLINE void enable_stencil_test(bool val); + INLINE void enable_clip_plane(int clip_plane, bool val); + INLINE void enable_multisample_alpha_one(bool val); + INLINE void enable_multisample_alpha_mask(bool val); + INLINE void enable_blend(bool val); + INLINE void enable_depth_test(bool val); + INLINE void enable_fog(bool val); + INLINE void enable_alpha_test(bool val); + INLINE void enable_polygon_offset(bool val); + + INLINE GLenum get_light_id(int index) const; + INLINE GLenum get_clip_plane_id(int index) const; + + void set_draw_buffer(const RenderBuffer &rb); + void set_read_buffer(const RenderBuffer &rb); + + void bind_texture(TextureContext *tc); + void specify_texture(Texture *tex); + void apply_texture_immediate(Texture *tex); + + GLenum get_texture_wrap_mode(Texture::WrapMode wm); + GLenum get_texture_filter_type(Texture::FilterType ft); + GLenum get_image_type(PixelBuffer::Type type); + GLenum get_external_image_format(PixelBuffer::Format format); + GLenum get_internal_image_format(PixelBuffer::Format format); + GLint get_texture_apply_mode_type( TextureApplyProperty::Mode am ) const; + GLenum get_depth_func_type(DepthTestProperty::Mode m) const; + GLenum get_stencil_func_type(StencilProperty::Mode m) const; + GLenum get_stencil_action_type(StencilProperty::Action a) const; + GLenum get_fog_mode_type(Fog::Mode m) const; + + GLclampf _clear_color_red, _clear_color_green, _clear_color_blue, + _clear_color_alpha; + GLclampd _clear_depth; + GLint _clear_stencil; + GLclampf _clear_accum_red, _clear_accum_green, _clear_accum_blue, + _clear_accum_alpha; + GLenum _draw_buffer_mode; + GLenum _read_buffer_mode; + GLenum _shade_model_mode; + GLint _scissor_x; + GLint _scissor_y; + GLsizei _scissor_width; + GLsizei _scissor_height; + GLint _viewport_x; + GLint _viewport_y; + GLsizei _viewport_width; + GLsizei _viewport_height; + GLboolean _lmodel_local; + GLboolean _lmodel_twoside; + Colorf _lmodel_ambient; + Colorf _material_ambient; + Colorf _material_diffuse; + Colorf _material_specular; + float _material_shininess; + Colorf _material_emission; + GLenum _stencil_func; + GLenum _stencil_op; + GLfloat _line_width; + GLfloat _point_size; + GLenum _blend_source_func; + GLenum _blend_dest_func; + GLboolean _depth_mask; + GLint _fog_mode; + GLfloat _fog_start; + GLfloat _fog_end; + GLfloat _fog_density; + Colorf _fog_color; + GLenum _alpha_func; + GLclampf _alpha_func_ref; + GLenum _polygon_mode; + + GLint _pack_alignment; + GLint _unpack_alignment; + + bool _multisample_enabled; + bool _line_smooth_enabled; + bool _point_smooth_enabled; + bool* _light_enabled; // bool[_max_lights] + bool _color_material_enabled; + bool _scissor_enabled; + bool _lighting_enabled; + bool _texturing_enabled; + bool _dither_enabled; + bool _stencil_test_enabled; + bool* _clip_plane_enabled; // bool[_max_clip_planes] + bool _multisample_alpha_one_enabled; + bool _multisample_alpha_mask_enabled; + bool _blend_enabled; + bool _depth_test_enabled; + bool _fog_enabled; + bool _alpha_test_enabled; + bool _polygon_offset_enabled; + bool _color_transform_enabled; + bool _alpha_transform_enabled; + int _decal_level; + + PTA(Light*) _available_light_ids; + GLint _max_lights; + bool* _cur_light_enabled; + int _cur_light_id; + Colorf _cur_ambient_light; + LMatrix4f _current_projection_mat; + int _projection_mat_stack_count; + + PTA(PlaneNode*) _available_clip_plane_ids; + GLint _max_clip_planes; + bool* _cur_clip_plane_enabled; + int _cur_clip_plane_id; + + CPT(DisplayRegion) _actual_display_region; + + LMatrix4f _current_color_mat; + float _current_alpha_offset; + float _current_alpha_scale; + + int _pass_number; + +public: + static GraphicsStateGuardian * + make_GlGraphicsStateGuardian(const FactoryParams ¶ms); + + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "glGraphicsStateGuardian.I" + +#endif + diff --git a/panda/src/glgsg/glSavedFrameBuffer.I b/panda/src/glgsg/glSavedFrameBuffer.I new file mode 100644 index 0000000000..60f8a62dd2 --- /dev/null +++ b/panda/src/glgsg/glSavedFrameBuffer.I @@ -0,0 +1,26 @@ +// Filename: glSavedFrameBuffer.I +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: GLSavedFrameBuffer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GLSavedFrameBuffer:: +GLSavedFrameBuffer(const RenderBuffer &buffer, CPT(DisplayRegion) dr) : + SavedFrameBuffer(buffer, dr) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: GLSavedFrameBuffer::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GLSavedFrameBuffer:: +~GLSavedFrameBuffer() { +} + diff --git a/panda/src/glgsg/glSavedFrameBuffer.cxx b/panda/src/glgsg/glSavedFrameBuffer.cxx new file mode 100644 index 0000000000..6b1a26f2b8 --- /dev/null +++ b/panda/src/glgsg/glSavedFrameBuffer.cxx @@ -0,0 +1,8 @@ +// Filename: glSavedFrameBuffer.cxx +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "glSavedFrameBuffer.h" + +TypeHandle GLSavedFrameBuffer::_type_handle; diff --git a/panda/src/glgsg/glSavedFrameBuffer.h b/panda/src/glgsg/glSavedFrameBuffer.h new file mode 100644 index 0000000000..c686feb52c --- /dev/null +++ b/panda/src/glgsg/glSavedFrameBuffer.h @@ -0,0 +1,51 @@ +// Filename: glSavedFrameBuffer.h +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GLSAVEDFRAMEBUFFER_H +#define GLSAVEDFRAMEBUFFER_H + +#include + +#include +#include +#include +#include + + +//////////////////////////////////////////////////////////////////// +// Class : GLSavedFrameBuffer +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAGL GLSavedFrameBuffer : public SavedFrameBuffer { +public: + INLINE GLSavedFrameBuffer(const RenderBuffer &buffer, + CPT(DisplayRegion) dr); + INLINE ~GLSavedFrameBuffer(); + + PT(Texture) _back_rgba; + PT(PixelBuffer) _depth; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + SavedFrameBuffer::init_type(); + register_type(_type_handle, "GLSavedFrameBuffer", + SavedFrameBuffer::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "glSavedFrameBuffer.I" + +#endif + diff --git a/panda/src/glgsg/glTextureContext.I b/panda/src/glgsg/glTextureContext.I new file mode 100644 index 0000000000..a4a98ca299 --- /dev/null +++ b/panda/src/glgsg/glTextureContext.I @@ -0,0 +1,18 @@ +// Filename: glTextureContext.I +// Created by: drose (07Oct99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: GLTextureContext::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GLTextureContext:: +GLTextureContext(Texture *tex) : + TextureContext(tex) +{ + _index = 0; + _priority = 0.5; // For keeping resident in texture memory +} diff --git a/panda/src/glgsg/glTextureContext.cxx b/panda/src/glgsg/glTextureContext.cxx new file mode 100644 index 0000000000..acbe05e617 --- /dev/null +++ b/panda/src/glgsg/glTextureContext.cxx @@ -0,0 +1,8 @@ +// Filename: glTextureContext.cxx +// Created by: drose (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "glTextureContext.h" + +TypeHandle GLTextureContext::_type_handle; diff --git a/panda/src/glgsg/glTextureContext.h b/panda/src/glgsg/glTextureContext.h new file mode 100644 index 0000000000..f6379ede33 --- /dev/null +++ b/panda/src/glgsg/glTextureContext.h @@ -0,0 +1,55 @@ +// Filename: glTextureContext.h +// Created by: drose (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GLTEXTURECONTEXT_H +#define GLTEXTURECONTEXT_H + +#ifdef PENV_WIN32 +// Must include windows.h before gl.h on NT +#define WIN32_LEAN_AND_MEAN +#include + +#include +#undef WIN32_LEAN_AND_MEAN +#endif +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : GLTextureContext +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAGL GLTextureContext : public TextureContext { +public: + INLINE GLTextureContext(Texture *tex); + + // This is the GL "name" of the texture object. + GLuint _index; + + // This is a GL texture priority. + GLfloat _priority; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TextureContext::init_type(); + register_type(_type_handle, "GLTextureContext", + TextureContext::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "glTextureContext.I" + +#endif + diff --git a/panda/src/glutdisplay/Sources.pp b/panda/src/glutdisplay/Sources.pp new file mode 100644 index 0000000000..ae4ff7a1fe --- /dev/null +++ b/panda/src/glutdisplay/Sources.pp @@ -0,0 +1,39 @@ +#define DIRECTORY_IF_GL yes +#define DIRECTORY_IF_GLUT yes + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase +#define USE_GL yes + +#begin lib_target + #define TARGET glutdisplay + #define LOCAL_LIBS \ + glgsg display putil gobj gsgbase pnmimage mathutil graph sgraph \ + light + + #define SOURCES \ + config_glutdisplay.cxx config_glutdisplay.h glutGraphicsPipe.cxx \ + glutGraphicsPipe.h glutGraphicsWindow.cxx glutGraphicsWindow.h + + #define INSTALL_HEADERS \ + glutGraphicsPipe.h glutGraphicsWindow.h + +#end lib_target + +#begin test_bin_target + #define TARGET test_glut + #define LOCAL_LIBS \ + putil graph display mathutil gobj sgraph + + #define SOURCES \ + test_glut.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET test_glut_win + + #define SOURCES \ + test_glut_win.cxx + +#end test_bin_target + diff --git a/panda/src/glutdisplay/config_glutdisplay.cxx b/panda/src/glutdisplay/config_glutdisplay.cxx new file mode 100644 index 0000000000..551d6b6c3a --- /dev/null +++ b/panda/src/glutdisplay/config_glutdisplay.cxx @@ -0,0 +1,22 @@ +// Filename: config_glutdisplay.cxx +// Created by: cary (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_glutdisplay.h" +#include "glutGraphicsPipe.h" +#include "glutGraphicsWindow.h" + +#include + +Configure(config_glutdisplay); +NotifyCategoryDef(glutdisplay, "display"); + +ConfigureFn(config_glutdisplay) { + glutGraphicsPipe::init_type(); + GraphicsPipe::_factory.register_factory(glutGraphicsPipe::get_class_type(), + glutGraphicsPipe::make_glutGraphicsPipe); + glutGraphicsWindow::init_type(); + GraphicsWindow::_factory.register_factory(glutGraphicsWindow::get_class_type(), + glutGraphicsWindow::make_GlutGraphicsWindow); +} diff --git a/panda/src/glutdisplay/config_glutdisplay.h b/panda/src/glutdisplay/config_glutdisplay.h new file mode 100644 index 0000000000..0cecfc1338 --- /dev/null +++ b/panda/src/glutdisplay/config_glutdisplay.h @@ -0,0 +1,15 @@ +// Filename: config_glutdisplay.h +// Created by: cary (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_GLUTDISPLAY_H +#define CONFIG_GLUTDISPLAY_H + +#include +#include + +NotifyCategoryDecl(glutdisplay, EXPCL_PANDAGLUT, EXPTP_PANDAGLUT); + + +#endif /* CONFIG_GLUTDISPLAY_H */ diff --git a/panda/src/glutdisplay/glutGraphicsPipe.cxx b/panda/src/glutdisplay/glutGraphicsPipe.cxx new file mode 100644 index 0000000000..4975d03c4f --- /dev/null +++ b/panda/src/glutdisplay/glutGraphicsPipe.cxx @@ -0,0 +1,70 @@ +// Filename: glutGraphicsPipe.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "glutGraphicsPipe.h" +#include "config_glutdisplay.h" + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle glutGraphicsPipe::_type_handle; + +glutGraphicsPipe::glutGraphicsPipe(const PipeSpecifier& spec) + : InteractiveGraphicsPipe(spec) {} + +//////////////////////////////////////////////////////////////////// +// Function: glutGraphicsPipe::get_window_type +// Access: Public, Virtual +// Description: Returns the TypeHandle of the kind of window +// preferred by this kind of pipe. +//////////////////////////////////////////////////////////////////// +TypeHandle glutGraphicsPipe:: +get_window_type() const { + return glutGraphicsWindow::get_class_type(); +} + +GraphicsPipe* +glutGraphicsPipe::make_glutGraphicsPipe(const FactoryParams ¶ms) { + GraphicsPipe::PipeSpec *pipe_param; + if (!get_param_into(pipe_param, params)) { + return new glutGraphicsPipe(PipeSpecifier()); + } else { + return new glutGraphicsPipe(pipe_param->get_specifier()); + } +} + +TypeHandle glutGraphicsPipe::get_class_type(void) { + return _type_handle; +} + +void glutGraphicsPipe::init_type(void) { + InteractiveGraphicsPipe::init_type(); + register_type(_type_handle, "glutGraphicsPipe", + InteractiveGraphicsPipe::get_class_type()); +} + +TypeHandle glutGraphicsPipe::get_type(void) const { + return get_class_type(); +} + +glutGraphicsPipe::glutGraphicsPipe(void) { + glutdisplay_cat.error() + << "glutGraphicsPipes should not be created with the default constructor" + << endl; +} + +glutGraphicsPipe::glutGraphicsPipe(const glutGraphicsPipe&) { + glutdisplay_cat.error() + << "glutGraphicsPipes should not be copied" << endl; +} + +glutGraphicsPipe& glutGraphicsPipe::operator=(const glutGraphicsPipe&) { + glutdisplay_cat.error() + << "glutGraphicsPipes should not be assigned" << endl; + return *this; +} diff --git a/panda/src/glutdisplay/glutGraphicsPipe.h b/panda/src/glutdisplay/glutGraphicsPipe.h new file mode 100644 index 0000000000..a23140a71d --- /dev/null +++ b/panda/src/glutdisplay/glutGraphicsPipe.h @@ -0,0 +1,50 @@ +// Filename: glutGraphicsPipe.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef GLUTGRAPHICSPIPE_H +#define GLUTGRAPHICSPIPE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include "glutGraphicsWindow.h" + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : glutGraphicsPipe +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAGLUT glutGraphicsPipe : public InteractiveGraphicsPipe +{ +public: + glutGraphicsPipe( const PipeSpecifier& ); + + virtual TypeHandle get_window_type() const; + +public: + static GraphicsPipe* make_glutGraphicsPipe(const FactoryParams ¶ms); + + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + +protected: + glutGraphicsPipe( void ); + glutGraphicsPipe( const glutGraphicsPipe& ); + glutGraphicsPipe& operator=(const glutGraphicsPipe&); +}; + +#endif diff --git a/panda/src/glutdisplay/glutGraphicsWindow.cxx b/panda/src/glutdisplay/glutGraphicsWindow.cxx new file mode 100644 index 0000000000..e65726bb52 --- /dev/null +++ b/panda/src/glutdisplay/glutGraphicsWindow.cxx @@ -0,0 +1,307 @@ +// Filename: glutGraphicsWindow.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "glutGraphicsWindow.h" +#include "glutGraphicsPipe.h" +#include "config_glutdisplay.h" + +#include +#include +#include +#include + +#ifdef PENV_WIN32 +#define WINDOWS_LEAN_AND_MEAN +#include +#undef WINDOWS_LEAN_AND_MEAN +#endif +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle glutGraphicsWindow::_type_handle; +glutGraphicsWindow* glutGraphicsWindow::_global_window; + +//////////////////////////////////////////////////////////////////// +// Function: Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +glutGraphicsWindow::glutGraphicsWindow( GraphicsPipe* pipe ) : + GraphicsWindow( pipe ) +{ + _global_window = this; + config(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +glutGraphicsWindow::glutGraphicsWindow( GraphicsPipe* pipe, const + GraphicsWindow::Properties& props ) : GraphicsWindow( pipe, props ) +{ + _global_window = this; + config(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Destructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +glutGraphicsWindow::~glutGraphicsWindow(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function: config +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glutGraphicsWindow::config( void ) +{ + glutInitDisplayMode( _props._mask ); + glutInitWindowPosition( _props._xorg, _props._yorg ); + glutInitWindowSize( _props._xsize, _props._ysize ); + _handle = glutCreateWindow( _props._title.c_str() ); + glutReshapeFunc( glut_handle_reshape ); + + // Mouse callbacks + glutMouseFunc( glut_handle_mouse_input ); + glutMotionFunc( glut_handle_mouse_motion ); + glutPassiveMotionFunc( glut_handle_mouse_motion ); + + // Keyboard callbacks + glutKeyboardFunc( glut_handle_ascii_keyboard_input ); + glutSpecialFunc( glut_handle_special_keyboard_input ); + + // Indicate that we have our keyboard/mouse device ready. + GraphicsWindowInputDevice device = + GraphicsWindowInputDevice::pointer_and_keyboard("keyboard/mouse"); + _input_devices.push_back(device); + + // Create a GSG to manage the graphics + make_gsg(); +} + +//////////////////////////////////////////////////////////////////// +// Function: end_frame +// Access: +// Description: Swaps the front and back buffers. +//////////////////////////////////////////////////////////////////// +void glutGraphicsWindow::end_frame( void ) +{ + { + PStatTimer timer(_swap_pcollector); + glutSwapBuffers(); + } + glutPostRedisplay(); + PStatClient::main_tick(); +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_reshape +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glutGraphicsWindow::handle_reshape( int w, int h ) +{ + _props._xsize = w; + _props._ysize = h; +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_mouse_input +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glutGraphicsWindow::handle_mouse_input( int button, int state, + int x, int y ) +{ + _input_devices[0].set_pointer_in_window(x, y); + + ButtonHandle handle; + switch (button) { + case GLUT_LEFT_BUTTON: + handle = MouseButton::one(); + break; + + case GLUT_MIDDLE_BUTTON: + handle = MouseButton::two(); + break; + + case GLUT_RIGHT_BUTTON: + handle = MouseButton::three(); + break; + } + + if (handle != ButtonHandle::none()) { + if (state == GLUT_DOWN) { + _input_devices[0].button_down(handle); + } else { + _input_devices[0].button_up(handle); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_mouse_motion +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glutGraphicsWindow::handle_mouse_motion( int x, int y ) +{ + _input_devices[0].set_pointer_in_window(x, y); +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_mouse_entry +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glutGraphicsWindow::handle_mouse_entry( int state ) +{ + if (state == GLUT_LEFT) { + _input_devices[0].set_pointer_out_of_window(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_ascii_keyboard_input +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glutGraphicsWindow::handle_ascii_keyboard_input( uchar key, int x, int y ) +{ + _input_devices[0].set_pointer_in_window(x, y); + + ButtonHandle handle = KeyboardButton::ascii_key(key); + if (handle != ButtonHandle::none()) { + _input_devices[0].button_down(handle); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_special_keyboard_input +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glutGraphicsWindow::handle_special_keyboard_input( int key, int x, int y ) +{ + _input_devices[0].set_pointer_in_window(x, y); + + ButtonHandle handle = ButtonHandle::none(); + + switch ( key ) { + case GLUT_KEY_F1: handle = KeyboardButton::f1(); break; + case GLUT_KEY_F2: handle = KeyboardButton::f2(); break; + case GLUT_KEY_F3: handle = KeyboardButton::f3(); break; + case GLUT_KEY_F4: handle = KeyboardButton::f4(); break; + case GLUT_KEY_F5: handle = KeyboardButton::f5(); break; + case GLUT_KEY_F6: handle = KeyboardButton::f6(); break; + case GLUT_KEY_F7: handle = KeyboardButton::f7(); break; + case GLUT_KEY_F8: handle = KeyboardButton::f8(); break; + case GLUT_KEY_F9: handle = KeyboardButton::f9(); break; + case GLUT_KEY_F10: handle = KeyboardButton::f10(); break; + case GLUT_KEY_F11: handle = KeyboardButton::f11(); break; + case GLUT_KEY_F12: handle = KeyboardButton::f12(); break; + case GLUT_KEY_LEFT: handle = KeyboardButton::left(); break; + case GLUT_KEY_RIGHT: handle = KeyboardButton::right(); break; + case GLUT_KEY_UP: handle = KeyboardButton::up(); break; + case GLUT_KEY_DOWN: handle = KeyboardButton::down(); break; + case GLUT_KEY_PAGE_UP: handle = KeyboardButton::page_up(); break; + case GLUT_KEY_PAGE_DOWN: handle = KeyboardButton::page_down(); break; + case GLUT_KEY_HOME: handle = KeyboardButton::home(); break; + case GLUT_KEY_END: handle = KeyboardButton::end(); break; + case GLUT_KEY_INSERT: handle = KeyboardButton::insert(); break; + } + + if (handle != ButtonHandle::none()) { + _input_devices[0].button_down(handle); + } +} + +void glutGraphicsWindow::flag_redisplay(void) { + glutPostRedisplay(); +} + +void glutGraphicsWindow::register_draw_function(GraphicsWindow::vfn f) { + GraphicsWindow::register_draw_function(f); + glutDisplayFunc(f); +} + +void glutGraphicsWindow::register_idle_function(GraphicsWindow::vfn f) { + GraphicsWindow::register_idle_function(f); + glutIdleFunc(f); +} + +void glutGraphicsWindow::register_resize_function(GraphicsWindow::vfnii f) { + GraphicsWindow::register_resize_function(f); + glutReshapeFunc(f); +} + +void glutGraphicsWindow::main_loop(void) { + glutMainLoop(); +} + +void glutGraphicsWindow::make_current(void) { + if (_global_window != this) { + PStatTimer timer(_make_current_pcollector); + glutSetWindow(_handle); + _global_window = this; + } +} + +void glutGraphicsWindow::unmake_current(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function: glutGraphicsWindow::get_gsg_type +// Access: Public, Virtual +// Description: Returns the TypeHandle of the kind of GSG preferred +// by this kind of window. +//////////////////////////////////////////////////////////////////// +TypeHandle glutGraphicsWindow:: +get_gsg_type() const { + return GLGraphicsStateGuardian::get_class_type(); +} + +GraphicsWindow *glutGraphicsWindow:: +make_GlutGraphicsWindow(const FactoryParams ¶ms) { + GraphicsWindow::WindowPipe *pipe_param; + if (!get_param_into(pipe_param, params)) { + glutdisplay_cat.error() + << "No pipe specified for window creation!" << endl; + return NULL; + } + + GraphicsPipe *pipe = pipe_param->get_pipe(); + + GraphicsWindow::WindowProps *props_param; + if (!get_param_into(props_param, params)) { + return new glutGraphicsWindow(pipe); + } else { + return new glutGraphicsWindow(pipe, props_param->get_properties()); + } +} + +TypeHandle glutGraphicsWindow::get_class_type(void) { + return _type_handle; +} + +void glutGraphicsWindow::init_type(void) { + GraphicsWindow::init_type(); + register_type(_type_handle, "glutGraphicsWindow", + GraphicsWindow::get_class_type()); +} + +TypeHandle glutGraphicsWindow::get_type(void) const { + return get_class_type(); +} diff --git a/panda/src/glutdisplay/glutGraphicsWindow.h b/panda/src/glutdisplay/glutGraphicsWindow.h new file mode 100644 index 0000000000..797e88dd9e --- /dev/null +++ b/panda/src/glutdisplay/glutGraphicsWindow.h @@ -0,0 +1,97 @@ +// Filename: glutGraphicsWindow.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef GLUTGRAPHICSWINDOW_H +#define GLUTGRAPHICSWINDOW_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +class glutGraphicsPipe; + +//////////////////////////////////////////////////////////////////// +// Class : glutGraphicsWindow +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAGLUT glutGraphicsWindow : public GraphicsWindow +{ +public: + glutGraphicsWindow( GraphicsPipe* pipe ); + glutGraphicsWindow( GraphicsPipe* pipe, + const GraphicsWindow::Properties& props ); + virtual ~glutGraphicsWindow( void ); + + virtual void config( void ); + virtual void end_frame( void ); + +public: + // context setting + virtual void make_current(void); + virtual void unmake_current(void); + +protected: + // Window routines + static void glut_handle_reshape( int w, int h ) { + _global_window->handle_reshape( w, h ); + } + void handle_reshape( int w, int h ); + + // Mouse routines + static void glut_handle_mouse_input( int button, int state, int x, + int y ) { + _global_window->handle_mouse_input( button, state, x, y ); + } + void handle_mouse_input( int button, int state, int x, int y ); + static void glut_handle_mouse_motion( int x, int y ) { + _global_window->handle_mouse_motion( x, y ); + } + void handle_mouse_motion( int x, int y ); + static void glut_handle_mouse_entry( int state ) { + _global_window->handle_mouse_entry( state ); + } + void handle_mouse_entry( int state ); + + // Keyboard routines + static void glut_handle_ascii_keyboard_input( uchar key, int x, int y ) { + _global_window->handle_ascii_keyboard_input( key, x, y ); + } + void handle_ascii_keyboard_input( uchar key, int, int ); + static void glut_handle_special_keyboard_input( int key, int x, int y ) { + _global_window->handle_special_keyboard_input( key, x, y ); + } + void handle_special_keyboard_input( int key, int, int ); + +protected: + static glutGraphicsWindow* _global_window; + int _handle; + +public: + virtual void flag_redisplay(void); + virtual void register_draw_function(GraphicsWindow::vfn); + virtual void register_idle_function(GraphicsWindow::vfn); + virtual void register_resize_function(GraphicsWindow::vfnii); + virtual void main_loop(void); + + virtual TypeHandle get_gsg_type() const; + static GraphicsWindow* make_GlutGraphicsWindow(const FactoryParams ¶ms); + +public: + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/glutdisplay/test_glut.cxx b/panda/src/glutdisplay/test_glut.cxx new file mode 100644 index 0000000000..26aca5f4e1 --- /dev/null +++ b/panda/src/glutdisplay/test_glut.cxx @@ -0,0 +1,68 @@ +// Filename: test_glut.cxx +// Created by: mike (02Feb00) +// +//////////////////////////////////////////////////////////////////// + +//#include "glutGraphicsPipe.h" +//#include "glutGraphicsWindow.h" +#include +#include +#include +#include + +#include + +PT(GraphicsPipe) main_pipe; +PT(GraphicsWindow) main_win; +PT_NamedNode render; +NodeAttributes initial_state; + +void render_frame(GraphicsPipe *pipe) { + GraphicsPipe::wins_iterator wi; + for (wi = pipe->get_win_begin(); + wi != pipe->get_win_end(); + ++wi) { + (*wi)->get_gsg()->render_frame(render, initial_state); + } +} + +void display_func(void) { + render_frame(main_pipe); +} + +int main() { + + GraphicsPipe::resolve_modules(); + + cout << "Known pipe types:" << endl; + GraphicsPipe::PipeFactory::FactoryTypesIter pti; + for (pti = GraphicsPipe::_factory.get_types_begin(); + pti != GraphicsPipe::_factory.get_types_end(); ++pti) { + cout << " " << (*pti) << endl; + } + cout << "after Known pipe types" << endl; + + GraphicsPipe::PipeFactory& factory = GraphicsPipe::_factory; + GraphicsPipe::PipePrioritiesIter pribegin = + GraphicsPipe::get_priorities_begin(); + GraphicsPipe::PipePrioritiesIter priend = GraphicsPipe::get_priorities_end(); + + GraphicsPipe::PipeParams ps1; + if (pribegin == priend) + main_pipe = factory.instance(InteractiveGraphicsPipe::get_class_type(), + ps1.begin(), ps1.end()); + else + main_pipe = factory.instance(InteractiveGraphicsPipe::get_class_type(), + ps1.begin(), ps1.end(), pribegin, priend); + nassertr(main_pipe != (GraphicsPipe*)0L, 0); + cout << "Opened a '" << main_pipe->get_type().get_name() + << "' interactive graphics pipe." << endl; + +#if 0 + main_win = new glutGraphicsWindow(main_pipe); + main_win->register_draw_function(display_func); + main_win->main_loop(); +#endif + + return 0; +} diff --git a/panda/src/glutdisplay/test_glut_win.cxx b/panda/src/glutdisplay/test_glut_win.cxx new file mode 100644 index 0000000000..57e5cc7416 --- /dev/null +++ b/panda/src/glutdisplay/test_glut_win.cxx @@ -0,0 +1,25 @@ +// Filename: test_glut_win.cxx +// Created by: mike (02Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include + +#ifdef WIN32_VC +#include +#endif +#include + +void display_func(void) { + cerr << "in display func" << endl; + glutPostRedisplay(); +} + +int main() { + glutInitWindowSize(256, 256); + glutCreateWindow("test glut window"); + glutDisplayFunc(display_func); + glutMainLoop(); + return 0; +} diff --git a/panda/src/glxdisplay/Sources.pp b/panda/src/glxdisplay/Sources.pp new file mode 100644 index 0000000000..5c5a5a5eac --- /dev/null +++ b/panda/src/glxdisplay/Sources.pp @@ -0,0 +1,21 @@ +#define DIRECTORY_IF_GL yes +#define DIRECTORY_IF_GLX yes + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase +#define USE_GL yes + +#begin lib_target + #define TARGET glxdisplay + #define LOCAL_LIBS \ + glgsg + + #define SOURCES \ + config_glxdisplay.cxx config_glxdisplay.h glxGraphicsPipe.cxx \ + glxGraphicsPipe.h glxGraphicsWindow.I glxGraphicsWindow.cxx \ + glxGraphicsWindow.h + + #define INSTALL_HEADERS \ + glxGraphicsPipe.h glxGraphicsWindow.I glxGraphicsWindow.h + +#end lib_target + diff --git a/panda/src/glxdisplay/config_glxdisplay.cxx b/panda/src/glxdisplay/config_glxdisplay.cxx new file mode 100644 index 0000000000..0105b75f3a --- /dev/null +++ b/panda/src/glxdisplay/config_glxdisplay.cxx @@ -0,0 +1,22 @@ +// Filename: config_glxdisplay.cxx +// Created by: cary (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_glxdisplay.h" +#include "glxGraphicsPipe.h" +#include "glxGraphicsWindow.h" + +#include + +Configure(config_glxdisplay); +NotifyCategoryDef(glxdisplay, "display"); + +ConfigureFn(config_glxdisplay) { + glxGraphicsPipe::init_type(); + GraphicsPipe::_factory.register_factory(glxGraphicsPipe::get_class_type(), + glxGraphicsPipe::make_glxGraphicsPipe); + glxGraphicsWindow::init_type(); + GraphicsWindow::_factory.register_factory(glxGraphicsWindow::get_class_type(), + glxGraphicsWindow::make_GlxGraphicsWindow); +} diff --git a/panda/src/glxdisplay/config_glxdisplay.h b/panda/src/glxdisplay/config_glxdisplay.h new file mode 100644 index 0000000000..1614e2f9e6 --- /dev/null +++ b/panda/src/glxdisplay/config_glxdisplay.h @@ -0,0 +1,15 @@ +// Filename: config_glxdisplay.h +// Created by: cary (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __CONFIG_GLXDISPLAY_H__ +#define __CONFIG_GLXDISPLAY_H__ + +#include +#include + +NotifyCategoryDecl(glxdisplay, EXPCL_PANDAGL, EXPTP_PANDAGL); + + +#endif /* __CONFIG_GLXDISPLAY_H__ */ diff --git a/panda/src/glxdisplay/glxGraphicsPipe.cxx b/panda/src/glxdisplay/glxGraphicsPipe.cxx new file mode 100644 index 0000000000..b0538d3d3b --- /dev/null +++ b/panda/src/glxdisplay/glxGraphicsPipe.cxx @@ -0,0 +1,110 @@ +// Filename: glxGraphicsPipe.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "glxGraphicsPipe.h" +#include "config_glxdisplay.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle glxGraphicsPipe::_type_handle; + +glxGraphicsPipe::glxGraphicsPipe(const PipeSpecifier& spec) + : InteractiveGraphicsPipe(spec) +{ + // _display = XOpenDisplay(get_name().c_str()); + _display = XOpenDisplay((spec.get_X_specifier()).c_str()); + if (!_display) { + glxdisplay_cat.fatal() + << "glxGraphicsPipe::construct(): Could not open display: " + << spec.get_X_specifier() << endl; + exit(0); + } + int errorBase, eventBase; + if (!glXQueryExtension(_display, &errorBase, &eventBase)) { + glxdisplay_cat.fatal() + << "glxGraphicsPipe::construct(): OpenGL GLX extension not " + << "supported by display: " << spec.get_X_specifier() << endl; + exit(0); + } + _screen = DefaultScreen(_display); + _root = RootWindow(_display, _screen); + _width = DisplayWidth(_display, _screen); + _height = DisplayHeight(_display, _screen); +} + +//////////////////////////////////////////////////////////////////// +// Function: glxGraphicsPipe::get_window_type +// Access: Public, Virtual +// Description: Returns the TypeHandle of the kind of window +// preferred by this kind of pipe. +//////////////////////////////////////////////////////////////////// +TypeHandle glxGraphicsPipe:: +get_window_type() const { + return glxGraphicsWindow::get_class_type(); +} + +GraphicsPipe *glxGraphicsPipe:: +make_glxGraphicsPipe(const FactoryParams ¶ms) { + GraphicsPipe::PipeSpec *pipe_param; + if (!get_param_into(pipe_param, params)) { + return new glxGraphicsPipe(PipeSpecifier()); + } else { + return new glxGraphicsPipe(pipe_param->get_specifier()); + } +} + +TypeHandle glxGraphicsPipe::get_class_type(void) { + return _type_handle; +} + +void glxGraphicsPipe::init_type(void) { + InteractiveGraphicsPipe::init_type(); + register_type(_type_handle, "glxGraphicsPipe", + InteractiveGraphicsPipe::get_class_type()); +} + +TypeHandle glxGraphicsPipe::get_type(void) const { + return get_class_type(); +} + +glxGraphicsPipe::glxGraphicsPipe(void) { + glxdisplay_cat.error() + << "glxGraphicsPipes should not be created with the default constructor" + << endl; +} + +glxGraphicsPipe::glxGraphicsPipe(const glxGraphicsPipe&) { + glxdisplay_cat.error() + << "glxGraphicsPipes should not be copied" << endl; +} + +glxGraphicsPipe& glxGraphicsPipe::operator=(const glxGraphicsPipe&) { + glxdisplay_cat.error() + << "glxGraphicsPipes should not be assigned" << endl; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: find_window +// Access: +// Description: Find the window that has the xwindow "win" in the +// window list for the pipe (if it exists) +//////////////////////////////////////////////////////////////////// +glxGraphicsWindow *glxGraphicsPipe:: +find_window(Window win) { + int num_windows = get_num_windows(); + for (int w = 0; w < num_windows; w++) { + glxGraphicsWindow *window = DCAST(glxGraphicsWindow, get_window(w)); + if (window->get_xwindow() == win) + return window; + } + return NULL; +} diff --git a/panda/src/glxdisplay/glxGraphicsPipe.h b/panda/src/glxdisplay/glxGraphicsPipe.h new file mode 100644 index 0000000000..062beadb1c --- /dev/null +++ b/panda/src/glxdisplay/glxGraphicsPipe.h @@ -0,0 +1,68 @@ +// Filename: glxGraphicsPipe.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef GLXGRAPHICSPIPE_H +#define GLXGRAPHICSPIPE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include "glxGraphicsWindow.h" +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +class Xclass; + +//////////////////////////////////////////////////////////////////// +// Class : glxGraphicsPipe +// Description : +//////////////////////////////////////////////////////////////////// +class glxGraphicsPipe : public InteractiveGraphicsPipe +{ + public: + + glxGraphicsPipe( const PipeSpecifier& ); + + virtual TypeHandle get_window_type() const; + + glxGraphicsWindow* find_window(Window win); + + public: + + static GraphicsPipe* make_glxGraphicsPipe(const FactoryParams ¶ms); + + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + + INLINE Display* get_display(void) { return _display; } + INLINE int get_screen(void) { return _screen; } + INLINE Window get_root(void) { return _root; } + + private: + + static TypeHandle _type_handle; + + Display* _display; + int _screen; + Window _root; + int _width; + int _height; + + protected: + + glxGraphicsPipe( void ); + glxGraphicsPipe( const glxGraphicsPipe& ); + glxGraphicsPipe& operator=(const glxGraphicsPipe&); +}; + +#endif diff --git a/panda/src/glxdisplay/glxGraphicsWindow.I b/panda/src/glxdisplay/glxGraphicsWindow.I new file mode 100644 index 0000000000..fe55883c83 --- /dev/null +++ b/panda/src/glxdisplay/glxGraphicsWindow.I @@ -0,0 +1,30 @@ +// Filename: glxGraphicsWindow.I +// Created by: mike (07Mar99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: add_event_mask +// Access: Protected +// Description: Ensures we only update window events once +//////////////////////////////////////////////////////////////////// +INLINE void glxGraphicsWindow::add_event_mask(long event_mask) +{ + if ((_event_mask & event_mask) != event_mask) { + _event_mask |= event_mask; + _change_mask |= GLXWIN_EVENT; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: remove_event_mask +// Access: Protected +// Description: Ensures we only update window events once +//////////////////////////////////////////////////////////////////// +INLINE void glxGraphicsWindow::remove_event_mask(long event_mask) +{ + if (_event_mask & event_mask) { + _event_mask &= ~event_mask; + _change_mask |= GLXWIN_EVENT; + } +} diff --git a/panda/src/glxdisplay/glxGraphicsWindow.cxx b/panda/src/glxdisplay/glxGraphicsWindow.cxx new file mode 100644 index 0000000000..39e272608e --- /dev/null +++ b/panda/src/glxdisplay/glxGraphicsWindow.cxx @@ -0,0 +1,1366 @@ +// Filename: glxGraphicsWindow.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "glxGraphicsWindow.h" +#include "glxGraphicsPipe.h" +#include "config_glxdisplay.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle glxGraphicsWindow::_type_handle; + +const char* glxGraphicsWindow::_glx_extensions = NULL; + +#define MOUSE_ENTERED 0 +#define MOUSE_EXITED 1 + +//////////////////////////////////////////////////////////////////// +// Function: Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +glxGraphicsWindow::glxGraphicsWindow( GraphicsPipe* pipe ) : + GraphicsWindow( pipe ) +{ + config(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +glxGraphicsWindow::glxGraphicsWindow( GraphicsPipe* pipe, const + GraphicsWindow::Properties& props ) : GraphicsWindow( pipe, props ) +{ + config(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Destructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +glxGraphicsWindow::~glxGraphicsWindow(void) +{ + // The GL context is already gone. Don't try to destroy it again; + // that will cause a seg fault on exit. + // unmake_current(); + // glXDestroyContext(_display, _context); + + XDestroyWindow(_display, _xwindow); + if (_colormap) + XFreeColormap(_display, _colormap); + XFree(_visual); +} + +//////////////////////////////////////////////////////////////////// +// Function: glx_supports +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +bool glxGraphicsWindow::glx_supports(const char* extension) +{ +#if defined(GLX_VERSION_1_1) + const char* start; + char* where, *terminator; + int major, minor; + + glXQueryVersion(_display, &major, &minor); + if ((major == 1 && minor >= 1) || (major > 1)) { + if (!_glx_extensions) { + _glx_extensions = glXQueryExtensionsString(_display, + ((glxGraphicsPipe *)_pipe)->get_screen()); + } + start = _glx_extensions; + for (;;) { + where = strstr(start, extension); + if (!where) + return false; + terminator = where + strlen(extension); + if (where == start || *(where - 1) == ' ') { + if (*terminator == ' ' || *terminator == '\0') { + return true; + } + } + start = terminator; + } + } + return false; +#else + return false; +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: try_for_visual +// Description: This is a static function that attempts to get the +// requested visual, if it is available. It's just a +// wrapper around glXChooseVisual(). It returns the +// visual information if possible, or NULL if it is not. +//////////////////////////////////////////////////////////////////// +static XVisualInfo * +try_for_visual(glxGraphicsPipe *pipe, int mask, + int want_depth_bits = 1, int want_color_bits = 1) { + static const int max_attrib_list = 32; + int attrib_list[max_attrib_list]; + int n=0; + + glxdisplay_cat.debug() + << "Trying for visual with: RGB(" << want_color_bits << ")"; + + int want_color_component_bits; + if (mask & W_ALPHA) { + want_color_component_bits = max(want_color_bits / 4, 1); + } else { + want_color_component_bits = max(want_color_bits / 3, 1); + } + + + attrib_list[n++] = GLX_RGBA; + attrib_list[n++] = GLX_RED_SIZE; + attrib_list[n++] = want_color_component_bits; + attrib_list[n++] = GLX_GREEN_SIZE; + attrib_list[n++] = want_color_component_bits; + attrib_list[n++] = GLX_BLUE_SIZE; + attrib_list[n++] = want_color_component_bits; + + if (mask & W_ALPHA) { + glxdisplay_cat.debug(false) << " ALPHA"; + attrib_list[n++] = GLX_ALPHA_SIZE; + attrib_list[n++] = want_color_component_bits; + } + if (mask & W_DOUBLE) { + glxdisplay_cat.debug(false) << " DOUBLEBUFFER"; + attrib_list[n++] = GLX_DOUBLEBUFFER; + } + if (mask & W_STEREO) { + glxdisplay_cat.debug(false) << " STEREO"; + attrib_list[n++] = GLX_STEREO; + } + if (mask & W_DEPTH) { + glxdisplay_cat.debug(false) << " DEPTH(" << want_depth_bits << ")"; + attrib_list[n++] = GLX_DEPTH_SIZE; + attrib_list[n++] = want_depth_bits; + } + if (mask & W_STENCIL) { + glxdisplay_cat.debug(false) << " STENCIL"; + attrib_list[n++] = GLX_STENCIL_SIZE; + attrib_list[n++] = 1; + } + if (mask & W_ACCUM) { + glxdisplay_cat.debug(false) << " ACCUM"; + attrib_list[n++] = GLX_ACCUM_RED_SIZE; + attrib_list[n++] = want_color_component_bits; + attrib_list[n++] = GLX_ACCUM_GREEN_SIZE; + attrib_list[n++] = want_color_component_bits; + attrib_list[n++] = GLX_ACCUM_BLUE_SIZE; + attrib_list[n++] = want_color_component_bits; + if (mask & W_ALPHA) { + attrib_list[n++] = GLX_ACCUM_ALPHA_SIZE; + attrib_list[n++] = want_color_component_bits; + } + } +#if defined(GLX_VERSION_1_1) && defined(GLX_SGIS_multisample) + if (mask & W_MULTISAMPLE) { + glxdisplay_cat.debug(false) << " MULTISAMPLE"; + attrib_list[n++] = GLX_SAMPLES_SGIS; + // We decide 4 is minimum number of samples + attrib_list[n++] = 4; + } +#endif + + // Terminate the list + nassertr(n < max_attrib_list, NULL); + attrib_list[n] = (int)None; + + XVisualInfo *vinfo = + glXChooseVisual(pipe->get_display(), pipe->get_screen(), attrib_list); + + if (glxdisplay_cat.is_debug()) { + if (vinfo != NULL) { + glxdisplay_cat.debug(false) << ", match found!\n"; + } else { + glxdisplay_cat.debug(false) << ", no match.\n"; + } + } + + return vinfo; +} + +//////////////////////////////////////////////////////////////////// +// Function: choose visual +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::choose_visual(void) +{ + glxGraphicsPipe* pipe = DCAST(glxGraphicsPipe, _pipe); + + int mask = _props._mask; + int want_depth_bits = _props._want_depth_bits; + int want_color_bits = _props._want_color_bits; + +#if defined(GLX_VERSION_1_1) && defined(GLX_SGIS_multisample) + if (mask & W_MULTISAMPLE) { + if (!glx_supports("GLX_SGIS_multisample")) { + glxdisplay_cat.info() + << "glxGraphicsWindow::config() - multisample not supported" + << endl; + mask &= ~W_MULTISAMPLE; + } + } +#endif + + _visual = try_for_visual(pipe, mask, want_depth_bits, want_color_bits); + + // This is the severity level at which we'll report the details of + // the visual we actually do find. Normally, it's debug-level + // information: we don't care about that much detail. + NotifySeverity show_visual_severity = NS_debug; + + if (_visual == NULL) { + glxdisplay_cat.info() + << "glxGraphicsWindow::choose_visual() - visual with requested\n" + << " capabilities not found; trying for lesser visual.\n"; + + // If we're unable to get the visual we asked for, however, we + // probably *do* care to know the details about what we actually + // got, even if we don't have debug mode set. So we'll report the + // visual at a higher level. + show_visual_severity = NS_info; + + bool special_size_request = + (want_depth_bits != 1 || want_color_bits != 1); + + // We try to be smart about choosing a close match for the visual. + // First, we'll eliminate some of the more esoteric options one at + // a time, then two at a time, and finally we'll try just the bare + // minimum. + + if (special_size_request) { + // Actually, first we'll eliminate all of the minimum sizes, to + // try to open a window with all of the requested options, but + // maybe not as many bits in some options as we'd like. + _visual = try_for_visual(pipe, mask); + } + + if (_visual == NULL) { + // Ok, not good enough. Now try to eliminate options, but keep + // as many bits as we asked for. + + // This array keeps the bitmasks of options that we pull out of + // the requested mask, in order. + + static const int strip_properties[] = { + // One esoteric option removed. + W_MULTISAMPLE, + W_STENCIL, + W_ACCUM, + W_ALPHA, + W_STEREO, + + // Two esoteric options removed. + W_STENCIL | W_MULTISAMPLE, + W_ACCUM | W_MULTISAMPLE, + W_ALPHA | W_MULTISAMPLE, + W_STEREO | W_MULTISAMPLE, + W_STENCIL | W_ACCUM, + W_ALPHA | W_STEREO, + W_STENCIL | W_ACCUM | W_MULTISAMPLE, + W_ALPHA | W_STEREO | W_MULTISAMPLE, + + // All esoteric options removed. + W_STENCIL | W_ACCUM | W_ALPHA | W_STEREO | W_MULTISAMPLE, + + // All esoteric options, plus some we'd really really prefer, + // removed. + W_STENCIL | W_ACCUM | W_ALPHA | W_STEREO | W_MULTISAMPLE | W_DOUBLE, + + // A zero marks the end of the array. + 0 + }; + + set tried_masks; + tried_masks.insert(mask); + + int i; + for (i = 0; _visual == NULL && strip_properties[i] != 0; i++) { + int new_mask = mask & ~strip_properties[i]; + if (tried_masks.insert(new_mask).second) { + _visual = try_for_visual(pipe, new_mask, want_depth_bits, + want_color_bits); + } + } + + if (special_size_request) { + tried_masks.clear(); + tried_masks.insert(mask); + + if (_visual == NULL) { + // Try once more, this time eliminating all of the size + // requests. + for (i = 0; _visual == NULL && strip_properties[i] != 0; i++) { + int new_mask = mask & ~strip_properties[i]; + if (tried_masks.insert(new_mask).second) { + _visual = try_for_visual(pipe, new_mask); + } + } + } + } + + if (_visual == NULL) { + // Here's our last-ditch desparation attempt: give us any GLX + // visual at all! + _visual = try_for_visual(pipe, 0); + } + + if (_visual == NULL) { + glxdisplay_cat.fatal() + << "glxGraphicsWindow::choose_visual() - could not get any " + "GLX visual." << endl; + exit(1); + } + } + } + + glxdisplay_cat.info() + << "Got visual 0x" << hex << (int)_visual->visualid << dec << ".\n"; + + if (glxdisplay_cat.is_on(show_visual_severity)) { + int render_mode, double_buffer, stereo, red_size, green_size, blue_size, + alpha_size, ared_size, agreen_size, ablue_size, aalpha_size, + depth_size, stencil_size; + + glXGetConfig(_display, _visual, GLX_RGBA, &render_mode); + glXGetConfig(_display, _visual, GLX_DOUBLEBUFFER, &double_buffer); + glXGetConfig(_display, _visual, GLX_STEREO, &stereo); + glXGetConfig(_display, _visual, GLX_RED_SIZE, &red_size); + glXGetConfig(_display, _visual, GLX_GREEN_SIZE, &green_size); + glXGetConfig(_display, _visual, GLX_BLUE_SIZE, &blue_size); + glXGetConfig(_display, _visual, GLX_ALPHA_SIZE, &alpha_size); + glXGetConfig(_display, _visual, GLX_ACCUM_RED_SIZE, &ared_size); + glXGetConfig(_display, _visual, GLX_ACCUM_GREEN_SIZE, &agreen_size); + glXGetConfig(_display, _visual, GLX_ACCUM_BLUE_SIZE, &ablue_size); + glXGetConfig(_display, _visual, GLX_ACCUM_ALPHA_SIZE, &aalpha_size); + glXGetConfig(_display, _visual, GLX_DEPTH_SIZE, &depth_size); + glXGetConfig(_display, _visual, GLX_STENCIL_SIZE, &stencil_size); + + glxdisplay_cat.out(show_visual_severity) + << "GLX Visual Info (# bits of each):" << endl + << " RGBA: " << red_size << " " << green_size << " " << blue_size + << " " << alpha_size << endl + << " Accum RGBA: " << ared_size << " " << agreen_size << " " + << ablue_size << " " << aalpha_size << endl + << " Depth: " << depth_size << endl + << " Stencil: " << stencil_size << endl + << " DoubleBuffer? " << double_buffer << endl + << " Stereo? " << stereo << endl; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: config +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::config( void ) +{ + GraphicsWindow::config(); + + // glxGraphicsPipe* pipe = (glxGraphicsPipe *)_pipe; + glxGraphicsPipe* pipe = DCAST(glxGraphicsPipe, _pipe); + _display = pipe->get_display(); + + // Configure the framebuffer according to parameters specified in _props + // Initializes _visual + choose_visual(); + + // Initializes _colormap + setup_colormap(); + + uint attrib_mask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask; + + // Do we even need structure notify? + _event_mask = StructureNotifyMask; + + // Initialize window attributes + XSetWindowAttributes wa; + wa.background_pixmap = None; + wa.border_pixel = 0; + wa.colormap = _colormap; + wa.event_mask = _event_mask; + wa.do_not_propagate_mask = 0; + + _xwindow = XCreateWindow(_display, pipe->get_root(), + _props._xorg, _props._yorg, _props._xsize, _props._ysize, 0, + _visual->depth, InputOutput, _visual->visual, attrib_mask, &wa); + if (!_xwindow) { + glxdisplay_cat.fatal() + << "glxGraphicsWindow::config() - failed to create Xwindow" << endl; + exit(0); + } + + setup_properties(); + + _context = glXCreateContext(_display, _visual, None, GL_TRUE); + if (!_context) { + glxdisplay_cat.fatal() + << "glxGraphicsWindow::config() - failed to create OpenGL rendering " + << "context" << endl; + exit(0); + } + + if (!(glXIsDirect(_display, _context))) { + glxdisplay_cat.info() + << "Graphics context is not direct (it does not bypass X)." + << endl; + } + + _change_mask = 0; + _configure_mask = 0; + + _mouse_input_enabled = false; + _mouse_motion_enabled = false; + _mouse_passive_motion_enabled = false; + _mouse_entry_enabled = false; + _entry_state = -1; + + XSelectInput(_display, _xwindow, _event_mask); + XMapWindow(_display, _xwindow); + + _connection_fd = ConnectionNumber(_display); + + // Enable detection of mouse input + enable_mouse_input(true); + enable_mouse_motion(true); + enable_mouse_passive_motion(true); + enable_mouse_entry(true); + + // Enable detection of keyboard input + add_event_mask(KeyPressMask | KeyReleaseMask); + + // Now indicate that we have our keyboard/mouse device ready. + GraphicsWindowInputDevice device = + GraphicsWindowInputDevice::pointer_and_keyboard("keyboard/mouse"); + _input_devices.push_back(device); + + // Create a GSG to manage the graphics + // First make the new context and window the current one so GL knows how + // to configure itself in the gsg + make_current(); + make_gsg(); +} + +//////////////////////////////////////////////////////////////////// +// Function: setup_colormap +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::setup_colormap(void) +{ + int visual_class, rc, is_rgb; +#if defined(__cplusplus) || defined(c_plusplus) + visual_class = _visual->c_class; +#else + visual_class = _visual->class; +#endif + + // glxGraphicsPipe* pipe = (glxGraphicsPipe *)_pipe; + glxGraphicsPipe* pipe = DCAST(glxGraphicsPipe, _pipe); + + switch (visual_class) { + case PseudoColor: + rc = glXGetConfig(_display, _visual, GLX_RGBA, &is_rgb); + if (rc == 0 && is_rgb) { + glxdisplay_cat.info() + << "glxGraphicsWindow::setup_colormap() - mesa pseudocolor " + << "not supported" << endl; + // this is a terrible terrible hack, that seems to work + _colormap = (Colormap)0; + } else { + _colormap = XCreateColormap(_display, pipe->get_root(), + _visual->visual, AllocAll); + } + break; + case TrueColor: + case DirectColor: + _colormap = XCreateColormap(_display, pipe->get_root(), + _visual->visual, AllocNone); + break; + case StaticColor: + case StaticGray: + case GrayScale: + _colormap = XCreateColormap(_display, pipe->get_root(), + _visual->visual, AllocNone); + break; + default: + glxdisplay_cat.error() + << "glxGraphicsWindow::setup_colormap() - could not allocate a " + << "colormap for visual type: " << visual_class << endl; + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: setup_properties +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::setup_properties(void) +{ + // Name the window if there is a name + XTextProperty window_name; + if (!_props._title.empty()) { + char *name = (char *)_props._title.c_str(); + if (XStringListToTextProperty(&name, 1, &window_name) == 0) { + glxdisplay_cat.error() + << "glxGraphicsWindow::config() - failed to allocate window name " + << "structure" << endl; + } + } + + // Setup size hints + XSizeHints size_hints = {0}; + if (_props._xorg >= 0 && _props._yorg >= 0) { + size_hints.x = _props._xorg; + size_hints.y = _props._yorg; + size_hints.flags |= USPosition; + } + else + size_hints.flags &= ~USPosition; + if (_props._xsize >= 0 && _props._ysize >= 0) { + size_hints.width = _props._xsize; + size_hints.height = _props._ysize; + size_hints.flags |= USSize; + } + else + size_hints.flags &= ~USSize; + + // Setup window manager hints + XWMHints* wm_hints; + if (!(wm_hints = XAllocWMHints())) { + glxdisplay_cat.fatal() + << "failed to allocate wm hints" << endl; + exit(-1); + } + wm_hints->initial_state = NormalState; + wm_hints->flags = StateHint; + + XSetWMProperties(_display, _xwindow, &window_name, &window_name, + NULL, 0, &size_hints, wm_hints, NULL); + XFree(wm_hints); + + // If we asked for a window without a border, there's no sure-fire + // way to arrange that. It really depends on the user's window + // manager of choice. Instead, we'll totally punt and just set the + // window's Class to "Undecorated", and let the user configure + // his/her window manager not to put a border around windows of this + // class. + if (!_props._border) { + XClassHint *class_hint = XAllocClassHint(); + class_hint->res_class = "Undecorated"; + XSetClassHint(_display, _xwindow, class_hint); + XFree(class_hint); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: end_frame +// Access: +// Description: Swaps the front and back buffers. +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::end_frame( void ) +{ + { + PStatTimer timer(_swap_pcollector); + glXSwapBuffers(_display, _xwindow); + } + + GraphicsWindow::end_frame(); +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_reshape +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::handle_reshape(int w, int h) +{ + resized(w, h); + // Do we want to reshape the glViewport here as well? + //glViewport(0, 0, (GLsizei)_props._xsize, (GLsizei)_props._ysize); +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_mouse_motion +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::handle_mouse_motion(int x, int y) { + _input_devices[0].set_pointer_in_window(x, y); +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_mouse_entry +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::handle_mouse_entry(int state) { + if (state == MOUSE_EXITED) { + _input_devices[0].set_pointer_out_of_window(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_keypress +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow:: +handle_keypress(ButtonHandle key, int x, int y) { + _input_devices[0].set_pointer_in_window(x, y); + if (key != ButtonHandle::none()) { + _input_devices[0].button_down(key); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_keyrelease +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow:: +handle_keyrelease(ButtonHandle key, int, int) { + if (key != ButtonHandle::none()) { + _input_devices[0].button_up(key); + } +} + +ButtonHandle glxGraphicsWindow:: +lookup_key(XEvent event) { + // First, get the string key name. If it fits in one character, it + // must be the ASCII equivalent for the key. + char tmp[1]; + if (XLookupString(&event.xkey, tmp, 1, NULL, NULL)) { + ButtonHandle button = KeyboardButton::ascii_key(tmp[0]); + if (button != ButtonHandle::none()) { + return button; + } + } + + // Otherwise, look it up the hard way. + KeySym ks = XLookupKeysym((XKeyEvent *)&event, 0); + + switch (ks) { + case XK_BackSpace: + return KeyboardButton::backspace(); + case XK_Tab: + return KeyboardButton::tab(); + case XK_Return: + return KeyboardButton::enter(); + case XK_Escape: + return KeyboardButton::escape(); + case XK_space: + return KeyboardButton::space(); + case XK_exclam: + return KeyboardButton::ascii_key('!'); + case XK_quotedbl: + return KeyboardButton::ascii_key('"'); + case XK_numbersign: + return KeyboardButton::ascii_key('#'); + case XK_dollar: + return KeyboardButton::ascii_key('$'); + case XK_percent: + return KeyboardButton::ascii_key('%'); + case XK_ampersand: + return KeyboardButton::ascii_key('&'); + case XK_apostrophe: // == XK_quoteright + return KeyboardButton::ascii_key('\''); + case XK_parenleft: + return KeyboardButton::ascii_key('('); + case XK_parenright: + return KeyboardButton::ascii_key(')'); + case XK_asterisk: + return KeyboardButton::ascii_key('*'); + case XK_plus: + return KeyboardButton::ascii_key('+'); + case XK_comma: + return KeyboardButton::ascii_key(','); + case XK_minus: + return KeyboardButton::ascii_key('-'); + case XK_period: + return KeyboardButton::ascii_key('.'); + case XK_slash: + return KeyboardButton::ascii_key('/'); + case XK_0: + return KeyboardButton::ascii_key('0'); + case XK_1: + return KeyboardButton::ascii_key('1'); + case XK_2: + return KeyboardButton::ascii_key('2'); + case XK_3: + return KeyboardButton::ascii_key('3'); + case XK_4: + return KeyboardButton::ascii_key('4'); + case XK_5: + return KeyboardButton::ascii_key('5'); + case XK_6: + return KeyboardButton::ascii_key('6'); + case XK_7: + return KeyboardButton::ascii_key('7'); + case XK_8: + return KeyboardButton::ascii_key('8'); + case XK_9: + return KeyboardButton::ascii_key('9'); + case XK_colon: + return KeyboardButton::ascii_key(':'); + case XK_semicolon: + return KeyboardButton::ascii_key(';'); + case XK_less: + return KeyboardButton::ascii_key('<'); + case XK_equal: + return KeyboardButton::ascii_key('='); + case XK_greater: + return KeyboardButton::ascii_key('>'); + case XK_question: + return KeyboardButton::ascii_key('?'); + case XK_at: + return KeyboardButton::ascii_key('@'); + case XK_A: + return KeyboardButton::ascii_key('A'); + case XK_B: + return KeyboardButton::ascii_key('B'); + case XK_C: + return KeyboardButton::ascii_key('C'); + case XK_D: + return KeyboardButton::ascii_key('D'); + case XK_E: + return KeyboardButton::ascii_key('E'); + case XK_F: + return KeyboardButton::ascii_key('F'); + case XK_G: + return KeyboardButton::ascii_key('G'); + case XK_H: + return KeyboardButton::ascii_key('H'); + case XK_I: + return KeyboardButton::ascii_key('I'); + case XK_J: + return KeyboardButton::ascii_key('J'); + case XK_K: + return KeyboardButton::ascii_key('K'); + case XK_L: + return KeyboardButton::ascii_key('L'); + case XK_M: + return KeyboardButton::ascii_key('M'); + case XK_N: + return KeyboardButton::ascii_key('N'); + case XK_O: + return KeyboardButton::ascii_key('O'); + case XK_P: + return KeyboardButton::ascii_key('P'); + case XK_Q: + return KeyboardButton::ascii_key('Q'); + case XK_R: + return KeyboardButton::ascii_key('R'); + case XK_S: + return KeyboardButton::ascii_key('S'); + case XK_T: + return KeyboardButton::ascii_key('T'); + case XK_U: + return KeyboardButton::ascii_key('U'); + case XK_V: + return KeyboardButton::ascii_key('V'); + case XK_W: + return KeyboardButton::ascii_key('W'); + case XK_X: + return KeyboardButton::ascii_key('X'); + case XK_Y: + return KeyboardButton::ascii_key('Y'); + case XK_Z: + return KeyboardButton::ascii_key('Z'); + case XK_bracketleft: + return KeyboardButton::ascii_key('['); + case XK_backslash: + return KeyboardButton::ascii_key('\\'); + case XK_bracketright: + return KeyboardButton::ascii_key(']'); + case XK_asciicircum: + return KeyboardButton::ascii_key('^'); + case XK_underscore: + return KeyboardButton::ascii_key('_'); + case XK_grave: // == XK_quoteleft + return KeyboardButton::ascii_key('`'); + case XK_a: + return KeyboardButton::ascii_key('a'); + case XK_b: + return KeyboardButton::ascii_key('b'); + case XK_c: + return KeyboardButton::ascii_key('c'); + case XK_d: + return KeyboardButton::ascii_key('d'); + case XK_e: + return KeyboardButton::ascii_key('e'); + case XK_f: + return KeyboardButton::ascii_key('f'); + case XK_g: + return KeyboardButton::ascii_key('g'); + case XK_h: + return KeyboardButton::ascii_key('h'); + case XK_i: + return KeyboardButton::ascii_key('i'); + case XK_j: + return KeyboardButton::ascii_key('j'); + case XK_k: + return KeyboardButton::ascii_key('k'); + case XK_l: + return KeyboardButton::ascii_key('l'); + case XK_m: + return KeyboardButton::ascii_key('m'); + case XK_n: + return KeyboardButton::ascii_key('n'); + case XK_o: + return KeyboardButton::ascii_key('o'); + case XK_p: + return KeyboardButton::ascii_key('p'); + case XK_q: + return KeyboardButton::ascii_key('q'); + case XK_r: + return KeyboardButton::ascii_key('r'); + case XK_s: + return KeyboardButton::ascii_key('s'); + case XK_t: + return KeyboardButton::ascii_key('t'); + case XK_u: + return KeyboardButton::ascii_key('u'); + case XK_v: + return KeyboardButton::ascii_key('v'); + case XK_w: + return KeyboardButton::ascii_key('w'); + case XK_x: + return KeyboardButton::ascii_key('x'); + case XK_y: + return KeyboardButton::ascii_key('y'); + case XK_z: + return KeyboardButton::ascii_key('z'); + case XK_braceleft: + return KeyboardButton::ascii_key('{'); + case XK_bar: + return KeyboardButton::ascii_key('|'); + case XK_braceright: + return KeyboardButton::ascii_key('}'); + case XK_asciitilde: + return KeyboardButton::ascii_key('~'); + case XK_F1: + return KeyboardButton::f1(); + case XK_F2: + return KeyboardButton::f2(); + case XK_F3: + return KeyboardButton::f3(); + case XK_F4: + return KeyboardButton::f4(); + case XK_F5: + return KeyboardButton::f5(); + case XK_F6: + return KeyboardButton::f6(); + case XK_F7: + return KeyboardButton::f7(); + case XK_F8: + return KeyboardButton::f8(); + case XK_F9: + return KeyboardButton::f9(); + case XK_F10: + return KeyboardButton::f10(); + case XK_F11: + return KeyboardButton::f11(); + case XK_F12: + return KeyboardButton::f12(); + case XK_KP_Left: + case XK_Left: + return KeyboardButton::left(); + case XK_KP_Up: + case XK_Up: + return KeyboardButton::up(); + case XK_KP_Right: + case XK_Right: + return KeyboardButton::right(); + case XK_KP_Down: + case XK_Down: + return KeyboardButton::down(); + case XK_KP_Prior: + case XK_Prior: + return KeyboardButton::page_up(); + case XK_KP_Next: + case XK_Next: + return KeyboardButton::page_down(); + case XK_KP_Home: + case XK_Home: + return KeyboardButton::home(); + case XK_KP_End: + case XK_End: + return KeyboardButton::end(); + case XK_KP_Insert: + case XK_Insert: + return KeyboardButton::insert(); + case XK_KP_Delete: + case XK_Delete: + return KeyboardButton::del(); + case XK_Shift_L: + case XK_Shift_R: + return KeyboardButton::shift(); + case XK_Control_L: + case XK_Control_R: + return KeyboardButton::control(); + case XK_Alt_L: + case XK_Alt_R: + return KeyboardButton::alt(); + case XK_Meta_L: + case XK_Meta_R: + return KeyboardButton::meta(); + case XK_Caps_Lock: + return KeyboardButton::caps_lock(); + case XK_Shift_Lock: + return KeyboardButton::shift_lock(); + } + + return ButtonHandle::none(); +} + +//////////////////////////////////////////////////////////////////// +// Function: interruptible_xnextevent +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +int glxGraphicsWindow::interruptible_xnextevent(Display* display, XEvent* event) +{ + XFlush(display); + if (XPending(display)) { + XNextEvent(display, event); + return 1; + } else + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_changes +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::handle_changes(void) +{ + // As an optimization, check to see if change is anything other than a + // redisplay + if (_change_mask & (GLXWIN_CONFIGURE | GLXWIN_EVENT)) { + if (_change_mask & GLXWIN_CONFIGURE) { + XWindowChanges changes; + changes.x = _props._xorg; + changes.y = _props._yorg; + if (_configure_mask & (CWWidth | CWHeight)) { + changes.width = _props._xsize; + changes.height = _props._ysize; + } + XConfigureWindow(_display, _xwindow, _configure_mask, &changes); + _configure_mask = 0; + } + if (_change_mask & GLXWIN_EVENT) { + XSelectInput(_display, _xwindow, _event_mask); + } + } + + _change_mask = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: process_event +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::process_event(XEvent event) +{ + glxGraphicsPipe* pipe = DCAST(glxGraphicsPipe, _pipe); + bool down = false; + + switch (event.type) { + case MappingNotify: + break; + case ConfigureNotify: + if (_props._xsize != event.xconfigure.width || + _props._ysize != event.xconfigure.height) { + _props._xsize = event.xconfigure.width; + _props._ysize = event.xconfigure.height; + make_current(); + // Do not execute OpenGL out of sequence with respect to the + // XResizeWindow request + glXWaitX(); + handle_reshape(event.xconfigure.width, event.xconfigure.height); + _change_mask |= GLXWIN_CONFIGURE; + } + break; + case Expose: + break; + + case ButtonPress: + make_current(); + handle_keypress(MouseButton::button(event.xbutton.button - 1), + event.xkey.x, event.xkey.y); + break; + + case ButtonRelease: + make_current(); + handle_keyrelease(MouseButton::button(event.xbutton.button - 1), + event.xkey.x, event.xkey.y); + break; + + case MotionNotify: + if (_mouse_motion_enabled && event.xmotion.state & + (Button1Mask | Button2Mask | Button3Mask)) { + make_current(); + handle_mouse_motion(event.xmotion.x, event.xmotion.y); + } else if (_mouse_passive_motion_enabled && + ((event.xmotion.state & + (Button1Mask | Button2Mask | Button3Mask)) == 0)) { + make_current(); + handle_mouse_motion(event.xmotion.x, event.xmotion.y); + } + break; + + case KeyPress: + make_current(); + handle_keypress(lookup_key(event), event.xkey.x, event.xkey.y); + break; + + case KeyRelease: + make_current(); + handle_keyrelease(lookup_key(event), event.xkey.x, event.xkey.y); + break; + + case EnterNotify: + case LeaveNotify: + if (_mouse_entry_enabled) { + if (event.type == EnterNotify) { + // Overlays can generate multiple enter events + if (_entry_state != EnterNotify) { + _entry_state = EnterNotify; + make_current(); + handle_mouse_entry(MOUSE_ENTERED); + if (_mouse_passive_motion_enabled) { + handle_mouse_motion(event.xcrossing.x, event.xcrossing.y); + } + } + } else { // event.type == LeaveNotify + if (_entry_state != LeaveNotify) { + _entry_state = LeaveNotify; + make_current(); + handle_mouse_entry(MOUSE_EXITED); + } + } + } else if (_mouse_passive_motion_enabled) { + make_current(); + handle_mouse_motion(event.xcrossing.x, event.xcrossing.y); + } + break; + case UnmapNotify: + case VisibilityNotify: + case ClientMessage: + case DestroyNotify: + case CirculateNotify: + case CreateNotify: + case GravityNotify: + case ReparentNotify: + break; + default: + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: process_events +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::process_events(void) +{ + XEvent event, ahead; + int got_event; + glxGraphicsWindow* window; + + // glxGraphicsPipe* pipe = (glxGraphicsPipe *)_pipe; + glxGraphicsPipe* pipe = DCAST(glxGraphicsPipe, _pipe); + + do { + got_event = interruptible_xnextevent(_display, &event); + if (got_event) { + switch (event.type) { + case MappingNotify: + XRefreshKeyboardMapping((XMappingEvent *) &event); + break; + case ConfigureNotify: + if ((window = pipe->find_window(event.xconfigure.window)) != NULL) + window->process_event(event); + break; + case Expose: + XEvent ahead; + while (XEventsQueued(_display, QueuedAfterReading) > 0) { + XPeekEvent(_display, &ahead); + if (ahead.type != Expose || + ahead.xexpose.window != event.xexpose.window) { + break; + } + XNextEvent(_display, &event); + } + break; + case ButtonPress: + case ButtonRelease: + if ((window = pipe->find_window(event.xbutton.window)) != NULL) + window->process_event(event); + break; + case MotionNotify: + if ((window = pipe->find_window(event.xmotion.window)) != NULL) + window->process_event(event); + break; + case KeyPress: + case KeyRelease: + if ((window = pipe->find_window(event.xmotion.window)) != NULL) + window->process_event(event); + break; + case EnterNotify: + case LeaveNotify: + if (event.xcrossing.mode != NotifyNormal || + event.xcrossing.mode == NotifyNonlinearVirtual || + event.xcrossing.mode == NotifyVirtual) { + // Ignore "virtual" window enter/leave events + break; + } + if ((window = pipe->find_window(event.xcrossing.window)) != NULL) + window->process_event(event); + break; + case UnmapNotify: + case VisibilityNotify: + break; + case ClientMessage: + break; + case DestroyNotify: + case CirculateNotify: + case CreateNotify: + case GravityNotify: + case ReparentNotify: + break; + default: + break; + } + } + } + while (XPending(_display)); +} + +//////////////////////////////////////////////////////////////////// +// Function: idle_wait +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::idle_wait(void) +{ + if (XPending(_display)) + process_events(); + + call_idle_callback(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: glxGraphicsWindow::supports_update +// Access: Public, Virtual +// Description: Returns true if this particular kind of +// GraphicsWindow supports use of the update() function +// to update the graphics one frame at a time, so that +// the window does not need to be the program's main +// loop. Returns false if the only way to update the +// window is to call main_loop(). +//////////////////////////////////////////////////////////////////// +bool glxGraphicsWindow:: +supports_update() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: update +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::update(void) +{ + _show_code_pcollector.stop(); + PStatClient::main_tick(); + + if (_change_mask) + handle_changes(); + + make_current(); + + // Always ask for a redisplay for now + call_draw_callback(true); + + if (_idle_callback) + idle_wait(); + else + process_events(); + _show_code_pcollector.start(); +} + +//////////////////////////////////////////////////////////////////// +// Function: enable_mouse_input +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::enable_mouse_input(bool val) +{ + long emask = ButtonPressMask | ButtonReleaseMask; + + // See if we need to enable or disable mouse events + if (_mouse_input_enabled == false && val == true) + add_event_mask(emask); + else if (_mouse_input_enabled == true && val == false) + remove_event_mask(emask); + _mouse_input_enabled = val; +} + +//////////////////////////////////////////////////////////////////// +// Function: enable_mouse_motion +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::enable_mouse_motion(bool val) +{ + // Some window managers (4Dwm by default) will mask motion events unless + // button presses are selected as well + long input_mask = ButtonPressMask | ButtonReleaseMask; + long motion_mask = Button1MotionMask | Button2MotionMask | Button3MotionMask; + if (_mouse_motion_enabled == false && val == true) { + add_event_mask(input_mask); + add_event_mask(motion_mask); + } + else if (_mouse_motion_enabled == true && val == false) { + remove_event_mask(motion_mask); + if (_mouse_input_enabled == false) + remove_event_mask(input_mask); + } + _mouse_motion_enabled = val; +} + +//////////////////////////////////////////////////////////////////// +// Function: enable_mouse_passive_motion +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::enable_mouse_passive_motion(bool val) +{ + long entry_mask = EnterWindowMask | LeaveWindowMask; + if (_mouse_passive_motion_enabled == false && val == true) { + add_event_mask(PointerMotionMask); + // Passive motion requires watching enters and leaves so a fake passive + // motion event can be generated on an enter + if (_mouse_entry_enabled == false) + add_event_mask(entry_mask); + } + else if (_mouse_passive_motion_enabled == true && val == false) { + remove_event_mask(PointerMotionMask); + if (_mouse_entry_enabled == false) + remove_event_mask(entry_mask); + } + _mouse_passive_motion_enabled = val; +} + +//////////////////////////////////////////////////////////////////// +// Function: enable_mouse_entry +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::enable_mouse_entry(bool val) +{ + if (_mouse_entry_enabled == false && val == true) { + add_event_mask(EnterWindowMask | LeaveWindowMask); + } + else if (_mouse_entry_enabled == true && val == false) { + remove_event_mask(EnterWindowMask | LeaveWindowMask); + } + _mouse_entry_enabled = val; +} + +//////////////////////////////////////////////////////////////////// +// Function: make_current +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::make_current(void) { + PStatTimer timer(_make_current_pcollector); + glXMakeCurrent(_display, _xwindow, _context); +} + +//////////////////////////////////////////////////////////////////// +// Function: unmake_current +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void glxGraphicsWindow::unmake_current(void) { + glXMakeCurrent(_display, None, NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: glxGraphicsWindow::get_gsg_type +// Access: Public, Virtual +// Description: Returns the TypeHandle of the kind of GSG preferred +// by this kind of window. +//////////////////////////////////////////////////////////////////// +TypeHandle glxGraphicsWindow:: +get_gsg_type() const { + return GLGraphicsStateGuardian::get_class_type(); +} + +GraphicsWindow *glxGraphicsWindow:: +make_GlxGraphicsWindow(const FactoryParams ¶ms) { + GraphicsWindow::WindowPipe *pipe_param; + if (!get_param_into(pipe_param, params)) { + glxdisplay_cat.error() + << "No pipe specified for window creation!" << endl; + return NULL; + } + + GraphicsPipe *pipe = pipe_param->get_pipe(); + + GraphicsWindow::WindowProps *props_param; + if (!get_param_into(props_param, params)) { + return new glxGraphicsWindow(pipe); + } else { + return new glxGraphicsWindow(pipe, props_param->get_properties()); + } +} + +TypeHandle glxGraphicsWindow::get_class_type(void) { + return _type_handle; +} + +void glxGraphicsWindow::init_type(void) { + GraphicsWindow::init_type(); + register_type(_type_handle, "glxGraphicsWindow", + GraphicsWindow::get_class_type()); +} + +TypeHandle glxGraphicsWindow::get_type(void) const { + return get_class_type(); +} diff --git a/panda/src/glxdisplay/glxGraphicsWindow.h b/panda/src/glxdisplay/glxGraphicsWindow.h new file mode 100644 index 0000000000..2b1381ae56 --- /dev/null +++ b/panda/src/glxdisplay/glxGraphicsWindow.h @@ -0,0 +1,115 @@ +// Filename: glxGraphicsWindow.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef GLXGRAPHICSWINDOW_H +#define GLXGRAPHICSWINDOW_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +class glxGraphicsPipe; + +const int GLXWIN_PROCESSING = 1; +const int GLXWIN_REDISPLAY = 2; +const int GLXWIN_CONFIGURE = 4; +const int GLXWIN_EVENT = 8; + +//////////////////////////////////////////////////////////////////// +// Class : glxGraphicsWindow +// Description : +//////////////////////////////////////////////////////////////////// +class glxGraphicsWindow : public GraphicsWindow +{ +public: + glxGraphicsWindow( GraphicsPipe* pipe ); + glxGraphicsWindow( GraphicsPipe* pipe, + const GraphicsWindow::Properties& props ); + virtual ~glxGraphicsWindow( void ); + + virtual bool supports_update() const; + virtual void update(void); + virtual void end_frame( void ); + + INLINE Window get_xwindow(void) { return _xwindow; } + + virtual TypeHandle get_gsg_type() const; + static GraphicsWindow* make_GlxGraphicsWindow(const FactoryParams ¶ms); + +public: + virtual void make_current(void); + virtual void unmake_current(void); + +protected: + void choose_visual(void); + virtual void config( void ); + void setup_colormap(void); + void setup_properties(void); + + void enable_mouse_input(bool val); + void enable_mouse_motion(bool val); + void enable_mouse_passive_motion(bool val); + void enable_mouse_entry(bool val); + + void handle_reshape( int w, int h ); + void handle_mouse_motion( int x, int y ); + void handle_mouse_entry( int state ); + void handle_keypress( ButtonHandle key, int x, int y ); + void handle_keyrelease( ButtonHandle key, int x, int y ); + ButtonHandle lookup_key(XEvent event); + + int interruptible_xnextevent(Display* display, XEvent* event); + bool glx_supports(const char* extension); + + void handle_changes(void); + void process_event(XEvent); + void process_events(void); + void idle_wait(void); + + INLINE void add_event_mask(long event_mask); + INLINE void remove_event_mask(long event_mask); + +private: + Display* _display; + Window _xwindow; + GLXContext _context; + XVisualInfo* _visual; + Colormap _colormap; + int _connection_fd; + uint _change_mask; + int _configure_mask; + long _event_mask; + + static const char* _glx_extensions; + bool _mouse_input_enabled; + bool _mouse_motion_enabled; + bool _mouse_passive_motion_enabled; + bool _mouse_entry_enabled; + int _entry_state; + bool _ignore_key_repeat; + +public: + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "glxGraphicsWindow.I" + +#endif diff --git a/panda/src/gobj/LOD.I b/panda/src/gobj/LOD.I new file mode 100644 index 0000000000..a380884259 --- /dev/null +++ b/panda/src/gobj/LOD.I @@ -0,0 +1,119 @@ +// Filename: LOD.I +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////// +// Function: LODSwitch::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LODSwitch:: +LODSwitch(float in, float out) { + set_range(in, out); +} + +//////////////////////////////////////////////////////////////////// +// Function: LODSwitch::get_range +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void LODSwitch:: +get_range(float &in, float &out) const { + in = sqrtf(_in); + out = sqrtf(_out); +} + +//////////////////////////////////////////////////////////////////// +// Function: LODSwitch::set_range +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void LODSwitch:: +set_range(float in, float out) { + // We actually store the square of the switching distances. This + // makes the LOD computation a little simpler. + _in = in * in; + _out = out * out; +} + +//////////////////////////////////////////////////////////////////// +// Function: LODSwitch::in_range +// Access: Public +// Description: Computes the distance between two points and returns +// true if the result is within the range for the LOD. +//////////////////////////////////////////////////////////////////// +INLINE bool LODSwitch:: +in_range(float dist_squared) const { + return (dist_squared >= _out && dist_squared < _in); +} + +//////////////////////////////////////////////////////////////////// +// Function: LODSwitch::rescale +// Access: Public +// Description: Scales the switching distances by the square root of +// the indicated factor. +//////////////////////////////////////////////////////////////////// +void LODSwitch:: +rescale(float factor_squared) { + _in *= factor_squared; + _out *= factor_squared; +} + +//////////////////////////////////////////////////////////////////// +// Function: LODSwitch::operator == +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool LODSwitch:: +operator == (const LODSwitch &) const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: LODSwitch::operator != +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool LODSwitch:: +operator != (const LODSwitch &) const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: LODSwitch::operator < +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool LODSwitch:: +operator < (const LODSwitch &) const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: LODSwitch::write_datagram +// Access: Public +// Description: Writes the contents of the LODSwitch out to the +// datagram, presumably in preparation to writing to a +// Bam file. +//////////////////////////////////////////////////////////////////// +INLINE void LODSwitch:: +write_datagram(Datagram &destination) const { + destination.add_float64(_in); + destination.add_float64(_out); +} + +//////////////////////////////////////////////////////////////////// +// Function: LODSwitch::read_datagram +// Access: Public +// Description: Reads the contents of the LODSwitch from the +// datagram, presumably in response to reading a Bam +// file. +//////////////////////////////////////////////////////////////////// +INLINE void LODSwitch:: +read_datagram(DatagramIterator &source) { + _in = source.get_float64(); + _out = source.get_float64(); +} diff --git a/panda/src/gobj/LOD.cxx b/panda/src/gobj/LOD.cxx new file mode 100644 index 0000000000..c16768d640 --- /dev/null +++ b/panda/src/gobj/LOD.cxx @@ -0,0 +1,129 @@ +// Filename: LOD.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "LOD.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle LOD::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LOD::constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +LOD:: +LOD(void) { + _center.set(0, 0, 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LOD::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +LOD:: +LOD(const LOD ©) : + _center(copy._center), + _switch_vector(copy._switch_vector) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: LOD::destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +LOD:: +~LOD(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function: LOD::xform +// Access: Public +// Description: Transforms the LOD specification by the indicated +// matrix. +//////////////////////////////////////////////////////////////////// +void LOD:: +xform(const LMatrix4f &mat) { + _center = _center * mat; + + // We'll take just the length of the y axis as the matrix's scale. + LVector3f y = mat.get_row3(1); + float factor_squared = y.length_squared(); + + LODSwitchVector::iterator si; + for (si = _switch_vector.begin(); si != _switch_vector.end(); ++si) { + (*si).rescale(factor_squared); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: LOD::compute_child +// Access: Public +// Description: Computes the distance between two points and returns +// the index for the child of the LOD by testing against +// the corresponding list of switching distances. +//////////////////////////////////////////////////////////////////// +int LOD:: +compute_child(const LPoint3f &cam_pos, const LPoint3f ¢er) const { + LVector3f v = cam_pos - center; + float dist = dot(v, v); + LODSwitchVector::const_iterator i; + int child = 0; + for (i = _switch_vector.begin(), child = 0; + i != _switch_vector.end(); ++i, ++child) { + if ((*i).in_range(dist)) + break; + } + return child; +} + +//////////////////////////////////////////////////////////////////// +// Function: LOD::write_datagram +// Access: Public +// Description: Writes the contents of the LOD out to the datagram, +// presumably in preparation to writing to a Bam file. +//////////////////////////////////////////////////////////////////// +void LOD:: +write_datagram(Datagram &destination) const { + _center.write_datagram(destination); + + destination.add_uint16(_switch_vector.size()); + + LODSwitchVector::const_iterator si; + for (si = _switch_vector.begin(); + si != _switch_vector.end(); + ++si) { + (*si).write_datagram(destination); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: LOD::read_datagram +// Access: Public +// Description: Reads the contents of the LOD from the datagram, +// presumably in response to reading a Bam file. +//////////////////////////////////////////////////////////////////// +void LOD:: +read_datagram(DatagramIterator &source) { + _center.read_datagram(source); + + _switch_vector.clear(); + + int num_switches = source.get_uint16(); + _switch_vector.reserve(num_switches); + for (int i = 0; i < num_switches; i++) { + _switch_vector.push_back(LODSwitch(0, 0)); + _switch_vector.back().read_datagram(source); + } +} diff --git a/panda/src/gobj/LOD.h b/panda/src/gobj/LOD.h new file mode 100644 index 0000000000..7aeb6dab89 --- /dev/null +++ b/panda/src/gobj/LOD.h @@ -0,0 +1,104 @@ +// Filename: LOD.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef LOD_H +#define LOD_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include + +class Datagram; +class DatagramIterator; + +//////////////////////////////////////////////////////////////////// +// Class : LODSwitch +// Description : Defines a switching region for an LOD. An object +// will be visible when it is closer than "in" units, +// but further than "out" units from the camera. +// +// The sense of in vs. out distances is as if the object +// were coming towards you from far away: it switches +// "in" at the far distance, and switches "out" at the +// close distance. Thus, "in" should be larger than +// "out". +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA LODSwitch { +public: + INLINE LODSwitch(float in, float out); + INLINE void get_range(float &in, float &out) const; + INLINE void set_range(float in, float out); + INLINE bool in_range(float dist_squared) const; + + INLINE void rescale(float factor_squared); + + // We must declare these operators to allow VC++ to explicitly + // export vector, below. They don't do anything useful. + INLINE bool operator == (const LODSwitch &other) const; + INLINE bool operator != (const LODSwitch &other) const; + INLINE bool operator < (const LODSwitch &other) const; + + INLINE void write_datagram(Datagram &destination) const; + INLINE void read_datagram(DatagramIterator &source); + +protected: + float _in; + float _out; +}; + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, std::vector) +typedef vector LODSwitchVector; + +//////////////////////////////////////////////////////////////////// +// Class : LOD +// Description : Computes whether a level-of-detail should be rendered +// or not based on distance from the rendering camera. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA LOD : public ReferenceCount, public TypedObject { +public: + LOD(void); + LOD(const LOD ©); + ~LOD(void); + + void xform(const LMatrix4f &mat); + + int compute_child(const LPoint3f &cam_pos, + const LPoint3f ¢er) const; + + void write_datagram(Datagram &destination) const; + void read_datagram(DatagramIterator &source); + +public: + LPoint3f _center; + LODSwitchVector _switch_vector; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ReferenceCount::init_type(); + TypedObject::init_type(); + register_type(_type_handle, "LOD", + ReferenceCount::get_class_type(), + TypedObject::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "LOD.I" + +#endif diff --git a/panda/src/gobj/Sources.pp b/panda/src/gobj/Sources.pp new file mode 100644 index 0000000000..9d9288727c --- /dev/null +++ b/panda/src/gobj/Sources.pp @@ -0,0 +1,45 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET gobj + #define LOCAL_LIBS \ + linmath mathutil pnmimage gsgbase graph putil + + #define SOURCES \ + LOD.I LOD.cxx LOD.h config_gobj.cxx config_gobj.h drawable.cxx \ + drawable.h fog.I fog.cxx fog.h geom.I geom.N geom.cxx geom.h \ + geomLine.cxx geomLine.h geomLinestrip.cxx geomLinestrip.h \ + geomPoint.cxx geomPoint.h geomPolygon.cxx geomPolygon.h \ + geomQuad.cxx geomQuad.h geomSphere.cxx geomSphere.h geomSprite.I \ + geomSprite.cxx geomSprite.h geomTri.cxx geomTri.h geomTrifan.cxx \ + geomTrifan.h geomTristrip.cxx geomTristrip.h imageBuffer.cxx \ + imageBuffer.h material.I material.cxx material.h orthoProjection.I \ + orthoProjection.cxx orthoProjection.h perspectiveProjection.I \ + perspectiveProjection.cxx perspectiveProjection.h pixelBuffer.I \ + pixelBuffer.N pixelBuffer.cxx pixelBuffer.h projection.cxx \ + projection.h texture.I texture.N texture.cxx texture.h \ + texturePool.I texturePool.cxx texturePool.h + + #define INSTALL_HEADERS \ + LOD.I LOD.h drawable.h fog.I fog.h geom.I geom.h geomLine.h \ + geomLinestrip.h geomPoint.h geomPolygon.h geomQuad.h geomSphere.h \ + geomSprite.I geomSprite.h geomTri.h geomTrifan.h geomTristrip.h \ + geomprimitives.h imageBuffer.h material.I material.h \ + orthoProjection.I orthoProjection.h perspectiveProjection.I \ + perspectiveProjection.h pixelBuffer.I pixelBuffer.h projection.h \ + texture.I texture.h texturePool.I texturePool.h + + #define IGATESCAN all + +#end lib_target + +#begin test_bin_target + #define TARGET test_gobj + #define LOCAL_LIBS \ + gobj putil + + #define SOURCES \ + test_gobj.cxx + +#end test_bin_target + diff --git a/panda/src/gobj/config_gobj.cxx b/panda/src/gobj/config_gobj.cxx new file mode 100644 index 0000000000..9b76201622 --- /dev/null +++ b/panda/src/gobj/config_gobj.cxx @@ -0,0 +1,98 @@ +// Filename: config_gobj.cxx +// Created by: drose (01Oct99) +// + +#include +#include "config_gobj.h" +#include "LOD.h" +#include "drawable.h" +#include "fog.h" +#include "geom.h" +#include "geomprimitives.h" +#include "imageBuffer.h" +#include "material.h" +#include "orthoProjection.h" +#include "perspectiveProjection.h" +#include "pixelBuffer.h" +#include "projection.h" +#include "texture.h" + +#include + +Configure(config_gobj); +NotifyCategoryDef(gobj, ""); + +// Set this to the maximum size a texture is allowed to be in either +// dimension. This is generally intended as a simple way to restrict +// texture sizes for limited graphics cards. When this is greater +// than zero, each texture image loaded from a file (but only those +// loaded from a file) will be automatically scaled down, if +// necessary, so that neither dimension is larger than this value. +const int max_texture_dimension = config_gobj.GetInt("max-texture-dimension", -1); + +// Set this to specify how textures should be written into Bam files. +// Currently, the options are: + +// fullpath - write the full pathname loaded. +// relative - search for the texture as a filename relative to the +// model-path or texture-path and write the relative pathname. +// basename - write only the basename of the file, no directory portion +// at all. + +BamTextureMode bam_texture_mode; + + +static BamTextureMode +parse_texture_mode(const string &mode) { + if (mode == "fullpath") { + return BTM_fullpath; + } else if (mode == "relative") { + return BTM_relative; + } else if (mode == "basename") { + return BTM_basename; + } + + gobj_cat.error() << "Invalid bam-texture-mode: " << mode << "\n"; + return BTM_relative; +} + +ConfigureFn(config_gobj) { + string texture_mode = config_util.GetString("bam-texture-mode", "relative"); + bam_texture_mode = parse_texture_mode(texture_mode); + + Fog::init_type(); + Geom::init_type(); + GeomLine::init_type(); + GeomLinestrip::init_type(); + GeomPoint::init_type(); + GeomSprite::init_type(); + GeomPolygon::init_type(); + GeomQuad::init_type(); + GeomSphere::init_type(); + GeomTri::init_type(); + GeomTrifan::init_type(); + GeomTristrip::init_type(); + ImageBuffer::init_type(); + LOD::init_type(); + Material::init_type(); + OrthoProjection::init_type(); + PerspectiveProjection::init_type(); + PixelBuffer::init_type(); + Projection::init_type(); + Texture::init_type(); + dDrawable::init_type(); + + //Registration of writeable object's creation + //functions with BamReader's factory + GeomPoint::register_with_read_factory(); + GeomLine::register_with_read_factory(); + GeomLinestrip::register_with_read_factory(); + GeomSprite::register_with_read_factory(); + GeomPolygon::register_with_read_factory(); + GeomQuad::register_with_read_factory(); + GeomTri::register_with_read_factory(); + GeomTristrip::register_with_read_factory(); + GeomTrifan::register_with_read_factory(); + GeomSphere::register_with_read_factory(); + Texture::register_with_read_factory(); +} diff --git a/panda/src/gobj/config_gobj.h b/panda/src/gobj/config_gobj.h new file mode 100644 index 0000000000..fabae31ca1 --- /dev/null +++ b/panda/src/gobj/config_gobj.h @@ -0,0 +1,26 @@ +// Filename: config_gobj.h +// Created by: drose (01Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_GOBJ_H +#define CONFIG_GOBJ_H + +#include +#include + +NotifyCategoryDecl(gobj, EXPCL_PANDA, EXPTP_PANDA); + +// Configure variables for gobj package. +extern const int max_texture_dimension; + +enum BamTextureMode { + BTM_fullpath, + BTM_relative, + BTM_basename +}; +extern BamTextureMode bam_texture_mode; + +#endif + + diff --git a/panda/src/gobj/drawable.cxx b/panda/src/gobj/drawable.cxx new file mode 100644 index 0000000000..27d1cfa36a --- /dev/null +++ b/panda/src/gobj/drawable.cxx @@ -0,0 +1,34 @@ +// Filename: drawable.cxx +// Created by: drose (15Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "drawable.h" + +TypeHandle dDrawable::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: Drawable::propagate_stale_bound +// Access: Protected, Virtual +// Description: Called by BoundedObject::mark_bound_stale(), this +// should make sure that all bounding volumes that +// depend on this one are marked stale also. +//////////////////////////////////////////////////////////////////// +void dDrawable:: +propagate_stale_bound() { + // Unforunately, we don't have a pointer to the GeomNode that + // includes us, so we can't propagate the bounding volume change + // upwards. Need to address this. +} + +//////////////////////////////////////////////////////////////////// +// Function: dDrawable::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void dDrawable:: +write_datagram(BamWriter *manager, Datagram &me) +{ + //dDrawable contains nothing, but it has to define write_datagram +} diff --git a/panda/src/gobj/drawable.h b/panda/src/gobj/drawable.h new file mode 100644 index 0000000000..8829d36d2d --- /dev/null +++ b/panda/src/gobj/drawable.h @@ -0,0 +1,77 @@ +// Filename: drawable.h +// Created by: mike (09Jan97) +// +// +#ifndef DDRAWABLE_H +#define DDRAWABLE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +class GraphicsStateGuardianBase; +class Datagram; +class DatagramIterator; +class BamReader; +class BamWriter; + +//////////////////////////////////////////////////////////////////// +// Class : Drawable +// Description : Object that can be drawn (i.e. issues graphics +// commands). +// NOTE: We had to change the name to dDrawable because +// the stupid bastards who wrote X didn't add a prefix +// to their variable names +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA dDrawable : public ReferenceCount, public WriteableConfigurable, + public BoundedObject { +public: + + dDrawable() : WriteableConfigurable() { } + virtual ~dDrawable() { } + + virtual void draw(GraphicsStateGuardianBase *) { if (is_dirty()) config(); } + +protected: + virtual void propagate_stale_bound(); + +public: + virtual void write_datagram(BamWriter* manager, Datagram &me); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ReferenceCount::init_type(); + WriteableConfigurable::init_type(); + BoundedObject::init_type(); + register_type(_type_handle, "dDrawable", + ReferenceCount::get_class_type(), + WriteableConfigurable::get_class_type(), + BoundedObject::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + + +private: + static TypeHandle _type_handle; +}; + +#endif + diff --git a/panda/src/gobj/fog.I b/panda/src/gobj/fog.I new file mode 100644 index 0000000000..4cfc1090b5 --- /dev/null +++ b/panda/src/gobj/fog.I @@ -0,0 +1,111 @@ +// Filename: fog.I +// Created by: mike (05Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: Fog::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Fog::Mode Fog::get_mode(void) const { + return _mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: Fog::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Fog::set_mode(Mode mode) { + _mode = mode; + compute_density(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Fog::get_color +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Colorf Fog::get_color(void) const { + return _color; +} + +//////////////////////////////////////////////////////////////////// +// Function: Fog::set_color +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Fog::set_color(const Colorf &color) { + _color = color; +} + +//////////////////////////////////////////////////////////////////// +// Function: Fog::get_range +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Fog::get_range(float &onset, float &opaque) const { + onset = _onset; + opaque = _opaque; +} + +//////////////////////////////////////////////////////////////////// +// Function: Fog::set_range +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Fog::set_range(float onset, float opaque) { + _onset = onset; + _opaque = opaque; + compute_density(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Fog::get_offsets +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Fog::get_offsets(float &onset, float &opaque) const { + onset = _onset_offset; + opaque = _opaque_offset; +} + +//////////////////////////////////////////////////////////////////// +// Function: Fog::set_offsets +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Fog::set_offsets(float onset, float opaque) { + _onset_offset = onset; + _opaque_offset = opaque; + compute_density(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Fog::get_start +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float Fog::get_start(void) const { + return _onset + _onset_offset; +} + +//////////////////////////////////////////////////////////////////// +// Function: Fog::get_end +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float Fog::get_end(void) const { + return _opaque + _opaque_offset; +} + +//////////////////////////////////////////////////////////////////// +// Function: Fog::get_density +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float Fog::get_density(void) const { + return _density; +} diff --git a/panda/src/gobj/fog.cxx b/panda/src/gobj/fog.cxx new file mode 100644 index 0000000000..ae4786fd12 --- /dev/null +++ b/panda/src/gobj/fog.cxx @@ -0,0 +1,104 @@ +// Filename: fog.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "fog.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle Fog::_type_handle; + +ostream & +operator << (ostream &out, Fog::Mode mode) { + switch (mode) { + case Fog::M_linear: + return out << "linear"; + + case Fog::M_exponential: + return out << "exponential"; + + case Fog::M_super_exponential: + return out << "super_exponential"; + + case Fog::M_spline: + return out << "spline"; + } + + return out << "**invalid**(" << (int)mode << ")"; +} + +//////////////////////////////////////////////////////////////////// +// Function: Fog::Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +Fog::Fog(Mode mode, int hardware_bits) { + _hardware_bits = hardware_bits; + set_mode(mode); + set_color(Colorf(0.0, 0.0, 0.0, 1.0)); + set_range(0.0f, 100.0f); + set_offsets(0.0f, 0.0f); + compute_density(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Fog::Destructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +Fog::~Fog(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function: Fog::set_mode +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void Fog::compute_density(void) { + _density = 1.0f; + float opaque_multiplier; + switch (_mode) { + case M_linear: + break; + case M_exponential: + // Multiplier = ln(2^bits) + opaque_multiplier = M_LN2 * _hardware_bits; + _density = opaque_multiplier / (_opaque + _opaque_offset); + break; + case M_super_exponential: + // Multiplier = ln(squrt(2^bits)) + opaque_multiplier = 0.5f * M_LN2 * _hardware_bits; + opaque_multiplier *= opaque_multiplier; + _density = opaque_multiplier / (_opaque + _opaque_offset); + break; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: Fog::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Fog:: +output(ostream &out) const { + out << "fog:" << _mode; + switch (_mode) { + case M_linear: + break; + case M_exponential: + case M_super_exponential: + out << "(" << _hardware_bits << "," << _density + << "," << _opaque << "," << _opaque_offset << ")"; + break; + }; +} diff --git a/panda/src/gobj/fog.h b/panda/src/gobj/fog.h new file mode 100644 index 0000000000..ce975c06cf --- /dev/null +++ b/panda/src/gobj/fog.h @@ -0,0 +1,103 @@ +// Filename: fog.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef FOG_H +#define FOG_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : Fog +// Description : Specifies atmospheric fog parameters +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Fog : public TypedReferenceCount { +public: + enum Mode { + M_linear, // f = (end - z) / (end - start) + M_exponential, // f = e^(-density * z) + M_super_exponential, // f = e^(-density * z)^2 + M_spline, // Not implemented yet + }; + + Fog(Mode mode = M_exponential, int hardware_bits = 8); + ~Fog(); + + void apply(GraphicsStateGuardianBase *gsg) { + gsg->apply_fog(this); + } + + INLINE Mode get_mode(void) const; + INLINE void set_mode(Mode mode); + + INLINE Colorf get_color(void) const; + INLINE void set_color(const Colorf &color); + + INLINE void get_range(float &onset, float &opaque) const; + INLINE void set_range(float onset, float opaque); + + INLINE void get_offsets(float &onset, float &opaque) const; + INLINE void set_offsets(float onset, float opaque); + + INLINE float get_start(void) const; + INLINE float get_end(void) const; + INLINE float get_density(void) const; + + void output(ostream &out) const; + +protected: + void compute_density(void); + +protected: + Mode _mode; + int _hardware_bits; + Colorf _color; + float _onset; + float _opaque; + float _onset_offset; + float _opaque_offset; + float _density; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "Fog", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + + static TypeHandle _type_handle; +}; + +ostream &operator << (ostream &out, Fog::Mode mode); + +INLINE ostream &operator << (ostream &out, const Fog &fog) { + fog.output(out); + return out; +} + +#include "fog.I" + +#endif diff --git a/panda/src/gobj/geom.I b/panda/src/gobj/geom.I new file mode 100644 index 0000000000..97f9abe1a3 --- /dev/null +++ b/panda/src/gobj/geom.I @@ -0,0 +1,126 @@ +// Filename: geom.I +// Created by: drose (04Feb99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// +// make_vertex_iterator(), get_next_vertex() +// make_normal_iterator(), get_next_normal() +// make_texcoord_iterator(), get_next_texcoord() +// make_color_iterator(), get_next_color() +// +// These functions all work together to walk through the vertex (or +// normal, etc.) values associated with the Geom. Begin with a call +// to make_vertex_iterator(), which returns an iterator value suitable +// for passing to get_next_vertex(). The first call to +// get_next_vertex() returns a const Vertexf &, which is the value of +// the first vertex. Each subsequent call to get_next_vertex() will +// return the value of the next following vertex. +// +// The actual value of the vertex is returned, regardless of whether +// the vertex array is indexed or nonindexed. +// +// There is no end-of-array indicator. It is up to the caller to know +// the length of the vertex array, and stop when the end is reached. +// +// Similar behavior is exhibited for normals, texcoords, and colors. +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Geom::make_vertex_iterator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Geom:: +VertexIterator Geom:: +make_vertex_iterator() const { + check_config(); + VertexIterator i; + i._array = _coords; + i._index = _vindex; + return i; +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::get_next_vertex +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const Vertexf &Geom:: +get_next_vertex(VertexIterator &viterator) const { + return _get_vertex(viterator); +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::make_normal_iterator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Geom::NormalIterator Geom:: +make_normal_iterator() const { + check_config(); + NormalIterator i; + i._array = _norms; + i._index = _nindex; + return i; +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::get_next_normal +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const Normalf &Geom:: +get_next_normal(NormalIterator &niterator) const { + return _get_normal(niterator); +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::make_texcoord_iterator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Geom::TexCoordIterator Geom:: +make_texcoord_iterator() const { + check_config(); + TexCoordIterator i; + i._array = _texcoords; + i._index = _tindex; + return i; +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::get_next_texcoord +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const TexCoordf &Geom:: +get_next_texcoord(TexCoordIterator &tciterator) const { + return _get_texcoord(tciterator); +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::make_color_iterator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Geom::ColorIterator Geom:: +make_color_iterator() const { + check_config(); + ColorIterator i; + i._array = _colors; + i._index = _cindex; + return i; +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::get_next_color +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const Colorf &Geom:: +get_next_color(ColorIterator &citerator) const { + return _get_color(citerator); +} diff --git a/panda/src/gobj/geom.N b/panda/src/gobj/geom.N new file mode 100644 index 0000000000..480faee987 --- /dev/null +++ b/panda/src/gobj/geom.N @@ -0,0 +1,12 @@ +ignoremember GetNextVertex +ignoremember GetNextNormal +ignoremember GetNextTexCoord +ignoremember GetNextColor +ignoremember get_coords +ignoremember get_normals +ignoremember get_colors +ignoremember get_texcoords +ignoreinvolved VertexIterator +ignoreinvolved NormalIterator +ignoreinvolved TexCoordIterator +ignoreinvolved ColorIterator diff --git a/panda/src/gobj/geom.cxx b/panda/src/gobj/geom.cxx new file mode 100644 index 0000000000..aa9f3101e1 --- /dev/null +++ b/panda/src/gobj/geom.cxx @@ -0,0 +1,750 @@ +// Filename: geom.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// + +#include "geom.h" +#include "config_gobj.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// + +TypeHandle Geom::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: get_*_nonindexed +// Description: Retrieves the next component of the nonindexed array. +//////////////////////////////////////////////////////////////////// +static const Vertexf &get_vertex_nonindexed(Geom::VertexIterator &vi) { + return *(vi._array++); +} +static const Normalf &get_normal_nonindexed(Geom::NormalIterator &vi) { + return *(vi._array++); +} +static const TexCoordf &get_texcoord_nonindexed(Geom::TexCoordIterator &vi) { + return *(vi._array++); +} +static const Colorf &get_color_nonindexed(Geom::ColorIterator &vi) { + return *(vi._array++); +} + +//////////////////////////////////////////////////////////////////// +// Function: get_*_indexed +// Description: Retrieves the next component of the indexed array. +//////////////////////////////////////////////////////////////////// +static const Vertexf &get_vertex_indexed(Geom::VertexIterator &vi) { + return vi._array[*(vi._index++)]; +} +static const Normalf &get_normal_indexed(Geom::NormalIterator &vi) { + return vi._array[*(vi._index++)]; +} +static const TexCoordf &get_texcoord_indexed(Geom::TexCoordIterator &vi) { + return vi._array[*(vi._index++)]; +} +static const Colorf &get_color_indexed(Geom::ColorIterator &vi) { + return vi._array[*(vi._index++)]; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_*_noop +// Description: Doesn't retrieve anything at all. +//////////////////////////////////////////////////////////////////// +static const Vertexf &get_vertex_noop(Geom::VertexIterator &) { + static Vertexf nothing; + return nothing; +} +static const Normalf &get_normal_noop(Geom::NormalIterator &) { + static Normalf nothing; + return nothing; +} +static const TexCoordf &get_texcoord_noop(Geom::TexCoordIterator &) { + static TexCoordf nothing; + return nothing; +} +static const Colorf &get_color_noop(Geom::ColorIterator &) { + static Colorf nothing; + return nothing; +} + + +//////////////////////////////////////////////////////////////////// +// Function: GeomBindType output operator +// Description: +//////////////////////////////////////////////////////////////////// +ostream &operator << (ostream &out, GeomBindType t) { + switch (t) { + case G_OFF: + return out << "off"; + case G_OVERALL: + return out << "overall"; + case G_PER_PRIM: + return out << "per prim"; + case G_PER_COMPONENT: + return out << "per component"; + case G_PER_VERTEX: + return out << "per vertex"; + } + return out << "(**invalid**)"; +} + + +//////////////////////////////////////////////////////////////////// +// Function: GeomAttrType output operator +// Description: +//////////////////////////////////////////////////////////////////// +ostream &operator << (ostream &out, GeomAttrType t) { + switch (t) { + case G_COORD: + return out << "coord"; + case G_COLOR: + return out << "color"; + case G_NORMAL: + return out << "normal"; + case G_TEXCOORD: + return out << "texcoord"; + } + return out << "(**invalid**)"; +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Geom:: +Geom(void) : dDrawable() { + init(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Geom:: +Geom(const Geom& copy) : dDrawable() { + *this = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Geom:: +~Geom(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Geom:: +operator = (const Geom ©) { + _coords = copy._coords; + _norms = copy._norms; + _colors = copy._colors; + _texcoords = copy._texcoords; + + _vindex = copy._vindex; + _nindex = copy._nindex; + _cindex = copy._cindex; + _tindex = copy._tindex; + + _numprims = copy._numprims; + _primlengths = copy._primlengths; + for (int i = 0; i < num_GeomAttrTypes; i++) { + _bind[i] = copy._bind[i]; + } + + _get_vertex = copy._get_vertex; + _get_normal = copy._get_normal; + _get_color = copy._get_color; + _get_texcoord = copy._get_texcoord; + + if (copy.is_dirty()) + make_dirty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::set_coords +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Geom:: +set_coords(const PTA_Vertexf &coords, GeomBindType bind, + const PTA_ushort &vindex) { + _coords = coords; + assert(bind == G_PER_VERTEX); + _bind[G_COORD] = bind; + + if ( vindex ) + _vindex = vindex; + + mark_bound_stale(); + make_dirty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::set_normals +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Geom:: +set_normals(const PTA_Normalf &norms, GeomBindType bind, + const PTA_ushort &nindex) { + _norms = norms; + _bind[G_NORMAL] = bind; + + if (nindex) + _nindex = nindex; + + make_dirty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::set_colors +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Geom:: +set_colors(const PTA_Colorf &colors, GeomBindType bind, + const PTA_ushort &cindex) { + _colors = colors; + _bind[G_COLOR] = bind; + + if ( cindex ) + _cindex = cindex; + + make_dirty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::set_texcoords +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Geom:: +set_texcoords(const PTA_TexCoordf &texcoords, GeomBindType bind, + const PTA_ushort &tindex) { + _texcoords = texcoords; + assert(bind == G_PER_VERTEX || bind == G_OFF); + _bind[G_TEXCOORD] = bind; + + if ( tindex ) + _tindex = tindex; + + make_dirty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::get_coords +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Geom:: +get_coords(PTA_Vertexf &coords, GeomBindType &bind, + PTA_ushort &vindex) const { + coords = _coords; + bind = _bind[G_COORD]; + vindex = _vindex; +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::get_normals +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Geom:: +get_normals(PTA_Normalf &norms, GeomBindType &bind, + PTA_ushort &nindex) const { + norms = _norms; + bind = _bind[G_NORMAL]; + nindex = _nindex; +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::get_colors +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Geom:: +get_colors(PTA_Colorf &colors, GeomBindType &bind, + PTA_ushort &cindex) const { + colors = _colors; + bind = _bind[G_COLOR]; + cindex = _cindex; +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::get_texcoords +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Geom:: +get_texcoords(PTA_TexCoordf &texcoords, GeomBindType &bind, + PTA_ushort &tindex) const { + texcoords = _texcoords; + bind = _bind[G_TEXCOORD]; + tindex = _tindex; +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::set_lengths +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Geom:: +set_lengths(const PTA_int &lengths) { + _primlengths = lengths; + make_dirty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::get_lengths +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PTA_int Geom:: +get_lengths() const { + return _primlengths; +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::explode +// Access: Public, Virtual +// Description: If the Geom is a composite type such as a tristrip, +// this allocates and returns a new Geom that represents +// the same geometry as a simple type, for instance a +// set of triangles. If the Geom is already a simple +// type, this allocates and returns a copy. This is +// just a convenience function for dealing with +// composite types when performance is less than +// paramount. +//////////////////////////////////////////////////////////////////// +Geom *Geom:: +explode() const { + return make_copy(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::get_tris +// Access: Public, Virtual +// Description: This is similar in principle to explode(), except it +// returns only a list of triangle vertex indices, with +// no information about color or whatever. The array +// returned is a set of indices into the geom's _coords +// array, as retrieve by get_coords(); there will be 3*n +// elements in the array, where n is the number of +// triangles described by the geometry. This is useful +// when it's important to determine the physical +// structure of the geometry, without necessarily +// worrying about its rendering properties, and when +// performance considerations are not overwhelming. +//////////////////////////////////////////////////////////////////// +PTA_ushort Geom:: +get_tris() const { + return PTA_ushort(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::config +// Access: Public +// Description: Configure rendering based on current settings +//////////////////////////////////////////////////////////////////// +void Geom:: +config(void) { + WriteableConfigurable::config(); + + // Only per vertex binding makes any sense + if (_coords != (Vertexf*)0L && _bind[G_COORD] != G_OFF) { + _get_vertex = + (_vindex == (ushort*)0L) ? get_vertex_nonindexed : get_vertex_indexed; + } else { + gobj_cat.error() + << "Geom::Config() - no vertex array!" << endl; + } + + // Set up normal rendering configuration + if (_norms != (Normalf*)0L && _bind[G_NORMAL] != G_OFF) { + _get_normal = + (_nindex == (ushort*)0L) ? get_normal_nonindexed : get_normal_indexed; + } else { + _get_normal = get_normal_noop; + } + + // Set up texture coordinate rendering configuration + if (_texcoords != (TexCoordf*)0L && _bind[G_TEXCOORD] != G_OFF) { + _get_texcoord = + (_tindex == (ushort*)0L) ? get_texcoord_nonindexed : get_texcoord_indexed; + } else { + _get_texcoord = get_texcoord_noop; + } + + // Set up color rendering configuration + if (_colors != (Colorf*)0L && _bind[G_COLOR] != G_OFF) { + _get_color = + (_cindex == (ushort*)0L) ? get_color_nonindexed : get_color_indexed; + } else { + _get_color = get_color_noop; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::print +// Access: Public +// Description: Writes the Geom information briefly to standard +// output. +//////////////////////////////////////////////////////////////////// +void Geom:: +print() const { + nout << *this << endl; +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void Geom:: +output(ostream &out) const { + out << get_type() << " (" << _numprims << ") "; + out << "v:"; + if ( _coords != (Vertexf*)0L ) out << "1 "; else out << "0 "; + out << "n:"; + if ( _norms != (Normalf*)0L ) out << "1 "; else out << "0 "; + out << "c:"; + if ( _colors != (Colorf*)0L ) out << "1 "; else out << "0 "; + out << "t:"; + if ( _texcoords != (TexCoordf*)0L ) out << "1 "; else out << "0 "; + out << "vi:"; + if ( _vindex != (ushort*)0L ) out << "1 "; else out << "0 "; + out << "ni:"; + if ( _nindex != (ushort*)0L ) out << "1 "; else out << "0 "; + out << "ci:"; + if ( _cindex != (ushort*)0L ) out << "1 "; else out << "0 "; + out << "ti:"; + if ( _tindex != (ushort*)0L ) out << "1 "; else out << "0 "; +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::init +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Geom:: +init(void) { + int i; + + _coords.clear(); + _norms.clear(); + _colors.clear(); + _texcoords.clear(); + _vindex.clear(); + _nindex.clear(); + _cindex.clear(); + _tindex.clear(); + _primlengths.clear(); + + for ( i = 0; i < num_GeomAttrTypes; i++ ) + _bind[i] = G_OFF; + + _get_vertex = get_vertex_noop; + _get_normal = get_normal_noop; + _get_texcoord = get_texcoord_noop; + _get_color = get_color_noop; + + WriteableConfigurable::config(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::recompute_bound +// Access: Protected, Virtual +// Description: Recomputes the dynamic bounding volume for this Geom. +// This includes all of the vertices. +//////////////////////////////////////////////////////////////////// +void Geom:: +recompute_bound() { + // First, get ourselves a fresh, empty bounding volume. + BoundedObject::recompute_bound(); + assert(_bound != (BoundingVolume*)0L); + + GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, _bound); + + // Now actually compute the bounding volume by putting it around all + // of our vertices. + vector vertices; + VertexIterator vi = make_vertex_iterator(); + + for (int p = 0; p < get_num_prims(); p++) { + for (int v = 0; v < get_length(p); v++) { + vertices.push_back(get_next_vertex(vi)); + } + } + + gbv->around(vertices.begin(), vertices.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void Geom:: +write_datagram(BamWriter *manager, Datagram &me) { + int i; + + //Coordinates + WRITE_PTA(manager, me, IPD_Vertexf::write_datagram, _coords) + //Normals + WRITE_PTA(manager, me, IPD_Normalf::write_datagram, _norms) + //Colors + WRITE_PTA(manager, me, IPD_Colorf::write_datagram, _colors) + //Texture Coordinates + WRITE_PTA(manager, me, IPD_TexCoordf::write_datagram, _texcoords) + + //Now write out the indices for each array + WRITE_PTA(manager, me, IPD_ushort::write_datagram, _vindex) + WRITE_PTA(manager, me, IPD_ushort::write_datagram, _nindex) + WRITE_PTA(manager, me, IPD_ushort::write_datagram, _cindex) + WRITE_PTA(manager, me, IPD_ushort::write_datagram, _tindex) + + me.add_uint16(_numprims); + WRITE_PTA(manager, me, IPD_int::write_datagram, _primlengths) + + //Write out the bindings for vertices, normals, + //colors and texture coordinates + for(i = 0; i < num_GeomAttrTypes; i++) { + me.add_uint8(_bind[i]); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: Geom::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void Geom:: +fillin(DatagramIterator& scan, BamReader* manager) { + int i; + + //Coordinates + READ_PTA(manager, scan, IPD_Vertexf::read_datagram, _coords) + //Normals + READ_PTA(manager, scan, IPD_Normalf::read_datagram, _norms) + //Colors + READ_PTA(manager, scan, IPD_Colorf::read_datagram, _colors) + //Texture Coordinates + READ_PTA(manager, scan, IPD_TexCoordf::read_datagram, _texcoords) + + //Now read in the indices for each array + READ_PTA(manager, scan, IPD_ushort::read_datagram, _vindex) + READ_PTA(manager, scan, IPD_ushort::read_datagram, _nindex) + READ_PTA(manager, scan, IPD_ushort::read_datagram, _cindex) + READ_PTA(manager, scan, IPD_ushort::read_datagram, _tindex) + + _numprims = scan.get_uint16(); + READ_PTA(manager, scan, IPD_int::read_datagram, _primlengths) + + //Write out the bindings for vertices, normals, + //colors and texture coordinates + for(i = 0; i < num_GeomAttrTypes; i++) { + _bind[i] = (enum GeomBindType) scan.get_uint8(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: describe_attr +// Description: A handy helper function for output_verbose, +// below. +//////////////////////////////////////////////////////////////////// +template +static void +describe_attr(ostream &out, const Geom *geom, + GeomBindType bind, const PTA(VecType) &array, + const char *pad) { + PTA_int lengths = geom->get_lengths(); + int num_prims = geom->get_num_prims(); + bool components = geom->uses_components(); + + int i, j, vi; + switch (bind) { + case G_PER_VERTEX: + out << "Per vertex:"; + vi = 0; + int num_verts; + num_verts = geom->get_num_vertices_per_prim(); + for (i = 0; i < num_prims; i++) { + if (components) { + num_verts = lengths[i]; + } + out << "\n [ "; + if (num_verts > 0) { + out << array[vi++]; + for (j = 1; j < num_verts; j++) { + out << pad << array[vi++]; + } + } + out << " ]"; + } + break; + + case G_PER_COMPONENT: + if (!components) { + out << "Invalid per-component attribute specified!"; + } else { + out << "Per component:"; + vi = 0; + for (i = 0; i < num_prims; i++) { + num_verts = lengths[i] - geom->get_num_more_vertices_than_components(); + out << "\n [ "; + if (num_verts > 0) { + out << array[vi++]; + for (j = 1; j < num_verts; j++) { + out << pad << array[vi++]; + } + out << " ]"; + } + } + } + break; + + case G_PER_PRIM: + out << "Per prim:"; + for (i = 0; i < num_prims; i++) { + out << pad << array[i]; + } + break; + + case G_OVERALL: + out << "Overall:" << pad << array[0]; + } + out << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::output_verbose +// Access: Public +// Description: Writes to the indicated ostream a formatted picture +// of the contents of the Geom, in detail--but hopefully +// not too much detail. +//////////////////////////////////////////////////////////////////// +void Geom:: +output_verbose(ostream &out) const { + GeomBindType bind_coords; + GeomBindType bind_normals; + GeomBindType bind_tcoords; + GeomBindType bind_colors; + + PTA_Vertexf g_coords; + PTA_Normalf g_normals; + PTA_TexCoordf g_tcoords; + PTA_Colorf g_colors; + + PTA_ushort i_coords; + PTA_ushort i_normals; + PTA_ushort i_tcoords; + PTA_ushort i_colors; + + get_coords(g_coords, bind_coords, i_coords); + get_normals(g_normals, bind_normals, i_normals); + get_texcoords(g_tcoords, bind_tcoords, i_tcoords); + get_colors(g_colors, bind_colors, i_colors); + + out << "\n" << get_type() << " contains " + << get_num_prims() << " primitives:\n"; + + if (bind_coords == G_OFF) { + out << "No coords\n"; + } else if (i_coords!=(ushort*)0L) { + out << "Indexed coords = " << (void *)g_coords << ", length = " + << g_coords.size() << ":\n"; + describe_attr(out, this, bind_coords, i_coords, " "); + } else { + out << "Nonindexed coords:\n"; + describe_attr(out, this, bind_coords, g_coords, "\n "); + } + + if (bind_colors == G_OFF) { + out << "No colors\n"; + } else if (i_colors!=(ushort*)0L) { + out << "Indexed colors = " << (void *)g_colors << ", length = " + << g_colors.size() << "\n"; + describe_attr(out, this, bind_colors, i_colors, " "); + } else { + out << "Nonindexed colors:\n"; + describe_attr(out, this, bind_colors, g_colors, "\n "); + } + + if (bind_tcoords == G_OFF) { + out << "No tcoords\n"; + } else if (i_tcoords!=(ushort*)0L) { + out << "Indexed tcoords = " << (void *)g_tcoords << ", length = " + << g_tcoords.size() << "\n"; + describe_attr(out, this, bind_tcoords, i_tcoords, " "); + } else { + out << "Nonindexed tcoords:\n"; + describe_attr(out, this, bind_tcoords, g_tcoords, "\n "); + } + + if (bind_normals == G_OFF) { + out << "No normals\n"; + } else if (i_normals!=(ushort*)0L) { + out << "Indexed normals = " << (void *)g_normals << ", length = " + << g_normals.size() << "\n"; + describe_attr(out, this, bind_normals, i_normals, " "); + } else { + out << "Nonindexed normals:\n"; + describe_attr(out, this, bind_normals, g_normals, "\n "); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Geom::get_min_max +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Geom:: +get_min_max(Vertexf& min, Vertexf& max) const { + int numv = _coords.size(); + + for (int i = 0; i < numv; i++) { + if (_coords[i][0] < min[0]) + min[0] = _coords[i][0]; + else if (_coords[i][0] > max[0]) + max[0] = _coords[i][0]; + + if (_coords[i][1] < min[1]) + min[1] = _coords[i][1]; + else if (_coords[i][1] > max[1]) + max[1] = _coords[i][1]; + + if (_coords[i][2] < min[2]) + min[2] = _coords[i][2]; + else if (_coords[i][2] > max[2]) + max[2] = _coords[i][2]; + } +} diff --git a/panda/src/gobj/geom.h b/panda/src/gobj/geom.h new file mode 100644 index 0000000000..355b6a7afb --- /dev/null +++ b/panda/src/gobj/geom.h @@ -0,0 +1,257 @@ +// Filename: geom.h +// Created by: mike (09Jan97) +// +// +#ifndef GEOM_H +#define GEOM_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "drawable.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "texture.h" + +class Datagram; +class DatagramIterator; +class BamReader; +class BamWriter; + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +enum GeomBindType +{ + G_OFF, + G_OVERALL, + G_PER_PRIM, + G_PER_COMPONENT, + G_PER_VERTEX +}; +static const int num_GeomBindTypes = 5; + +enum GeomAttrType +{ + G_COORD, + G_COLOR, + G_NORMAL, + G_TEXCOORD +}; +static const int num_GeomAttrTypes = 4; + +ostream &operator << (ostream &out, GeomBindType t); +ostream &operator << (ostream &out, GeomAttrType t); + +//////////////////////////////////////////////////////////////////// +// Class : Geom +// Description : Geometry parent class +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Geom : public dDrawable { +public: + + // These classes are used to iterate through all the vertices, + // normals, etc. They're returned by make_vertex_iterator(), and + // operated on by get_next_vertex(), etc. + class VertexIterator { + public: + const Vertexf *_array; + const ushort *_index; + }; + class NormalIterator { + public: + const Normalf *_array; + const ushort *_index; + }; + class TexCoordIterator { + public: + const TexCoordf *_array; + const ushort *_index; + }; + class ColorIterator { + public: + const Colorf *_array; + const ushort *_index; + }; + + // Declare some function types. This declares several typenames + // which are pointers to function types--these are not themselves + // functions. A function pointed to by a variable of this type, + // when given an iterator of the appropriate type from the Geom, + // will retrieve the next element from the array and increment the + // iterator appropriately for next time. + typedef const Vertexf &GetNextVertex(VertexIterator &); + typedef const Normalf &GetNextNormal(NormalIterator &); + typedef const TexCoordf &GetNextTexCoord(TexCoordIterator &); + typedef const Colorf &GetNextColor(ColorIterator &); + + + Geom( void ); + Geom( const Geom& copy ); + ~Geom( void ); + + void operator = ( const Geom © ); + virtual Geom *make_copy() const=0; + + void print() const; + virtual void output(ostream &out) const; + void output_verbose(ostream &out) const; + + // From parent dDrawable + virtual void draw(GraphicsStateGuardianBase *gsg) { + dDrawable::draw(gsg); draw_immediate(gsg); + } + + // From parent Configurable + virtual void config( void ); + + // Immediate mode drawing functions - issue graphics commands + virtual void draw_immediate(GraphicsStateGuardianBase *) const = 0; + virtual void print_draw_immediate( void ) const = 0; + +public: + + void get_min_max( Vertexf& min, Vertexf& max ) const; + + GeomBindType get_binding( int attr ) const { + return _bind[attr]; + } + + void set_coords( const PTA_Vertexf &coords, + GeomBindType bind, + const PTA_ushort &vindex = + PTA_ushort() ); + void set_normals( const PTA_Normalf &norms, + GeomBindType bind, + const PTA_ushort &nindex = + PTA_ushort() ); + void set_colors( const PTA_Colorf &colors, + GeomBindType bind, + const PTA_ushort &cindex = + PTA_ushort() ); + void set_texcoords( const PTA_TexCoordf &texcoords, + GeomBindType bind, + const PTA_ushort &tindex = + PTA_ushort() ); + + void get_coords( PTA_Vertexf &coords, + GeomBindType &bind, + PTA_ushort &vindex) const; + void get_normals( PTA_Normalf &norms, + GeomBindType &bind, + PTA_ushort &nindex) const; + void get_colors( PTA_Colorf &colors, + GeomBindType &bind, + PTA_ushort &cindex) const; + void get_texcoords( PTA_TexCoordf &texcoords, + GeomBindType &bind, + PTA_ushort &tindex) const; + + INLINE void set_num_prims(int num) { _numprims = num; make_dirty(); } + INLINE int get_num_prims(void) const { return _numprims; } + + void set_lengths( const PTA_int &lengths ); + PTA_int get_lengths() const; + + virtual int get_num_vertices_per_prim() const=0; + virtual int get_num_more_vertices_than_components() const=0; + virtual bool uses_components() const=0; + + // Returns the length of the indicated primitive. Often this is the + // same for all primitives in the Geom. However, geoms which use + // the lengths array will redefine this appropriately. + virtual int get_length(int prim) const=0; + + + virtual Geom *explode() const; + virtual PTA_ushort get_tris() const; + + + INLINE VertexIterator make_vertex_iterator() const; + INLINE const Vertexf &get_next_vertex(VertexIterator &viterator) const; + + INLINE NormalIterator make_normal_iterator() const; + INLINE const Normalf &get_next_normal(NormalIterator &niterator) const; + + INLINE TexCoordIterator make_texcoord_iterator() const; + INLINE const TexCoordf &get_next_texcoord(TexCoordIterator &tciterator) const; + + INLINE ColorIterator make_color_iterator() const; + INLINE const Colorf &get_next_color(ColorIterator &citerator) const; + +protected: + + void init( void ); + virtual void recompute_bound(); + +protected: + + PTA_Vertexf _coords; + PTA_Normalf _norms; + PTA_Colorf _colors; + PTA_TexCoordf _texcoords; + + PTA_ushort _vindex; + PTA_ushort _nindex; + PTA_ushort _cindex; + PTA_ushort _tindex; + + int _numprims; + PTA_int _primlengths; + enum GeomBindType _bind[num_GeomAttrTypes]; + + // Functions to extract component values, one at a time. + GetNextVertex *_get_vertex; + GetNextNormal *_get_normal; + GetNextTexCoord *_get_texcoord; + GetNextColor *_get_color; + + +public: + //static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + + //static TypedWriteable *make_Generic(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + dDrawable::init_type(); + register_type(_type_handle, "Geom", + dDrawable::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + +}; + +INLINE ostream &operator <<(ostream &out, const Geom &geom) { + geom.output(out); + return out; +} + +#include "geom.I" + +#endif diff --git a/panda/src/gobj/geomLine.cxx b/panda/src/gobj/geomLine.cxx new file mode 100644 index 0000000000..1c4d96ee8f --- /dev/null +++ b/panda/src/gobj/geomLine.cxx @@ -0,0 +1,97 @@ +// Filename: geomLine.cxx +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "geomLine.h" + +TypeHandle GeomLine::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GeomLine::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Geom that is a shallow copy +// of this one. It will be a different Geom pointer, +// but its internal data may or may not be shared with +// that of the original Geom. +//////////////////////////////////////////////////////////////////// +Geom *GeomLine:: +make_copy() const { + return new GeomLine(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomLine::draw_immediate +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void GeomLine:: +draw_immediate(GraphicsStateGuardianBase *gsg) const { + gsg->draw_line(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomLine::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void GeomLine:: +write_datagram(BamWriter *manager, Datagram &me) +{ + Geom::write_datagram(manager, me); + me.add_uint32(_width); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomLine::make_GeomLine +// Access: Protected +// Description: Factory method to generate a GeomLine object +//////////////////////////////////////////////////////////////////// +TypedWriteable* GeomLine:: +make_GeomLine(const FactoryParams ¶ms) { + GeomLine *me = new GeomLine; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + me->make_dirty(); + me->config(); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomLine::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void GeomLine:: +fillin(DatagramIterator& scan, BamReader* manager) { + Geom::fillin(scan, manager); + _width = scan.get_uint32(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomLine::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a GeomLine object +//////////////////////////////////////////////////////////////////// +void GeomLine:: +register_with_read_factory(void) { + BamReader::get_factory()->register_factory(get_class_type(), make_GeomLine); +} diff --git a/panda/src/gobj/geomLine.h b/panda/src/gobj/geomLine.h new file mode 100644 index 0000000000..dfc23ac283 --- /dev/null +++ b/panda/src/gobj/geomLine.h @@ -0,0 +1,74 @@ +// Filename: geomLine.h +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMLINE_H +#define GEOMLINE_H + +#include "geom.h" + +//////////////////////////////////////////////////////////////////// +// Class : GeomLine +// Description : Line Primitive +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomLine : public Geom +{ +public: + GeomLine( void ) : Geom() { _width = 1.0; } + virtual Geom *make_copy() const; + virtual void print_draw_immediate( void ) const { } + virtual void draw_immediate(GraphicsStateGuardianBase *gsg) const; + + virtual int get_num_vertices_per_prim() const { + return 2; + } + virtual int get_num_more_vertices_than_components() const { + return 0; + } + virtual bool uses_components() const { + return false; + } + + virtual int get_length(int) const { + return 2; + } + + virtual Geom *explode() const { + return new GeomLine(*this); + } + + INLINE void set_width(float width) { _width = width; } + INLINE float get_width(void) const { return _width; } + +protected: + float _width; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + + static TypedWriteable *make_GeomLine(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Geom::init_type(); + register_type(_type_handle, "GeomLine", + Geom::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/gobj/geomLinestrip.cxx b/panda/src/gobj/geomLinestrip.cxx new file mode 100644 index 0000000000..0449efb9de --- /dev/null +++ b/panda/src/gobj/geomLinestrip.cxx @@ -0,0 +1,109 @@ +// Filename: geomLinestrip.cxx +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config_gobj.h" +#include "geomLinestrip.h" + +TypeHandle GeomLinestrip::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GeomLinestrip::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Geom that is a shallow copy +// of this one. It will be a different Geom pointer, +// but its internal data may or may not be shared with +// that of the original Geom. +//////////////////////////////////////////////////////////////////// +Geom *GeomLinestrip:: +make_copy() const { + return new GeomLinestrip(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomLinestrip::draw_immediate +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void GeomLinestrip:: +draw_immediate(GraphicsStateGuardianBase *gsg) const { + gsg->draw_linestrip(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomLinestrip::explode +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +Geom *GeomLinestrip:: +explode() const { + gobj_cat.error() + << "GeomLinestrip::explode() - not implemented yet!!!" << endl; + return new GeomLinestrip(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomLinestrip::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void GeomLinestrip:: +write_datagram(BamWriter *manager, Datagram &me) { + Geom::write_datagram(manager, me); + me.add_uint32(_width); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomLinestrip::make_GeomLinestrip +// Access: Protected +// Description: Factory method to generate a GeomLinestrip object +//////////////////////////////////////////////////////////////////// +TypedWriteable* GeomLinestrip:: +make_GeomLinestrip(const FactoryParams ¶ms) { + GeomLinestrip *me = new GeomLinestrip; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + me->make_dirty(); + me->config(); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomLinestrip::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void GeomLinestrip:: +fillin(DatagramIterator& scan, BamReader* manager) { + Geom::fillin(scan, manager); + _width = scan.get_uint32(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomLinestrip::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a GeomLinestrip object +//////////////////////////////////////////////////////////////////// +void GeomLinestrip:: +register_with_read_factory(void) { + BamReader::get_factory()->register_factory(get_class_type(), make_GeomLinestrip); +} diff --git a/panda/src/gobj/geomLinestrip.h b/panda/src/gobj/geomLinestrip.h new file mode 100644 index 0000000000..843be4b2d0 --- /dev/null +++ b/panda/src/gobj/geomLinestrip.h @@ -0,0 +1,73 @@ +// Filename: geomLinestrip.h +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMLINESTRIP_H +#define GEOMLINESTRIP_H + +#include "geom.h" + +//////////////////////////////////////////////////////////////////// +// Class : GeomLinestrip +// Description : Linestrip Primitive +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomLinestrip : public Geom +{ +public: + GeomLinestrip(void) : Geom() { _width = 1.0; } + virtual Geom *make_copy() const; + virtual void print_draw_immediate( void ) const { } + virtual void draw_immediate(GraphicsStateGuardianBase *gsg) const; + + // Undefined for this type of primitive + virtual int get_num_vertices_per_prim() const { + return 0; + } + virtual int get_num_more_vertices_than_components() const { + return 1; + } + virtual bool uses_components() const { + return true; + } + + virtual int get_length(int prim) const { + return _primlengths[prim]; + } + + virtual Geom *explode() const; + + INLINE void set_width(float width) { _width = width; } + INLINE float get_width(void) const { return _width; } + +protected: + float _width; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + + static TypedWriteable *make_GeomLinestrip(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Geom::init_type(); + register_type(_type_handle, "GeomLinestrip", + Geom::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif // GEOMLINESTRIP_H diff --git a/panda/src/gobj/geomPoint.cxx b/panda/src/gobj/geomPoint.cxx new file mode 100644 index 0000000000..e5e1e70140 --- /dev/null +++ b/panda/src/gobj/geomPoint.cxx @@ -0,0 +1,105 @@ +// Filename: geomPoint.cxx +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "geomPoint.h" + +TypeHandle GeomPoint::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GeomPoint::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Geom that is a shallow copy +// of this one. It will be a different Geom pointer, +// but its internal data may or may not be shared with +// that of the original Geom. +//////////////////////////////////////////////////////////////////// +Geom *GeomPoint:: +make_copy() const { + return new GeomPoint(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomPoint::print_draw_immediate +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void GeomPoint:: +print_draw_immediate(void) const { +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomPoint::draw_immediate +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void GeomPoint:: +draw_immediate(GraphicsStateGuardianBase *gsg) const { + gsg->draw_point(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomPoint::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void GeomPoint:: +write_datagram(BamWriter *manager, Datagram &me) { + Geom::write_datagram(manager, me); + me.add_uint32(_size); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomPoint::make_GeomPoint +// Access: Protected +// Description: Factory method to generate a GeomPoint object +//////////////////////////////////////////////////////////////////// +TypedWriteable* GeomPoint:: +make_GeomPoint(const FactoryParams ¶ms) { + GeomPoint *me = new GeomPoint; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + me->make_dirty(); + me->config(); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomPoint::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void GeomPoint:: +fillin(DatagramIterator& scan, BamReader* manager) { + Geom::fillin(scan, manager); + _size = scan.get_uint32(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomPoint::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a GeomPoint object +//////////////////////////////////////////////////////////////////// +void GeomPoint:: +register_with_read_factory(void) { + BamReader::get_factory()->register_factory(get_class_type(), make_GeomPoint); +} diff --git a/panda/src/gobj/geomPoint.h b/panda/src/gobj/geomPoint.h new file mode 100644 index 0000000000..7698873764 --- /dev/null +++ b/panda/src/gobj/geomPoint.h @@ -0,0 +1,74 @@ +// Filename: geomPoint.h +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMPOINT_H +#define GEOMPOINT_H + +#include "geom.h" + +//////////////////////////////////////////////////////////////////// +// Class : GeomPoint +// Description : Point Primitive +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomPoint : public Geom { +public: + GeomPoint() : Geom() { _size = 1.0; } + virtual Geom *make_copy() const; + + virtual void print_draw_immediate() const; + virtual void draw_immediate(GraphicsStateGuardianBase *gsg) const; + + virtual int get_num_vertices_per_prim() const { + return 1; + } + virtual int get_num_more_vertices_than_components() const { + return 0; + } + virtual bool uses_components() const { + return false; + } + + virtual int get_length(int) const { + return 1; + } + + virtual Geom *explode() const { + return new GeomPoint(*this); + } + + INLINE void set_size(float size) { _size = size; } + INLINE float get_size(void) const { return _size; } + +protected: + float _size; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + + static TypedWriteable *make_GeomPoint(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Geom::init_type(); + register_type(_type_handle, "GeomPoint", + Geom::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif // GEOMPOINT_H diff --git a/panda/src/gobj/geomPolygon.cxx b/panda/src/gobj/geomPolygon.cxx new file mode 100644 index 0000000000..1027b1e59e --- /dev/null +++ b/panda/src/gobj/geomPolygon.cxx @@ -0,0 +1,80 @@ +// Filename: geomPolygon.cxx +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "geomPolygon.h" + +TypeHandle GeomPolygon::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GeomPolygon::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Geom that is a shallow copy +// of this one. It will be a different Geom pointer, +// but its internal data may or may not be shared with +// that of the original Geom. +//////////////////////////////////////////////////////////////////// +Geom *GeomPolygon:: +make_copy() const { + return new GeomPolygon(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomPolygon::print_draw_immediate +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void GeomPolygon:: +print_draw_immediate(void) const { +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomPolygon::draw_immediate +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void GeomPolygon:: +draw_immediate(GraphicsStateGuardianBase *gsg) const { + gsg->draw_polygon(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomPolygon::make_GeomPolygon +// Access: Protected +// Description: Factory method to generate a GeomPolygon object +//////////////////////////////////////////////////////////////////// +TypedWriteable* GeomPolygon:: +make_GeomPolygon(const FactoryParams ¶ms) { + GeomPolygon *me = new GeomPolygon; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + me->make_dirty(); + me->config(); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomPolygon::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a GeomPolygon object +//////////////////////////////////////////////////////////////////// +void GeomPolygon:: +register_with_read_factory(void) { + BamReader::get_factory()->register_factory(get_class_type(), make_GeomPolygon); +} + diff --git a/panda/src/gobj/geomPolygon.h b/panda/src/gobj/geomPolygon.h new file mode 100644 index 0000000000..76f3b894bb --- /dev/null +++ b/panda/src/gobj/geomPolygon.h @@ -0,0 +1,62 @@ +// Filename: geomPolygon.h +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMPOLYGON_H +#define GEOMPOLYGON_H + +#include "geom.h" + +//////////////////////////////////////////////////////////////////// +// Class : GeomPolygon +// Description : Polygon Primitive +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomPolygon : public Geom { +public: + GeomPolygon() : Geom() { } + virtual Geom *make_copy() const; + virtual void print_draw_immediate() const; + virtual void draw_immediate(GraphicsStateGuardianBase *gsg) const; + + virtual int get_num_vertices_per_prim() const { + return 0; + } + virtual int get_num_more_vertices_than_components() const { + return 0; + } + virtual bool uses_components() const { + return false; + } + + virtual int get_length(int prim) const { + return _primlengths[prim]; + } + + virtual Geom *explode() const { + return new GeomPolygon(*this); + } + +public: + static void register_with_read_factory(void); + static TypedWriteable *make_GeomPolygon(const FactoryParams ¶ms); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Geom::init_type(); + register_type(_type_handle, "GeomPolygon", + Geom::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif // GEOMPOLYGON_H diff --git a/panda/src/gobj/geomQuad.cxx b/panda/src/gobj/geomQuad.cxx new file mode 100644 index 0000000000..7c865166d9 --- /dev/null +++ b/panda/src/gobj/geomQuad.cxx @@ -0,0 +1,130 @@ +// Filename: geomQuad.cxx +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "geomTri.h" +#include "geomQuad.h" + +TypeHandle GeomQuad::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GeomQuad::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Geom that is a shallow copy +// of this one. It will be a different Geom pointer, +// but its internal data may or may not be shared with +// that of the original Geom. +//////////////////////////////////////////////////////////////////// +Geom *GeomQuad:: +make_copy() const { + return new GeomQuad(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomQuad::print_draw_immediate +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void GeomQuad:: +print_draw_immediate(void) const { +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomQuad::draw_immediate +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void GeomQuad:: +draw_immediate(GraphicsStateGuardianBase *gsg) const { + gsg->draw_quad(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomQuad::get_tris +// Access: Public, Virtual +// Description: This is similar in principle to explode(), except it +// returns only a list of triangle vertex indices, with +// no information about color or whatever. The array +// returned is a set of indices into the geom's _coords +// array, as retrieve by get_coords(); there will be 3*n +// elements in the array, where n is the number of +// triangles described by the geometry. This is useful +// when it's important to determine the physical +// structure of the geometry, without necessarily +// worrying about its rendering properties, and when +// performance considerations are not overwhelming. +//////////////////////////////////////////////////////////////////// +PTA_ushort GeomQuad:: +get_tris() const { + int num_tris = _numprims * 2; + PTA_ushort tris; + tris.reserve(num_tris * 3); + + int k = 0; + + for (int i = 0; i < _numprims; i++) { + ushort indices[4]; + if (_vindex.empty()) { + for (int j = 0; j < 4; j++) { + indices[j] = k++; + } + } else { + for (int j = 0; j < 4; j++) { + indices[j] = _vindex[k++]; + } + } + + // First tri. Vertices 0, 1, 2. + tris.push_back(indices[0]); + tris.push_back(indices[1]); + tris.push_back(indices[2]); + + // Second tri. Vertices 0, 2, 3. + tris.push_back(indices[0]); + tris.push_back(indices[2]); + tris.push_back(indices[3]); + } + + nassertr(tris.size() == num_tris * 3, PTA_ushort()); + return tris; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomQuad::make_GeomQuad +// Access: Protected +// Description: Factory method to generate a GeomQuad object +//////////////////////////////////////////////////////////////////// +TypedWriteable* GeomQuad:: +make_GeomQuad(const FactoryParams ¶ms) { + GeomQuad *me = new GeomQuad; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + me->make_dirty(); + me->config(); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomQuad::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a GeomQuad object +//////////////////////////////////////////////////////////////////// +void GeomQuad:: +register_with_read_factory(void) { + BamReader::get_factory()->register_factory(get_class_type(), make_GeomQuad); +} diff --git a/panda/src/gobj/geomQuad.h b/panda/src/gobj/geomQuad.h new file mode 100644 index 0000000000..188dbc520b --- /dev/null +++ b/panda/src/gobj/geomQuad.h @@ -0,0 +1,60 @@ +// Filename: geomQuad.h +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMQUAD_H +#define GEOMQUAD_H + +#include "geom.h" + +//////////////////////////////////////////////////////////////////// +// Class : GeomQuad +// Description : Quadrilateral Primitive +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomQuad : public Geom { +public: + GeomQuad() { } + virtual Geom *make_copy() const; + virtual void print_draw_immediate() const; + virtual void draw_immediate(GraphicsStateGuardianBase *gsg) const; + + virtual int get_num_vertices_per_prim() const { + return 4; + } + virtual int get_num_more_vertices_than_components() const { + return 0; + } + virtual bool uses_components() const { + return false; + } + + virtual int get_length(int) const { + return 4; + } + + virtual PTA_ushort get_tris() const; + +public: + static void register_with_read_factory(void); + static TypedWriteable *make_GeomQuad(const FactoryParams ¶ms); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Geom::init_type(); + register_type(_type_handle, "GeomQuad", + Geom::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif // GEOMQUAD_H diff --git a/panda/src/gobj/geomSphere.cxx b/panda/src/gobj/geomSphere.cxx new file mode 100644 index 0000000000..0a2e654834 --- /dev/null +++ b/panda/src/gobj/geomSphere.cxx @@ -0,0 +1,66 @@ +// Filename: geomSphere.cxx +// Created by: drose (01Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "geomSphere.h" +#include +#include +#include +#include +#include + +TypeHandle GeomSphere::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: GeomSphere::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Geom that is a shallow copy +// of this one. It will be a different Geom pointer, +// but its internal data may or may not be shared with +// that of the original Geom. +//////////////////////////////////////////////////////////////////// +Geom *GeomSphere:: +make_copy() const { + return new GeomSphere(*this); +} + +void GeomSphere:: +draw_immediate(GraphicsStateGuardianBase *gsg) const { + gsg->draw_sphere(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomSphere::make_GeomSphere +// Access: Protected +// Description: Factory method to generate a GeomSphere object +//////////////////////////////////////////////////////////////////// +TypedWriteable* GeomSphere:: +make_GeomSphere(const FactoryParams ¶ms) +{ + GeomSphere *me = new GeomSphere; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomSphere::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a GeomSphere object +//////////////////////////////////////////////////////////////////// +void GeomSphere:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_GeomSphere); +} + + + + diff --git a/panda/src/gobj/geomSphere.h b/panda/src/gobj/geomSphere.h new file mode 100644 index 0000000000..1622b89530 --- /dev/null +++ b/panda/src/gobj/geomSphere.h @@ -0,0 +1,75 @@ +// Filename: geomSphere.h +// Created by: drose (01Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMSPHERE_H +#define GEOMSPHERE_H + +#include + +#include "geom.h" + +//////////////////////////////////////////////////////////////////// +// Class : GeomSphere +// Description : A sphere primitive. This isn't actually a primitive +// under OpenGL (or probably most backends), but it's +// handy to be able to render a sphere from time to +// time, and it may be a primitive on some backends. +// This will render the best way the backend knows how +// to render a sphere. +// +// This structure contains a sequence of spheres. Each +// two consecutive vertices defines a sphere, where the +// first vertex represents the center, and the second +// vertex represents a point on the surface. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomSphere : public Geom { +public: + GeomSphere() : Geom() { } + virtual Geom *make_copy() const; + virtual void print_draw_immediate() const { } + virtual void draw_immediate(GraphicsStateGuardianBase *gsg) const; + + virtual int get_num_vertices_per_prim() const { + return 2; + } + virtual int get_num_more_vertices_than_components() const { + return 0; + } + virtual bool uses_components() const { + return false; + } + + virtual int get_length(int) const { + return 2; + } + + virtual Geom *explode() const { + return new GeomSphere(*this); + } + +public: + static void register_with_read_factory(void); + + static TypedWriteable *make_GeomSphere(const FactoryParams ¶ms); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Geom::init_type(); + register_type(_type_handle, "GeomSphere", + Geom::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/gobj/geomSprite.I b/panda/src/gobj/geomSprite.I new file mode 100644 index 0000000000..b84100b451 --- /dev/null +++ b/panda/src/gobj/geomSprite.I @@ -0,0 +1,107 @@ +// Filename: geomSprite.I +// Created by: charles (18Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_texture +// Access : public +// Description : +//////////////////////////////////////////////////////////////////// +INLINE void GeomSprite:: +set_texture(Texture *tex) { + _texture = tex; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_texture +// Access : public +// Description : +//////////////////////////////////////////////////////////////////// +INLINE Texture *GeomSprite:: +get_texture(void) const { + return _texture; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_alpha_disable +// Access : public +// Description : +//////////////////////////////////////////////////////////////////// +INLINE void GeomSprite:: +set_alpha_disable(bool a) { + _alpha_disable = a; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_alpha_disable +// Access : public +// Description : +//////////////////////////////////////////////////////////////////// +INLINE bool GeomSprite:: +get_alpha_disable(void) const { + return _alpha_disable; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_x_texel_ratio +// Access : public +// Description : +//////////////////////////////////////////////////////////////////// +INLINE void GeomSprite:: +set_x_texel_ratio(PTA_float x_texel_ratio, GeomBindType x_bind_type) { + _x_texel_ratio = x_texel_ratio; + _x_bind_type = x_bind_type; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_y_texel_ratio +// Access : public +// Description : +//////////////////////////////////////////////////////////////////// +INLINE void GeomSprite:: +set_y_texel_ratio(PTA_float y_texel_ratio, GeomBindType y_bind_type) { + _y_texel_ratio = y_texel_ratio; + _y_bind_type = y_bind_type; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_thetas +// Access : public +// Description : +//////////////////////////////////////////////////////////////////// +INLINE void GeomSprite:: +set_thetas(PTA_float theta, GeomBindType theta_bind_type) { + _theta = theta; + _theta_bind_type = theta_bind_type; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_x_bind_type +// Access : public +// Description : +//////////////////////////////////////////////////////////////////// +INLINE GeomBindType GeomSprite:: +get_x_bind_type(void) const { + return _x_bind_type; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_y_bind_type +// Access : public +// Description : +//////////////////////////////////////////////////////////////////// +INLINE GeomBindType GeomSprite:: +get_y_bind_type(void) const { + return _y_bind_type; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_theta_bind_type +// Access : public +// Description : +//////////////////////////////////////////////////////////////////// +INLINE GeomBindType GeomSprite:: +get_theta_bind_type(void) const { + return _theta_bind_type; +} diff --git a/panda/src/gobj/geomSprite.cxx b/panda/src/gobj/geomSprite.cxx new file mode 100644 index 0000000000..2770396af1 --- /dev/null +++ b/panda/src/gobj/geomSprite.cxx @@ -0,0 +1,146 @@ +// Filename: geomSprite.cxx +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "geomSprite.h" + +TypeHandle GeomSprite::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GeomSprite +// Access: public +// Description: constructor +//////////////////////////////////////////////////////////////////// +GeomSprite:: +GeomSprite(Texture *tex, bool alpha_disable) : + _texture(tex), _alpha_disable(alpha_disable) { + _x_texel_ratio.clear(); + _y_texel_ratio.clear(); + + _theta_bind_type = G_OFF; + // note that the other bind types are intentionally left + // uninitialized; the arrays themselves can not be set without a + // bind type. +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomSprite::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Geom that is a shallow copy +// of this one. It will be a different Geom pointer, +// but its internal data may or may not be shared with +// that of the original Geom. +//////////////////////////////////////////////////////////////////// +Geom *GeomSprite:: +make_copy() const { + return new GeomSprite(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomSprite::print_draw_immediate +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void GeomSprite:: +print_draw_immediate(void) const { +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomSprite::draw_immediate +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void GeomSprite:: +draw_immediate(GraphicsStateGuardianBase *gsg) const { + gsg->draw_sprite(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomSprite::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void GeomSprite:: +write_datagram(BamWriter *manager, Datagram &me) { + Geom::write_datagram(manager, me); + WRITE_PTA(manager, me, IPD_float::write_datagram, _x_texel_ratio); + WRITE_PTA(manager, me, IPD_float::write_datagram, _y_texel_ratio); + me.add_uint8(_x_bind_type); + me.add_uint8(_y_bind_type); + me.add_uint8(_alpha_disable); + manager->write_pointer(me, _texture); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomSprite::make_GeomSprite +// Access: Protected +// Description: Factory method to generate a GeomSprite object +//////////////////////////////////////////////////////////////////// +TypedWriteable* GeomSprite:: +make_GeomSprite(const FactoryParams ¶ms) { + GeomSprite *me = new GeomSprite; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + me->make_dirty(); + me->config(); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomSprite::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void GeomSprite:: +fillin(DatagramIterator& scan, BamReader* manager) { + Geom::fillin(scan, manager); + READ_PTA(manager, scan, IPD_float::read_datagram, _x_texel_ratio); + READ_PTA(manager, scan, IPD_float::read_datagram, _y_texel_ratio); + _x_bind_type = (GeomBindType) scan.get_uint8(); + _y_bind_type = (GeomBindType) scan.get_uint8(); + _alpha_disable = (bool) scan.get_uint8(); + manager->read_pointer(scan, this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomSprite::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a GeomSprite object +//////////////////////////////////////////////////////////////////// +void GeomSprite:: +register_with_read_factory(void) { + BamReader::get_factory()->register_factory(get_class_type(), make_GeomSprite); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomSprite::complete_pointers +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +int GeomSprite:: +complete_pointers(vector_typedWriteable &plist, BamReader *manager) { + int index = Geom::complete_pointers(plist, manager); + _texture = DCAST(Texture, plist[index]); + + return index + 1; +} diff --git a/panda/src/gobj/geomSprite.h b/panda/src/gobj/geomSprite.h new file mode 100644 index 0000000000..265ee98fa0 --- /dev/null +++ b/panda/src/gobj/geomSprite.h @@ -0,0 +1,96 @@ +// Filename: geomSprite.h +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMSPRITE_H +#define GEOMSPRITE_H + +#include +#include "geom.h" + +//////////////////////////////////////////////////////////////////// +// Class : GeomSprite +// Description : Sprite Primitive +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomSprite : public Geom { +public: + GeomSprite(Texture *tex = (Texture *) NULL, + bool alpha_disable = false); + + virtual Geom *make_copy() const; + virtual void print_draw_immediate() const; + virtual void draw_immediate(GraphicsStateGuardianBase *gsg) const; + + virtual int get_num_vertices_per_prim() const { return 1; } + virtual int get_num_more_vertices_than_components() const { return 0; } + virtual bool uses_components() const { return false; } + virtual int get_length(int) const { return 1; } + + virtual Geom *explode() const { + return new GeomSprite(*this); } + + static float get_frustum_top(void) { return 1.0f; } + static float get_frustum_bottom(void) { return -1.0f; } + static float get_frustum_left(void) { return -1.0f; } + static float get_frustum_right(void) { return 1.0f; } + + INLINE void set_texture(Texture *tex); + INLINE Texture *get_texture(void) const; + + INLINE void set_alpha_disable(bool a); + INLINE bool get_alpha_disable(void) const; + + INLINE void set_x_texel_ratio(PTA_float x_texel_ratio, GeomBindType x_bind_type); + INLINE void set_y_texel_ratio(PTA_float y_texel_ratio, GeomBindType y_bind_type); + INLINE void set_thetas(PTA_float theta, GeomBindType theta_bind_type); + + INLINE GeomBindType get_x_bind_type(void) const; + INLINE GeomBindType get_y_bind_type(void) const; + INLINE GeomBindType get_theta_bind_type(void) const; + + // public so we don't have to issue them... + PTA_float _x_texel_ratio; + PTA_float _y_texel_ratio; + PTA_float _theta; + +protected: + PT(Texture) _texture; + + bool _alpha_disable; + + GeomBindType _x_bind_type; + GeomBindType _y_bind_type; + GeomBindType _theta_bind_type; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter *manager, Datagram &me); + + int complete_pointers(vector_typedWriteable &plist, BamReader *manager); + static TypedWriteable *make_GeomSprite(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator &scan, BamReader *manager); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Geom::init_type(); + register_type(_type_handle, "GeomSprite", + Geom::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "geomSprite.I" + +#endif // GEOMSPRITE_H diff --git a/panda/src/gobj/geomTri.cxx b/panda/src/gobj/geomTri.cxx new file mode 100644 index 0000000000..cf3ae958c3 --- /dev/null +++ b/panda/src/gobj/geomTri.cxx @@ -0,0 +1,245 @@ +// Filename: geomTri.cxx +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "geomTri.h" + +TypeHandle GeomTri::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GeomTri::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Geom that is a shallow copy +// of this one. It will be a different Geom pointer, +// but its internal data may or may not be shared with +// that of the original Geom. +//////////////////////////////////////////////////////////////////// +Geom *GeomTri:: +make_copy() const { + return new GeomTri(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTri::print_draw_immediate +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void GeomTri:: +print_draw_immediate(void) const +{ + /* + int i; + int j; + int nprims = _numprims; + Vertexf* tcoords = _coords; + Normalf* tnorms = _norms; + Colorf* tcolors = _colors; + TexCoordf* ttexcoords = _texcoords; + ushort* tvindex = _vindex; + ushort* tnindex = _nindex; + ushort* tcindex = _cindex; + ushort* ttindex = _tindex; + + // Draw overall + if ( _color_command[G_OVERALL] != _issue_color_noop ) + { + nout << "Color (Overall): "; + if ( tcindex ) + { + nout << "idx: " << *tcindex << " "; + nout << tcolors[*(tcindex++)]; + } + else + nout << *(tcolors++); + nout << endl; + } + if ( _normal_command[G_OVERALL] != _issue_normal_noop ) + { + nout << "Normal (Overall): "; + if ( tnindex ) + { + nout << "idx: " << *tnindex << " "; + nout << tnorms[*(tnindex++)]; + } + else + nout << *(tnorms++); + nout << endl; + } + + nout << "BeginGfx()" << endl; + + for ( i = nprims; i > 0; i-- ) + { + // Draw per primitive + if ( _color_command[G_PER_PRIM] != _issue_color_noop ) + { + nout << "Color (Per Prim): "; + if ( tcindex ) + { + nout << "idx: " << *tcindex << " "; + nout << tcolors[*(tcindex++)]; + } + else + nout << *(tcolors++); + nout << endl; + } + if ( _normal_command[G_PER_PRIM] != _issue_normal_noop ) + { + nout << "Normal (Per Prim): "; + if ( tnindex ) + { + nout << "idx: " << *tnindex << " "; + nout << tnorms[*(tnindex++)]; + } + else + nout << *(tnorms++); + nout << endl; + } + + for ( j = 0; j < 3; j++ ) + { + // Draw per vertex + if ( _color_command[G_PER_VERTEX] != _issue_color_noop ) + { + nout << "Color (Per Vertex): "; + if ( tcindex ) + { + nout << "idx: " << *tcindex << " "; + nout << tcolors[*(tcindex++)]; + } + else + nout << *(tcolors++); + nout << endl; + } + if ( _normal_command[G_PER_VERTEX] != _issue_normal_noop ) + { + nout << "Normal (Per Vertex): "; + if ( tnindex ) + { + nout << "idx: " << *tnindex << " "; + nout << tnorms[*(tnindex++)]; + } + else + nout << *(tnorms++); + nout << endl; + } + if ( _texcoord_command[G_PER_VERTEX] != _issue_texcoord_noop ) + { + nout << "TexCoord (Per Vertex): "; + if ( ttindex ) + { + nout << "idx: " << *ttindex << " "; + nout << ttexcoords[*(ttindex++)]; + } + else + nout << *(ttexcoords++); + nout << endl; + } + if ( _vertex_command[G_PER_VERTEX] != _issue_vertex_noop ) + { + nout << "Vertex (Per Vertex): "; + if ( tvindex ) + { + nout << "idx: " << *tvindex << " "; + nout << tcoords[*(tvindex++)]; + } + else + nout << *(tcoords++); + nout << endl; + } + } + } + + nout << "EndGfx()" << endl; + */ +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTri::draw_immediate +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void GeomTri:: +draw_immediate(GraphicsStateGuardianBase *gsg) const { + gsg->draw_tri(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTri::get_tris +// Access: Public, Virtual +// Description: This is similar in principle to explode(), except it +// returns only a list of triangle vertex indices, with +// no information about color or whatever. The array +// returned is a set of indices into the geom's _coords +// array, as retrieve by get_coords(); there will be 3*n +// elements in the array, where n is the number of +// triangles described by the geometry. This is useful +// when it's important to determine the physical +// structure of the geometry, without necessarily +// worrying about its rendering properties, and when +// performance considerations are not overwhelming. +//////////////////////////////////////////////////////////////////// +PTA_ushort GeomTri:: +get_tris() const { + int num_tris = _numprims; + PTA_ushort tris; + tris.reserve(num_tris * 3); + + int k = 0; + + for (int i = 0; i < _numprims; i++) { + ushort indices[3]; + if (_vindex.empty()) { + for (int j = 0; j < 3; j++) { + tris.push_back(k++); + } + } else { + for (int j = 0; j < 3; j++) { + tris.push_back(_vindex[k++]); + } + } + } + + nassertr(tris.size() == num_tris * 3, PTA_ushort()); + return tris; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTri::make_GeomTri +// Access: Protected +// Description: Factory method to generate a GeomTri object +//////////////////////////////////////////////////////////////////// +TypedWriteable* GeomTri:: +make_GeomTri(const FactoryParams ¶ms) { + GeomTri *me = new GeomTri; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + me->make_dirty(); + me->config(); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTri::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a GeomTri object +//////////////////////////////////////////////////////////////////// +void GeomTri:: +register_with_read_factory(void) { + BamReader::get_factory()->register_factory(get_class_type(), make_GeomTri); +} diff --git a/panda/src/gobj/geomTri.h b/panda/src/gobj/geomTri.h new file mode 100644 index 0000000000..fcc93e591e --- /dev/null +++ b/panda/src/gobj/geomTri.h @@ -0,0 +1,60 @@ +// Filename: geomTri.h +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMTRI_H +#define GEOMTRI_H + +#include "geom.h" + +//////////////////////////////////////////////////////////////////// +// Class : GeomTri +// Description : Triangle Primitive +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomTri : public Geom { +public: + GeomTri() { } + virtual Geom *make_copy() const; + virtual void print_draw_immediate() const; + virtual void draw_immediate(GraphicsStateGuardianBase *gsg) const; + + virtual int get_num_vertices_per_prim() const { + return 3; + } + virtual int get_num_more_vertices_than_components() const { + return 0; + } + virtual bool uses_components() const { + return false; + } + + virtual int get_length(int) const { + return 3; + } + + virtual PTA_ushort get_tris() const; + +public: + static void register_with_read_factory(void); + static TypedWriteable *make_GeomTri(const FactoryParams ¶ms); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Geom::init_type(); + register_type(_type_handle, "GeomTri", + Geom::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif // GEOMTRI_H diff --git a/panda/src/gobj/geomTrifan.cxx b/panda/src/gobj/geomTrifan.cxx new file mode 100644 index 0000000000..9273eb4c4c --- /dev/null +++ b/panda/src/gobj/geomTrifan.cxx @@ -0,0 +1,410 @@ +// Filename: geomTrifan.cxx +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "geomTri.h" +#include "geomTrifan.h" + +TypeHandle GeomTrifan::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GeomTrifan::get_num_tris +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +int GeomTrifan:: +get_num_tris() const { + int numtris = 0; + for (int i = 0; i < _numprims; i++) { + numtris += _primlengths[i] - 2; + } + return numtris; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTrifan::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Geom that is a shallow copy +// of this one. It will be a different Geom pointer, +// but its internal data may or may not be shared with +// that of the original Geom. +//////////////////////////////////////////////////////////////////// +Geom *GeomTrifan:: +make_copy() const { + return new GeomTrifan(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTrifan::print_draw_immediate +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void GeomTrifan:: +print_draw_immediate( void ) const +{ + /* + int i; + int j; + int nprims = _numprims; + int* plen = _primlengths; + Vertexf* tcoords = _coords; + Normalf* tnorms = _norms; + Colorf* tcolors = _colors; + TexCoordf* ttexcoords = _texcoords; + ushort* tvindex = _vindex; + ushort* tnindex = _nindex; + ushort* tcindex = _cindex; + ushort* ttindex = _tindex; + + // Draw overall + if ( _color_command[G_OVERALL] != _issue_color_noop ) + { + nout << "Color (Overall): "; + if ( tcindex ) + { + nout << "idx: " << *tcindex << " "; + nout << tcolors[*(tcindex++)]; + } + else + nout << *(tcolors++); + nout << endl; + } + if ( _normal_command[G_OVERALL] != _issue_normal_noop ) + { + nout << "Normal (Overall): "; + if ( tnindex ) + { + nout << "idx: " << *tnindex << " "; + nout << tnorms[*(tnindex++)]; + } + else + nout << *(tnorms++); + nout << endl; + } + + for ( i = nprims; i > 0; i-- ) + { + // Draw per primitive + if ( _color_command[G_PER_PRIM] != _issue_color_noop ) + { + nout << "Color (Per Prim): "; + if ( tcindex ) + { + nout << "idx: " << *tcindex << " "; + nout << tcolors[*(tcindex++)]; + } + else + nout << *(tcolors++); + nout << endl; + } + if ( _normal_command[G_PER_PRIM] != _issue_normal_noop ) + { + nout << "Normal (Per Prim): "; + if ( tnindex ) + { + nout << "idx: " << *tnindex << " "; + nout << tnorms[*(tnindex++)]; + } + else + nout << *(tnorms++); + nout << endl; + } + + nout << "BeginGfx()" << endl; + + for ( j = *(plen++); j > 0; j-- ) + { + // Draw per vertex + if ( _color_command[G_PER_VERTEX] != _issue_color_noop ) + { + nout << "Color (Per Vertex): "; + if ( tcindex ) + { + nout << "idx: " << *tcindex << " "; + nout << tcolors[*(tcindex++)]; + } + else + nout << *(tcolors++); + nout << endl; + } + if ( _normal_command[G_PER_VERTEX] != _issue_normal_noop ) + { + nout << "Normal (Per Vertex): "; + if ( tnindex ) + { + nout << "idx: " << *tnindex << " "; + nout << tnorms[*(tnindex++)]; + } + else + nout << *(tnorms++); + nout << endl; + } + if ( _texcoord_command[G_PER_VERTEX] != _issue_texcoord_noop ) + { + nout << "TexCoord (Per Vertex): "; + if ( ttindex ) + { + nout << "idx: " << *ttindex << " "; + nout << ttexcoords[*(ttindex++)]; + } + else + nout << *(ttexcoords++); + nout << endl; + } + if ( _vertex_command[G_PER_VERTEX] != _issue_vertex_noop ) + { + nout << "Vertex (Per Vertex): "; + if ( tvindex ) + { + nout << "idx: " << *tvindex << " "; + nout << tcoords[*(tvindex++)]; + } + else + nout << *(tcoords++); + nout << endl; + } + } + + nout << "EndGfx()" << endl; + } + */ +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTrifan::draw_immediate +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void GeomTrifan:: +draw_immediate(GraphicsStateGuardianBase *gsg) const { + gsg->draw_trifan(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTrifan::explode +// Access: Public, Virtual +// Description: Allocate and return a new Geom which represents the +// individual triangles that make up the trifan. +//////////////////////////////////////////////////////////////////// +Geom *GeomTrifan:: +explode() const { + PTA_Vertexf coords(0); + PTA_Normalf normals(0); + PTA_TexCoordf texcoords(0); + PTA_Colorf colors(0); + + VertexIterator vi = make_vertex_iterator(); + NormalIterator ni = make_normal_iterator(); + TexCoordIterator ti = make_texcoord_iterator(); + ColorIterator ci = make_color_iterator(); + + // Get overall values + if (get_binding(G_COLOR) == G_OVERALL) { + colors.push_back(get_next_color(ci)); + } + if (get_binding(G_NORMAL) == G_OVERALL) { + normals.push_back(get_next_normal(ni)); + } + + int num_tris = 0; + int num_prims = get_num_prims(); + + for (int i = 0; i < num_prims; i++) { + // Get per-primitive values + Colorf per_trifan_color; + Normalf per_trifan_normal; + if (get_binding(G_COLOR) == G_PER_PRIM) { + per_trifan_color = get_next_color(ci); + } + if (get_binding(G_NORMAL) == G_PER_PRIM) { + per_trifan_normal = get_next_normal(ni); + } + + int num_verts = get_length(i); + assert(num_verts >= 3); + + // A few temporary arrays to hold the three most recent per-vertex + // values. + Vertexf f3vertex[3]; + Normalf f3normal[3]; + Colorf f3color[3]; + TexCoordf f3texcoord[3]; + + // Get the first two vertices. We get these into positions [0] + // and [2]. + + int v; + for (v = 0; v <= 2; v += 2) { + if (get_binding(G_COORD) == G_PER_VERTEX) { + f3vertex[v] = get_next_vertex(vi); + } + if (get_binding(G_NORMAL) == G_PER_VERTEX) { + f3normal[v] = get_next_normal(ni); + } + if (get_binding(G_TEXCOORD) == G_PER_VERTEX) { + f3texcoord[v] = get_next_texcoord(ti); + } + if (get_binding(G_COLOR) == G_PER_VERTEX) { + f3color[v] = get_next_color(ci); + } + } + + // Now fill each triangle. Each vertex from this point on defines + // a new triangle. + for (v = 2; v < num_verts; v++) { + num_tris++; + + f3vertex[1] = f3vertex[2]; + f3normal[1] = f3normal[2]; + f3texcoord[1] = f3texcoord[2]; + f3color[1] = f3color[2]; + + if (get_binding(G_COLOR) == G_PER_PRIM) { + colors.push_back(per_trifan_color); + } else if (get_binding(G_COLOR) == G_PER_COMPONENT) { + colors.push_back(get_next_color(ci)); + } + + if (get_binding(G_NORMAL) == G_PER_PRIM) { + normals.push_back(per_trifan_normal); + } else if (get_binding(G_NORMAL) == G_PER_COMPONENT) { + normals.push_back(get_next_normal(ni)); + } + + // Per-vertex attributes. + if (get_binding(G_COORD) == G_PER_VERTEX) { + f3vertex[2] = get_next_vertex(vi); + coords.push_back(f3vertex[0]); + coords.push_back(f3vertex[1]); + coords.push_back(f3vertex[2]); + } + if (get_binding(G_NORMAL) == G_PER_VERTEX) { + f3normal[2] = get_next_normal(ni); + normals.push_back(f3normal[0]); + normals.push_back(f3normal[1]); + normals.push_back(f3normal[2]); + } + if (get_binding(G_TEXCOORD) == G_PER_VERTEX) { + f3texcoord[2] = get_next_texcoord(ti); + texcoords.push_back(f3texcoord[0]); + texcoords.push_back(f3texcoord[1]); + texcoords.push_back(f3texcoord[2]); + } + if (get_binding(G_COLOR) == G_PER_VERTEX) { + f3color[2] = get_next_color(ci); + colors.push_back(f3color[0]); + colors.push_back(f3color[1]); + colors.push_back(f3color[2]); + } + } + } + + Geom *tris = new GeomTri; + tris->set_coords(coords, get_binding(G_COORD)); + tris->set_normals(normals, + get_binding(G_NORMAL) == G_PER_COMPONENT ? + G_PER_PRIM : get_binding(G_NORMAL)); + tris->set_texcoords(texcoords, get_binding(G_TEXCOORD)); + tris->set_colors(colors, + get_binding(G_COLOR) == G_PER_COMPONENT ? + G_PER_PRIM : get_binding(G_COLOR)); + tris->set_num_prims(num_tris); + + return tris; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTrifan::get_tris +// Access: Public, Virtual +// Description: This is similar in principle to explode(), except it +// returns only a list of triangle vertex indices, with +// no information about color or whatever. The array +// returned is a set of indices into the geom's _coords +// array, as retrieve by get_coords(); there will be 3*n +// elements in the array, where n is the number of +// triangles described by the geometry. This is useful +// when it's important to determine the physical +// structure of the geometry, without necessarily +// worrying about its rendering properties, and when +// performance considerations are not overwhelming. +//////////////////////////////////////////////////////////////////// +PTA_ushort GeomTrifan:: +get_tris() const { + int num_tris = get_num_tris(); + PTA_ushort tris; + tris.reserve(num_tris * 3); + + int k = 0; + + for (int i = 0; i < _numprims; i++) { + ushort v0, v1, v2; + if (_vindex.empty()) { + v0 = k++; + v1 = k++; + } else { + v0 = _vindex[k++]; + v1 = _vindex[k++]; + } + + bool even = true; + int len = _primlengths[i] - 2; + + for (int j = 0; j < len; j++) { + if (_vindex.empty()) { + v2 = k++; + } else { + v2 = _vindex[k++]; + } + + tris.push_back(v0); + tris.push_back(v1); + tris.push_back(v2); + + v1 = v2; + } + } + + nassertr(tris.size() == num_tris * 3, PTA_ushort()); + return tris; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTrifan::make_GeomTrifan +// Access: Protected +// Description: Factory method to generate a GeomTrifan object +//////////////////////////////////////////////////////////////////// +TypedWriteable* GeomTrifan:: +make_GeomTrifan(const FactoryParams ¶ms) { + GeomTrifan *me = new GeomTrifan; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + me->make_dirty(); + me->config(); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTrifan::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a GeomTrifan object +//////////////////////////////////////////////////////////////////// +void GeomTrifan:: +register_with_read_factory(void) { + BamReader::get_factory()->register_factory(get_class_type(), make_GeomTrifan); +} + diff --git a/panda/src/gobj/geomTrifan.h b/panda/src/gobj/geomTrifan.h new file mode 100644 index 0000000000..2183826fba --- /dev/null +++ b/panda/src/gobj/geomTrifan.h @@ -0,0 +1,63 @@ +// Filename: geomTrifan.h +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMTRIFAN_H +#define GEOMTRIFAN_H + +#include "geom.h" + +//////////////////////////////////////////////////////////////////// +// Class : GeomTrifan +// Description : Triangle Fan Primitive +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomTrifan : public Geom +{ +public: + GeomTrifan( void ) : Geom() { } + virtual Geom *make_copy() const; + virtual void print_draw_immediate( void ) const; + int get_num_tris( void ) const; + virtual void draw_immediate(GraphicsStateGuardianBase *gsg) const; + + virtual int get_num_vertices_per_prim() const { + return 0; + } + virtual int get_num_more_vertices_than_components() const { + return 2; + } + virtual bool uses_components() const { + return true; + } + + virtual int get_length(int prim) const { + return _primlengths[prim]; + } + + virtual Geom *explode() const; + virtual PTA_ushort get_tris() const; + +public: + static void register_with_read_factory(void); + static TypedWriteable *make_GeomTrifan(const FactoryParams ¶ms); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Geom::init_type(); + register_type(_type_handle, "GeomTrifan", + Geom::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif // GEOMTRIFAN_H diff --git a/panda/src/gobj/geomTristrip.cxx b/panda/src/gobj/geomTristrip.cxx new file mode 100644 index 0000000000..97eb13eb27 --- /dev/null +++ b/panda/src/gobj/geomTristrip.cxx @@ -0,0 +1,519 @@ +// Filename: geomTristrip.cxx +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "geomTri.h" +#include "geomTristrip.h" + +TypeHandle GeomTristrip::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: GeomTristrip::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Geom that is a shallow copy +// of this one. It will be a different Geom pointer, +// but its internal data may or may not be shared with +// that of the original Geom. +//////////////////////////////////////////////////////////////////// +Geom *GeomTristrip:: +make_copy() const { + return new GeomTristrip(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTristrip::print_draw_immediate +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void GeomTristrip::print_draw_immediate( void ) const +{ + /* + int i; + int j; + int k; + int nprims = _numprims; + int* plen = _primlengths; + Vertexf* tcoords = _coords; + Normalf* tnorms = _norms; + Colorf* tcolors = _colors; + TexCoordf* ttexcoords = _texcoords; + ushort* tvindex = _vindex; + ushort* tnindex = _nindex; + ushort* tcindex = _cindex; + ushort* ttindex = _tindex; + + // Draw overall + if ( _color_command[G_OVERALL] != _issue_color_noop ) + { + nout << "Color (Overall): "; + if ( tcindex ) + { + nout << "idx: " << *tcindex << " "; + nout << tcolors[*(tcindex++)]; + } + else + nout << *(tcolors++); + nout << endl; + } + if ( _normal_command[G_OVERALL] != _issue_normal_noop ) + { + nout << "Normal (Overall): "; + if ( tnindex ) + { + nout << "idx: " << *tnindex << " "; + nout << tnorms[*(tnindex++)]; + } + else + nout << *(tnorms++); + nout << endl; + } + + for ( i = nprims; i > 0; i-- ) + { + // Draw per primitive + if ( _color_command[G_PER_PRIM] != _issue_color_noop ) + { + nout << "Color (Per Prim): "; + if ( tcindex ) + { + nout << "idx: " << *tcindex << " "; + nout << tcolors[*(tcindex++)]; + } + else + nout << *(tcolors++); + nout << endl; + } + if ( _normal_command[G_PER_PRIM] != _issue_normal_noop ) + { + nout << "Normal (Per Prim): "; + if ( tnindex ) + { + nout << "idx: " << *tnindex << " "; + nout << tnorms[*(tnindex++)]; + } + else + nout << *(tnorms++); + nout << endl; + } + + nout << "BeginGfx()" << endl; + + for ( j = *(plen++); j > 1; j -= 2 ) + { + // Draw per component + if ( _color_command[G_PER_COMPONENT] != _issue_color_noop ) + { + nout << "Color (Per Component): "; + if ( tcindex ) + { + nout << "idx: " << *tcindex << " "; + nout << tcolors[*(tcindex++)]; + } + else + nout << *(tcolors++); + nout << endl; + } + if ( _normal_command[G_PER_COMPONENT] != _issue_normal_noop ) + { + nout << "Normal (Per Component): "; + if ( tnindex ) + { + nout << "idx: " << *tnindex << " "; + nout << tnorms[*(tnindex++)]; + } + else + nout << *(tnorms++); + nout << endl; + } + + for ( k = 0; k < 2; k++ ) + { + // Draw per vertex + if ( _color_command[G_PER_VERTEX] != _issue_color_noop ) + { + nout << "Color (Per Vertex): "; + if ( tcindex ) + { + nout << "idx: " << *tcindex << " "; + nout << tcolors[*(tcindex++)]; + } + else + nout << *(tcolors++); + nout << endl; + } + if ( _normal_command[G_PER_VERTEX] != _issue_normal_noop ) + { + nout << "Normal (Per Vertex): "; + if ( tnindex ) + { + nout << "idx: " << *tnindex << " "; + nout << tnorms[*(tnindex++)]; + } + else + nout << *(tnorms++); + nout << endl; + } + if ( _texcoord_command[G_PER_VERTEX] != _issue_texcoord_noop ) + { + nout << "TexCoord (Per Vertex): "; + if ( ttindex ) + { + nout << "idx: " << *ttindex << " "; + nout << ttexcoords[*(ttindex++)]; + } + else + nout << *(ttexcoords++); + nout << endl; + } + if ( _vertex_command[G_PER_VERTEX] != _issue_vertex_noop ) + { + nout << "Vertex (Per Vertex): "; + if ( tvindex ) + { + nout << "idx: " << *tvindex << " "; + nout << tcoords[*(tvindex++)]; + } + else + nout << *(tcoords++); + nout << endl; + } + } + } + if ( !j ) + { + nout << "EndGfx()" << endl; + continue; + } + else + { + // Draw per vertex + if ( _color_command[G_PER_VERTEX] != _issue_color_noop ) + { + nout << "Color (Per Vertex): "; + if ( tcindex ) + { + nout << "idx: " << *tcindex << " "; + nout << tcolors[*(tcindex++)]; + } + else + nout << *(tcolors++); + nout << endl; + } + if ( _normal_command[G_PER_VERTEX] != _issue_normal_noop ) + { + nout << "Normal (Per Vertex): "; + if ( tnindex ) + { + nout << "idx: " << *tnindex << " "; + nout << tnorms[*(tnindex++)]; + } + else + nout << *(tnorms++); + nout << endl; + } + if ( _texcoord_command[G_PER_VERTEX] != _issue_texcoord_noop ) + { + nout << "TexCoord (Per Vertex): "; + if ( ttindex ) + { + nout << "idx: " << *ttindex << " "; + nout << ttexcoords[*(ttindex++)]; + } + else + nout << *(ttexcoords++); + nout << endl; + } + if ( _vertex_command[G_PER_VERTEX] != _issue_vertex_noop ) + { + nout << "Vertex (Per Vertex): "; + if ( tvindex ) + { + nout << "idx: " << *tvindex << " "; + nout << tcoords[*(tvindex++)]; + } + else + nout << *(tcoords++); + nout << endl; + } + + nout << "EndGfx()" << endl; + continue; + } + } + */ +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTristrip::draw_immediate +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void GeomTristrip::draw_immediate(GraphicsStateGuardianBase *gsg) const { + gsg->draw_tristrip(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTristrip::get_num_tris +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +int +GeomTristrip::get_num_tris() const { + int numtris = 0; + for (int i = 0; i < _numprims; i++) { + numtris += _primlengths[i] - 2; + } + return numtris; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTristrip::explode +// Access: Public, Virtual +// Description: Allocate and return a new Geom which represents the +// individual triangles that make up the tristrip. +//////////////////////////////////////////////////////////////////// +Geom *GeomTristrip:: +explode() const { + PTA_Vertexf coords(0); + PTA_Normalf normals(0); + PTA_TexCoordf texcoords(0); + PTA_Colorf colors(0); + + VertexIterator vi = make_vertex_iterator(); + NormalIterator ni = make_normal_iterator(); + TexCoordIterator ti = make_texcoord_iterator(); + ColorIterator ci = make_color_iterator(); + + // Get overall values + if (get_binding(G_COLOR) == G_OVERALL) { + colors.push_back(get_next_color(ci)); + } + if (get_binding(G_NORMAL) == G_OVERALL) { + normals.push_back(get_next_normal(ni)); + } + + int num_tris = 0; + int num_prims = get_num_prims(); + + for (int i = 0; i < num_prims; i++) { + // Get per-primitive values + Colorf per_tristrip_color; + Normalf per_tristrip_normal; + if (get_binding(G_COLOR) == G_PER_PRIM) { + per_tristrip_color = get_next_color(ci); + } + if (get_binding(G_NORMAL) == G_PER_PRIM) { + per_tristrip_normal = get_next_normal(ni); + } + + int num_verts = get_length(i); + assert(num_verts >= 3); + + // A few temporary arrays to hold the three most recent per-vertex + // values. + Vertexf f3vertex[3]; + Normalf f3normal[3]; + Colorf f3color[3]; + TexCoordf f3texcoord[3]; + + // Get the first two vertices. We get these into positions [0] + // and [2], to prepare for the back-and-forth switchback logic in + // the following loop. + + int v; + for (v = 0; v <= 2; v += 2) { + if (get_binding(G_COORD) == G_PER_VERTEX) { + f3vertex[v] = get_next_vertex(vi); + } + if (get_binding(G_NORMAL) == G_PER_VERTEX) { + f3normal[v] = get_next_normal(ni); + } + if (get_binding(G_TEXCOORD) == G_PER_VERTEX) { + f3texcoord[v] = get_next_texcoord(ti); + } + if (get_binding(G_COLOR) == G_PER_VERTEX) { + f3color[v] = get_next_color(ci); + } + } + + // Now fill each triangle. Each vertex from this point on defines + // a new triangle. + for (v = 2; v < num_verts; v++) { + num_tris++; + + if (get_binding(G_COLOR) == G_PER_PRIM) { + colors.push_back(per_tristrip_color); + } else if (get_binding(G_COLOR) == G_PER_COMPONENT) { + colors.push_back(get_next_color(ci)); + } + + if (get_binding(G_NORMAL) == G_PER_PRIM) { + normals.push_back(per_tristrip_normal); + } else if (get_binding(G_NORMAL) == G_PER_COMPONENT) { + normals.push_back(get_next_normal(ni)); + } + + if ((v & 1) == 0) { + // Even. The triangle is counter-clockwise. + f3vertex[1] = f3vertex[2]; + f3normal[1] = f3normal[2]; + f3texcoord[1] = f3texcoord[2]; + f3color[1] = f3color[2]; + } else { + // Odd. The triangle is clockwise. + f3vertex[0] = f3vertex[2]; + f3normal[0] = f3normal[2]; + f3texcoord[0] = f3texcoord[2]; + f3color[0] = f3color[2]; + } + + // Per-vertex attributes. + if (get_binding(G_COORD) == G_PER_VERTEX) { + f3vertex[2] = get_next_vertex(vi); + coords.push_back(f3vertex[0]); + coords.push_back(f3vertex[1]); + coords.push_back(f3vertex[2]); + } + if (get_binding(G_NORMAL) == G_PER_VERTEX) { + f3normal[2] = get_next_normal(ni); + normals.push_back(f3normal[0]); + normals.push_back(f3normal[1]); + normals.push_back(f3normal[2]); + } + if (get_binding(G_TEXCOORD) == G_PER_VERTEX) { + f3texcoord[2] = get_next_texcoord(ti); + texcoords.push_back(f3texcoord[0]); + texcoords.push_back(f3texcoord[1]); + texcoords.push_back(f3texcoord[2]); + } + if (get_binding(G_COLOR) == G_PER_VERTEX) { + f3color[2] = get_next_color(ci); + colors.push_back(f3color[0]); + colors.push_back(f3color[1]); + colors.push_back(f3color[2]); + } + } + } + + Geom *tris = new GeomTri; + tris->set_coords(coords, get_binding(G_COORD)); + tris->set_normals(normals, + get_binding(G_NORMAL) == G_PER_COMPONENT ? + G_PER_PRIM : get_binding(G_NORMAL)); + tris->set_texcoords(texcoords, get_binding(G_TEXCOORD)); + tris->set_colors(colors, + get_binding(G_COLOR) == G_PER_COMPONENT ? + G_PER_PRIM : get_binding(G_COLOR)); + tris->set_num_prims(num_tris); + + return tris; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTristrip::get_tris +// Access: Public, Virtual +// Description: This is similar in principle to explode(), except it +// returns only a list of triangle vertex indices, with +// no information about color or whatever. The array +// returned is a set of indices into the geom's _coords +// array, as retrieve by get_coords(); there will be 3*n +// elements in the array, where n is the number of +// triangles described by the geometry. This is useful +// when it's important to determine the physical +// structure of the geometry, without necessarily +// worrying about its rendering properties, and when +// performance considerations are not overwhelming. +//////////////////////////////////////////////////////////////////// +PTA_ushort GeomTristrip:: +get_tris() const { + int num_tris = get_num_tris(); + PTA_ushort tris; + tris.reserve(num_tris * 3); + + int k = 0; + + for (int i = 0; i < _numprims; i++) { + ushort v0, v1, v2; + if (_vindex.empty()) { + v0 = k++; + v1 = k++; + } else { + v0 = _vindex[k++]; + v1 = _vindex[k++]; + } + + bool even = true; + int len = _primlengths[i]; + + for (int j = 2; j < len; j++) { + if (_vindex.empty()) { + v2 = k++; + } else { + v2 = _vindex[k++]; + } + + even = !even; + + if (even) { + ushort vtmp = v1; + v1 = v2; + v2 = vtmp; + } + + tris.push_back(v0); + tris.push_back(v1); + tris.push_back(v2); + + if (even) { + v0 = v2; + } else { + v0 = v1; + v1 = v2; + } + } + } + + nassertr(tris.size() == num_tris * 3, PTA_ushort()); + return tris; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTristrip::make_GeomTristrip +// Access: Protected +// Description: Factory method to generate a GeomTristrip object +//////////////////////////////////////////////////////////////////// +TypedWriteable* GeomTristrip:: +make_GeomTristrip(const FactoryParams ¶ms) { + GeomTristrip *me = new GeomTristrip; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + me->make_dirty(); + me->config(); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTristrip::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a GeomTristrip object +//////////////////////////////////////////////////////////////////// +void GeomTristrip:: +register_with_read_factory(void) { + BamReader::get_factory()->register_factory(get_class_type(), make_GeomTristrip); +} diff --git a/panda/src/gobj/geomTristrip.h b/panda/src/gobj/geomTristrip.h new file mode 100644 index 0000000000..e1067d775d --- /dev/null +++ b/panda/src/gobj/geomTristrip.h @@ -0,0 +1,62 @@ +// Filename: geomTristrip.h +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMTRISTRIP_H +#define GEOMTRISTRIP_H + +#include "geom.h" + +//////////////////////////////////////////////////////////////////// +// Class : GeomTristrip +// Description : Triangle Strip Primitive +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomTristrip : public Geom { +public: + GeomTristrip() : Geom() { } + virtual Geom *make_copy() const; + virtual void print_draw_immediate() const; + int get_num_tris() const; + virtual void draw_immediate(GraphicsStateGuardianBase *gsg) const; + + virtual int get_num_vertices_per_prim() const { + return 0; + } + virtual int get_num_more_vertices_than_components() const { + return 2; + } + virtual bool uses_components() const { + return true; + } + + virtual int get_length(int prim) const { + return _primlengths[prim]; + } + + virtual Geom *explode() const; + virtual PTA_ushort get_tris() const; + +public: + static void register_with_read_factory(void); + static TypedWriteable *make_GeomTristrip(const FactoryParams ¶ms); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Geom::init_type(); + register_type(_type_handle, "GeomTristrip", + Geom::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif // GEOMTRISTRIP_H diff --git a/panda/src/gobj/geomprimitives.h b/panda/src/gobj/geomprimitives.h new file mode 100644 index 0000000000..a9e61ae279 --- /dev/null +++ b/panda/src/gobj/geomprimitives.h @@ -0,0 +1,20 @@ +// Filename: geomprimitives.h +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMPRIMITIVES_H +#define GEOMPRIMITIVES_H + +#include "geomPoint.h" +#include "geomLine.h" +#include "geomLinestrip.h" +#include "geomSprite.h" +#include "geomTri.h" +#include "geomTristrip.h" +#include "geomTrifan.h" +#include "geomQuad.h" +#include "geomPolygon.h" +#include "geomSphere.h" + +#endif // GEOMPRIMITIVES_H diff --git a/panda/src/gobj/imageBuffer.cxx b/panda/src/gobj/imageBuffer.cxx new file mode 100644 index 0000000000..d01f59e794 --- /dev/null +++ b/panda/src/gobj/imageBuffer.cxx @@ -0,0 +1,71 @@ +// Filename: imageBuffer.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "imageBuffer.h" +#include "config_gobj.h" +#include "config_util.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle ImageBuffer::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ImageBuffer::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void ImageBuffer:: +write_datagram(BamWriter *, Datagram &me) +{ + Filename filename = get_name(); + + switch (bam_texture_mode) { + case BTM_fullpath: + break; + + case BTM_relative: + filename.find_on_searchpath(get_texture_path()); + filename.find_on_searchpath(get_model_path()); + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "Texture file " << get_name() << " found as " << filename << "\n"; + } + break; + + case BTM_basename: + filename = filename.get_basename(); + break; + + default: + gobj_cat.error() + << "Unsupported bam-texture-mode: " << bam_texture_mode << "\n"; + } + + me.add_string(filename); +} + +//////////////////////////////////////////////////////////////////// +// Function: ImageBuffer::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void ImageBuffer:: +fillin(DatagramIterator& scan, BamReader*) +{ + set_name(scan.get_string()); +} diff --git a/panda/src/gobj/imageBuffer.h b/panda/src/gobj/imageBuffer.h new file mode 100644 index 0000000000..1196523229 --- /dev/null +++ b/panda/src/gobj/imageBuffer.h @@ -0,0 +1,81 @@ +// Filename: imageBuffer.h +// Created by: mike (09Jan97) +//////////////////////////////////////////////////////////////////// + +#ifndef IMAGEBUFFER_H +#define IMAGEBUFFER_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "drawable.h" +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +class RenderBuffer; +class DisplayRegion; + +//////////////////////////////////////////////////////////////////// +// Class : ImageBuffer +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ImageBuffer : public dDrawable, public Namable +{ +public: + + ImageBuffer( void ) : dDrawable() { } + virtual ~ImageBuffer( void ) { } + + virtual bool read( const string& name ) = 0; + virtual bool write( const string& name = "" ) const = 0; + + virtual void config( void ) { WriteableConfigurable::config(); } + + virtual void copy(GraphicsStateGuardianBase *, const DisplayRegion *)=0; + virtual void copy(GraphicsStateGuardianBase *, const DisplayRegion *, + const RenderBuffer &rb)=0; + virtual void draw(GraphicsStateGuardianBase *)=0; + virtual void draw(GraphicsStateGuardianBase *, const DisplayRegion *)=0; + virtual void draw(GraphicsStateGuardianBase *, const DisplayRegion *, + const RenderBuffer &rb)=0; + +public: + //Abstract class, so no factory methods for Reading and Writing + virtual void write_datagram(BamWriter* manager, Datagram &me); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + dDrawable::init_type(); + Namable::init_type(); + register_type(_type_handle, "ImageBuffer", + dDrawable::get_class_type(), + Namable::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + + static TypeHandle _type_handle; +}; + +#endif + + diff --git a/panda/src/gobj/material.I b/panda/src/gobj/material.I new file mode 100644 index 0000000000..a63fb2a294 --- /dev/null +++ b/panda/src/gobj/material.I @@ -0,0 +1,145 @@ +// Filename: material.I +// Created by: mike (05Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: Material::get_ambient +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Colorf Material::get_ambient( void ) const +{ + return _ambient; +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::set_ambient +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Material::set_ambient( const Colorf& color ) +{ + _ambient = color; +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::get_diffuse +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Colorf Material::get_diffuse( void ) const +{ + return _diffuse; +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::set_diffuse +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Material::set_diffuse( const Colorf& color ) +{ + _diffuse = color; +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::get_specular +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Colorf Material::get_specular( void ) const +{ + return _specular; +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::set_specular +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Material::set_specular( const Colorf& color ) +{ + _specular = color; +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::get_shininess +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float Material::get_shininess( void ) const +{ + return _shininess; +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::set_shininess +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Material::set_shininess( float shininess ) +{ + _shininess = shininess; +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::get_emission +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Colorf Material::get_emission( void ) const +{ + return _emission; +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::set_emission +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Material::set_emission( const Colorf& color ) +{ + _emission = color; +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::get_local +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool Material::get_local( void ) const +{ + return _local; +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::set_local +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Material::set_local( bool local ) +{ + _local = local; +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::get_twoside +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool Material::get_twoside( void ) const +{ + return _twoside; +} + +//////////////////////////////////////////////////////////////////// +// Function: Material::set_twoside +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Material::set_twoside( bool twoside ) +{ + _twoside = twoside; +} diff --git a/panda/src/gobj/material.cxx b/panda/src/gobj/material.cxx new file mode 100644 index 0000000000..a45624809e --- /dev/null +++ b/panda/src/gobj/material.cxx @@ -0,0 +1,75 @@ +// Filename: material.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include +#include "material.h" + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle Material::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +Material::Material( void ) +{ + set_ambient( Colorf( 0.2, 0.2, 0.2, 1 ) ); + set_diffuse( Colorf( 0.8, 0.8, 0.8, 1 ) ); + set_specular( Colorf( 0, 0, 0, 1 ) ); + set_shininess( 0.0 ); + set_emission( Colorf( 0, 0, 0, 1 ) ); + + set_local( false ); + set_twoside( false ); +} + +//////////////////////////////////////////////////////////////////// +// Function: Destructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +Material::~Material( void ) +{ +} + + +//////////////////////////////////////////////////////////////////// +// Function: output +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void Material::output( ostream &out ) const +{ + out << "a" << _ambient[0] << ",d" << _diffuse[0] + << ",s" << _specular[0] << ",s" << _shininess + << ",e" << _emission[0] << ",l" << _local + << ",t" << _twoside; +} + +//////////////////////////////////////////////////////////////////// +// Function: write +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void Material::write( ostream &out, int indent_level ) const +{ + indent(out, indent_level) << "ambient = " << _ambient << "\n"; + indent(out, indent_level) << "diffuse = " << _diffuse << "\n"; + indent(out, indent_level) << "specular = " << _specular << "\n"; + indent(out, indent_level) << "shininess = " << _shininess << "\n"; + indent(out, indent_level) << "emission = " << _emission << "\n"; + indent(out, indent_level) << "local = " << _local << "\n"; + indent(out, indent_level) << "twoside = " << _twoside << "\n"; +} diff --git a/panda/src/gobj/material.h b/panda/src/gobj/material.h new file mode 100644 index 0000000000..6d4ad74eb9 --- /dev/null +++ b/panda/src/gobj/material.h @@ -0,0 +1,94 @@ +// Filename: material.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef MATERIAL_H +#define MATERIAL_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : Material +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Material : public TypedReferenceCount +{ + public: + + Material( void ); + ~Material( void ); + + void apply( GraphicsStateGuardianBase *gsg ) { + gsg->apply_material( this ); + } + + INLINE Colorf get_ambient( void ) const; + INLINE void set_ambient( const Colorf& color ); + INLINE Colorf get_diffuse( void ) const; + INLINE void set_diffuse( const Colorf& color ); + INLINE Colorf get_specular( void ) const; + INLINE void set_specular( const Colorf& color ); + INLINE float get_shininess( void ) const; + INLINE void set_shininess( float shininess ); + INLINE Colorf get_emission( void ) const; + INLINE void set_emission( const Colorf& color ); + + INLINE bool get_local( void ) const; + INLINE void set_local( bool local ); + INLINE bool get_twoside( void ) const; + INLINE void set_twoside( bool twoside ); + + void output( ostream &out ) const; + void write( ostream &out, int indent = 0 ) const; + + protected: + + Colorf _ambient; + Colorf _diffuse; + Colorf _specular; + float _shininess; + Colorf _emission; + + bool _local; + bool _twoside; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "Material", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + + static TypeHandle _type_handle; +}; + +INLINE ostream &operator << (ostream &out, const Material &m) { + m.output(out); + return out; +} + +#include "material.I" + +#endif diff --git a/panda/src/gobj/orthoProjection.I b/panda/src/gobj/orthoProjection.I new file mode 100644 index 0000000000..fd3795bfd7 --- /dev/null +++ b/panda/src/gobj/orthoProjection.I @@ -0,0 +1,28 @@ +// Filename: orthoProjection.I +// Created by: mike (18Feb99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: OrthoProjection::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE OrthoProjection:: +OrthoProjection(const Frustumf &frustum) : _frustum(frustum) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: OrthoProjection::get_frustum +// Access: Public +// Description: Returns the frustum that defines the perspective +// projection. +//////////////////////////////////////////////////////////////////// +INLINE const Frustumf &OrthoProjection:: +get_frustum() const { + return _frustum; +} + + + diff --git a/panda/src/gobj/orthoProjection.cxx b/panda/src/gobj/orthoProjection.cxx new file mode 100644 index 0000000000..e1aea6cdd5 --- /dev/null +++ b/panda/src/gobj/orthoProjection.cxx @@ -0,0 +1,129 @@ +// Filename: orthoProjection.cxx +// Created by: mike (18Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "geomLine.h" +#include "orthoProjection.h" + +#include + +TypeHandle OrthoProjection::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: OrthoProjection::make_copy +// Access: Public, Virtual +// Description: Allocates a new Projection just like this one. +//////////////////////////////////////////////////////////////////// +Projection *OrthoProjection:: +make_copy() const { + return new OrthoProjection(*this); +} + + +//////////////////////////////////////////////////////////////////// +// Function: OrthoProjection::get_projection_mat +// Access: Public, Virtual +// Description: This computes a transform matrix that performs the +// orthographic transform defined by the frustum. +//////////////////////////////////////////////////////////////////// +LMatrix4f OrthoProjection:: +get_projection_mat(CoordinateSystem cs) const { + return _frustum.get_ortho_projection_mat(cs); +} + +//////////////////////////////////////////////////////////////////// +// Function: OrthoProjection::make_geometry +// Access: Public, Virtual +// Description: Creates a GeomLine that describes the shape of the +// frustum for this projection +//////////////////////////////////////////////////////////////////// +Geom *OrthoProjection:: +make_geometry(const Colorf &color, + CoordinateSystem cs) const { + Vertexf rtn, ltn, lbn, rbn; + Vertexf rtf, ltf, lbf, rbf; + + // x, y, and z here refer to the right, forward, and up vectors, + // which are not necessarily the x, y, and z axes. + + LVector3f x = LVector3f::right(cs); + LVector3f y = LVector3f::forward(cs); + LVector3f z = LVector3f::up(cs); + LPoint3f o = LPoint3f::origin(cs); + + Vertexf xl = x * _frustum._l; + Vertexf xr = x * _frustum._r; + Vertexf zt = z * _frustum._t; + Vertexf zb = z * _frustum._b; + Vertexf yn = o + (y * _frustum._fnear); + + rtn = yn + zt + xr; + ltn = yn + zt + xl; + lbn = yn + zb + xl; + rbn = yn + zb + xr; + + float fs = _frustum._ffar / _frustum._fnear; + Vertexf yf = o + (y * _frustum._ffar); + + rtf = yf + ((zt + xr) * fs); + ltf = yf + ((zt + xl) * fs); + lbf = yf + ((zb + xl) * fs); + rbf = yf + ((zb + xr) * fs); + + PTA_Vertexf coords(0); + PTA_ushort vindex(0); + PTA_Colorf colors(0); + + // We just specify overall color + colors.push_back(color); + + coords.push_back(rtn); + coords.push_back(ltn); + coords.push_back(lbn); + coords.push_back(rbn); + coords.push_back(rtf); + coords.push_back(ltf); + coords.push_back(lbf); + coords.push_back(rbf); + coords.push_back(o); + + // Draw the near plane + vindex.push_back(0); vindex.push_back(1); + vindex.push_back(1); vindex.push_back(2); + vindex.push_back(2); vindex.push_back(3); + vindex.push_back(3); vindex.push_back(0); + + // Draw the far plane + vindex.push_back(4); vindex.push_back(5); + vindex.push_back(5); vindex.push_back(6); + vindex.push_back(6); vindex.push_back(7); + vindex.push_back(7); vindex.push_back(4); + + // Draw lines from eye to the corners + vindex.push_back(8); vindex.push_back(4); + vindex.push_back(8); vindex.push_back(5); + vindex.push_back(8); vindex.push_back(6); + vindex.push_back(8); vindex.push_back(7); + + GeomLine* gline = new GeomLine; + gline->set_coords(coords, G_PER_VERTEX, vindex); + gline->set_colors(colors, G_OVERALL); + gline->set_num_prims(12); + + return gline; +} + +//////////////////////////////////////////////////////////////////// +// Function: OrthoProjection::make_bounds +// Access: Public, Virtual +// Description: Allocates and returns a new BoundingVolume that +// encloses the frustum used for this kind of +// projection, if possible. If a suitable bounding +// volume cannot be created, returns NULL. +//////////////////////////////////////////////////////////////////// +BoundingVolume *OrthoProjection:: +make_bounds(CoordinateSystem cs) const { + return new BoundingHexahedron(_frustum, true, cs); +} diff --git a/panda/src/gobj/orthoProjection.h b/panda/src/gobj/orthoProjection.h new file mode 100644 index 0000000000..ba277ca9be --- /dev/null +++ b/panda/src/gobj/orthoProjection.h @@ -0,0 +1,58 @@ +// Filename: orthoProjection.h +// Created by: mike (18Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ORTHOPROJECTION_H +#define ORTHOPROJECTION_H + +#include + +#include "projection.h" +#include + + +//////////////////////////////////////////////////////////////////// +// Class : OrthoProjection +// Description : An orthographic-type projection, with a frustum. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA OrthoProjection : public Projection { +public: + INLINE OrthoProjection(const Frustumf &frustum); + virtual Projection *make_copy() const; + virtual LMatrix4f get_projection_mat(CoordinateSystem cs = CS_default) const; + + virtual Geom* make_geometry(const Colorf &color = Colorf(0.0, 1.0, 0.0, 1.0), + CoordinateSystem cs = CS_default) const; + + virtual BoundingVolume *make_bounds(CoordinateSystem cs = CS_default) const; + + INLINE const Frustumf &get_frustum() const; + + +protected: + Frustumf _frustum; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Projection::init_type(); + register_type(_type_handle, "OrthoProjection", + Projection::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "orthoProjection.I" + +#endif + + diff --git a/panda/src/gobj/perspectiveProjection.I b/panda/src/gobj/perspectiveProjection.I new file mode 100644 index 0000000000..c2fab31136 --- /dev/null +++ b/panda/src/gobj/perspectiveProjection.I @@ -0,0 +1,31 @@ +// Filename: perspectiveProjection.I +// Created by: drose (18Feb99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: PerspectiveProjection::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PerspectiveProjection:: +PerspectiveProjection(const Frustumf &frustum) : _frustum(frustum) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: PerspectiveProjection::get_frustum +// Access: Public +// Description: Returns the frustum that defines the perspective +// projection. +//////////////////////////////////////////////////////////////////// +INLINE const Frustumf &PerspectiveProjection:: +get_frustum() const { + return _frustum; +} + +INLINE void PerspectiveProjection:: +set_frustum(const Frustumf &frust) { + _frustum = frust; +} + diff --git a/panda/src/gobj/perspectiveProjection.cxx b/panda/src/gobj/perspectiveProjection.cxx new file mode 100644 index 0000000000..4385c22da7 --- /dev/null +++ b/panda/src/gobj/perspectiveProjection.cxx @@ -0,0 +1,168 @@ +// Filename: perspectiveProjection.cxx +// Created by: drose (18Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "geomLine.h" +#include "perspectiveProjection.h" + +#include + + +TypeHandle PerspectiveProjection::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: PerspectiveProjection::make_copy +// Access: Public, Virtual +// Description: Allocates a new Projection just like this one. +//////////////////////////////////////////////////////////////////// +Projection *PerspectiveProjection:: +make_copy() const { + return new PerspectiveProjection(*this); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PerspectiveProjection::get_projection_mat +// Access: Public, Virtual +// Description: This computes a transform matrix that performs the +// perspective transform defined by the frustum. +//////////////////////////////////////////////////////////////////// +LMatrix4f PerspectiveProjection:: +get_projection_mat(CoordinateSystem cs) const { + return _frustum.get_perspective_projection_mat(cs); +} + +//////////////////////////////////////////////////////////////////// +// Function: PerspectiveProjection::make_geometry +// Access: Public, Virtual +// Description: Creates a GeomLine that describes the shape of the +// frustum for this projection +//////////////////////////////////////////////////////////////////// +Geom *PerspectiveProjection:: +make_geometry(const Colorf &color, + CoordinateSystem cs) const { + Vertexf rtn, ltn, lbn, rbn; + Vertexf rtf, ltf, lbf, rbf; + + // x, y, and z here refer to the right, forward, and up vectors, + // which are not necessarily the x, y, and z axes. + + LVector3f x = LVector3f::right(cs); + LVector3f y = LVector3f::forward(cs); + LVector3f z = LVector3f::up(cs); + LPoint3f o = LPoint3f::origin(cs); + + Vertexf xl = x * _frustum._l; + Vertexf xr = x * _frustum._r; + Vertexf zt = z * _frustum._t; + Vertexf zb = z * _frustum._b; + Vertexf yn = o + (y * _frustum._fnear); + + rtn = yn + zt + xr; + ltn = yn + zt + xl; + lbn = yn + zb + xl; + rbn = yn + zb + xr; + + float fs = _frustum._ffar / _frustum._fnear; + Vertexf yf = o + (y * _frustum._ffar); + + rtf = yf + ((zt + xr) * fs); + ltf = yf + ((zt + xl) * fs); + lbf = yf + ((zb + xl) * fs); + rbf = yf + ((zb + xr) * fs); + + PTA_Vertexf coords(0); + PTA_ushort vindex(0); + PTA_Colorf colors(0); + + // We just specify overall color + colors.push_back(color); + + coords.push_back(rtn); + coords.push_back(ltn); + coords.push_back(lbn); + coords.push_back(rbn); + coords.push_back(rtf); + coords.push_back(ltf); + coords.push_back(lbf); + coords.push_back(rbf); + coords.push_back(o); + + // Draw the near plane + vindex.push_back(0); vindex.push_back(1); + vindex.push_back(1); vindex.push_back(2); + vindex.push_back(2); vindex.push_back(3); + vindex.push_back(3); vindex.push_back(0); + + // Draw the far plane + vindex.push_back(4); vindex.push_back(5); + vindex.push_back(5); vindex.push_back(6); + vindex.push_back(6); vindex.push_back(7); + vindex.push_back(7); vindex.push_back(4); + + // Draw lines from eye to the corners + vindex.push_back(8); vindex.push_back(4); + vindex.push_back(8); vindex.push_back(5); + vindex.push_back(8); vindex.push_back(6); + vindex.push_back(8); vindex.push_back(7); + + GeomLine* gline = new GeomLine; + gline->set_coords(coords, G_PER_VERTEX, vindex); + gline->set_colors(colors, G_OVERALL); + gline->set_num_prims(12); + + return gline; +} + +//////////////////////////////////////////////////////////////////// +// Function: PerspectiveProjection::make_bounds +// Access: Public, Virtual +// Description: Allocates and returns a new BoundingVolume that +// encloses the frustum used for this kind of +// projection, if possible. If a suitable bounding +// volume cannot be created, returns NULL. +//////////////////////////////////////////////////////////////////// +BoundingVolume *PerspectiveProjection:: +make_bounds(CoordinateSystem cs) const { + return new BoundingHexahedron(_frustum, false, cs); +} + +//////////////////////////////////////////////////////////////////// +// Function: PerspectiveProjection::extrude +// Access: Public, Virtual +// Description: Given a 2-d point in the range (-1,1) in both +// dimensions, where (0,0) is the center of the +// projection and (-1,-1) is the lower-left corner, +// compute the corresponding vector in space that maps +// to this point, if such a vector can be determined. +// Returns true if the vector is defined (in which case +// origin and direction are set to define the vector), +// or false otherwise. +//////////////////////////////////////////////////////////////////// +bool PerspectiveProjection:: +extrude(const LPoint2f &point2d, LPoint3f &origin, LVector3f &direction, + CoordinateSystem cs) const { + if (point2d[0] < -1 || point2d[0] > 1 || + point2d[1] < -1 || point2d[1] > 1) { + // The point is off the near plane. + return false; + } + + // Scale the point from (-1,1) to the range of the frustum. + + LPoint2f scaled(_frustum._l + 0.5 * (point2d[0] + 1.0) * + (_frustum._r - _frustum._l), + _frustum._b + 0.5 * (point2d[1] + 1.0) * + (_frustum._t - _frustum._b)); + + LVector3f near_vector = + LVector3f::rfu(scaled[0], _frustum._fnear, scaled[1], cs); + LVector3f far_vector = + near_vector * _frustum._ffar / _frustum._fnear; + + origin = LPoint3f::origin(cs) + near_vector; + direction = far_vector - near_vector; + return true; +} diff --git a/panda/src/gobj/perspectiveProjection.h b/panda/src/gobj/perspectiveProjection.h new file mode 100644 index 0000000000..abddbacd10 --- /dev/null +++ b/panda/src/gobj/perspectiveProjection.h @@ -0,0 +1,62 @@ +// Filename: perspectiveProjection.h +// Created by: drose (18Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PERSPECTIVEPROJECTION_H +#define PERSPECTIVEPROJECTION_H + +#include + +#include "projection.h" +#include + + +//////////////////////////////////////////////////////////////////// +// Class : PerspectiveProjection +// Description : A perspective-type projection, with a frustum. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PerspectiveProjection : public Projection { +public: + INLINE PerspectiveProjection(const Frustumf &frustum); + virtual Projection *make_copy() const; + virtual LMatrix4f get_projection_mat(CoordinateSystem cs = CS_default) const; + + virtual Geom* make_geometry(const Colorf &color = Colorf(0.0, 1.0, 0.0, 1.0), + CoordinateSystem cs = CS_default) const; + + virtual BoundingVolume *make_bounds(CoordinateSystem cs = CS_default) const; + + virtual bool extrude(const LPoint2f &point2d, + LPoint3f &origin, LVector3f &direction, + CoordinateSystem cs = CS_default) const; + + INLINE const Frustumf &get_frustum() const; + INLINE void set_frustum(const Frustumf &frust); + +protected: + Frustumf _frustum; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Projection::init_type(); + register_type(_type_handle, "PerspectiveProjection", + Projection::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "perspectiveProjection.I" + +#endif + + diff --git a/panda/src/gobj/pixelBuffer.I b/panda/src/gobj/pixelBuffer.I new file mode 100644 index 0000000000..5a24721b25 --- /dev/null +++ b/panda/src/gobj/pixelBuffer.I @@ -0,0 +1,379 @@ +// Filename: pixelBuffer.I +// Created by: drose (05Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PixelBuffer:: +PixelBuffer(void) : ImageBuffer() +{ + _xsize = 0; + _ysize = 0; + _xorg = 0; + _yorg = 0; + _border = 0; + _format = F_rgb; + _type = T_unsigned_byte; + _components = 3; + _component_width = 1; + _image = PTA_uchar(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PixelBuffer:: +PixelBuffer(int xsize, int ysize, int components, int component_width, + Type type, Format format) : + ImageBuffer() +{ + _xsize = xsize; + _ysize = ysize; + _xorg = 0; + _yorg = 0; + _border = 0; + _components = components; + _component_width = component_width; + _type = type; + _format = format; + _image = PTA_uchar(_xsize * _ysize * _components * _component_width); +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PixelBuffer:: +PixelBuffer(const PixelBuffer ©) : + _xsize(copy._xsize), + _ysize(copy._ysize), + _xorg(copy._xorg), + _yorg(copy._yorg), + _border(copy._border), + _components(copy._components), + _component_width(copy._component_width), + _format(copy._format), + _type(copy._type), + _image(copy._image) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PixelBuffer:: +operator = (const PixelBuffer ©) { + _xsize = copy._xsize; + _ysize = copy._ysize; + _xorg = copy._xorg; + _yorg = copy._yorg; + _border = copy._border; + _components = copy._components; + _component_width = copy._component_width; + _format = copy._format; + _type = copy._type; + _image = copy._image; +} + +//////////////////////////////////////////////////////////////////// +// Function: rgb_buffer +// Access: Public +// Description: Constructs a PixelBuffer suitable for RGB +//////////////////////////////////////////////////////////////////// +INLINE PixelBuffer PixelBuffer:: +rgb_buffer(int xsize, int ysize) { + return PixelBuffer(xsize, ysize, 3, sizeof(uchar), T_unsigned_byte, + F_rgb); +} + +//////////////////////////////////////////////////////////////////// +// Function: rgba_buffer +// Access: Public +// Description: Constructs a PixelBuffer suitable for RGBA +//////////////////////////////////////////////////////////////////// +INLINE PixelBuffer PixelBuffer:: +rgba_buffer(int xsize, int ysize) { + return PixelBuffer(xsize, ysize, 4, sizeof(uchar), T_unsigned_byte, + F_rgba); +} + +//////////////////////////////////////////////////////////////////// +// Function: depth_buffer +// Access: Public +// Description: Constructs a PixelBuffer suitable for depth maps +//////////////////////////////////////////////////////////////////// +INLINE PixelBuffer PixelBuffer:: +depth_buffer(int xsize, int ysize) { + return PixelBuffer(xsize, ysize, 1, sizeof(float), T_float, + F_depth_component); +} + +//////////////////////////////////////////////////////////////////// +// Function: stencil_buffer +// Access: Public +// Description: Constructs a PixelBuffer suitable for stencil buffers +//////////////////////////////////////////////////////////////////// +INLINE PixelBuffer PixelBuffer:: +stencil_buffer(int xsize, int ysize) { + return PixelBuffer(xsize, ysize, 1, sizeof(uchar), T_unsigned_byte, + F_stencil_index); +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PixelBuffer:: +~PixelBuffer(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::set_xsize +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PixelBuffer::set_xsize(int size) +{ + if (_xsize != size && size > 1) { + _xsize = size; + make_dirty(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::set_ysize +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PixelBuffer::set_ysize(int size) +{ + if (_ysize != size && size > 1) { + _ysize = size; + make_dirty(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::set_xorg +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PixelBuffer::set_xorg(int org) +{ + if (_xorg != org && org >= 0) { + _xorg = org; + make_dirty(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::set_yorg +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PixelBuffer::set_yorg(int org) +{ + if (_yorg != org && org >= 0) { + _yorg = org; + make_dirty(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::set_format +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PixelBuffer:: +set_format(PixelBuffer::Format format) +{ + if (_format != format) { + _format = format; + make_dirty(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::get_xsize +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int PixelBuffer:: +get_xsize() const { + return _xsize; +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::get_ysize +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int PixelBuffer:: +get_ysize() const { + return _ysize; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::get_xorg +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int PixelBuffer:: +get_xorg() const { + return _xorg; +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::get_yorg +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int PixelBuffer:: +get_yorg() const { + return _yorg; +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::get_border +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int PixelBuffer:: +get_border() const { + return _border; +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::get_num_components +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int PixelBuffer:: +get_num_components() const { + return _components; +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::get_component_width +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int PixelBuffer:: +get_component_width() const { + return _component_width; +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::get_format +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PixelBuffer::Format PixelBuffer:: +get_format() const { + return _format; +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::get_image_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PixelBuffer::Type PixelBuffer:: +get_image_type() const { + return _type; +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::set_uchar_rgb_texel +// Access: Public +// Description: This is only valid when the PixelBuffer is an RGB +// buffer with uchar components. +//////////////////////////////////////////////////////////////////// +INLINE void PixelBuffer:: +set_uchar_rgb_texel(const uchar color[3], int x, int y, int width) +{ + int i = y * 3 * width + x * 3; + _image[i] = color[0]; + _image[i+1] = color[1]; + _image[i+2] = color[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::store_unsigned_byte +// Access: Private +// Description: This is used by load() to store the next consecutive +// component value into the indicated element of the +// array, which is taken to be an array of unsigned +// bytes. +//////////////////////////////////////////////////////////////////// +INLINE void PixelBuffer:: +store_unsigned_byte(int &index, double value) { + nassertv(index >= 0 && index < _image.size()); + _image[index++] = (uchar)(value * 255.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::store_unsigned_short +// Access: Private +// Description: This is used by load() to store the next consecutive +// component value into the indicated element of the +// array, which is taken to be an array of unsigned +// shorts. +//////////////////////////////////////////////////////////////////// +INLINE void PixelBuffer:: +store_unsigned_short(int &index, double value) { + nassertv(index >= 0 && index+1 < _image.size()); + union { + ushort us; + uchar uc[2]; + } v; + v.us = (ushort)(value * 65535.0); + _image[index++] = v.uc[0]; + _image[index++] = v.uc[1]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::get_unsigned_byte +// Access: Private +// Description: This is used by store() to retieve the next +// consecutive component value from the indicated +// element of the array, which is taken to be an array +// of unsigned bytes. +//////////////////////////////////////////////////////////////////// +INLINE double PixelBuffer:: +get_unsigned_byte(int &index) const { + nassertr(index >= 0 && index < _image.size(), 0.0); + return (double)_image[index++] / 255.0; +} + +//////////////////////////////////////////////////////////////////// +// Function: PixelBuffer::get_unsigned_short +// Access: Private +// Description: This is used by store() to retieve the next +// consecutive component value from the indicated +// element of the array, which is taken to be an array +// of unsigned shorts. +//////////////////////////////////////////////////////////////////// +INLINE double PixelBuffer:: +get_unsigned_short(int &index) const { + nassertr(index >= 0 && index+1 < _image.size(), 0.0); + union { + ushort us; + uchar uc[2]; + } v; + v.uc[0] = _image[index++]; + v.uc[1] = _image[index++]; + return (double)v.us / 65535.0; +} diff --git a/panda/src/gobj/pixelBuffer.N b/panda/src/gobj/pixelBuffer.N new file mode 100644 index 0000000000..70bc9abb4f --- /dev/null +++ b/panda/src/gobj/pixelBuffer.N @@ -0,0 +1,3 @@ +ignoreinvolved Type +ignoreinvolved Format + diff --git a/panda/src/gobj/pixelBuffer.cxx b/panda/src/gobj/pixelBuffer.cxx new file mode 100644 index 0000000000..dfa0cdb7b5 --- /dev/null +++ b/panda/src/gobj/pixelBuffer.cxx @@ -0,0 +1,274 @@ +// Filename: pixelBuffer.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "pixelBuffer.h" +#include "config_gobj.h" + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle PixelBuffer::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: config +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void PixelBuffer::config(void) +{ + ImageBuffer::config(); +} + +//////////////////////////////////////////////////////////////////// +// Function: read +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +bool PixelBuffer::read(const string& name) +{ + PNMImage pnmimage; + + if (!pnmimage.read(name)) { + gobj_cat.error() + << "PixelBuffer::read() - couldn't read: " << name << endl; + return false; + } + + set_name(name); + return load(pnmimage); +} + +//////////////////////////////////////////////////////////////////// +// Function: write +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +bool PixelBuffer::write( const string& name ) const +{ + PNMImage pnmimage; + if (!store(pnmimage)) { + return false; + } + + string tname; + if (name.empty()) { + tname = get_name(); + } else { + tname = name; + } + + if (!pnmimage.write(tname)) { + gobj_cat.error() + << "PixelBuffer::write() - couldn't write: " << name << endl; + return false; + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: read +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +bool PixelBuffer::load(const PNMImage& pnmimage) +{ + _xsize = pnmimage.get_x_size(); + _ysize = pnmimage.get_y_size(); + _components = pnmimage.get_num_channels(); + + xelval maxval = pnmimage.get_maxval(); + + // Come up with a default format based on the number of channels. + switch (pnmimage.get_color_type()) { + case PNMImage::CT_grayscale: + _format = F_luminance; + break; + + case PNMImage::CT_two_channel: + _format = F_luminance_alpha; + break; + + case PNMImage::CT_color: + // Choose a suitable default format based on the depth of the + // image components. + if (maxval > 255) { + _format = F_rgb12; + } else if (maxval > 31) { + _format = F_rgb8; + } else { + _format = F_rgb5; + } + break; + + case PNMImage::CT_four_channel: + // Choose a suitable default format based on the depth of the + // image components. + if (maxval > 255) { + _format = F_rgba12; + } else if (maxval > 15) { + _format = F_rgba8; + } else { + _format = F_rgba4; + } + break; + + default: + // Eh? + nassertr(false, false); + _format = F_rgb; + }; + + bool has_alpha = pnmimage.has_alpha(); + bool is_grayscale = pnmimage.is_grayscale(); + + if (maxval > 255) { + // Wide pixels; we need to use a short for each component. + _type = T_unsigned_short; + _image = PTA_uchar(_xsize * _ysize * _components * 2); + int idx = 0; + + for (int j = _ysize-1; j >= 0; j--) { + for (int i = 0; i < _xsize; i++) { + if (is_grayscale) { + store_unsigned_short(idx, pnmimage.get_gray(i, j)); + } else { + store_unsigned_short(idx, pnmimage.get_red(i, j)); + store_unsigned_short(idx, pnmimage.get_green(i, j)); + store_unsigned_short(idx, pnmimage.get_blue(i, j)); + } + if (has_alpha) { + store_unsigned_short(idx, pnmimage.get_alpha(i, j)); + } + } + } + } else { + // Normal pixels: a byte per component will do. + _type = T_unsigned_byte; + _image = PTA_uchar(_xsize * _ysize * _components); + int idx = 0; + + for (int j = _ysize-1; j >= 0; j--) { + for (int i = 0; i < _xsize; i++) { + if (is_grayscale) { + store_unsigned_byte(idx, pnmimage.get_gray(i, j)); + } else { + store_unsigned_byte(idx, pnmimage.get_red(i, j)); + store_unsigned_byte(idx, pnmimage.get_green(i, j)); + store_unsigned_byte(idx, pnmimage.get_blue(i, j)); + } + if (has_alpha) { + store_unsigned_byte(idx, pnmimage.get_alpha(i, j)); + } + } + } + } + + config(); + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: store +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +bool PixelBuffer:: +store(PNMImage &pnmimage) const { + if (_type == T_unsigned_byte) { + pnmimage.clear(_xsize, _ysize, _components); + bool has_alpha = pnmimage.has_alpha(); + bool is_grayscale = pnmimage.is_grayscale(); + + int idx = 0; + for (int j = _ysize-1; j >= 0; j--) { + for (int i = 0; i < _xsize; i++) { + if (is_grayscale) { + pnmimage.set_gray(i, j, get_unsigned_byte(idx)); + } else { + pnmimage.set_red(i, j, get_unsigned_byte(idx)); + pnmimage.set_green(i, j, get_unsigned_byte(idx)); + pnmimage.set_blue(i, j, get_unsigned_byte(idx)); + } + if (has_alpha) + pnmimage.set_alpha(i, j, get_unsigned_byte(idx)); + } + } + return true; + + } else if (_type == T_unsigned_short) { + pnmimage.clear(_xsize, _ysize, _components, 65535); + bool has_alpha = pnmimage.has_alpha(); + bool is_grayscale = pnmimage.is_grayscale(); + + int idx = 0; + for (int j = _ysize-1; j >= 0; j--) { + for (int i = 0; i < _xsize; i++) { + if (is_grayscale) { + pnmimage.set_gray(i, j, get_unsigned_short(idx)); + } else { + pnmimage.set_red(i, j, get_unsigned_short(idx)); + pnmimage.set_green(i, j, get_unsigned_short(idx)); + pnmimage.set_blue(i, j, get_unsigned_short(idx)); + } + if (has_alpha) + pnmimage.set_alpha(i, j, get_unsigned_short(idx)); + } + } + return true; + } + + gobj_cat.error() + << "Couldn't write image for " << get_name() + << "; inappropriate type " << _type << ".\n"; + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: copy +// Access: +// Description: Deep copy of pixel buffer +//////////////////////////////////////////////////////////////////// +void PixelBuffer:: +copy(const PixelBuffer *pb) { + nassertv(pb != NULL); + _xorg = pb->_xorg; + _yorg = pb->_yorg; + _xsize = pb->_xsize; + _ysize = pb->_ysize; + _border = pb->_border; + _components = pb->_components; + _format = pb->_format; + _image = PTA_uchar(0); + if (!pb->_image.empty()) + _image.v() = pb->_image.v(); +} + +void PixelBuffer::copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr) { + gsg->copy_pixel_buffer(this, dr); +} + +void PixelBuffer::copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr, + const RenderBuffer &rb) { + gsg->copy_pixel_buffer(this, dr, rb); +} + +void PixelBuffer::draw(GraphicsStateGuardianBase *) { + gobj_cat.error() + << "DisplayRegion required to draw pixel buffer.\n"; +} + +void PixelBuffer::draw(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr) { + gsg->draw_pixel_buffer(this, dr); +} + +void PixelBuffer::draw(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr, + const RenderBuffer &rb) { + gsg->draw_pixel_buffer(this, dr, rb); +} + diff --git a/panda/src/gobj/pixelBuffer.h b/panda/src/gobj/pixelBuffer.h new file mode 100644 index 0000000000..0111b898d2 --- /dev/null +++ b/panda/src/gobj/pixelBuffer.h @@ -0,0 +1,158 @@ +// Filename: pixelBuffer.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef PIXELBUFFER_H +#define PIXELBUFFER_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "imageBuffer.h" + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +class RenderBuffer; + +//////////////////////////////////////////////////////////////////// +// Class : PixelBuffer +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PixelBuffer : public ImageBuffer { +public: + enum Type { + T_unsigned_byte, + T_unsigned_short, + T_unsigned_byte_332, + T_float, + }; + + enum Format { + F_color_index, + F_stencil_index, + F_depth_component, + F_red, + F_green, + F_blue, + F_alpha, + F_rgb, + F_rgb5, // specifically, 5 bits per R,G,B channel + F_rgb8, // 8 bits per R,G,B channel + F_rgb12, // 12 bits per R,G,B channel + F_rgb332, // 3 bits per R & G, 2 bits for B + F_rgba, + F_rgba4, // 4 bits per R,G,B,A channel + F_rgba5, // 5 bits per R,G,B channel, 1 bit alpha + F_rgba8, // 8 bits per R,G,B,A channel + F_rgba12, // 12 bits per R,G,B,A channel + F_luminance, + F_luminance_alpha + }; + + INLINE PixelBuffer(void); + INLINE PixelBuffer(int xsize, int ysize, int components, + int component_width, Type type, Format format); + INLINE PixelBuffer(const PixelBuffer ©); + INLINE void operator = (const PixelBuffer ©); + + // Some named constructors for common PixelBuffer types. + INLINE static PixelBuffer rgb_buffer(int xsize, int ysize); + INLINE static PixelBuffer rgba_buffer(int xsize, int ysize); + INLINE static PixelBuffer depth_buffer(int xsize, int ysize); + INLINE static PixelBuffer stencil_buffer(int xsize, int ysize); + + INLINE ~PixelBuffer(void); + + virtual void config( void ); + + virtual bool read( const string& name ); + virtual bool write( const string& name = "" ) const; + + bool load( const PNMImage& pnmimage ); + bool store( PNMImage& pnmimage ) const; + + void copy(const PixelBuffer *pb); + + virtual void copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr); + virtual void copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr, + const RenderBuffer &rb); + virtual void draw(GraphicsStateGuardianBase *gsg); + virtual void draw(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr); + virtual void draw(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr, + const RenderBuffer &rb); + + INLINE void set_xsize(int size); + INLINE void set_ysize(int size); + INLINE void set_xorg(int org); + INLINE void set_yorg(int org); + INLINE void set_format(Format format); + + INLINE int get_xsize() const; + INLINE int get_ysize() const; + INLINE int get_xorg() const; + INLINE int get_yorg() const; + INLINE int get_border() const; + INLINE int get_num_components() const; + INLINE int get_component_width() const; + INLINE Format get_format() const; + INLINE Type get_image_type() const; + + INLINE void set_uchar_rgb_texel(const uchar color[3], + int x, int y, int width); + +private: + INLINE void store_unsigned_byte(int &index, double value); + INLINE void store_unsigned_short(int &index, double value); + INLINE double get_unsigned_byte(int &index) const; + INLINE double get_unsigned_short(int &index) const; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ImageBuffer::init_type(); + register_type(_type_handle, "PixelBuffer", + ImageBuffer::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + + static TypeHandle _type_handle; + + //////////////////////////////////////////////////////////////////// + +protected: + int _xsize; + int _ysize; + int _xorg; + int _yorg; + int _border; + int _components; + int _component_width; + Format _format; + Type _type; + +public: + // This is public to allow direct manipulation of the image data. + // Know what you are doing! + PTA_uchar _image; +}; + +#include "pixelBuffer.I" + +#endif diff --git a/panda/src/gobj/projection.cxx b/panda/src/gobj/projection.cxx new file mode 100644 index 0000000000..3ae35dc3ad --- /dev/null +++ b/panda/src/gobj/projection.cxx @@ -0,0 +1,52 @@ +// Filename: projection.cxx +// Created by: drose (18Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "projection.h" + +TypeHandle Projection::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: Projection::make_geometry +// Access: Public, Virtual +// Description: Allocates and returns a new Geom that can be rendered +// to show a visible representation of the frustum used +// for this kind of projection, if it makes sense to do +// so. If a visible representation cannot be created, +// returns NULL. +//////////////////////////////////////////////////////////////////// +Geom *Projection:: +make_geometry(const Colorf &, CoordinateSystem) const { + return (Geom *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: Projection::make_bounds +// Access: Public, Virtual +// Description: Allocates and returns a new BoundingVolume that +// encloses the frustum used for this kind of +// projection, if possible. If a suitable bounding +// volume cannot be created, returns NULL. +//////////////////////////////////////////////////////////////////// +BoundingVolume *Projection:: +make_bounds(CoordinateSystem) const { + return (BoundingVolume *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: Projection::extrude +// Access: Public, Virtual +// Description: Given a 2-d point in the range (-1,1) in both +// dimensions, where (0,0) is the center of the +// projection and (-1,-1) is the lower-left corner, +// compute the corresponding vector in space that maps +// to this point, if such a vector can be determined. +// Returns true if the vector is defined (in which case +// origin and direction are set to define the vector), +// or false otherwise. +//////////////////////////////////////////////////////////////////// +bool Projection:: +extrude(const LPoint2f &, LPoint3f &, LVector3f &, CoordinateSystem) const { + return false; +} diff --git a/panda/src/gobj/projection.h b/panda/src/gobj/projection.h new file mode 100644 index 0000000000..3ead5fb9a3 --- /dev/null +++ b/panda/src/gobj/projection.h @@ -0,0 +1,55 @@ +// Filename: projection.h +// Created by: drose (18Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PROJECTION_H +#define PROJECTION_H + +#include + +#include +#include +#include "geom.h" + +class BoundingVolume; + +//////////////////////////////////////////////////////////////////// +// Class : Projection +// Description : A base class for any number of different kinds of +// linear projections. Presently, this includes +// perspective and orthographic projections. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Projection : public TypedReferenceCount { +public: + virtual LMatrix4f get_projection_mat(CoordinateSystem cs = CS_default) const=0; + virtual Projection *make_copy() const=0; + virtual Geom* make_geometry(const Colorf &color = Colorf(0.0, 1.0, 0.0, 1.0), + CoordinateSystem cs = CS_default) const; + + virtual BoundingVolume *make_bounds(CoordinateSystem cs = CS_default) const; + + virtual bool extrude(const LPoint2f &point2d, + LPoint3f &origin, LVector3f &direction, + CoordinateSystem cs = CS_default) const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "Projection", + TypedReferenceCount::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#endif + diff --git a/panda/src/gobj/test_gobj.cxx b/panda/src/gobj/test_gobj.cxx new file mode 100644 index 0000000000..43679311c0 --- /dev/null +++ b/panda/src/gobj/test_gobj.cxx @@ -0,0 +1,17 @@ +// Filename: test_gobj.cxx +// Created by: shochet (02Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "geom.h" +#include "perspectiveProjection.h" + +int main() { + nout << "running test_gobj" << endl; + PT(GeomTri) triangle = new GeomTri; + Frustumf frust; + PT(PerspectiveProjection) proj = new PerspectiveProjection(frust); + LMatrix4f mat = proj->get_projection_mat(); + nout << "default proj matrix: " << mat; + return 0; +} diff --git a/panda/src/gobj/texture.I b/panda/src/gobj/texture.I new file mode 100644 index 0000000000..f746bb5cc1 --- /dev/null +++ b/panda/src/gobj/texture.I @@ -0,0 +1,66 @@ +// Filename: texture.I +// Created by: drose (05Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: Texture::apply +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Texture:: +apply(GraphicsStateGuardianBase *gsg) { + gsg->apply_texture(prepare(gsg)); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Texture::get_level +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int Texture:: +get_level() const { + return _level; +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::get_wrapu +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Texture::WrapMode Texture:: +get_wrapu() const { + return _wrapu; +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::get_wrapv +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Texture::WrapMode Texture:: +get_wrapv() const { + return _wrapv; +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::get_minfilter +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Texture::FilterType Texture:: +get_minfilter() const { + return _minfilter; +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::get_magfilter +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Texture::FilterType Texture:: +get_magfilter() const { + return _magfilter; +} diff --git a/panda/src/gobj/texture.N b/panda/src/gobj/texture.N new file mode 100644 index 0000000000..66007cb6c9 --- /dev/null +++ b/panda/src/gobj/texture.N @@ -0,0 +1,2 @@ +ignoreinvolved FilterType +ignoreinvolved WrapMode diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx new file mode 100644 index 0000000000..3ea2da817c --- /dev/null +++ b/panda/src/gobj/texture.cxx @@ -0,0 +1,379 @@ +// Filename: texture.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include +#include "texture.h" +#include "config_gobj.h" + +#include +#include +#include +#include +#include + +//Should this be here? +#include "texturePool.h" + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle Texture::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +Texture:: +Texture() : ImageBuffer() { + _level = 0; // Mipmap level + _magfilter = FT_nearest; + _minfilter = FT_nearest; + _wrapu = WM_repeat; + _wrapv = WM_repeat; + _pbuffer = new PixelBuffer; + _has_requested_size = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: Destructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +Texture:: +~Texture() { + unprepare(); +} + +//////////////////////////////////////////////////////////////////// +// Function: read +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +bool Texture::read(const string& name) +{ + PNMImage pnmimage; + + if (!pnmimage.read(name)) { + gobj_cat.error() + << "Texture::read() - couldn't read: " << name << endl; + return false; + } + + if (max_texture_dimension > 0 && + (pnmimage.get_x_size() > max_texture_dimension || + pnmimage.get_y_size() > max_texture_dimension)) { + int new_xsize = min(pnmimage.get_x_size(), max_texture_dimension); + int new_ysize = min(pnmimage.get_y_size(), max_texture_dimension); + gobj_cat.info() + << "Automatically rescaling " << name << " from " + << pnmimage.get_x_size() << " by " << pnmimage.get_y_size() << " to " + << new_xsize << " by " << new_ysize << "\n"; + + PNMImage scaled(new_xsize, new_ysize, pnmimage.get_num_channels(), + pnmimage.get_maxval(), pnmimage.get_type()); + scaled.gaussian_filter_from(0.5, pnmimage); + pnmimage = scaled; + } + + set_name(name); + return load(pnmimage); +} + +//////////////////////////////////////////////////////////////////// +// Function: write +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +bool Texture::write(const string& name) const +{ + return _pbuffer->write(name); +} + +//////////////////////////////////////////////////////////////////// +// Function: load +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +bool Texture::load(const PNMImage& pnmimage) +{ + if (_pbuffer->load( pnmimage ) == false) + return false; + + unprepare(); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: store +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +bool Texture::store(PNMImage& pnmimage) const +{ + return _pbuffer->store( pnmimage ); +} + +//////////////////////////////////////////////////////////////////// +// Function: prepare +// Access: Public +// Description: Creates a context for the texture on the particular +// GSG, if it does not already exist. Returns the new +// (or old) TextureContext. +//////////////////////////////////////////////////////////////////// +TextureContext *Texture:: +prepare(GraphicsStateGuardianBase *gsg) { + Contexts::const_iterator ci; + ci = _contexts.find(gsg); + if (ci != _contexts.end()) { + return (*ci).second; + } + + TextureContext *tc = gsg->prepare_texture(this); + _contexts[gsg] = tc; + return tc; +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::unprepare +// Access: Public +// Description: Frees the context allocated on all GSG's for which +// the texture has been declared. +//////////////////////////////////////////////////////////////////// +void Texture:: +unprepare() { + // We have to traverse a copy of the _contexts list, because the GSG + // will call clear_gsg() in response to each release_texture(), and + // we don't want to be modifying the _contexts list while we're + // traversing it. + Contexts temp = _contexts; + + Contexts::const_iterator ci; + for (ci = temp.begin(); ci != temp.end(); ++ci) { + GraphicsStateGuardianBase *gsg = (*ci).first; + TextureContext *tc = (*ci).second; + gsg->release_texture(tc); + } + + // Now that we've called release_texture() on every known GSG, the + // _contexts list should have completely emptied itself. + nassertv(_contexts.empty()); +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::unprepare +// Access: Public +// Description: Frees the texture context only on the indicated GSG, +// if it exists there. +//////////////////////////////////////////////////////////////////// +void Texture:: +unprepare(GraphicsStateGuardianBase *gsg) { + Contexts::iterator ci; + ci = _contexts.find(gsg); + if (ci != _contexts.end()) { + TextureContext *tc = (*ci).second; + gsg->release_texture(tc); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::clear_gsg +// Access: Public +// Description: Removes the indicated GSG from the Texture's known +// GSG's, without actually releasing the texture on that +// GSG. This is intended to be called only from +// GSG::release_texture(); it should never be called by +// user code. +//////////////////////////////////////////////////////////////////// +void Texture:: +clear_gsg(GraphicsStateGuardianBase *gsg) { + Contexts::iterator ci; + ci = _contexts.find(gsg); + if (ci != _contexts.end()) { + TextureContext *tc = (*ci).second; + _contexts.erase(ci); + } else { + // If this assertion fails, clear_gsg() was called on a GSG which + // the texture didn't know about. + nassertv(false); + } +} + +void Texture::copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr) { + gsg->copy_texture(prepare(gsg), dr); +} + +void Texture::copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr, + const RenderBuffer &rb) { + gsg->copy_texture(prepare(gsg), dr, rb); +} + +void Texture::draw(GraphicsStateGuardianBase *) { + gobj_cat.error() + << "DisplayRegion required to draw texture.\n"; +} + +void Texture::draw(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr) { + gsg->draw_texture(prepare(gsg), dr); +} + +void Texture::draw(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr, + const RenderBuffer &rb) { + gsg->draw_texture(prepare(gsg), dr, rb); +} + +//////////////////////////////////////////////////////////////////// +// Function: set_wrapu +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void Texture::set_wrapu(WrapMode wrap) +{ + if (_wrapu != wrap) { + unprepare(); + _wrapu = wrap; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: set_wrapv +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void Texture::set_wrapv(WrapMode wrap) +{ + if (_wrapv != wrap) { + unprepare(); + _wrapv = wrap; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: set_minfilter +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void Texture::set_minfilter(FilterType filter) +{ + if (_minfilter != filter) { + unprepare(); + _minfilter = filter; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: set_magfilter +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void Texture::set_magfilter(FilterType filter) +{ + if (_magfilter != filter) { + unprepare(); + _magfilter = filter; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void Texture:: +write_datagram(BamWriter *manager, Datagram &me) +{ + ImageBuffer::write_datagram(manager, me); + me.add_uint32(_level); + me.add_uint8(_wrapu); + me.add_uint8(_wrapv); + me.add_uint8(_minfilter); + me.add_uint8(_magfilter); + me.add_uint8(_magfiltercolor); + me.add_uint8(_magfilteralpha); +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void Texture:: +fillin(DatagramIterator& scan, BamReader*) +{ + //We don't want to call ImageBuffer::fillin, like we + //would normally, since due to needing to know the name + //of the Texture before creating it, we have already read + //that name in. This is something of a problem as it forces + //Texture to know how the parent write_datagram works. And + //makes the assumption that the only data being written is + //the name + _level = scan.get_uint32(); + _wrapu = (enum WrapMode) scan.get_uint8(); + _wrapv = (enum WrapMode) scan.get_uint8(); + _minfilter = (enum FilterType) scan.get_uint8(); + _magfilter = (enum FilterType) scan.get_uint8(); + _magfiltercolor = (enum FilterType) scan.get_uint8(); + _magfilteralpha = (enum FilterType) scan.get_uint8(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::make_Texture +// Access: Protected +// Description: Factory method to generate a Texture object +//////////////////////////////////////////////////////////////////// +TypedWriteable* Texture:: +make_Texture(const FactoryParams ¶ms) +{ + //The process of making a texture is slightly + //different than making other Writeable objects. + //That is because all creation of Textures should + //be done through calls to TexturePool, which ensures + //that any loads of the same Texture, refer to the + //same memory + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + string name = scan.get_string(); + PT(Texture) me = TexturePool::load_texture(name); + if (me == (Texture *)NULL) { + // Oops, we couldn't load the texture; we'll just return NULL. + // But we do need a dummy texture to read in and ignore all of the + // attributes. + PT(Texture) dummy = new Texture; + dummy->fillin(scan, manager); + + } else { + if (gobj_cat->is_debug()) { + gobj_cat->debug() << "Created texture " << me->get_name() << endl; + } + me->fillin(scan, manager); + } + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a Texture object +//////////////////////////////////////////////////////////////////// +void Texture:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_Texture); +} + + + diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h new file mode 100644 index 0000000000..9902af67ad --- /dev/null +++ b/panda/src/gobj/texture.h @@ -0,0 +1,152 @@ +// Filename: texture.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef TEXTURE_H +#define TEXTURE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "imageBuffer.h" +#include "pixelBuffer.h" +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +class PNMImage; + +//////////////////////////////////////////////////////////////////// +// Class : Texture +// Description : 2D texture class +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Texture : public ImageBuffer { +public: + enum FilterType { + // Mag Filter and Min Filter + FT_nearest, + FT_linear, + + // Min Filter Only + FT_nearest_mipmap_nearest, + FT_linear_mipmap_nearest, + FT_nearest_mipmap_linear, + FT_linear_mipmap_linear, + }; + + enum WrapMode { + WM_clamp, + WM_repeat, + }; + + Texture(); + ~Texture(); + + virtual bool read( const string& name ); + virtual bool write( const string& name = "" ) const; + + bool load( const PNMImage& pnmimage ); + bool store( PNMImage& pnmimage ) const; + + TextureContext *prepare(GraphicsStateGuardianBase *gsg); + void unprepare(); + void unprepare(GraphicsStateGuardianBase *gsg); + void clear_gsg(GraphicsStateGuardianBase *gsg); + + INLINE void apply( GraphicsStateGuardianBase *gsg ); + + virtual void copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr); + virtual void copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr, + const RenderBuffer &rb); + virtual void draw(GraphicsStateGuardianBase *gsg); + virtual void draw(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr); + virtual void draw(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr, + const RenderBuffer &rb); + + INLINE bool has_ram_image(void) const { + return !_pbuffer->_image.empty(); + } + + void set_wrapu( WrapMode wrap ); + void set_wrapv( WrapMode wrap ); + void set_minfilter( FilterType filter ); + void set_magfilter( FilterType filter ); + + INLINE int get_level() const; + INLINE WrapMode get_wrapu() const; + INLINE WrapMode get_wrapv() const; + INLINE FilterType get_minfilter() const; + INLINE FilterType get_magfilter() const; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + + static TypedWriteable *make_Texture(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ImageBuffer::init_type(); + register_type(_type_handle, "Texture", + ImageBuffer::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + + static TypeHandle _type_handle; + + //////////////////////////////////////////////////////////////////// + +protected: + + int _level; + WrapMode _wrapu; + WrapMode _wrapv; + FilterType _minfilter; + FilterType _magfilter; + FilterType _magfiltercolor; + FilterType _magfilteralpha; + + // A Texture keeps a list (actually, a map) of all the GSG's that it + // has been prepared into. Each GSG conversely keeps a list (a set) + // of all the Texture's that have been prepared there. When either + // destructs, it removes itself from the other's list. + typedef map Contexts; + Contexts _contexts; + + + // These are public to allow direct manipulation of the underlying + // pixel buffer when needed. Know what you are doing! +public: + PT(PixelBuffer) _pbuffer; + + // If you request a region from the framebuffer that is not a power of 2, + // we need to grab a larger region that is a power of 2 that contains the + // requested region and set the pixel buffer size accordingly. We store + // the size you requested in the members below. + bool _has_requested_size; + int _requested_w; + int _requested_h; +}; + +#include "texture.I" + +#endif + diff --git a/panda/src/gobj/texturePool.I b/panda/src/gobj/texturePool.I new file mode 100644 index 0000000000..59b0909e35 --- /dev/null +++ b/panda/src/gobj/texturePool.I @@ -0,0 +1,98 @@ +// Filename: texturePool.I +// Created by: drose (26Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TexturePool::has_texture +// Access: Public, Static +// Description: Returns true if the texture has ever been loaded, +// false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool TexturePool:: +has_texture(const string &filename) { + return get_ptr()->ns_has_texture(filename); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexturePool::verify_texture +// Access: Public, Static +// Description: Loads the given filename up into a texture, if it has +// not already been loaded, and returns true to indicate +// success, or false to indicate failure. If this +// returns true, it is guaranteed that a subsequent call +// to load_texture() with the same texture name will +// return a valid Texture pointer. +//////////////////////////////////////////////////////////////////// +INLINE bool TexturePool:: +verify_texture(const string &filename) { + return load_texture(filename) != (Texture *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexturePool::load_texture +// Access: Public, Static +// Description: Loads the given filename up into a texture, if it has +// not already been loaded, and returns the new texture. +// If a texture with the same filename was previously +// loaded, returns that one instead. If the texture +// file cannot be found, returns NULL. +//////////////////////////////////////////////////////////////////// +INLINE Texture *TexturePool:: +load_texture(const string &filename) { + return get_ptr()->ns_load_texture(filename); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexturePool::add_texture +// Access: Public, Static +// Description: Adds the indicated already-loaded texture to the +// pool. The texture must have a filename set for its +// name. The texture will always replace any +// previously-loaded texture in the pool that had the +// same filename. +//////////////////////////////////////////////////////////////////// +INLINE void TexturePool:: +add_texture(Texture *texture) { + get_ptr()->ns_add_texture(texture); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexturePool::release_texture +// Access: Public, Static +// Description: Removes the indicated texture from the pool, +// indicating it will never be loaded again; the texture +// may then be freed. If this function is never called, +// a reference count will be maintained on every texture +// every loaded, and textures will never be freed. +// +// The texture's name should not have been changed +// during its lifetime, or this function may fail to +// locate it in the pool. +//////////////////////////////////////////////////////////////////// +INLINE void TexturePool:: +release_texture(Texture *texture) { + get_ptr()->ns_release_texture(texture); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexturePool::release_all_textures +// Access: Public, Static +// Description: Releases all textures in the pool and restores the +// pool to the empty state. +//////////////////////////////////////////////////////////////////// +INLINE void TexturePool:: +release_all_textures() { + get_ptr()->ns_release_all_textures(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexturePool::Constructor +// Access: Private +// Description: The constructor is not intended to be called +// directly; there's only supposed to be one TexturePool +// in the universe and it constructs itself. +//////////////////////////////////////////////////////////////////// +INLINE TexturePool:: +TexturePool() { +} diff --git a/panda/src/gobj/texturePool.cxx b/panda/src/gobj/texturePool.cxx new file mode 100644 index 0000000000..e063511294 --- /dev/null +++ b/panda/src/gobj/texturePool.cxx @@ -0,0 +1,117 @@ +// Filename: texturePool.cxx +// Created by: drose (26Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "texturePool.h" +#include "config_gobj.h" +#include "config_util.h" + + +TexturePool *TexturePool::_global_ptr = (TexturePool *)NULL; + + +//////////////////////////////////////////////////////////////////// +// Function: TexturePool::ns_has_texture +// Access: Private +// Description: The nonstatic implementation of has_texture(). +//////////////////////////////////////////////////////////////////// +bool TexturePool:: +ns_has_texture(Filename filename) { + filename.resolve_filename(get_texture_path()); + filename.resolve_filename(get_model_path()); + + Textures::const_iterator ti; + ti = _textures.find(filename); + if (ti != _textures.end()) { + // This texture was previously loaded. + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexturePool::ns_load_texture +// Access: Private +// Description: The nonstatic implementation of load_texture(). +//////////////////////////////////////////////////////////////////// +Texture *TexturePool:: +ns_load_texture(Filename filename) { + filename.resolve_filename(get_texture_path()); + filename.resolve_filename(get_model_path()); + + Textures::const_iterator ti; + ti = _textures.find(filename); + if (ti != _textures.end()) { + // This texture was previously loaded. + return (*ti).second; + } + + gobj_cat.info() + << "Loading texture " << filename << "\n"; + PT(Texture) tex = new Texture; + if (!tex->read(filename)) { + // This texture was not found. + gobj_cat.error() << "Unable to read texture " << filename << "\n"; + return NULL; + } + + _textures[filename] = tex; + return tex; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexturePool::ns_add_texture +// Access: Private +// Description: The nonstatic implementation of add_texture(). +//////////////////////////////////////////////////////////////////// +void TexturePool:: +ns_add_texture(Texture *tex) { + string filename = tex->get_name(); + if (filename.empty()) { + gobj_cat.error() << "Attempt to call add_texture() on an unnamed texture.\n"; + } + + // We blow away whatever texture was there previously, if any. + _textures[filename] = tex; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexturePool::ns_release_texture +// Access: Private +// Description: The nonstatic implementation of release_texture(). +//////////////////////////////////////////////////////////////////// +void TexturePool:: +ns_release_texture(Texture *tex) { + string filename = tex->get_name(); + Textures::iterator ti; + ti = _textures.find(filename); + if (ti != _textures.end() && (*ti).second == tex) { + _textures.erase(ti); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TexturePool::ns_release_all_textures +// Access: Private +// Description: The nonstatic implementation of release_all_textures(). +//////////////////////////////////////////////////////////////////// +void TexturePool:: +ns_release_all_textures() { + _textures.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexturePool::get_ptr +// Access: Private, Static +// Description: Initializes and/or returns the global pointer to the +// one TexturePool object in the system. +//////////////////////////////////////////////////////////////////// +TexturePool *TexturePool:: +get_ptr() { + if (_global_ptr == (TexturePool *)NULL) { + _global_ptr = new TexturePool; + } + return _global_ptr; +} diff --git a/panda/src/gobj/texturePool.h b/panda/src/gobj/texturePool.h new file mode 100644 index 0000000000..c9a0ee0421 --- /dev/null +++ b/panda/src/gobj/texturePool.h @@ -0,0 +1,57 @@ +// Filename: texturePool.h +// Created by: drose (26Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TEXTUREPOOL_H +#define TEXTUREPOOL_H + +#include + +#include "texture.h" + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : TexturePool +// Description : This is the preferred interface for loading textures +// from image files. It unifies all references to the +// same filename, so that multiple models that reference +// the same textures don't waste texture memory +// unnecessarily. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TexturePool { +public: + // These functions take string parameters instead of Filenames + // because that's somewhat more convenient to the scripting + // language. + INLINE static bool has_texture(const string &filename); + INLINE static bool verify_texture(const string &filename); + INLINE static Texture *load_texture(const string &filename); + INLINE static void add_texture(Texture *texture); + INLINE static void release_texture(Texture *texture); + INLINE static void release_all_textures(); + +private: + INLINE TexturePool(); + + bool ns_has_texture(Filename filename); + Texture *ns_load_texture(Filename filename); + void ns_add_texture(Texture *texture); + void ns_release_texture(Texture *texture); + void ns_release_all_textures(); + + static TexturePool *get_ptr(); + + static TexturePool *_global_ptr; + typedef map Textures; + Textures _textures; +}; + +#include "texturePool.I" + +#endif + + diff --git a/panda/src/graph/Sources.pp b/panda/src/graph/Sources.pp new file mode 100644 index 0000000000..d02b02a953 --- /dev/null +++ b/panda/src/graph/Sources.pp @@ -0,0 +1,100 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET graph + #define LOCAL_LIBS \ + putil mathutil + + #define SOURCES \ + allAttributesWrapper.I allAttributesWrapper.cxx \ + allAttributesWrapper.h allTransitionsWrapper.I \ + allTransitionsWrapper.cxx allTransitionsWrapper.h \ + bitMask32Transition.cxx bitMask32Transition.h boundedObject.I \ + boundedObject.N boundedObject.cxx boundedObject.h config_graph.cxx \ + config_graph.h graphReducer.cxx graphReducer.h immediateAttribute.I \ + immediateAttribute.cxx immediateAttribute.h immediateTransition.I \ + immediateTransition.cxx immediateTransition.h \ + lmatrix4fTransition.cxx lmatrix4fTransition.h multiNodeAttribute.I \ + multiNodeAttribute.cxx multiNodeAttribute.h multiNodeTransition.I \ + multiNodeTransition.cxx multiNodeTransition.h namedNode.I \ + namedNode.cxx namedNode.h node.I node.cxx node.h nodeAttribute.I \ + nodeAttribute.N nodeAttribute.cxx nodeAttribute.h \ + nodeAttributeWrapper.I nodeAttributeWrapper.cxx \ + nodeAttributeWrapper.h nodeAttributes.I nodeAttributes.N \ + nodeAttributes.cxx nodeAttributes.h nodeRelation.I nodeRelation.N \ + nodeRelation.cxx nodeRelation.h nodeTransition.I nodeTransition.N \ + nodeTransition.cxx nodeTransition.h nodeTransitionCache.I \ + nodeTransitionCache.cxx nodeTransitionCache.h \ + nodeTransitionCacheEntry.I nodeTransitionCacheEntry.cxx \ + nodeTransitionCacheEntry.h nodeTransitionWrapper.I \ + nodeTransitionWrapper.cxx nodeTransitionWrapper.h nodeTransitions.I \ + nodeTransitions.cxx nodeTransitions.h nullAttributeWrapper.I \ + nullAttributeWrapper.cxx nullAttributeWrapper.h nullLevelState.cxx \ + nullLevelState.h nullTransitionWrapper.I nullTransitionWrapper.cxx \ + nullTransitionWrapper.h onAttribute.I onAttribute.cxx onAttribute.h \ + onOffAttribute.I onOffAttribute.cxx onOffAttribute.h \ + onOffTransition.I onOffTransition.cxx onOffTransition.h \ + onTransition.I onTransition.cxx onTransition.h pt_NamedNode.N \ + pt_NamedNode.cxx pt_NamedNode.h pt_Node.N pt_Node.cxx pt_Node.h \ + vector_PT_Node.cxx vector_PT_Node.h + + #define INSTALL_HEADERS \ + allAttributesWrapper.I allAttributesWrapper.h \ + allTransitionsWrapper.I allTransitionsWrapper.h \ + bitMask32Transition.h bitMaskAttribute.I bitMaskAttribute.h \ + bitMaskTransition.I bitMaskTransition.h boundedObject.I \ + boundedObject.h config_graph.h dftraverser.I dftraverser.h \ + graphReducer.h immediateAttribute.I immediateAttribute.h \ + immediateTransition.I immediateTransition.h lmatrix4fTransition.h \ + matrixAttribute.I matrixAttribute.h matrixTransition.I \ + matrixTransition.h multiAttribute.I multiAttribute.h \ + multiNodeAttribute.I multiNodeAttribute.h multiNodeTransition.I \ + multiNodeTransition.h multiTransition.I multiTransition.h \ + multiTransitionHelpers.I multiTransitionHelpers.h namedNode.I \ + namedNode.h node.I node.h nodeAttribute.I nodeAttribute.h \ + nodeAttributeWrapper.I nodeAttributeWrapper.h nodeAttributes.I \ + nodeAttributes.h nodeRelation.I nodeRelation.h nodeTransition.I \ + nodeTransition.h nodeTransitionCache.I nodeTransitionCache.h \ + nodeTransitionCacheEntry.I nodeTransitionCacheEntry.h \ + nodeTransitionWrapper.I nodeTransitionWrapper.h nodeTransitions.I \ + nodeTransitions.h nullAttributeWrapper.I nullAttributeWrapper.h \ + nullLevelState.h nullTransitionWrapper.I nullTransitionWrapper.h \ + onAttribute.I onAttribute.h onOffAttribute.I onOffAttribute.h \ + onOffTransition.I onOffTransition.h onTransition.I onTransition.h \ + pointerNameClass.h pt_NamedNode.h pt_Node.h transitionDirection.h \ + traverserVisitor.I traverserVisitor.h vector_PT_Node.h wrt.I wrt.h + + #define IGATESCAN all + +#end lib_target + +#begin test_bin_target + #define TARGET test_graph + #define LOCAL_LIBS \ + graph putil + + #define SOURCES \ + test_graph.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET test_graphRead + #define LOCAL_LIBS \ + putil graph + + #define SOURCES \ + test_graphRead.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET test_graphWrite + #define LOCAL_LIBS \ + graph putil + + #define SOURCES \ + test_graphWrite.cxx + +#end test_bin_target + diff --git a/panda/src/graph/allAttributesWrapper.I b/panda/src/graph/allAttributesWrapper.I new file mode 100644 index 0000000000..bc0a25e482 --- /dev/null +++ b/panda/src/graph/allAttributesWrapper.I @@ -0,0 +1,231 @@ +// Filename: allAttributesWrapper.I +// Created by: drose (21Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AllAttributesWrapper:: +AllAttributesWrapper() { +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AllAttributesWrapper:: +AllAttributesWrapper(const NodeAttributes &attrib) : + _attrib(attrib) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AllAttributesWrapper:: +AllAttributesWrapper(const AllAttributesWrapper ©) : + _attrib(copy._attrib) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void AllAttributesWrapper:: +operator = (const AllAttributesWrapper ©) { + _attrib = copy._attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::init_from +// Access: Public, Static +// Description: This is a named constructor that creates an empty +// AllAttributesWrapper ready to access the same type +// of AllAttributes as the other. +//////////////////////////////////////////////////////////////////// +INLINE AllAttributesWrapper AllAttributesWrapper:: +init_from(const AllTransitionsWrapper &) { + return AllAttributesWrapper(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AllAttributesWrapper:: +~AllAttributesWrapper() { +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::is_empty +// Access: Public +// Description: Returns true if there are no Attributes stored in +// the set, or false if there are any (even initial) +// Attributes. +//////////////////////////////////////////////////////////////////// +INLINE bool AllAttributesWrapper:: +is_empty() const { + return _attrib.is_empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::set_attribute +// Access: Public +// Description: This flavor of set_attribute() accepts a specific +// TypeHandle, indicating the type of attribute that we +// are setting, and a NodeAttribute pointer indicating +// the value of the attribute. The NodeAttribute may +// be NULL indicating that the attribute should be +// cleared. If the NodeAttribute is not NULL, it must +// match the type indicated by the TypeHandle. +// +// The return value is a pointer to the *previous* +// attribute in the set, if any, or NULL if there was +// none. +//////////////////////////////////////////////////////////////////// +INLINE PT(NodeAttribute) AllAttributesWrapper:: +set_attribute(TypeHandle handle, NodeAttribute *trans) { + return _attrib.set_attribute(handle, trans); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::set_attribute +// Access: Public +// Description: This flavor of set_attribute() accepts a pointer to +// a NodeAttribute only. It infers the type of the +// NodeAttribute from the pointer. However, it is not +// valid to pass a NULL pointer to this flavor of +// set_attribute; if the pointer might be NULL, use the +// above flavor instead (or just call clear_attribute). +//////////////////////////////////////////////////////////////////// +INLINE PT(NodeAttribute) AllAttributesWrapper:: +set_attribute(NodeAttribute *trans) { + nassertr(trans != (NodeAttribute *)NULL, NULL); + nassertr(trans->get_handle() != TypeHandle::none(), NULL); + return set_attribute(trans->get_handle(), trans); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::clear_attribute +// Access: Public +// Description: Removes any attribute associated with the indicated +// handle from the set. +// +// The return value is a pointer to the previous +// attribute in the set, if any, or NULL if there was +// none. +//////////////////////////////////////////////////////////////////// +INLINE PT(NodeAttribute) AllAttributesWrapper:: +clear_attribute(TypeHandle handle) { + return _attrib.clear_attribute(handle); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::has_attribute +// Access: Public +// Description: Returns true if ab attribute associated with the +// indicated handle has been stored in the set (even if +// it is the initial attribute), or false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool AllAttributesWrapper:: +has_attribute(TypeHandle handle) const { + return _attrib.has_attribute(handle); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::get_attribute +// Access: Public +// Description: Returns the attribute associated with the indicated +// handle, or NULL if no such attribute has been stored +// in the set. +//////////////////////////////////////////////////////////////////// +INLINE NodeAttribute *AllAttributesWrapper:: +get_attribute(TypeHandle handle) const { + return _attrib.get_attribute(handle); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::get_attributes +// Access: Public +// Description: Returns the entire set of attributes associated with +// the wrapper. +//////////////////////////////////////////////////////////////////// +INLINE const NodeAttributes &AllAttributesWrapper:: +get_attributes() const { + return _attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::get_attributes +// Access: Public +// Description: Returns the entire set of attributes associated with +// the wrapper. +//////////////////////////////////////////////////////////////////// +INLINE NodeAttributes &AllAttributesWrapper:: +get_attributes() { + return _attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::is_initial +// Access: Public +// Description: Returns true if the wrapper represents an initial +// attribute. +//////////////////////////////////////////////////////////////////// +INLINE bool AllAttributesWrapper:: +is_initial() const { + return _attrib.is_initial(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int AllAttributesWrapper:: +compare_to(const AllAttributesWrapper &other) const { + return _attrib.compare_to(other._attrib); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::make_initial +// Access: Public +// Description: Resets the wrapper to the initial attribute. +//////////////////////////////////////////////////////////////////// +INLINE void AllAttributesWrapper:: +make_initial() { + _attrib.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: get_attribute_into +// Description: This external template function is handy for +// extracting a attribute of a particular type from the +// set. If the attribute exists, it is automatically +// downcasted to the correct type and stored in the +// pointer given in the first parameter, and the return +// value is true. If the attribute does not exist, the +// pointer is filled with NULL and the return value is +// false. +//////////////////////////////////////////////////////////////////// +template +INLINE bool +get_attribute_into(Attribute *&ptr, const AllAttributesWrapper &attrib, + TypeHandle transition_type) { + NodeAttribute *nt = attrib.get_attribute(transition_type); + if (nt == (NodeAttribute *)NULL) { + ptr = (Attribute *)NULL; + return false; + } + DCAST_INTO_R(ptr, nt, false); + return true; +} diff --git a/panda/src/graph/allAttributesWrapper.cxx b/panda/src/graph/allAttributesWrapper.cxx new file mode 100644 index 0000000000..decc95c0c2 --- /dev/null +++ b/panda/src/graph/allAttributesWrapper.cxx @@ -0,0 +1,73 @@ +// Filename: allAttributesWrapper.cxx +// Created by: drose (21Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "allAttributesWrapper.h" +#include "allTransitionsWrapper.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::apply_in_place +// Access: Public +// Description: Modifies the attribute by applying the transition. +//////////////////////////////////////////////////////////////////// +void AllAttributesWrapper:: +apply_in_place(const AllTransitionsWrapper &trans) { + if (trans._cache != (NodeTransitionCache *)NULL) { + _attrib.apply_in_place(*trans._cache); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::apply_from +// Access: Public +// Description: Modifies the attribute to reflect the application of +// the indicated transition to the other attribute. +//////////////////////////////////////////////////////////////////// +void AllAttributesWrapper:: +apply_from(const AllAttributesWrapper &other, + const AllTransitionsWrapper &trans) { + if (trans._cache != (NodeTransitionCache *)NULL) { + _attrib.apply_from(other._attrib, *trans._cache); + } else { + _attrib = other._attrib; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::apply +// Access: Public +// Description: Allocates and returns a new NodeAttributes object +// that reflects the application of the transitions to +// this wrapper. +//////////////////////////////////////////////////////////////////// +NodeAttributes *AllAttributesWrapper:: +apply(const AllTransitionsWrapper &trans) const { + if (trans._cache != (NodeTransitionCache *)NULL) { + return _attrib.apply(*trans._cache); + } else { + return new NodeAttributes(_attrib); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void AllAttributesWrapper:: +output(ostream &out) const { + out << _attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: AllAttributesWrapper::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void AllAttributesWrapper:: +write(ostream &out, int indent_level) const { + _attrib.write(out, indent_level); +} diff --git a/panda/src/graph/allAttributesWrapper.h b/panda/src/graph/allAttributesWrapper.h new file mode 100644 index 0000000000..aefbe4bb58 --- /dev/null +++ b/panda/src/graph/allAttributesWrapper.h @@ -0,0 +1,74 @@ +// Filename: allAttributesWrapper.h +// Created by: drose (21Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ALLATTRIBUTESWRAPPER_H +#define ALLATTRIBUTESWRAPPER_H + +#include + +#include "nodeAttributes.h" + +#include +#include + +class AllTransitionsWrapper; + +//////////////////////////////////////////////////////////////////// +// Class : AllAttributesWrapper +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA AllAttributesWrapper { +public: + typedef AllTransitionsWrapper TransitionWrapper; + typedef AllAttributesWrapper AttributeWrapper; + + INLINE AllAttributesWrapper(); + INLINE AllAttributesWrapper(const NodeAttributes &attrib); + INLINE AllAttributesWrapper(const AllAttributesWrapper ©); + INLINE void operator = (const AllAttributesWrapper ©); + INLINE static AllAttributesWrapper + init_from(const AllTransitionsWrapper &trans); + INLINE ~AllAttributesWrapper(); + + INLINE bool is_empty() const; + INLINE PT(NodeAttribute) set_attribute(TypeHandle handle, + NodeAttribute *trans); + INLINE PT(NodeAttribute) set_attribute(NodeAttribute *trans); + INLINE PT(NodeAttribute) clear_attribute(TypeHandle handle); + INLINE bool has_attribute(TypeHandle handle) const; + INLINE NodeAttribute *get_attribute(TypeHandle handle) const; + + INLINE const NodeAttributes &get_attributes() const; + INLINE NodeAttributes &get_attributes(); + + INLINE bool is_initial() const; + INLINE int compare_to(const AllAttributesWrapper &other) const; + + INLINE void make_initial(); + void apply_in_place(const AllTransitionsWrapper &trans); + void apply_from(const AllAttributesWrapper &other, + const AllTransitionsWrapper &trans); + NodeAttributes *apply(const AllTransitionsWrapper &trans) const; + + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + +private: + NodeAttributes _attrib; +}; + +INLINE ostream &operator << (ostream &out, const AllAttributesWrapper &a) { + a.output(out); + return out; +} + +template +INLINE bool +get_attribute_into(Attribute *&ptr, const AllAttributesWrapper &attrib, + TypeHandle transition_type); + +#include "allAttributesWrapper.I" + +#endif diff --git a/panda/src/graph/allTransitionsWrapper.I b/panda/src/graph/allTransitionsWrapper.I new file mode 100644 index 0000000000..39f8e82a65 --- /dev/null +++ b/panda/src/graph/allTransitionsWrapper.I @@ -0,0 +1,422 @@ +// Filename: allTransitionsWrapper.I +// Created by: drose (21Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nodeRelation.h" + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AllTransitionsWrapper:: +AllTransitionsWrapper() { +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AllTransitionsWrapper:: +AllTransitionsWrapper(const AllTransitionsWrapper ©) : + _cache(copy._cache) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void AllTransitionsWrapper:: +operator = (const AllTransitionsWrapper ©) { + _cache = copy._cache; +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AllTransitionsWrapper:: +~AllTransitionsWrapper() { +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::init_from +// Access: Public, Static +// Description: This is a named constructor that creates an empty +// AllTransitionsWrapper ready to access the same type +// of NodeTransition as the other. +//////////////////////////////////////////////////////////////////// +INLINE AllTransitionsWrapper AllTransitionsWrapper:: +init_from(const AllTransitionsWrapper &) { + return AllTransitionsWrapper(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::init_from +// Access: Public, Static +// Description: This is a named constructor that creates an empty +// AllTransitionsWrapper ready to access the same type +// of NodeTransition as the other. +//////////////////////////////////////////////////////////////////// +INLINE AllTransitionsWrapper AllTransitionsWrapper:: +init_from(const AllAttributesWrapper &) { + return AllTransitionsWrapper(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::is_empty +// Access: Public +// Description: Returns true if there are no Transitions stored in +// the wrapper, or false if there are any (even identity) +// Transitions. +//////////////////////////////////////////////////////////////////// +INLINE bool AllTransitionsWrapper:: +is_empty() const { + return + (_cache == (NodeTransitionCache *)NULL) || + _cache->is_empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::set_transition +// Access: Public +// Description: This flavor of set_transition() accepts a specific +// TypeHandle, indicating the type of transition that we +// are setting, and a NodeTransition pointer indicating +// the value of the transition. The NodeTransition may +// be NULL indicating that the transition should be +// cleared. If the NodeTransition is not NULL, it must +// match the type indicated by the TypeHandle. +// +// The return value is a pointer to the *previous* +// transition in the set, if any, or NULL if there was +// none. +//////////////////////////////////////////////////////////////////// +INLINE PT(NodeTransition) AllTransitionsWrapper:: +set_transition(TypeHandle handle, NodeTransition *trans) { + if (_cache == (NodeTransitionCache *)NULL) { + _cache = new NodeTransitionCache; + } + return _cache->set_transition(handle, trans); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapperset_transition +// Access: Public +// Description: This flavor of set_transition() accepts a pointer to +// a NodeTransition only. It infers the type of the +// NodeTransition from the pointer. However, it is not +// valid to pass a NULL pointer to this flavor of +// set_transition; if the pointer might be NULL, use the +// above flavor instead (or just call clear_transition). +//////////////////////////////////////////////////////////////////// +INLINE PT(NodeTransition) AllTransitionsWrapper:: +set_transition(NodeTransition *trans) { + nassertr(trans != (NodeTransition *)NULL, NULL); + nassertr(trans->get_handle() != TypeHandle::none(), NULL); + return set_transition(trans->get_handle(), trans); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::clear_transition +// Access: Public +// Description: Removes any transition associated with the indicated +// handle from the set. +// +// The return value is a pointer to the previous +// transition in the set, if any, or NULL if there was +// none. +//////////////////////////////////////////////////////////////////// +INLINE PT(NodeTransition) AllTransitionsWrapper:: +clear_transition(TypeHandle handle) { + if (_cache == (NodeTransitionCache *)NULL) { + return NULL; + } + return _cache->clear_transition(handle); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::has_transition +// Access: Public +// Description: Returns true if a transition associated with the +// indicated handle has been stored in the wrapper, or +// false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool AllTransitionsWrapper:: +has_transition(TypeHandle handle) const { + if (_cache == (NodeTransitionCache *)NULL) { + return false; + } + return _cache->has_transition(handle); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::get_transition +// Access: Public +// Description: Returns the transition associated with the indicated +// handle, or NULL if no such transition has been stored +// in the wrapper. +//////////////////////////////////////////////////////////////////// +INLINE NodeTransition *AllTransitionsWrapper:: +get_transition(TypeHandle handle) const { + if (_cache == (NodeTransitionCache *)NULL) { + return NULL; + } + return _cache->get_transition(handle); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::get_transitions +// Access: Public +// Description: Returns the entire cache of transitions. You +// shouldn't modify this, or probably even store it for +// any length of time. +//////////////////////////////////////////////////////////////////// +INLINE const NodeTransitionCache &AllTransitionsWrapper:: +get_transitions() const { + static NodeTransitionCache empty_transitions; + + if (_cache == (NodeTransitionCache *)NULL) { + return empty_transitions; + } + return *_cache; +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::is_identity +// Access: Public +// Description: Returns true if the wrapper represents an identity +// transition. +//////////////////////////////////////////////////////////////////// +INLINE bool AllTransitionsWrapper:: +is_identity() const { + if (_cache == (NodeTransitionCache *)NULL) { + return true; + } + return _cache->is_identity(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int AllTransitionsWrapper:: +compare_to(const AllTransitionsWrapper &other) const { + if (_cache == other._cache) { + return 0; + } + if (_cache == (NodeTransitionCache *)NULL) { + return -1; + } + if (other._cache == (NodeTransitionCache *)NULL) { + return 1; + } + + return _cache->compare_to(*other._cache); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::make_identity +// Access: Public +// Description: Resets the wrapper to the empty set. +//////////////////////////////////////////////////////////////////// +INLINE void AllTransitionsWrapper:: +make_identity() { + _cache = (NodeTransitionCache *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::extract_from +// Access: Public +// Description: Sets the wrapper to the set of transitions contained +// on the arc. +//////////////////////////////////////////////////////////////////// +INLINE void AllTransitionsWrapper:: +extract_from(const NodeRelation *arc) { + nassertv(arc != (NodeRelation *)NULL); + if (arc->_transitions.is_empty()) { + _cache = (NodeTransitionCache *)NULL; + } else { + _cache = new NodeTransitionCache(arc->_transitions); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::store_to +// Access: Public +// Description: Stores all the transitions in the wrapper to the arc. +//////////////////////////////////////////////////////////////////// +INLINE void AllTransitionsWrapper:: +store_to(NodeRelation *arc) const { + nassertv(arc != (NodeRelation *)NULL); + NodeTransitionCache::store_to(_cache, arc, arc->_transitions); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::compose_in_place +// Access: Public +// Description: Sets this transition to the composition of this +// transition and the following one. +//////////////////////////////////////////////////////////////////// +INLINE void AllTransitionsWrapper:: +compose_in_place(const AllTransitionsWrapper &other) { + _cache = NodeTransitionCache::compose(_cache, other._cache); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::invert_in_place +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void AllTransitionsWrapper:: +invert_in_place() { + _cache = NodeTransitionCache::invert(_cache); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::invert_compose_in_place +// Access: Public +// Description: Sets this transition to the composition of this +// transition and the following one. +//////////////////////////////////////////////////////////////////// +INLINE void AllTransitionsWrapper:: +invert_compose_in_place(const AllTransitionsWrapper &other) { + _cache = NodeTransitionCache::invert_compose(_cache, other._cache); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::extract_from_cache +// Access: Public +// Description: Sets this wrapper to the set of transitions indicated +// in the cache on the arc. Also returns the arc's +// top_subtree indication. +//////////////////////////////////////////////////////////////////// +INLINE Node *AllTransitionsWrapper:: +extract_from_cache(const NodeRelation *arc) { + _cache = arc->_net_transitions; + return arc->_top_subtree; +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::store_to_cache +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void AllTransitionsWrapper:: +store_to_cache(NodeRelation *arc, Node *top_subtree) { + arc->_top_subtree = top_subtree; + arc->_net_transitions = + NodeTransitionCache::c_union(arc->_net_transitions, _cache); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::is_cache_verified +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool AllTransitionsWrapper:: +is_cache_verified(UpdateSeq) const { + // We can't ever know if all elements in the set are verified, + // because the set might not be complete. + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::set_computed_verified +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void AllTransitionsWrapper:: +set_computed_verified(UpdateSeq now) { + _cache = NodeTransitionCache::set_computed_verified(_cache, now); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::cached_compose +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void AllTransitionsWrapper:: +cached_compose(const AllTransitionsWrapper &cache, + const AllTransitionsWrapper &value, + UpdateSeq now) { + _cache = NodeTransitionCache::cached_compose(_cache, cache._cache, + value._cache, now); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AllTransitionsWrapper::size_type AllTransitionsWrapper:: +size() const { + if (_cache == (NodeTransitionCache *)NULL) { + return 0; + } + return _cache->size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::begin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AllTransitionsWrapper::const_iterator AllTransitionsWrapper:: +begin() const { + if (_cache == (NodeTransitionCache *)NULL) { + return _empty_cache.begin(); + } + return _cache->begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::end +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AllTransitionsWrapper::const_iterator AllTransitionsWrapper:: +end() const { + if (_cache == (NodeTransitionCache *)NULL) { + // We intentionally return begin() instead of end() here, just in + // case the "empty" cache accidentally gets something added to it. + return _empty_cache.begin(); + } + return _cache->end(); +} + +//////////////////////////////////////////////////////////////////// +// Function: get_transition_into +// Description: This external template function is handy for +// extracting a transition of a particular type from the +// set. If the transition exists, it is automatically +// downcasted to the correct type and stored in the +// pointer given in the first parameter, and the return +// value is true. If the transition does not exist, the +// pointer is filled with NULL and the return value is +// false. +//////////////////////////////////////////////////////////////////// +template +INLINE bool +get_transition_into(Transition *&ptr, const AllTransitionsWrapper &trans, + TypeHandle transition_type) { + NodeTransition *nt = trans.get_transition(transition_type); + if (nt == (NodeTransition *)NULL) { + ptr = (Transition *)NULL; + return false; + } + DCAST_INTO_R(ptr, nt, false); + return true; +} + +template +INLINE bool +get_transition_into(Transition *&ptr, const AllTransitionsWrapper &trans) { + return get_transition_into(ptr, trans, Transition::get_class_type()); +} diff --git a/panda/src/graph/allTransitionsWrapper.cxx b/panda/src/graph/allTransitionsWrapper.cxx new file mode 100644 index 0000000000..a82765920f --- /dev/null +++ b/panda/src/graph/allTransitionsWrapper.cxx @@ -0,0 +1,35 @@ +// Filename: allTransitionsWrapper.cxx +// Created by: drose (21Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "allTransitionsWrapper.h" +#include "nodeRelation.h" + +#include + +NodeTransitionCache AllTransitionsWrapper::_empty_cache; + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void AllTransitionsWrapper:: +output(ostream &out) const { + if (_cache != (NodeTransitionCache *)NULL) { + out << *_cache; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AllTransitionsWrapper::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void AllTransitionsWrapper:: +write(ostream &out, int indent_level) const { + if (_cache != (NodeTransitionCache *)NULL) { + _cache->write(out, indent_level); + } +} diff --git a/panda/src/graph/allTransitionsWrapper.h b/panda/src/graph/allTransitionsWrapper.h new file mode 100644 index 0000000000..07e24cab38 --- /dev/null +++ b/panda/src/graph/allTransitionsWrapper.h @@ -0,0 +1,131 @@ +// Filename: allTransitionsWrapper.h +// Created by: drose (21Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ALLTRANSITIONSWRAPPER_H +#define ALLTRANSITIONSWRAPPER_H + +// +// There are several flavors of TransitionWrappers (and their +// corresponding AttributeWrappers). These are classes that represent +// one or a number of transitions (or attributes) simultaneously and +// are passed to template functions like df_traverse() and wrt() so +// that the same functions can be used to operate on either one +// transition type or a number of them. +// +// Since these classes are used as template parameters, they do not +// need to use inheritance or virtual functions; but they all must +// have a similar interface. +// + +#include + +#include "nodeTransition.h" +#include "nodeTransitionCache.h" + +#include + +class Node; +class NodeRelation; +class NodeAttribute; +class AllAttributesWrapper; + +//////////////////////////////////////////////////////////////////// +// Class : AllTransitionsWrapper +// Description : This wrapper represents all transitions that might be +// stored on arcs. It is especially useful when +// performing a traversal or computing a wrt() and you +// want to determine the complete state at a given node. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA AllTransitionsWrapper { +public: + typedef AllTransitionsWrapper TransitionWrapper; + typedef AllAttributesWrapper AttributeWrapper; + + INLINE AllTransitionsWrapper(); + INLINE AllTransitionsWrapper(const AllTransitionsWrapper ©); + INLINE void operator = (const AllTransitionsWrapper ©); + INLINE ~AllTransitionsWrapper(); + + INLINE static AllTransitionsWrapper + init_from(const AllTransitionsWrapper &other); + INLINE static AllTransitionsWrapper + init_from(const AllAttributesWrapper &attrib); + + INLINE bool is_empty() const; + INLINE PT(NodeTransition) set_transition(TypeHandle handle, + NodeTransition *trans); + INLINE PT(NodeTransition) set_transition(NodeTransition *trans); + INLINE PT(NodeTransition) clear_transition(TypeHandle handle); + INLINE bool has_transition(TypeHandle handle) const; + INLINE NodeTransition *get_transition(TypeHandle handle) const; + + INLINE const NodeTransitionCache &get_transitions() const; + + INLINE bool is_identity() const; + INLINE int compare_to(const AllTransitionsWrapper &other) const; + + INLINE void make_identity(); + INLINE void extract_from(const NodeRelation *arc); + INLINE void store_to(NodeRelation *arc) const; + + INLINE void compose_in_place(const AllTransitionsWrapper &other); + INLINE void invert_in_place(); + INLINE void invert_compose_in_place(const AllTransitionsWrapper &other); + + INLINE Node *extract_from_cache(const NodeRelation *arc); + INLINE void store_to_cache(NodeRelation *arc, Node *top_subtree); + INLINE bool is_cache_verified(UpdateSeq now) const; + INLINE void set_computed_verified(UpdateSeq now); + + INLINE void cached_compose(const AllTransitionsWrapper &cache, + const AllTransitionsWrapper &value, + UpdateSeq now); + +public: + // STL-like definitions to allow read-only traversal of the + // individual transitions. Note that each of these is a + // NodeTransitionCacheEntry, not simply a PT(NodeTransition). + // Beware! It is not safe to use this interface outside of + // PANDA.DLL. + typedef NodeTransitionCache::const_iterator iterator; + typedef NodeTransitionCache::const_iterator const_iterator; + typedef NodeTransitionCache::value_type value_type; + typedef NodeTransitionCache::size_type size_type; + + INLINE size_type size() const; + INLINE const_iterator begin() const; + INLINE const_iterator end() const; + +public: + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + +private: + PT(NodeTransitionCache) _cache; + + // This is a special cache object which is always around and always + // empty. It's used just so we can have a sensible return value + // from begin() and end() when our pointer is NULL. + static NodeTransitionCache _empty_cache; +friend class AllAttributesWrapper; +}; + +INLINE ostream &operator << (ostream &out, const AllTransitionsWrapper &ntw) { + ntw.output(out); + return out; +} + +template +INLINE bool +get_transition_into(Transition *&ptr, const AllTransitionsWrapper &trans, + TypeHandle transition_type); + +template +INLINE bool +get_transition_into(Transition *&ptr, const AllTransitionsWrapper &trans); + +#include "allTransitionsWrapper.I" + +#endif diff --git a/panda/src/graph/bitMask32Transition.cxx b/panda/src/graph/bitMask32Transition.cxx new file mode 100644 index 0000000000..57ed08a766 --- /dev/null +++ b/panda/src/graph/bitMask32Transition.cxx @@ -0,0 +1,12 @@ +// Filename: bitMask32Transition.cxx +// Created by: drose (08Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "bitMask32Transition.h" + +#ifdef __GNUC__ +#pragma implementation +#endif + + diff --git a/panda/src/graph/bitMask32Transition.h b/panda/src/graph/bitMask32Transition.h new file mode 100644 index 0000000000..6aa73b488e --- /dev/null +++ b/panda/src/graph/bitMask32Transition.h @@ -0,0 +1,35 @@ +// Filename: bitMask32Transition.h +// Created by: drose (08Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BITMASK32TRANSITION_H +#define BITMASK32TRANSITION_H + +#include + +#include "bitMaskTransition.h" +#include "bitMaskAttribute.h" + +#include + + +//////////////////////////////////////////////////////////////////// +// Class : BitMask32Transition +// Description : This is just an instantation of BitMaskTransition +// using BitMask32, the most common bitmask type. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, BitMaskTransition); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, BitMaskAttribute); + +typedef BitMaskTransition BitMask32Transition; +typedef BitMaskAttribute BitMask32Attribute; + +#ifdef __GNUC__ +#pragma interface +#endif + +#endif + + diff --git a/panda/src/graph/bitMaskAttribute.I b/panda/src/graph/bitMaskAttribute.I new file mode 100644 index 0000000000..0d2130123a --- /dev/null +++ b/panda/src/graph/bitMaskAttribute.I @@ -0,0 +1,103 @@ +// Filename: bitMaskAttribute.I +// Created by: drose (08Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include + +template +TypeHandle BitMaskAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BitMaskAttribute:: +BitMaskAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BitMaskAttribute:: +BitMaskAttribute(const MaskType &mask) : + _mask(mask) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskAttribute::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BitMaskAttribute:: +BitMaskAttribute(const BitMaskAttribute ©) : + NodeAttribute(copy), + _mask(copy._mask) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskAttribute::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMaskAttribute:: +operator = (const BitMaskAttribute ©) { + NodeAttribute::operator = (copy); + _mask = copy._mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskAttribute::set_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMaskAttribute:: +set_mask(const MaskType &mask) { + _mask = mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskAttribute::get_maskType +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE const MaskType &BitMaskAttribute:: +get_mask() const { + return _mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskAttribute::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void BitMaskAttribute:: +output(ostream &out) const { + out << _mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskAttribute::internal_compare_to +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +int BitMaskAttribute:: +internal_compare_to(const NodeAttribute *other) const { + const BitMaskAttribute *ot; + DCAST_INTO_R(ot, other, false); + + return _mask.compare_to(ot->_mask); +} diff --git a/panda/src/graph/bitMaskAttribute.h b/panda/src/graph/bitMaskAttribute.h new file mode 100644 index 0000000000..31df11689f --- /dev/null +++ b/panda/src/graph/bitMaskAttribute.h @@ -0,0 +1,64 @@ +// Filename: bitMaskAttribute.h +// Created by: drose (08Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BITMASKATTRIBUTE_H +#define BITMASKATTRIBUTE_H + +#include + +#include "nodeAttribute.h" + +template +class BitMaskTransition; + +//////////////////////////////////////////////////////////////////// +// Class : BitMaskAttribute +// Description : +//////////////////////////////////////////////////////////////////// +template +class BitMaskAttribute : public NodeAttribute { +protected: + INLINE BitMaskAttribute(); + INLINE BitMaskAttribute(const MaskType &mask); + INLINE BitMaskAttribute(const BitMaskAttribute ©); + INLINE void operator = (const BitMaskAttribute ©); + +public: + INLINE void set_mask(const MaskType &mask); + INLINE const MaskType &get_mask() const; + + virtual void output(ostream &out) const; + +protected: + virtual int internal_compare_to(const NodeAttribute *other) const; + +private: + MaskType _mask; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeAttribute::init_type(); + MaskType::init_type(); + register_type(_type_handle, + string("BitMaskAttribute<") + + MaskType::get_class_type().get_name() + ">", + NodeAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +friend class BitMaskTransition; +}; + +#include "bitMaskAttribute.I" + +#endif diff --git a/panda/src/graph/bitMaskTransition.I b/panda/src/graph/bitMaskTransition.I new file mode 100644 index 0000000000..217bf9ffa4 --- /dev/null +++ b/panda/src/graph/bitMaskTransition.I @@ -0,0 +1,230 @@ +// Filename: bitMaskTransition.I +// Created by: drose (08Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "bitMaskAttribute.h" + +#include + +template +TypeHandle BitMaskTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BitMaskTransition:: +BitMaskTransition() { + _and = MaskType::all_on(); + _or = MaskType::all_off(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BitMaskTransition:: +BitMaskTransition(const MaskType &and, const MaskType &or) { + _and = and; + _or = or; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskTransition::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BitMaskTransition:: +BitMaskTransition(const BitMaskTransition ©) : + NodeTransition(copy), + _and(copy._and), + _or(copy._or) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskTransition::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMaskTransition:: +operator = (const BitMaskTransition ©) { + NodeTransition::operator = (copy); + _and = copy._and; + _or = copy._or; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskTransition::set_and +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMaskTransition:: +set_and(const MaskType &and) { + _and = and; + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskTransition::get_and +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE const MaskType &BitMaskTransition:: +get_and() const { + return _and; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskTransition::set_or +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMaskTransition:: +set_or(const MaskType &or) { + _or = or; + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskTransition::get_or +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE const MaskType &BitMaskTransition:: +get_or() const { + return _or; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskTransition::compose +// Access: Public, Virtual +// Description: Returns a new transition that corresponds to the +// composition of this transition with the second +// transition (which must be of an equivalent type). +// This may return the same pointer as either source +// transition. Applying the transition returned from +// this function to an attribute attribute will produce +// the same effect as applying each transition +// separately. +//////////////////////////////////////////////////////////////////// +template +NodeTransition *BitMaskTransition:: +compose(const NodeTransition *other) const { + const BitMaskTransition *ot; + DCAST_INTO_R(ot, other, NULL); + + if (ot->_priority < _priority) { + // The other transition is too low-priority; the result is + // unchanged. + return (BitMaskTransition *)this; + } + + return make_with_masks(ot->_and & _and, ot->_or | _or); +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskTransition::invert +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +NodeTransition *BitMaskTransition:: +invert() const { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskTransition::apply +// Access: Public, Virtual +// Description: Returns a new attribute (or possibly the same +// attribute) that represents the effect of applying this +// indicated transition to the indicated attribute. The +// source attribute may be NULL, indicating the initial +// attribute. +//////////////////////////////////////////////////////////////////// +template +NodeAttribute *BitMaskTransition:: +apply(const NodeAttribute *attrib) const { + BitMaskAttribute *result; + DCAST_INTO_R(result, make_attrib(), NULL); + + if (attrib == (const NodeAttribute *)NULL) { + // If there is no root attribute, the default attribute is always + // all bits on. + result->_mask = (MaskType::all_on() & _and) | _or; + return result; + } + + if (_priority < result->_priority) { + // The priority is too low to affect the attribute. + return result; + } + + const BitMaskAttribute *at; + DCAST_INTO_R(at, attrib, NULL); + + result->_mask = (at->_mask & _and) | _or; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskTransition::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void BitMaskTransition:: +output(ostream &out) const { + out << "&"; + _and.output_hex(out); + out << " |"; + _or.output_hex(out); +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskTransition::internal_compare_to +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +int BitMaskTransition:: +internal_compare_to(const NodeTransition *other) const { + const BitMaskTransition *ot; + DCAST_INTO_R(ot, other, false); + + int result = _and.compare_to(ot->_and); + if (result == 0) { + result = _or.compare_to(ot->_or); + } + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMaskTransition::make_with_masks +// Access: Protected, Virtual +// Description: This function creates a new BitMaskTransition of the +// appropriate type, given the indicated and & or masks. +// +// This is a pure virtual function and normally wouldn't +// require a definition, but for some reason VC++ +// objects to instantiating the template if it's missing +// any function definitions--even those for pure-virtual +// functions. +//////////////////////////////////////////////////////////////////// +template +BitMaskTransition *BitMaskTransition:: +make_with_masks(const MaskType &, const MaskType &) const { + return NULL; +} diff --git a/panda/src/graph/bitMaskTransition.h b/panda/src/graph/bitMaskTransition.h new file mode 100644 index 0000000000..9f7b7e92e5 --- /dev/null +++ b/panda/src/graph/bitMaskTransition.h @@ -0,0 +1,79 @@ +// Filename: bitMaskTransition.h +// Created by: drose (08Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BITMASKTRANSITION_H +#define BITMASKTRANSITION_H + +#include + +#include "nodeTransition.h" + +class NodeAttribute; +class NodeRelation; + +//////////////////////////////////////////////////////////////////// +// Class : BitMaskTransition +// Description : This is an abstract template class that encapsulates +// transitions on a bitmask. Each transition may add +// and/or remove bits from the accumulated bitmask. +// +// It's the base class for a number of scene graph +// transitions like DrawMaskTransition and +// CollideMaskTransition. +//////////////////////////////////////////////////////////////////// +template +class BitMaskTransition : public NodeTransition { +protected: + INLINE BitMaskTransition(); + INLINE BitMaskTransition(const MaskType &and, const MaskType &or); + INLINE BitMaskTransition(const BitMaskTransition ©); + INLINE void operator = (const BitMaskTransition ©); + +public: + INLINE void set_and(const MaskType &and); + INLINE const MaskType &get_and() const; + INLINE void set_or(const MaskType &or); + INLINE const MaskType &get_or() const; + + virtual NodeTransition *compose(const NodeTransition *other) const; + virtual NodeTransition *invert() const; + virtual NodeAttribute *apply(const NodeAttribute *attrib) const; + + virtual void output(ostream &out) const; + +protected: + virtual int internal_compare_to(const NodeTransition *other) const; + +protected: + virtual BitMaskTransition * + make_with_masks(const MaskType &and, const MaskType &or) const=0; + +private: + MaskType _and, _or; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeTransition::init_type(); + MaskType::init_type(); + register_type(_type_handle, + string("BitMaskTransition<") + + MaskType::get_class_type().get_name() + ">", + NodeTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "bitMaskTransition.I" + +#endif diff --git a/panda/src/graph/boundedObject.I b/panda/src/graph/boundedObject.I new file mode 100644 index 0000000000..bea70d8a63 --- /dev/null +++ b/panda/src/graph/boundedObject.I @@ -0,0 +1,112 @@ +// Filename: boundedObject.I +// Created by: drose (02Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////// +// Function: BoundedObject::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BoundedObject:: +BoundedObject() { + _bound_type = BVT_dynamic_sphere; + _bound_stale = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundedObject::set_bound +// Access: Public +// Description: Sets the type of the bounding volume that will be +// dynamically computed for this particular node. +// Presently, this should only be BVT_dynamic_sphere. +//////////////////////////////////////////////////////////////////// +INLINE void BoundedObject:: +set_bound(BoundedObject::BoundingVolumeType type) { + nassertv(type != BVT_static); + mark_bound_stale(); + _bound_type = type; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundedObject::set_bound +// Access: Public +// Description: Explicitly sets a new bounding volume on this node. +// This will be a static bounding volume that will no +// longer be recomputed automatically. +//////////////////////////////////////////////////////////////////// +INLINE void BoundedObject:: +set_bound(const BoundingVolume &bound) { + mark_bound_stale(); + _bound_type = BVT_static; + _bound_stale = false; + _bound = bound.make_copy(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundedObject::get_bound +// Access: Public +// Description: Returns the current bounding volume on this node, +// possibly forcing a recompute. A node's bounding +// volume encloses only the node itself, irrespective of +// the nodes above or below it in the graph. This is +// different from the bounding volumes on the arcs, +// which enclose all geometry below them. +//////////////////////////////////////////////////////////////////// +INLINE const BoundingVolume &BoundedObject:: +get_bound() const { + if (_bound_type == BVT_static) { + ((BoundedObject *)this)->_bound_stale = false; + } else if (_bound_stale || _bound == (BoundingVolume *)NULL) { + ((BoundedObject *)this)->recompute_bound(); + } + return *_bound; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundedObject::mark_bound_stale +// Access: Public +// Description: Marks the current bounding volume as stale, so that +// it will be recomputed later. This may have a +// cascading effect up to the root of all graphs of +// which the node is a part. Returns true if the +// setting was changed, or false if it was already +// marked stale (or if it is a static bounding volume). +//////////////////////////////////////////////////////////////////// +INLINE bool BoundedObject:: +mark_bound_stale() { + if (_bound_stale) { + return false; + } + _bound_stale = true; + propagate_stale_bound(); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundedObject::force_bound_stale +// Access: Public +// Description: Marks the current volume as stale and propagates the +// effect at least one level, even if it had already +// been marked stale. +//////////////////////////////////////////////////////////////////// +INLINE void BoundedObject:: +force_bound_stale() { + _bound_stale = true; + propagate_stale_bound(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundedObject::is_bound_stale +// Access: Public +// Description: Returns true if the bound is currently marked stale +// and will be recomputed the next time get_bound() is +// called. +//////////////////////////////////////////////////////////////////// +INLINE bool BoundedObject:: +is_bound_stale() const { + return _bound_stale; +} diff --git a/panda/src/graph/boundedObject.N b/panda/src/graph/boundedObject.N new file mode 100644 index 0000000000..93a0d92259 --- /dev/null +++ b/panda/src/graph/boundedObject.N @@ -0,0 +1 @@ +forcetype BoundedObject diff --git a/panda/src/graph/boundedObject.cxx b/panda/src/graph/boundedObject.cxx new file mode 100644 index 0000000000..ed95e5d7b8 --- /dev/null +++ b/panda/src/graph/boundedObject.cxx @@ -0,0 +1,57 @@ +// Filename: boundedObject.cxx +// Created by: drose (02Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "boundedObject.h" +#include "config_graph.h" + +#include + +TypeHandle BoundedObject::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: BoundedObject::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +BoundedObject:: +~BoundedObject() { +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundedObject::propagate_stale_bound +// Access: Protected, Virtual +// Description: Called by BoundedObject::mark_bound_stale(), this +// should make sure that all bounding volumes that +// depend on this one are marked stale also. +//////////////////////////////////////////////////////////////////// +void BoundedObject:: +propagate_stale_bound() { +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundedObject::recompute_bound +// Access: Protected, Virtual +// Description: Recomputes the dynamic bounding volume for this +// object. The default behavior is the compute an empty +// bounding volume; this may be overridden to extend it +// to create a nonempty bounding volume. +//////////////////////////////////////////////////////////////////// +void BoundedObject:: +recompute_bound() { + switch (_bound_type) { + case BVT_dynamic_sphere: + _bound = new BoundingSphere; + break; + + default: + graph_cat.error() + << "Unexpected _bound_type: " << _bound_type + << " in BoundedObject::recompute_bound()\n"; + _bound = NULL; + } + + _bound_stale = false; + // By default, the bound is empty. +} diff --git a/panda/src/graph/boundedObject.h b/panda/src/graph/boundedObject.h new file mode 100644 index 0000000000..969616f51b --- /dev/null +++ b/panda/src/graph/boundedObject.h @@ -0,0 +1,68 @@ +// Filename: boundedObject.h +// Created by: drose (02Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BOUNDEDOBJECT_H +#define BOUNDEDOBJECT_H + +#include + +#include "boundingVolume.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : BoundedObject +// Description : This is any object (particularly in a scene graph) +// that may have a bounding volume established for it. +// The user may set a fixed bounding volume, or s/he may +// specify that the volume should be recomputed +// dynamically. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA BoundedObject { +public: + enum BoundingVolumeType { + BVT_static, + BVT_dynamic_sphere, + }; + + INLINE BoundedObject(); + virtual ~BoundedObject(); + + INLINE void set_bound(BoundingVolumeType type); + INLINE void set_bound(const BoundingVolume &volume); + INLINE const BoundingVolume &get_bound() const; + + INLINE bool mark_bound_stale(); + INLINE void force_bound_stale(); + INLINE bool is_bound_stale() const; + +protected: + virtual void propagate_stale_bound(); + virtual void recompute_bound(); + +private: + bool _bound_stale; + BoundingVolumeType _bound_type; + +protected: + PT(BoundingVolume) _bound; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + register_type(_type_handle, "BoundedObject"); + } + +private: + static TypeHandle _type_handle; +}; + +#include "boundedObject.I" + +#endif + diff --git a/panda/src/graph/config_graph.cxx b/panda/src/graph/config_graph.cxx new file mode 100644 index 0000000000..a57c077485 --- /dev/null +++ b/panda/src/graph/config_graph.cxx @@ -0,0 +1,67 @@ +// Filename: config_graph.cxx +// Created by: drose (01Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_graph.h" +#include "namedNode.h" +#include "node.h" +#include "nodeRelation.h" +#include "nodeTransition.h" +#include "nodeAttribute.h" +#include "onOffTransition.h" +#include "onOffAttribute.h" +#include "multiNodeTransition.h" +#include "multiNodeAttribute.h" +#include "immediateTransition.h" +#include "immediateAttribute.h" + +#include + +Configure(config_graph); +NotifyCategoryDef(graph, ""); +NotifyCategoryDef(wrt, graph_cat); + +ConfigureFn(config_graph) { + BoundedObject::init_type(); + NamedNode::init_type(); + Node::init_type(); + NodeRelation::init_type(); + NodeTransition::init_type(); + NodeAttribute::init_type(); + OnOffTransition::init_type(); + OnOffAttribute::init_type(); + MultiNodeTransition::init_type(); + MultiNodeAttribute::init_type(); + ImmediateTransition::init_type(); + ImmediateAttribute::init_type(); + + NodeRelation::register_with_factory(); + + //Registration of writeable object's creation + //functions with BamReader's factory + Node::register_with_read_factory(); + NamedNode::register_with_read_factory(); + NodeRelation::register_with_read_factory(); +} + +// Set this true if you want to cache some of the work of computing +// wrt(), so that the second time wrt() is called on a particular +// node-node pair it will be much cheaper than the first time. This +// is the default behavior; you'd only want to turn it off if for some +// reason it was broken. +const bool cache_wrt = config_graph.GetBool("cache-wrt", true); + +// Set this true to double-check the cached value of wrt(), above, by +// performing an explicit uncached wrt() and comparing the results. +// Obviously very slow. This cannot be turned on in optimized +// (NDEBUG) mode. +const bool paranoid_wrt = config_graph.GetBool("paranoid-wrt", false); + +// This is similar to paranoid-wrt, above. Set it true to +// double-check each parent/unparent operation, to make sure that it +// always places the arc in the correct position in the parent's arc +// list, and that the arc list always remains properly sorted. Again, +// it cannot be turned on in NDEBUG mode. +const bool paranoid_graph = config_graph.GetBool("paranoid-graph", false); + diff --git a/panda/src/graph/config_graph.h b/panda/src/graph/config_graph.h new file mode 100644 index 0000000000..f2a0ef5024 --- /dev/null +++ b/panda/src/graph/config_graph.h @@ -0,0 +1,20 @@ +// Filename: config_graph.h +// Created by: drose (01Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_GRAPH_H +#define CONFIG_GRAPH_H + +#include +#include + +NotifyCategoryDecl(graph, EXPCL_PANDA, EXPTP_PANDA); +NotifyCategoryDecl(wrt, EXPCL_PANDA, EXPTP_PANDA); + +// Configure variables for graph package. +extern const bool EXPCL_PANDA cache_wrt; +extern const bool EXPCL_PANDA paranoid_wrt; +extern const bool EXPCL_PANDA paranoid_graph; + +#endif diff --git a/panda/src/graph/dftraverser.I b/panda/src/graph/dftraverser.I new file mode 100644 index 0000000000..e5f3dd9f5d --- /dev/null +++ b/panda/src/graph/dftraverser.I @@ -0,0 +1,102 @@ +// Filename: dftraverser.cxx +// Created by: drose (26Oct98) +// +//////////////////////////////////////////////////////////////////// + +#include "dftraverser.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: DFTraverser::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE DFTraverser:: +DFTraverser(NodeRelation *arc, + Visitor &visitor, + const AttributeWrapper &initial_render_state, + const LevelState &initial_level_state, + TypeHandle graph_type) : + _visitor(visitor), + _initial_render_state(initial_render_state), + _graph_type(graph_type) +{ + traverse(arc, _initial_render_state, initial_level_state); +} + +//////////////////////////////////////////////////////////////////// +// Function: DFTraverser::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE DFTraverser:: +DFTraverser(Node *root, + Visitor &visitor, + const AttributeWrapper &initial_render_state, + const LevelState &initial_level_state, + TypeHandle graph_type) : + _visitor(visitor), + _initial_render_state(initial_render_state), + _graph_type(graph_type) +{ + LevelState level_state(initial_level_state); + traverse(root, _initial_render_state, level_state); +} + + +//////////////////////////////////////////////////////////////////// +// Function: DFTraverser::Traverse +// Access: Public +// Description: Walks to the next arc of the graph. +//////////////////////////////////////////////////////////////////// +template +void DFTraverser:: +traverse(NodeRelation *arc, AttributeWrapper state, LevelState level_state) { + nassertv(arc->get_child() != (Node *)NULL); + + // Give the visitor a chance to veto this arc. + TransitionWrapper trans = + TransitionWrapper::init_from(_initial_render_state); + trans.extract_from(arc); + + AttributeWrapper post_state(state); + post_state.apply_in_place(trans); + if (_visitor.forward_arc(arc, trans, state, post_state, level_state)) { + traverse(arc->get_child(), post_state, level_state); + _visitor.backward_arc(arc, trans, state, post_state, level_state); + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DFTraverser::Traverse +// Access: Public +// Description: Walks to the next node of the graph. +//////////////////////////////////////////////////////////////////// +template +void DFTraverser:: +traverse(Node *node, AttributeWrapper &state, LevelState &level_state) { + // Tell the visitor we reached the node, and give it a chance to + // veto our further progress. + if (_visitor.reached_node(node, state, level_state)) { + + // Look for further children of the given NodeRelation. + DownRelations::const_iterator dri; + dri = node->_children.find(_graph_type); + if (dri != node->_children.end()) { + // Here are some! + const DownRelationPointers &drp = (*dri).second; + + // Now visit each of the children in turn. + DownRelationPointers::const_iterator drpi; + for (drpi = drp.begin(); drpi != drp.end(); ++drpi) { + traverse(*drpi, state, level_state); + } + } + } +} + diff --git a/panda/src/graph/dftraverser.h b/panda/src/graph/dftraverser.h new file mode 100644 index 0000000000..d700d6cb7b --- /dev/null +++ b/panda/src/graph/dftraverser.h @@ -0,0 +1,78 @@ +// Filename: dftraverser.h +// Created by: drose (26Oct98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DFTRAVERSER_H +#define DFTRAVERSER_H + +#include + +#include +#include +#include + +/////////////////////////////////////////////////////////////////// +// Class : DFTraverser +// Description : Implements a depth-first traversal of the graph +// beginning at the indicated node or arc. DFTraverser +// will also call visitor.forward_arc() and +// visitor.backward_arc() to allow the visitor to manage +// state. See traverserVisitor.h. +//////////////////////////////////////////////////////////////////// +template +class DFTraverser { +public: + typedef TYPENAME Visitor::TransitionWrapper TransitionWrapper; + typedef TYPENAME Visitor::AttributeWrapper AttributeWrapper; + + INLINE DFTraverser(NodeRelation *arc, + Visitor &visitor, + const AttributeWrapper &initial_render_state, + const LevelState &initial_level_state, + TypeHandle graph_type); + INLINE DFTraverser(Node *root, + Visitor &visitor, + const AttributeWrapper &initial_render_state, + const LevelState &initial_level_state, + TypeHandle graph_type); + +protected: + void traverse(NodeRelation *arc, + AttributeWrapper render_state, + LevelState level_state); + void traverse(Node *node, + AttributeWrapper &render_state, + LevelState &level_state); + + Visitor &_visitor; + AttributeWrapper _initial_render_state; + TypeHandle _graph_type; +}; + +// Convenience functions. +template +INLINE void +df_traverse(NodeRelation *arc, Visitor &visitor, + const AttributeWrapper &initial_render_state, + const LevelState &initial_level_state, + TypeHandle graph_type) { + DFTraverser + dft(arc, visitor, initial_render_state, + initial_level_state, graph_type); +} + +template +INLINE void +df_traverse(Node *root, Visitor &visitor, + const AttributeWrapper &initial_render_state, + const LevelState &initial_level_state, + TypeHandle graph_type) { + DFTraverser + dft(root, visitor, initial_render_state, + initial_level_state, graph_type); +} + +#include "dftraverser.I" + +#endif diff --git a/panda/src/graph/graphReducer.cxx b/panda/src/graph/graphReducer.cxx new file mode 100644 index 0000000000..8b376d80ff --- /dev/null +++ b/panda/src/graph/graphReducer.cxx @@ -0,0 +1,446 @@ +// Filename: graphReducer.cxx +// Created by: drose (26Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "graphReducer.h" +#include "config_graph.h" +#include "namedNode.h" +#include "pt_Node.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: GraphReducer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphReducer:: +GraphReducer(TypeHandle graph_type) : + _graph_type(graph_type) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphReducer::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GraphReducer:: +~GraphReducer() { +} + + +//////////////////////////////////////////////////////////////////// +// Function: GraphReducer::flatten +// Access: Public +// Description: Simplifies the graph by removing unnecessary nodes +// and arcs. +// +// In general, a node (and its parent arc) is a +// candidate for removal if the node has no siblings and +// the node and arc have no special properties. The +// definition of what, precisely, is a 'special +// property' may be extended by subclassing from this +// type and redefining consider_arc() appropriately. +// +// If combine_siblings is true, sibling nodes may also +// be collapsed into a single node. This will further +// reduce scene graph complexity, sometimes +// substantially, at the cost of reduced spatial +// separation. +// +// Returns the number of arcs removed from the graph. +//////////////////////////////////////////////////////////////////// +int GraphReducer:: +flatten(Node *root, bool combine_siblings) { + int num_total_nodes = 0; + int num_pass_nodes; + + do { + num_pass_nodes = 0; + + DownRelations::const_iterator dri; + dri = root->_children.find(_graph_type); + if (dri != root->_children.end()) { + const DownRelationPointers &drp = (*dri).second; + + // Get a copy of the children list, so we don't have to worry + // about self-modifications. + DownRelationPointers drp_copy = drp; + + // Now visit each of the children in turn. + DownRelationPointers::const_iterator drpi; + for (drpi = drp_copy.begin(); drpi != drp_copy.end(); ++drpi) { + NodeRelation *arc = (*drpi); + num_pass_nodes += r_flatten(arc->get_child(), combine_siblings); + } + } + + num_total_nodes += num_pass_nodes; + + // If combine_siblings is true, we should repeat the above until + // we don't get any more benefit from flattening, because each + // pass could convert cousins into siblings, which may get + // flattened next pass. If combine_siblings is not true, the + // first pass will be fully effective, and there's no point in + // trying again. + } while (combine_siblings && num_pass_nodes != 0); + + return num_total_nodes; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphReducer::r_flatten +// Access: Protected +// Description: The recursive implementation of flatten(). +//////////////////////////////////////////////////////////////////// +int GraphReducer:: +r_flatten(Node *root, bool combine_siblings) { + int num_nodes = 0; + + DownRelations::const_iterator dri; + dri = root->_children.find(_graph_type); + if (dri != root->_children.end()) { + const DownRelationPointers &drp = (*dri).second; + + // Get a copy of the children list, so we don't have to worry + // about self-modifications. + DownRelationPointers drp_copy = drp; + + // Now visit each of the children in turn. + DownRelationPointers::const_iterator drpi; + for (drpi = drp_copy.begin(); drpi != drp_copy.end(); ++drpi) { + NodeRelation *arc = (*drpi); + num_nodes += r_flatten(arc->get_child(), combine_siblings); + } + + if (combine_siblings && drp.size() >= 2) { + num_nodes += flatten_siblings(root); + } + + if (drp.size() == 1) { + // If we have exactly one child, consider flattening it. + NodeRelation *arc = *drp.begin(); + if (consider_arc(arc)) { + if (flatten_arc(arc)) { + num_nodes++; + } + } + } + } + + return num_nodes; +} + +class SortByTransitions { +public: + INLINE bool + operator () (const NodeRelation *arc1, const NodeRelation *arc2) const; +}; + +INLINE bool SortByTransitions:: +operator () (const NodeRelation *arc1, const NodeRelation *arc2) const { + return (arc1->compare_transitions_to(arc2) < 0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: GraphReducer::flatten_siblings +// Access: Protected +// Description: Attempts to collapse together any pairs of siblings +// of the indicated node that share the same properties. +//////////////////////////////////////////////////////////////////// +int GraphReducer:: +flatten_siblings(Node *root) { + int num_nodes = 0; + + // First, collect the children into groups of arcs with common + // properties. + typedef map, SortByTransitions> Children; + Children children; + + DownRelations::const_iterator dri; + dri = root->_children.find(_graph_type); + if (dri != root->_children.end()) { + const DownRelationPointers &drp = (*dri).second; + + DownRelationPointers::const_iterator drpi; + for (drpi = drp.begin(); drpi != drp.end(); ++drpi) { + NodeRelation *arc = (*drpi); + + children[arc].push_back(arc); + } + } + + // Now visit each of those groups and try to collapse them together. + Children::iterator ci; + for (ci = children.begin(); ci != children.end(); ++ci) { + list &arcs = (*ci).second; + + list::iterator ai1; + ai1 = arcs.begin(); + while (ai1 != arcs.end()) { + list::iterator ai1_hold = ai1; + NodeRelation *arc1 = (*ai1); + ++ai1; + list::iterator ai2 = ai1; + while (ai2 != arcs.end()) { + list::iterator ai2_hold = ai2; + NodeRelation *arc2 = (*ai2); + ++ai2; + + if (consider_siblings(root, arc1, arc2)) { + NodeRelation *new_arc = collapse_siblings(root, arc1, arc2); + if (new_arc != (NodeRelation *)NULL) { + // We successfully collapsed an arc. + arcs.erase(ai2_hold); + arcs.erase(ai1_hold); + arcs.push_back(new_arc); + ai1 = arcs.begin(); + ai2 = arcs.end(); + num_nodes++; + } + } + } + } + } + + return num_nodes; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphReducer::consider_arc +// Access: Protected, Virtual +// Description: Decides whether or not the indicated arc is a +// suitable candidate for removal. Returns true if the +// arc may be removed, false if it should be kept. This +// function may be extended in a user class to protect +// special kinds of arcs from deletion. +//////////////////////////////////////////////////////////////////// +bool GraphReducer:: +consider_arc(NodeRelation *arc) { + if (arc->has_sub_render_trans()) { + if (graph_cat.is_debug()) { + graph_cat.debug() + << "Not removing " << *arc + << " because it contains a sub_render transition.\n"; + } + return false; + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphReducer::consider_siblings +// Access: Protected, Virtual +// Description: Decides whether or not the indicated sibling nodes +// (and their associated arcs) should be collapsed into +// a single node or not. Returns true if the arcs may +// be collapsed, false if they should be kept distinct. +//////////////////////////////////////////////////////////////////// +bool GraphReducer:: +consider_siblings(Node *, NodeRelation *arc1, NodeRelation *arc2) { + // Don't attempt to combine any sibling arcs with different + // transitions. + return (arc1->compare_transitions_to(arc2) == 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphReducer::flatten_arc +// Access: Protected, Virtual +// Description: Removes the indicated arc, collapsing together the +// two nodes and leaving them parented in the same +// place. The return value is true if the arc is +// successfully collapsed, false if we chickened out. +// +// This function may be extended in a user class to +// handle special kinds of nodes. +//////////////////////////////////////////////////////////////////// +bool GraphReducer:: +flatten_arc(NodeRelation *arc) { + PT_Node parent = arc->get_parent(); + PT_Node child = arc->get_child(); + + if (graph_cat.is_debug()) { + graph_cat.debug() + << "Removing " << *arc << "\n"; + } + + PT_Node new_parent = collapse_nodes(parent, child, false); + if (new_parent == (Node *)NULL) { + if (graph_cat.is_debug()) { + graph_cat.debug() + << "Decided not to remove " << *arc << "\n"; + } + return false; + } + + choose_name(new_parent, parent, child); + + move_children(new_parent, parent); + move_children(new_parent, child); + + // Move all of the transitions from the arc to the parent's arcs. + int num_grandparents = parent->get_num_parents(_graph_type); + for (int i = 0; i < num_grandparents; i++) { + NodeRelation *grandparent_arc = parent->get_parent(_graph_type, i); + + grandparent_arc->compose_transitions_from(arc); + if (new_parent != parent) { + // Also switch out the parent node. + grandparent_arc->change_child(new_parent); + } + } + + remove_arc(arc); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphReducer::collapse_siblings +// Access: Protected, Virtual +// Description: Performs the work of collapsing two sibling arcs +// together into a single arc (and their associated +// nodes into a single node). +// +// Returns a pointer to a NodeRelation the reflects the +// combined arc (which may be either of the source arcs, +// or a new arc altogether) if the siblings are +// successfully collapsed, or NULL if we chickened out. +//////////////////////////////////////////////////////////////////// +NodeRelation *GraphReducer:: +collapse_siblings(Node *parent, NodeRelation *arc1, NodeRelation *arc2) { + PT_Node node1 = arc1->get_child(); + PT_Node node2 = arc2->get_child(); + + PT_Node new_node = collapse_nodes(node1, node2, true); + if (new_node == (Node *)NULL) { + graph_cat.debug() + << "Decided not to collapse " << *node1 << " and " << *node2 << "\n"; + return NULL; + } + + move_children(new_node, node1); + move_children(new_node, node2); + + arc1->change_parent_and_child(parent, node1); + remove_arc(arc2); + + return arc1; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphReducer::collapse_nodes +// Access: Protected, Virtual +// Description: Collapses the two nodes into a single node, if +// possible. The 'siblings' flag is true if the two +// nodes are siblings nodes; otherwise, node1 is a +// parent of node2. The return value is the resulting +// node, which may be either one of the source nodes, or +// a new node altogether, or it may be NULL to indicate +// that the collapse operation could not take place. +// +// This function may be extended in a user class to +// handle combining special kinds of nodes. +//////////////////////////////////////////////////////////////////// +Node *GraphReducer:: +collapse_nodes(Node *node1, Node *node2, bool) { + // We get to choose whether to remove node1 or node2. + if (node2->is_exact_type(Node::get_class_type()) || + node2->is_exact_type(NamedNode::get_class_type())) { + // Node2 isn't anything special, so preserve node1. + return node1; + + } else if (node1->is_exact_type(Node::get_class_type()) || + node1->is_exact_type(NamedNode::get_class_type())) { + // Node1 isn't anything special, so preserve node2. + return node2; + + } else { + // Both node1 and node2 are some special kind of node. Don't + // want to risk removing either of them. + return NULL; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphReducer::choose_name +// Access: Protected, Virtual +// Description: Chooses a suitable name for the collapsed node, based +// on the names of the two sources nodes. +//////////////////////////////////////////////////////////////////// +void GraphReducer:: +choose_name(Node *preserve, Node *source1, Node *source2) { + if (!preserve->is_of_type(NamedNode::get_class_type())) { + // It's not even a namable class, so we can't name it anyway. + // Never mind. + return; + } + + NamedNode *named_preserve; + DCAST_INTO_V(named_preserve, preserve); + + if (source1->is_of_type(NamedNode::get_class_type())) { + NamedNode *named_source1; + DCAST_INTO_V(named_source1, source1); + + if (!named_source1->get_name().empty()) { + named_preserve->set_name(named_source1->get_name()); + return; + } + } + + if (source2->is_of_type(NamedNode::get_class_type())) { + NamedNode *named_source2; + DCAST_INTO_V(named_source2, source2); + + if (!named_source2->get_name().empty()) { + named_preserve->set_name(named_source2->get_name()); + return; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphReducer::move_children +// Access: Protected +// Description: Moves all of the children arcs of the 'from' node to +// the 'to' node. +//////////////////////////////////////////////////////////////////// +void GraphReducer:: +move_children(Node *to, Node *from) { + if (to != from) { + int num_children = from->get_num_children(_graph_type); + while (num_children > 0) { + NodeRelation *arc = + from->get_child(_graph_type, num_children - 1); + arc->change_parent(to); + num_children--; + nassertv(num_children == from->get_num_children(_graph_type)); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphReducer::copy_children +// Access: Protected +// Description: Copies all of the children arcs of the 'from' node to +// the 'to' node, without removing the from the 'from' +// node. +//////////////////////////////////////////////////////////////////// +void GraphReducer:: +copy_children(Node *to, Node *from) { + if (to != from) { + int num_children = from->get_num_children(_graph_type); + for (int i = 0; i < num_children; i++) { + NodeRelation *arc = from->get_child(_graph_type, i); + NodeRelation *new_arc = NodeRelation::create_typed_arc + (_graph_type, to, arc->get_child()); + nassertv(new_arc != (NodeRelation *)NULL); + nassertv(new_arc->is_exact_type(_graph_type)); + new_arc->copy_transitions_from(arc); + } + } +} diff --git a/panda/src/graph/graphReducer.h b/panda/src/graph/graphReducer.h new file mode 100644 index 0000000000..734929706a --- /dev/null +++ b/panda/src/graph/graphReducer.h @@ -0,0 +1,57 @@ +// Filename: graphReducer.h +// Created by: drose (26Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GRAPHREDUCER_H +#define GRAPHREDUCER_H + +#include + +#include "nodeRelation.h" + +#include + +/////////////////////////////////////////////////////////////////// +// Class : GraphReducer +// Description : A generic interface to simplify a graph in various +// ways, generally as a performance optimization. This +// class is designed for generic graphs; also see the +// SceneGraphReducer (which inherits from this class) +// for a specific class to optimize renderable scene +// graphs. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GraphReducer { +public: + GraphReducer(TypeHandle graph_type); + virtual ~GraphReducer(); + + int flatten(Node *root, bool combine_siblings); + +protected: + int r_flatten(Node *root, bool combine_siblings); + int flatten_siblings(Node *root); + + virtual bool consider_arc(NodeRelation *arc); + virtual bool consider_siblings(Node *parent, + NodeRelation *arc1, NodeRelation *arc2); + + virtual bool flatten_arc(NodeRelation *arc); + virtual NodeRelation *collapse_siblings(Node *parent, NodeRelation *arc1, + NodeRelation *arc2); + + virtual Node *collapse_nodes(Node *node1, Node *node2, bool siblings); + virtual void choose_name(Node *preserve, Node *source1, Node *source2); + +protected: + void move_children(Node *to, Node *from); + void copy_children(Node *to, Node *from); + +protected: + TypeHandle _graph_type; +}; + +#endif + + + diff --git a/panda/src/graph/immediateAttribute.I b/panda/src/graph/immediateAttribute.I new file mode 100644 index 0000000000..5d760c405c --- /dev/null +++ b/panda/src/graph/immediateAttribute.I @@ -0,0 +1,34 @@ +// Filename: immediateAttribute.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ImmediateAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ImmediateAttribute:: +ImmediateAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ImmediateAttribute::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ImmediateAttribute:: +ImmediateAttribute(const ImmediateAttribute ©) : + NodeAttribute(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ImmediateAttribute::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ImmediateAttribute:: +operator = (const ImmediateAttribute ©) { + NodeAttribute::operator = (copy); +} diff --git a/panda/src/graph/immediateAttribute.cxx b/panda/src/graph/immediateAttribute.cxx new file mode 100644 index 0000000000..d7d4387178 --- /dev/null +++ b/panda/src/graph/immediateAttribute.cxx @@ -0,0 +1,51 @@ +// Filename: immediateAttribute.cxx +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "immediateAttribute.h" +#include "immediateTransition.h" + +#include + +TypeHandle ImmediateAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ImmediateAttribute::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *ImmediateAttribute:: +make_copy() const { + return new ImmediateAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ImmediateAttribute::make_initial +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *ImmediateAttribute:: +make_initial() const { + return new ImmediateAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ImmediateAttribute::get_handle +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +TypeHandle ImmediateAttribute:: +get_handle() const { + return ImmediateTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ImmediateAttribute::internal_compare_to +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int ImmediateAttribute:: +internal_compare_to(const NodeAttribute *) const { + return 0; +} diff --git a/panda/src/graph/immediateAttribute.h b/panda/src/graph/immediateAttribute.h new file mode 100644 index 0000000000..f6af4ec77b --- /dev/null +++ b/panda/src/graph/immediateAttribute.h @@ -0,0 +1,53 @@ +// Filename: immediateAttribute.h +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef IMMEDIATEATTRIBUTE_H +#define IMMEDIATEATTRIBUTE_H + +#include + +#include "nodeAttribute.h" + +class ImmediateTransition; + +//////////////////////////////////////////////////////////////////// +// Class : ImmediateAttribute +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ImmediateAttribute : public NodeAttribute { +public: + INLINE ImmediateAttribute(); + INLINE ImmediateAttribute(const ImmediateAttribute ©); + INLINE void operator = (const ImmediateAttribute ©); + +public: + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + virtual TypeHandle get_handle() const; + +protected: + virtual int internal_compare_to(const NodeAttribute *other) const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeAttribute::init_type(); + register_type(_type_handle, "ImmediateAttribute", + NodeAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "immediateAttribute.I" + +#endif diff --git a/panda/src/graph/immediateTransition.I b/panda/src/graph/immediateTransition.I new file mode 100644 index 0000000000..11c110e3b1 --- /dev/null +++ b/panda/src/graph/immediateTransition.I @@ -0,0 +1,34 @@ +// Filename: immediateTransition.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ImmediateTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ImmediateTransition:: +ImmediateTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ImmediateTransition::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ImmediateTransition:: +ImmediateTransition(const ImmediateTransition ©) : + NodeTransition(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ImmediateTransition::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ImmediateTransition:: +operator = (const ImmediateTransition ©) { + NodeTransition::operator = (copy); +} diff --git a/panda/src/graph/immediateTransition.cxx b/panda/src/graph/immediateTransition.cxx new file mode 100644 index 0000000000..de13d5dd18 --- /dev/null +++ b/panda/src/graph/immediateTransition.cxx @@ -0,0 +1,75 @@ +// Filename: immediateTransition.cxx +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "immediateTransition.h" +#include "immediateAttribute.h" + +#include + +TypeHandle ImmediateTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ImmediateTransition::make_attrib +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttribute *ImmediateTransition:: +make_attrib() const { + return new ImmediateAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ImmediateTransition::compose +// Access: Public, Virtual +// Description: Returns a new transition that corresponds to the +// composition of this transition with the second +// transition (which must be of an equivalent type). +// This may return the same pointer as either source +// transition. Applying the transition returned from +// this function to an attribute attribute will produce +// the same effect as applying each transition +// separately. +//////////////////////////////////////////////////////////////////// +NodeTransition *ImmediateTransition:: +compose(const NodeTransition *) const { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: ImmediateTransition::invert +// Access: Public, Virtual +// Description: Returns a new transition that corresponds to the +// inverse of this transition. If the transition was +// identity, this may return the same pointer. Returns +// NULL if the transition cannot be inverted. +//////////////////////////////////////////////////////////////////// +NodeTransition *ImmediateTransition:: +invert() const { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: ImmediateTransition::apply +// Access: Public, Virtual +// Description: Returns a new attribute (or possibly the same +// attribute) that represents the effect of applying this +// indicated transition to the indicated attribute. The +// source attribute may be NULL, indicating the initial +// attribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ImmediateTransition:: +apply(const NodeAttribute *) const { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: ImmediateTransition::internal_compare_to +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int ImmediateTransition:: +internal_compare_to(const NodeTransition *) const { + return 0; +} diff --git a/panda/src/graph/immediateTransition.h b/panda/src/graph/immediateTransition.h new file mode 100644 index 0000000000..2e24d77401 --- /dev/null +++ b/panda/src/graph/immediateTransition.h @@ -0,0 +1,78 @@ +// Filename: immediateTransition.h +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef IMMEDIATETRANSITION_H +#define IMMEDIATETRANSITION_H + +#include + +#include "nodeTransition.h" + +class NodeAttribute; +class NodeRelation; + +//////////////////////////////////////////////////////////////////// +// Class : ImmediateTransition +// Description : This is a special kind of transition that doesn't +// accumulate state, but instead has some immediate +// effect when it is encountered in the scene graph. +// BillboardTransition and ShaderTransition are examples +// of these; rather than changing the state of geometry +// below them, they cause everything to be rendered +// differently somehow from that point and below. +// +// The compose() etc. methods for an ImmediateTransition +// do nothing. Presumably any transitions that inherit +// from ImmediateTransition will redefine sub_render() +// to do something suitably interesting. +// +// Classes that derive from ImmediateTransition need not +// also define a corresponding derivation from +// ImmediateAttribute; since the attribute does not +// represent any actual state, you may use +// ImmediateAttribute directly (without extending it). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ImmediateTransition : public NodeTransition { +protected: + INLINE ImmediateTransition(); + INLINE ImmediateTransition(const ImmediateTransition ©); + INLINE void operator = (const ImmediateTransition ©); + +public: + virtual NodeAttribute *make_attrib() const; + + virtual NodeTransition *compose(const NodeTransition *other) const; + virtual NodeTransition *invert() const; + virtual NodeAttribute *apply(const NodeAttribute *attrib) const; + +protected: + virtual int internal_compare_to(const NodeTransition *other) const; + + //ImmediateTransition is merely an interface class, so no need to + //define the code necessary for anything relating to reading/writing + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeTransition::init_type(); + register_type(_type_handle, "ImmediateTransition", + NodeTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "immediateTransition.I" + +#endif + + diff --git a/panda/src/graph/lmatrix4fTransition.cxx b/panda/src/graph/lmatrix4fTransition.cxx new file mode 100644 index 0000000000..439e78fd0c --- /dev/null +++ b/panda/src/graph/lmatrix4fTransition.cxx @@ -0,0 +1,12 @@ +// Filename: lmatrix4fTransition.cxx +// Created by: drose (05May00) +// +//////////////////////////////////////////////////////////////////// + +#include "lmatrix4fTransition.h" + +#ifdef __GNUC__ +#pragma implementation +#endif + + diff --git a/panda/src/graph/lmatrix4fTransition.h b/panda/src/graph/lmatrix4fTransition.h new file mode 100644 index 0000000000..a6ebf9fdbe --- /dev/null +++ b/panda/src/graph/lmatrix4fTransition.h @@ -0,0 +1,35 @@ +// Filename: lmatrix4fTransition.h +// Created by: drose (05May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LMATRIX4FTRANSITION_H +#define LMATRIX4FTRANSITION_H + +#include + +#include "matrixTransition.h" +#include "matrixAttribute.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : LMatrix4fTransition +// Description : This is just an instantation of MatrixTransition +// using LMatrix4f, the most common transform matrix +// type. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, MatrixTransition); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, MatrixAttribute); + +typedef MatrixTransition LMatrix4fTransition; +typedef MatrixAttribute LMatrix4fAttribute; + +#ifdef __GNUC__ +#pragma interface +#endif + +#endif + + diff --git a/panda/src/graph/matrixAttribute.I b/panda/src/graph/matrixAttribute.I new file mode 100644 index 0000000000..4e6ef12a8b --- /dev/null +++ b/panda/src/graph/matrixAttribute.I @@ -0,0 +1,103 @@ +// Filename: matrixAttribute.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include + +template +TypeHandle MatrixAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: MatrixAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE MatrixAttribute:: +MatrixAttribute() { + _matrix = Matrix::ident_mat(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixAttribute::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE MatrixAttribute:: +MatrixAttribute(const MatrixAttribute ©) : + NodeAttribute(copy), + _matrix(copy._matrix) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixAttribute::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void MatrixAttribute:: +operator = (const MatrixAttribute ©) { + NodeAttribute::operator = (copy); + _matrix = copy._matrix; +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixAttribute::set_matrix +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void MatrixAttribute:: +set_matrix(const Matrix &mat) { + _matrix = mat; +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixAttribute::get_matrix +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE const Matrix &MatrixAttribute:: +get_matrix() const { + return _matrix; +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixAttribute::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void MatrixAttribute:: +output(ostream &out) const { + out << _matrix; +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixAttribute::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void MatrixAttribute:: +write(ostream &out, int indent_level) const { + _matrix.write(out, indent_level); +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixAttribute::internal_compare_to +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +int MatrixAttribute:: +internal_compare_to(const NodeAttribute *other) const { + const MatrixAttribute *ot; + DCAST_INTO_R(ot, other, false); + + return _matrix.compare_to(ot->_matrix); +} diff --git a/panda/src/graph/matrixAttribute.h b/panda/src/graph/matrixAttribute.h new file mode 100644 index 0000000000..177308c6cc --- /dev/null +++ b/panda/src/graph/matrixAttribute.h @@ -0,0 +1,64 @@ +// Filename: matrixAttribute.h +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MATRIXATTRIBUTE_H +#define MATRIXATTRIBUTE_H + +#include + +#include "nodeAttribute.h" + +template +class MatrixTransition; + +//////////////////////////////////////////////////////////////////// +// Class : MatrixAttribute +// Description : +//////////////////////////////////////////////////////////////////// +template +class MatrixAttribute : public NodeAttribute { +protected: + INLINE MatrixAttribute(); + INLINE MatrixAttribute(const MatrixAttribute ©); + INLINE void operator = (const MatrixAttribute ©); + +public: + INLINE void set_matrix(const Matrix &mat); + INLINE const Matrix &get_matrix() const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeAttribute *other) const; + +private: + Matrix _matrix; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeAttribute::init_type(); + Matrix::init_type(); + register_type(_type_handle, + string("MatrixAttribute<") + + Matrix::get_class_type().get_name() + ">", + NodeAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +friend class MatrixTransition; +}; + +#include "matrixAttribute.I" + +#endif diff --git a/panda/src/graph/matrixTransition.I b/panda/src/graph/matrixTransition.I new file mode 100644 index 0000000000..e33cc6787c --- /dev/null +++ b/panda/src/graph/matrixTransition.I @@ -0,0 +1,229 @@ +// Filename: matrixTransition.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "matrixAttribute.h" + +#include + +template +TypeHandle MatrixTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: MatrixTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE MatrixTransition:: +MatrixTransition() { + _matrix = Matrix::ident_mat(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE MatrixTransition:: +MatrixTransition(const Matrix &matrix) { + _matrix = matrix; +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixTransition::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE MatrixTransition:: +MatrixTransition(const MatrixTransition ©) : + NodeTransition(copy), + _matrix(copy._matrix) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixTransition::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void MatrixTransition:: +operator = (const MatrixTransition ©) { + NodeTransition::operator = (copy); + _matrix = copy._matrix; +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixTransition::set_matrix +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void MatrixTransition:: +set_matrix(const Matrix &mat) { + _matrix = mat; + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixTransition::get_matrix +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE const Matrix &MatrixTransition:: +get_matrix() const { + return _matrix; +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixTransition::compose +// Access: Public, Virtual +// Description: Returns a new transition that corresponds to the +// composition of this transition with the second +// transition (which must be of an equivalent type). +// This may return the same pointer as either source +// transition. Applying the transition returned from +// this function to an attribute attribute will produce +// the same effect as applying each transition +// separately. +//////////////////////////////////////////////////////////////////// +template +NodeTransition *MatrixTransition:: +compose(const NodeTransition *other) const { + const MatrixTransition *ot; + DCAST_INTO_R(ot, other, NULL); + + return make_with_matrix(ot->_matrix * _matrix); +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixTransition::invert +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +NodeTransition *MatrixTransition:: +invert() const { + if (_matrix.almost_equal(Matrix::ident_mat())) { + return (MatrixTransition *)this; + } + return make_with_matrix(::invert(_matrix)); +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixTransition::apply +// Access: Public, Virtual +// Description: Returns a new attribute (or possibly the same +// attribute) that represents the effect of applying this +// indicated transition to the indicated attribute. The +// source attribute may be NULL, indicating the initial +// attribute. +//////////////////////////////////////////////////////////////////// +template +NodeAttribute *MatrixTransition:: +apply(const NodeAttribute *attrib) const { + + MatrixAttribute *result; + DCAST_INTO_R(result, make_attrib(), NULL); + + if (attrib == (const NodeAttribute *)NULL) { + result->_matrix = _matrix; + return result; + } + + const MatrixAttribute *at; + DCAST_INTO_R(at, attrib, NULL); + + result->_matrix = _matrix * at->_matrix; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixTransition::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void MatrixTransition:: +output(ostream &out) const { + out << _matrix; +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixTransition::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void MatrixTransition:: +write(ostream &out, int indent_level) const { + _matrix.write(out, indent_level); +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixTransition::internal_compare_to +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +int MatrixTransition:: +internal_compare_to(const NodeTransition *other) const { + const MatrixTransition *ot; + DCAST_INTO_R(ot, other, false); + + // return _matrix.compare_to(ot->_matrix); + return this - other; +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixTransition::make_with_matrix +// Access: Protected, Virtual +// Description: This function creates a new MatrixTransition of the +// appropriate type, given the indicated matrix. +// +// This is a pure virtual function and normally wouldn't +// require a definition, but for some reason VC++ +// objects to instantiating the template if it's missing +// any function definitions--even those for pure-virtual +// functions. +//////////////////////////////////////////////////////////////////// +template +MatrixTransition *MatrixTransition:: +make_with_matrix(const Matrix &) const { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixTransition::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +template +void MatrixTransition:: +write_datagram(BamWriter *manager, Datagram &me) +{ + NodeTransition::write_datagram(manager, me); + _matrix.write_datagram(me); +} + +//////////////////////////////////////////////////////////////////// +// Function: MatrixTransition::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +template +void MatrixTransition:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + NodeTransition::fillin(scan, manager); + _matrix.read_datagram(scan); +} diff --git a/panda/src/graph/matrixTransition.h b/panda/src/graph/matrixTransition.h new file mode 100644 index 0000000000..e808d94d41 --- /dev/null +++ b/panda/src/graph/matrixTransition.h @@ -0,0 +1,87 @@ +// Filename: matrixTransition.h +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MATRIXTRANSITION_H +#define MATRIXTRANSITION_H + +#include + +#include "nodeTransition.h" + +class NodeAttribute; +class NodeRelation; + +//////////////////////////////////////////////////////////////////// +// Class : MatrixTransition +// Description : This is an abstract template class that encapsulates +// all transitions that involve some kind of matrix, +// either a 4x4 or a 3x3, either float or double. It +// templates on the matrix type. +// +// It's the base class for a number of scene graph +// transitions like TransformTransition and +// TexMatrixTransition. +//////////////////////////////////////////////////////////////////// +template +class MatrixTransition : public NodeTransition { +protected: + INLINE MatrixTransition(); + INLINE MatrixTransition(const Matrix &matrix); + INLINE MatrixTransition(const MatrixTransition ©); + INLINE void operator = (const MatrixTransition ©); + +public: + INLINE void set_matrix(const Matrix &mat); + INLINE const Matrix &get_matrix() const; + + virtual NodeTransition *compose(const NodeTransition *other) const; + virtual NodeTransition *invert() const; + virtual NodeAttribute *apply(const NodeAttribute *attrib) const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeTransition *other) const; + +protected: + virtual MatrixTransition * + make_with_matrix(const Matrix &matrix) const=0; + +private: + Matrix _matrix; + +public: + virtual void write_datagram(BamWriter* manager, Datagram &me); + + //Matrix has no factory method as it is a virtual template + //class +protected: + virtual void fillin(DatagramIterator& scan, BamReader* manager); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeTransition::init_type(); + Matrix::init_type(); + register_type(_type_handle, + string("MatrixTransition<") + + Matrix::get_class_type().get_name() + ">", + NodeTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "matrixTransition.I" + +#endif diff --git a/panda/src/graph/multiAttribute.I b/panda/src/graph/multiAttribute.I new file mode 100644 index 0000000000..3a289efe37 --- /dev/null +++ b/panda/src/graph/multiAttribute.I @@ -0,0 +1,330 @@ +// Filename: multiAttribute.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "multiTransition.h" +#include "multiTransitionHelpers.h" + +#include +#include + +template +TypeHandle MultiAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +MultiAttribute:: +MultiAttribute() { + _properties_is_on = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +MultiAttribute:: +MultiAttribute(const MultiAttribute ©) : + NodeAttribute(copy), + _properties(copy._properties), + _properties_is_on(copy._properties_is_on) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void MultiAttribute:: +operator = (const MultiAttribute ©) { + NodeAttribute::operator = (copy); + _properties = copy._properties; + _properties_is_on = copy._properties_is_on; +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::set_on +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void MultiAttribute:: +set_on(const Property &property) { + if (_properties_is_on) { + set_property(property); + } else { + clear_property(property); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::set_off +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void MultiAttribute:: +set_off(const Property &property) { + if (_properties_is_on) { + clear_property(property); + } else { + set_property(property); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::is_on +// Access: Public +// Description: Returns true if the particular properties member is +// on, false if it is not. +//////////////////////////////////////////////////////////////////// +template +bool MultiAttribute:: +is_on(const Property &property) const { + if (_properties_is_on) { + return has_property(property); + } else { + return !has_property(property); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::is_off +// Access: Public +// Description: Returns true if the particular properties member is +// off, false if it is not. +//////////////////////////////////////////////////////////////////// +template +bool MultiAttribute:: +is_off(const Property &property) const { + if (_properties_is_on) { + return !has_property(property); + } else { + return has_property(property); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::properties_is_on +// Access: Public +// Description: Returns true if inclusion in the set traversible via +// begin()/end() indicates that the given property is +// on, the default. Returns false if inclusion in the +// set indicates that the given property is off. +//////////////////////////////////////////////////////////////////// +template +INLINE bool MultiAttribute:: +get_properties_is_on() const { + return _properties_is_on; +} + + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void MultiAttribute:: +output(ostream &out) const { + if (!_properties.empty()) { + Properties::const_iterator pi = _properties.begin(); + output_property(out, *pi); + ++pi; + while (pi != _properties.end()) { + out << " "; + output_property(out, *pi); + ++pi; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void MultiAttribute:: +write(ostream &out, int indent_level) const { + Properties::const_iterator pi; + for (pi = _properties.begin(); pi != _properties.end(); ++pi) { + write_property(out, *pi, indent_level); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::internal_compare_to +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +int MultiAttribute:: +internal_compare_to(const NodeAttribute *other) const { + const MultiAttribute *ot; + DCAST_INTO_R(ot, other, false); + + return bmap_compare(_properties.begin(), _properties.end(), + ot->_properties.begin(), ot->_properties.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::output_property +// Access: Public, Virtual +// Description: This is a pure virtual function and normally would +// not need a body, except that VC++ doesn't seem happy +// about instantiating the template otherwise. +//////////////////////////////////////////////////////////////////// +template +void MultiAttribute:: +output_property(ostream &, const Property &) const { +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::write_property +// Access: Public, Virtual +// Description: This is a pure virtual function and normally would +// not need a body, except that VC++ doesn't seem happy +// about instantiating the template otherwise. +//////////////////////////////////////////////////////////////////// +template +void MultiAttribute:: +write_property(ostream &, const Property &, int) const { +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::set_property +// Access: Private +// Description: An internal function that adds the indicated property +// to the sorted vector, if it is not already there. +//////////////////////////////////////////////////////////////////// +template +INLINE void MultiAttribute:: +set_property(const Property &prop) { + bool found_flag = false; + Properties::iterator pi = find_property(prop, found_flag); + if (!found_flag) { + _properties.insert(pi, prop); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::set_property +// Access: Private +// Description: An internal function that returns true if the +// indicated property is a member of the sorted vector, +// false if not. +//////////////////////////////////////////////////////////////////// +template +INLINE bool MultiAttribute:: +has_property(const Property &prop) const { + bool found_flag = false; + ((MultiAttribute *)this)->find_property(prop, found_flag); + return found_flag; +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::clear_property +// Access: Private +// Description: An internal function that removes the indicated +// property from the sorted vector. +//////////////////////////////////////////////////////////////////// +template +INLINE void MultiAttribute:: +clear_property(const Property &prop) { + bool found_flag = false; + Properties::iterator pi = find_property(prop, found_flag); + if (found_flag) { + _properties.erase(pi); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::find_property +// Access: Private +// Description: An internal function that searches for the indicated +// property within the sorted vector. If the property +// is found, found_flag is set true, and its iterator is +// returned; otherwise, found_flag is unchanged, and the +// iterator at which the property should be inserted is +// returned. +//////////////////////////////////////////////////////////////////// +template +INLINE MultiAttribute::Properties::iterator MultiAttribute:: +find_property(const Property &prop, bool &found_flag) { + return binary_search_property(_properties.begin(), _properties.end(), + prop, found_flag); +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::binary_search_property +// Access: Private +// Description: An internal function that implements a binary search +// for a particular property on the sorted vector. If +// the property is found, found_flag is set true, and +// its iterator is returned; otherwise, found_flag is +// unchanged, and the iterator at which the property +// should be inserted is returned. +//////////////////////////////////////////////////////////////////// +template +MultiAttribute::Properties::iterator MultiAttribute:: +binary_search_property(Properties::iterator begin, Properties::iterator end, + const Property &prop, bool &found_flag) { + if (begin == end) { + return begin; + } + Properties::iterator mid = begin + (end - begin) / 2; + if (prop < *mid) { + return binary_search_property(begin, mid, prop, found_flag); + + } else if (*mid < prop) { + return binary_search_property(mid + 1, end, prop, found_flag); + + } else { // *mid == prop + found_flag = true; + return mid; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE MultiAttribute::size_type MultiAttribute:: +size() const { + return _properties.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::begin +// Access: Public +// Description: Returns an iterator that begins the sequence of +// properties in the attribute. +//////////////////////////////////////////////////////////////////// +template +INLINE MultiAttribute::const_iterator MultiAttribute:: +begin() const { + return _properties.begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiAttribute::end +// Access: Public +// Description: Returns an iterator that marks the end of the +// sequence of properties in the attribute. +//////////////////////////////////////////////////////////////////// +template +INLINE MultiAttribute::const_iterator MultiAttribute:: +end() const { + return _properties.end(); +} diff --git a/panda/src/graph/multiAttribute.h b/panda/src/graph/multiAttribute.h new file mode 100644 index 0000000000..227d44d49b --- /dev/null +++ b/panda/src/graph/multiAttribute.h @@ -0,0 +1,105 @@ +// Filename: multiAttribute.h +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MULTIATTRIBUTE_H +#define MULTIATTRIBUTE_H + +#include + +#include "nodeAttribute.h" + +template +class MultiTransition; + +//////////////////////////////////////////////////////////////////// +// Class : MultiAttribute +// Description : +//////////////////////////////////////////////////////////////////// +template +class MultiAttribute : public NodeAttribute { +private: + typedef vector Properties; + +protected: + MultiAttribute(); + MultiAttribute(const MultiAttribute ©); + void operator = (const MultiAttribute ©); + +public: + void set_on(const Property &prop); + void set_off(const Property &prop); + + bool is_on(const Property &prop) const; + bool is_off(const Property &prop) const; + + INLINE bool get_properties_is_on() const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeAttribute *other) const; + + virtual void output_property(ostream &out, const Property &prop) const=0; + virtual void write_property(ostream &out, const Property &prop, + int indent_level) const=0; + +private: + INLINE void set_property(const Property &prop); + INLINE bool has_property(const Property &prop) const; + INLINE void clear_property(const Property &prop); + + INLINE Properties::iterator + find_property(const Property &prop, bool &found_flag); + + Properties::iterator + binary_search_property(Properties::iterator begin, Properties::iterator end, + const Property &prop, bool &found_flag); + +public: + // These functions and typedefs allow one to peruse all of the + // Properties in the transition. Remember to export the + // vector template class if you intend to use these + // outside of PANDA.DLL. + typedef Properties::const_iterator iterator; + typedef Properties::const_iterator const_iterator; + typedef Properties::value_type value_type; + typedef Properties::size_type size_type; + + INLINE size_type size() const; + INLINE const_iterator begin() const; + INLINE const_iterator end() const; + +private: + // The following is a set of properties listed in the attribute. If + // _properties_is_on is true, the default, it is a list of all 'on' + // properties; all others are off. If _properties_is_on is false, + // it is a list of all 'off' properties; all others are on. + Properties _properties; + bool _properties_is_on; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeAttribute::init_type(); + register_type(_type_handle, + string("MultiAttribute<")+NameClass::get_class_name()+">", + NodeAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +friend class MultiTransition; +}; + +#include "multiAttribute.I" + +#endif diff --git a/panda/src/graph/multiNodeAttribute.I b/panda/src/graph/multiNodeAttribute.I new file mode 100644 index 0000000000..b8620eeb77 --- /dev/null +++ b/panda/src/graph/multiNodeAttribute.I @@ -0,0 +1,34 @@ +// Filename: multiNodeAttribute.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: MultiNodeAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MultiNodeAttribute:: +MultiNodeAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiNodeAttribute::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MultiNodeAttribute:: +MultiNodeAttribute(const MultiNodeAttribute ©) : + MultiAttribute(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiNodeAttribute::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void MultiNodeAttribute:: +operator = (const MultiNodeAttribute ©) { + MultiAttribute::operator = (copy); +} diff --git a/panda/src/graph/multiNodeAttribute.cxx b/panda/src/graph/multiNodeAttribute.cxx new file mode 100644 index 0000000000..aa8fbef6ac --- /dev/null +++ b/panda/src/graph/multiNodeAttribute.cxx @@ -0,0 +1,8 @@ +// Filename: multiNodeAttribute.cxx +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "multiNodeAttribute.h" + +TypeHandle MultiNodeAttribute::_type_handle; diff --git a/panda/src/graph/multiNodeAttribute.h b/panda/src/graph/multiNodeAttribute.h new file mode 100644 index 0000000000..d17775a0fe --- /dev/null +++ b/panda/src/graph/multiNodeAttribute.h @@ -0,0 +1,58 @@ +// Filename: multiNodeAttribute.h +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MULTINODEATTRIBUTE_H +#define MULTINODEATTRIBUTE_H + +#include + +#include "multiAttribute.h" +#include "pointerNameClass.h" +#include "node.h" +#include "pt_Node.h" +#include "vector_PT_Node.h" + +#include + +class MultiNodeTransition; + +// We need to define this temporary macro so we can pass a parameter +// containing a comma through the macro. +#define MULTIATTRIBUTE_NODE MultiAttribute +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, MULTIATTRIBUTE_NODE); + +//////////////////////////////////////////////////////////////////// +// Class : MultiNodeAttribute +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA MultiNodeAttribute : + public MultiAttribute { +protected: + INLINE MultiNodeAttribute(); + INLINE MultiNodeAttribute(const MultiNodeAttribute ©); + INLINE void operator = (const MultiNodeAttribute ©); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MultiAttribute::init_type(); + register_type(_type_handle, "MultiNodeAttribute", + MultiAttribute:: + get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "multiNodeAttribute.I" + +#endif diff --git a/panda/src/graph/multiNodeTransition.I b/panda/src/graph/multiNodeTransition.I new file mode 100644 index 0000000000..543c749bd3 --- /dev/null +++ b/panda/src/graph/multiNodeTransition.I @@ -0,0 +1,34 @@ +// Filename: multiNodeTransition.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: MultiNodeTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MultiNodeTransition:: +MultiNodeTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiNodeTransition::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MultiNodeTransition:: +MultiNodeTransition(const MultiNodeTransition ©) : + MultiTransition(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiNodeTransition::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void MultiNodeTransition:: +operator = (const MultiNodeTransition ©) { + MultiTransition::operator = (copy); +} diff --git a/panda/src/graph/multiNodeTransition.cxx b/panda/src/graph/multiNodeTransition.cxx new file mode 100644 index 0000000000..ed9de3be90 --- /dev/null +++ b/panda/src/graph/multiNodeTransition.cxx @@ -0,0 +1,8 @@ +// Filename: multiNodeTransition.cxx +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "multiNodeTransition.h" + +TypeHandle MultiNodeTransition::_type_handle; diff --git a/panda/src/graph/multiNodeTransition.h b/panda/src/graph/multiNodeTransition.h new file mode 100644 index 0000000000..11c7c5be1a --- /dev/null +++ b/panda/src/graph/multiNodeTransition.h @@ -0,0 +1,66 @@ +// Filename: multiNodeTransition.h +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MULTINODETRANSITION_H +#define MULTINODETRANSITION_H + +#include + +#include "multiTransition.h" +#include "pointerNameClass.h" +#include "node.h" +#include "pt_Node.h" + +#include + +class NodeAttribute; +class NodeRelation; + +// We need to define this temporary macro so we can pass a parameter +// containing a comma through the macro. +#define MULTITRANSITION_NODE MultiTransition +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, MULTITRANSITION_NODE); + +//////////////////////////////////////////////////////////////////// +// Class : MultiNodeTransition +// Description : This is a particular instantiation of MultiTransition +// on PT_Node. It is its own class in an attempt to +// cut down on code bloat; if a particular transition +// wants to be a MultiTransition on some kind of +// Node, it should inherit from MultiNodeTransition +// (rather than instantiating a whole new kind of +// MultiTransition). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA MultiNodeTransition : + public MultiTransition { +protected: + INLINE MultiNodeTransition(); + INLINE MultiNodeTransition(const MultiNodeTransition ©); + INLINE void operator = (const MultiNodeTransition ©); + +public: + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MultiTransition::init_type(); + register_type(_type_handle, "MultiNodeTransition", + MultiTransition:: + get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "multiNodeTransition.I" + +#endif diff --git a/panda/src/graph/multiTransition.I b/panda/src/graph/multiTransition.I new file mode 100644 index 0000000000..d9ca690b8f --- /dev/null +++ b/panda/src/graph/multiTransition.I @@ -0,0 +1,561 @@ +// Filename: multiTransition.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "multiTransitionHelpers.h" +#include "multiAttribute.h" + +#include + +template +TypeHandle MultiTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +MultiTransition:: +MultiTransition() { + _default_dir = TD_identity; +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +MultiTransition:: +MultiTransition(const MultiTransition ©) : + NodeTransition(copy), + _properties(copy._properties), + _default_dir(copy._default_dir) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void MultiTransition:: +operator = (const MultiTransition ©) { + NodeTransition::operator = (copy); + _properties = copy._properties; + _default_dir = copy._default_dir; +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::clear +// Access: Public +// Description: Removes all properties from the set, and resets it to +// its initial state. +//////////////////////////////////////////////////////////////////// +template +void MultiTransition:: +clear() { + _properties.clear(); + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::is_identity +// Access: Public +// Description: Returns true if this transition does not have any +// effect. +//////////////////////////////////////////////////////////////////// +template +bool MultiTransition:: +is_identity() const { + if (_default_dir != TD_identity) { + return false; + } + + Properties::const_iterator pi; + for (pi = _properties.begin(); pi != _properties.end(); ++pi) { + if ((*pi).second != TD_identity) { + return false; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::set_default_dir +// Access: Public +// Description: Sets the default direction on the MultiTransition. +// This controls whether the MultiTransition applies +// itself by completely replacing everything that went +// before, or by adding to whatever has gone before. +// +// When this is set to either TD_on or TD_off, all +// properties which are not explicitly mentioned are +// assumed to be 'on' or 'off' transitions, +// respectively; when it is set to TD_identity, the +// default, all properties which are not explicitly +// mentions are assumed to be identity transitions and +// are left unchanged. +//////////////////////////////////////////////////////////////////// +template +INLINE void MultiTransition:: +set_default_dir(TransitionDirection dir) { + _default_dir = dir; +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::get_default_dir +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE TransitionDirection MultiTransition:: +get_default_dir() const { + return _default_dir; +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::set_identity +// Access: Public +// Description: Changes the indicated property to an identity +// transition: this property will not be changed by the +// application of the MultiTransition. +//////////////////////////////////////////////////////////////////// +template +void MultiTransition:: +set_identity(const Property &property) { + _properties[property] = TD_identity; + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::set_on +// Access: Public +// Description: Changes the indicated property to an 'on' transition: +// this property will be explicitly turned on by the +// application of the MultiTransition. +//////////////////////////////////////////////////////////////////// +template +void MultiTransition:: +set_on(const Property &property) { + _properties[property] = TD_on; + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::set_off +// Access: Public +// Description: Changes the indicated property to an 'off' transition: +// this property will be explicitly turned off by the +// application of the MultiTransition. +//////////////////////////////////////////////////////////////////// +template +void MultiTransition:: +set_off(const Property &property) { + _properties[property] = TD_off; + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::is_identity +// Access: Public +// Description: Returns true if this property has been indicated as +// an identity transition, false otherwise. +//////////////////////////////////////////////////////////////////// +template +bool MultiTransition:: +is_identity(const Property &property) const { + Properties::const_iterator pi; + pi = _properties.find(property); + if (pi != _properties.end()) { + return (*pi).second == TD_identity; + } + + // The property is not mentioned. It's implicitly identity only if + // the default_dir flag is identity. + return (_default_dir == TD_identity); +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::is_on +// Access: Public +// Description: Returns true if this property has been indicated as +// an 'on' transition, false otherwise. +//////////////////////////////////////////////////////////////////// +template +bool MultiTransition:: +is_on(const Property &property) const { + Properties::const_iterator pi; + pi = _properties.find(property); + if (pi != _properties.end()) { + return (*pi).second == TD_on; + } + + // The property is not mentioned. It's implicitly 'on' only if the + // default_dir flag is TD_on. + return (_default_dir == TD_on); +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::is_on +// Access: Public +// Description: Returns true if this property has been indicated as +// an 'off' transition, false otherwise. +//////////////////////////////////////////////////////////////////// +template +bool MultiTransition:: +is_off(const Property &property) const { + Properties::const_iterator pi; + pi = _properties.find(property); + if (pi != _properties.end()) { + return (*pi).second == TD_off; + } + + // The property is not mentioned. It's implicitly 'off' only if the + // default_dir flag is TD_off. + return (_default_dir == TD_off); +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::make_identity +// Access: Public, Virtual +// Description: This is a pure virtual function and normally would +// not need a body, except that VC++ doesn't seem happy +// about instantiating the template otherwise. +//////////////////////////////////////////////////////////////////// +template +NodeTransition *MultiTransition:: +make_identity() const { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::compose +// Access: Public, Virtual +// Description: Returns a new transition that corresponds to the +// composition of this transition with the second +// transition (which must be of an equivalent type). +// This may return the same pointer as either source +// transition. Applying the transition returned from +// this function to an attribute attribute will produce +// the same effect as applying each transition +// separately. +//////////////////////////////////////////////////////////////////// +template +NodeTransition *MultiTransition:: +compose(const NodeTransition *other) const { + const MultiTransition *ot; + DCAST_INTO_R(ot, other, NULL); + + if (ot->_priority < _priority) { + // The other transition too low-priority; the result is unchanged. + return (MultiTransition *)this; + + } else if (ot->_priority > _priority) { + // The other priority is higher: the result is the same as the other. + return (MultiTransition *)ot; + + } else if (ot->_default_dir != TD_identity) { + // The other transition is a complete-world transition: the result + // is the same as other. + return (MultiTransition *)ot; + + } else if (is_identity()) { + // We're identity: the result is the same as other. + return (MultiTransition *)ot; + + } else if (ot->is_identity()) { + // The other transition is identity: it has no effect. + return (MultiTransition *)this; + + } else { + // Neither of us is identity. Build and return a new list. + MultiTransition *result; + DCAST_INTO_R(result, make_identity(), NULL); + result->_default_dir = _default_dir; + + dmap_compose(_properties.begin(), _properties.end(), + ot->_properties.begin(), ot->_properties.end(), + inserter(result->_properties, result->_properties.begin())); + + return result; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::invert +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +NodeTransition *MultiTransition:: +invert() const { + if (is_identity()) { + return (MultiTransition *)this; + } + + MultiTransition *result; + DCAST_INTO_R(result, make_identity(), NULL); + + result->_default_dir = TD_identity; + + dmap_invert(_properties.begin(), _properties.end(), + inserter(result->_properties, result->_properties.begin())); + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::apply +// Access: Public, Virtual +// Description: Returns a new attribute (or possibly the same +// attribute) that represents the effect of applying this +// indicated transition to the indicated attribute. The +// source attribute may be NULL, indicating the initial +// attribute. +//////////////////////////////////////////////////////////////////// +template +NodeAttribute *MultiTransition:: +apply(const NodeAttribute *attrib) const { + + MultiAttribute *result; + if (attrib == (const NodeAttribute *)NULL) { + DCAST_INTO_R(result, make_attrib(), NULL); + } else { + DCAST_INTO_R(result, (NodeAttribute *)attrib, NULL); + } + + if (_priority < result->_priority || is_identity()) { + // The priority is too low to affect the attribute, or we're + // identity. + return result; + } + + if (result->get_count() > 1) { + // Copy on write. + DCAST_INTO_R(result, result->make_copy(), NULL); + } + + result->_priority = _priority; + + MultiAttribute::Properties temp; + if (_default_dir == TD_identity) { + // In this case, the normal case, the transition does not specify + // completely what the attributes should be. + bmap_apply(result->_properties.begin(), result->_properties.end(), + _properties.begin(), _properties.end(), + false, TD_on, + inserter(temp, temp.begin())); + result->_properties_is_on = true; + + } else if (_default_dir == TD_off) { + // In this case, the transition completely specifies what the + // attributes should be; any property not explicitly mentioned by + // the transition should be turned off. + bmap_apply(result->_properties.begin(), result->_properties.end(), + _properties.begin(), _properties.end(), + true, TD_on, + inserter(temp, temp.begin())); + result->_properties_is_on = true; + + } else { // _default_dir == TD_on + // In this case, the transition completely specifies what the + // attributes should be; any property not explicitly mentioned by + // the transition should be turned on. + bmap_apply(result->_properties.begin(), result->_properties.end(), + _properties.begin(), _properties.end(), + true, TD_off, + inserter(temp, temp.begin())); + result->_properties_is_on = false; + } + + result->_properties.swap(temp); + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void MultiTransition:: +output(ostream &out) const { + Properties::const_iterator pi; + + bool any_identity = false; + for (pi = _properties.begin(); pi != _properties.end(); ++pi) { + if ((*pi).second == TD_identity) { + if (!any_identity) { + out << " identity("; + any_identity = true; + } else { + out << ", "; + } + output_property(out, (*pi).first); + } + } + if (any_identity) { + out << ")"; + } + + bool any_on = false; + for (pi = _properties.begin(); pi != _properties.end(); ++pi) { + if ((*pi).second == TD_on) { + if (!any_on) { + out << " on("; + any_on = true; + } else { + out << ", "; + } + output_property(out, (*pi).first); + } + } + if (any_on) { + out << ")"; + } + + bool any_off = false; + for (pi = _properties.begin(); pi != _properties.end(); ++pi) { + if ((*pi).second == TD_off) { + if (!any_off) { + out << " off("; + any_off = true; + } else { + out << ", "; + } + output_property(out, (*pi).first); + } + } + if (any_off) { + out << ")"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +void MultiTransition:: +write(ostream &out, int indent_level) const { + Properties::const_iterator pi; + + bool any_identity = false; + for (pi = _properties.begin(); pi != _properties.end(); ++pi) { + if ((*pi).second == TD_identity) { + if (!any_identity) { + indent(out, indent_level) << "identity:\n"; + any_identity = true; + } + write_property(out, (*pi).first, indent_level + 2); + } + } + + bool any_on = false; + for (pi = _properties.begin(); pi != _properties.end(); ++pi) { + if ((*pi).second == TD_on) { + if (!any_on) { + indent(out, indent_level) << "on:\n"; + any_on = true; + } + write_property(out, (*pi).first, indent_level + 2); + } + } + + bool any_off = false; + for (pi = _properties.begin(); pi != _properties.end(); ++pi) { + if ((*pi).second == TD_off) { + if (!any_off) { + indent(out, indent_level) << "off:\n"; + any_off = true; + } + write_property(out, (*pi).first, indent_level + 2); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::internal_compare_to +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +template +int MultiTransition:: +internal_compare_to(const NodeTransition *other) const { + const MultiTransition *ot; + DCAST_INTO_R(ot, other, false); + + if (_default_dir != ot->_default_dir) { + return (int)_default_dir - (int)ot->_default_dir; + } + + return dmap_compare(_properties.begin(), _properties.end(), + ot->_properties.begin(), ot->_properties.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::output_property +// Access: Public, Virtual +// Description: This is a pure virtual function and normally would +// not need a body, except that VC++ doesn't seem happy +// about instantiating the template otherwise. +//////////////////////////////////////////////////////////////////// +template +void MultiTransition:: +output_property(ostream &, const Property &) const { +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::write_property +// Access: Public, Virtual +// Description: This is a pure virtual function and normally would +// not need a body, except that VC++ doesn't seem happy +// about instantiating the template otherwise. +//////////////////////////////////////////////////////////////////// +template +void MultiTransition:: +write_property(ostream &, const Property &, int) const { +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE MultiTransition::size_type MultiTransition:: +size() const { + return _properties.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::begin +// Access: Public +// Description: Returns an iterator that begins the sequence of +// properties in the transition. +//////////////////////////////////////////////////////////////////// +template +INLINE MultiTransition::const_iterator MultiTransition:: +begin() const { + return _properties.begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MultiTransition::end +// Access: Public +// Description: Returns an iterator that marks the end of the +// sequence of properties in the transition. +//////////////////////////////////////////////////////////////////// +template +INLINE MultiTransition::const_iterator MultiTransition:: +end() const { + return _properties.end(); +} + diff --git a/panda/src/graph/multiTransition.h b/panda/src/graph/multiTransition.h new file mode 100644 index 0000000000..4d925f7626 --- /dev/null +++ b/panda/src/graph/multiTransition.h @@ -0,0 +1,128 @@ +// Filename: multiTransition.h +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MULTITRANSITION_H +#define MULTITRANSITION_H + +#include + +#include "nodeTransition.h" +#include "transitionDirection.h" + +//////////////////////////////////////////////////////////////////// +// Class : MultiTransition +// Description : This is an abstract template class that encapsulates +// states that may have any number of subcomponents, in +// no particular order, any of which may be on or off at +// a given time. The classic example of this is a +// LightTransition, which may represent any arbitrary +// set of lights that are to be considered on at a given +// time. +// +// This is a template class; it templates on the type of +// thing that is to be on or off, as well as a class +// that encodes the name of the thing. This class +// (which might be the same as the Property class) +// should contain a static method called +// "get_class_name()" which returns the string name of +// the Property class. This is necessary to allow the +// MultiTransition it initialize its TypeHandle +// properly. +// +// Also see MultiNodeTransition. +//////////////////////////////////////////////////////////////////// +template +class MultiTransition : public NodeTransition { +private: + typedef map Properties; + +protected: + MultiTransition(); + MultiTransition(const MultiTransition ©); + void operator = (const MultiTransition ©); + +public: + void clear(); + bool is_identity() const; + + // Typically a MultiTransition will be in "incomplete" mode; that + // is, it presents an incomplete view of the state that we desire. + // That means that components which are not mentioned in a + // particular transition will not be affected by the transition; if + // they were off, they will remain off; if they were on, they will + // remain on. + + // Sometimes you want a transition to be able to turn off all + // components (for instance, to disable lighting by turning off all + // lights) or to turn on all components. To achieve either effect, + // set the default direction, which indicates whether to implicitly + // turn on or off (or leave alone) any property not explicitly + // mentioned in the transition. + INLINE void set_default_dir(TransitionDirection dir); + INLINE TransitionDirection get_default_dir() const; + + void set_identity(const Property &prop); + void set_on(const Property &prop); + void set_off(const Property &prop); + + bool is_identity(const Property &prop) const; + bool is_on(const Property &prop) const; + bool is_off(const Property &prop) const; + + virtual NodeTransition *make_identity() const=0; + + virtual NodeTransition *compose(const NodeTransition *other) const; + virtual NodeTransition *invert() const; + virtual NodeAttribute *apply(const NodeAttribute *attrib) const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeTransition *other) const; + + virtual void output_property(ostream &out, const Property &prop) const=0; + virtual void write_property(ostream &out, const Property &prop, + int indent_level) const=0; + +public: + // These functions and typedefs allow one to peruse all of the + // Properties in the transition. Beware! It is not safe to use + // this interface outside of PANDA.DLL. + typedef Properties::const_iterator iterator; + typedef Properties::const_iterator const_iterator; + typedef Properties::value_type value_type; + typedef Properties::size_type size_type; + + INLINE size_type size() const; + INLINE const_iterator begin() const; + INLINE const_iterator end() const; + +private: + Properties _properties; + TransitionDirection _default_dir; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeTransition::init_type(); + register_type(_type_handle, + string("MultiTransition<")+NameClass::get_class_name()+">", + NodeTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "multiTransition.I" + +#endif diff --git a/panda/src/graph/multiTransitionHelpers.I b/panda/src/graph/multiTransitionHelpers.I new file mode 100644 index 0000000000..96dfff6fb7 --- /dev/null +++ b/panda/src/graph/multiTransitionHelpers.I @@ -0,0 +1,436 @@ +// Filename: multiTransitionHelpers.I +// Created by: drose (15Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: dmap_compose +// Description: Accepts two DirectionMaps, and builds a new +// list (another DirectionMap) which represents the +// memberwise composition of the two input maps. +//////////////////////////////////////////////////////////////////// +template +OutputIterator +dmap_compose(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result) { + while (first1 != last1 && first2 != last2) { + if ((*first1).first < (*first2).first) { + *result = *first1; + ++first1; + ++result; + } else if ((*first2).first < (*first1).first) { + *result = *first2; + ++first2; + ++result; + } else { + // If the second Transition is identity, it has no effect. + // Otherwise, it replaces us. + if ((*first2).second != TD_identity) { + *result = *first2; + } else { + *result = *first1; + } + ++first1; + ++first2; + ++result; + } + } + + while (first1 != last1) { + *result = *first1; + ++first1; + ++result; + } + + while (first2 != last2) { + *result = *first2; + ++first2; + ++result; + } + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: dmap_invert_compose +// Description: Accepts two DirectionMaps, and builds a new +// list (another DirectionMap) which represents the +// memberwise result inverting and composing. +//////////////////////////////////////////////////////////////////// +template +OutputIterator +dmap_invert_compose(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result) { + typedef TYPENAME InputIterator1::value_type OutputType; + + while (first1 != last1 && first2 != last2) { + if ((*first1).first < (*first2).first) { + (*result) = OutputType((*first1).first, TD_inverse); + ++first1; + ++result; + } else if ((*first2).first < (*first1).first) { + *result = *first2; + ++first2; + ++result; + } else { + TransitionDirection dir1 = (*first1).second; + TransitionDirection dir2 = (*first2).second; + + if (dir1 == dir2 && + (dir1 != TD_normal || (*first1).first == (*first2).first)) { + // The two transitions are equivalent, so the invert_compose + // will be identity. Don't bother to store it. + + } else { + if ((*first2).second != TD_identity) { + *result = *first2; + } else { + (*result) = OutputType((*first1).first, TD_inverse); + } + ++result; + } + ++first1; + ++first2; + } + } + + while (first1 != last1) { + (*result) = OutputType((*first1).first, TD_inverse); + ++first1; + ++result; + } + + while (first2 != last2) { + *result = *first2; + ++first2; + ++result; + } + return result; +} + + +//////////////////////////////////////////////////////////////////// +// Function: dmap_invert +// Description: Accepts a DirectionMap, and builds a new list +// which represents the memberwise inversion of the +// input. Guarantees that the new list will have +// exactly the same length as the input list. +//////////////////////////////////////////////////////////////////// +template +OutputIterator +dmap_invert(InputIterator first, InputIterator last, + OutputIterator result) { + typedef TYPENAME InputIterator::value_type OutputType; + + while (first != last) { + switch ((*first).second) { + case TD_identity: + (*result) = OutputType((*first).first, TD_identity); + ++result; + break; + + case TD_on: + (*result) = OutputType((*first).first, TD_off); + ++result; + break; + + case TD_off: + (*result) = OutputType((*first).first, TD_on); + ++result; + break; + } + ++first; + } + return result; +} + + +//////////////////////////////////////////////////////////////////// +// Function: dmap_equiv +// Description: Accepts a pair of DirectionMaps, and returns +// true if they are equivalent, false otherwise. Two +// DirectionMaps are defined to be equivalent if +// all nonidentity members present in one set are +// present and equivalent in the other set, +//////////////////////////////////////////////////////////////////// +template +bool +dmap_equiv(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) { + while (first1 != last1 && first2 != last2) { + if ((*first1).first < (*first2).first) { + if ((*first1).second != TD_identity) { + return false; + } + ++first1; + } else if ((*first2).first < (*first1).first) { + if ((*first2).second != TD_identity) { + return false; + } + ++first2; + } else { + TransitionDirection dir1 = (*first1).second; + TransitionDirection dir2 = (*first2).second; + + if (dir1 != dir2) { + return false; + } + ++first1; + ++first2; + } + } + + while (first1 != last1) { + if ((*first1).second != TD_identity) { + return false; + } + ++first1; + } + + while (first2 != last2) { + if ((*first2).second != TD_identity) { + return false; + } + ++first2; + } + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: dmap_compare +// Description: Accepts a pair of DirectionMaps, and returns +// < 0 if the first one sorts before the second one, > 0 +// if the first one sorts after, 0 if they are +// equivalent. +//////////////////////////////////////////////////////////////////// +template +int +dmap_compare(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) { + while (first1 != last1 && first2 != last2) { + if ((*first1).first < (*first2).first) { + if ((*first1).second != TD_identity) { + // list1 has the first value that does not appear in list2. + return 1; + } + ++first1; + } else if ((*first2).first < (*first1).first) { + if ((*first2).second != TD_identity) { + // list2 has the first value that does not appear in list1. + return -1; + } + ++first2; + } else { + TransitionDirection dir1 = (*first1).second; + TransitionDirection dir2 = (*first2).second; + + if (dir1 != dir2) { + if (dir1 == TD_identity) { + // list1 has the first value that does not appear in list2. + return 1; + } else if (dir2 == TD_identity) { + // list2 has the first value that does not appear in list1. + return -1; + } + + // This value appears in both lists, but it is TD_normal in + // one and TD_inverse in the other. + return (dir1 < dir2) ? -1 : 1; + } + + ++first1; + ++first2; + } + } + + while (first1 != last1) { + if ((*first1).second != TD_identity) { + // list1 is longer. + return -1; + } + ++first1; + } + + while (first2 != last2) { + if ((*first2).second != TD_identity) { + // list2 is longer. + return 1; + } + ++first2; + } + + return 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: dmap_is_identity +// Description: Accepts a DirectionMap, and returns true if all +// elements in the map correspond to the identity +// transition, false otherwise. +//////////////////////////////////////////////////////////////////// +template +bool +dmap_is_identity(InputIterator first, InputIterator last) { + while (first != last) { + if ((*first).second != TD_identity) { + return false; + } + ++first; + } + return true; +} + + + +//////////////////////////////////////////////////////////////////// +// Function: bmap_equiv +// Description: Accepts a pair of BoolMaps, and returns true if they +// are equivalent, false otherwise. Two BoolMaps are +// defined to be equivalent if all 'on' members present +// in one set are present and equivalent in the other +// set, +//////////////////////////////////////////////////////////////////// +template +bool +bmap_equiv(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) { + while (first1 != last1 && first2 != last2) { + if ((*first1) < (*first2)) { + return false; + } else if ((*first2) < (*first1)) { + return false; + } else { + ++first1; + ++first2; + } + } + + if (first1 != last1 || first2 != last2) { + return false; + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: bmap_compare +// Description: Accepts a pair of BoolMaps, and returns < 0 if the +// first one sorts first, > 0 if the second one sorts +// first, 0 if they are equivalent. +//////////////////////////////////////////////////////////////////// +template +int +bmap_compare(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) { + while (first1 != last1 && first2 != last2) { + if ((*first1) < (*first2)) { + return -1; + } else if ((*first2) < (*first1)) { + return 1; + } else { + ++first1; + ++first2; + } + } + + if (first1 != last1) { + // list 1 is longer. + return -1; + } + if (first2 != last2) { + // list 2 is longer. + return 1; + } + + return 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: bmap_apply +// Description: Accepts a BoolMap and a DirectionMap, and builds a +// new list (another BoolMap) which represents the +// memberwise application of the two input maps. +//////////////////////////////////////////////////////////////////// +template +OutputIterator +bmap_apply(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + bool complete_transition, TransitionDirection want_dirs, + OutputIterator result) { + while (first1 != last1 && first2 != last2) { + if ((*first1) < (*first2).first) { + // Here's an attribute property that wasn't mentioned by the + // transition. It stays, unless the transition is "complete". + if (!complete_transition) { + *result = *first1; + ++result; + } + ++first1; + + } else if ((*first2).first < (*first1)) { + // The transition mentions this property that wasn't previously + // in the attribute. It becomes a member of the attribute only + // if the transition explicitly turned it on. + + if ((*first2).second == want_dirs) { + *result = (*first2).first; + ++result; + break; + } + + ++first2; + + } else { // ((*first2).first == (*first1)) + + // The transition mentions this property that was already in the + // attribute. Does the transition replace, preserve, or delete + // the attribute? + + if ((*first2).second == TD_identity) { + // Preserve. + *result = *first1; + ++result; + + } else if ((*first2).second == want_dirs) { + // Replace. + *result = (*first2).first; + ++result; + + } else { + // Omit. + } + + ++first1; + ++first2; + } + } + + while (first1 != last1) { + // Here's an attribute property that wasn't mentioned by the + // transition. It stays, unless the transition is "complete". + if (!complete_transition) { + *result = *first1; + ++result; + } + ++first1; + } + + while (first2 != last2) { + // The transition mentions this property that wasn't previously + // in the attribute. It becomes a member of the attribute only + // if the transition explicitly turned it on. + + if ((*first2).second == want_dirs) { + *result = (*first2).first; + ++result; + break; + } + + ++first2; + } + return result; +} diff --git a/panda/src/graph/multiTransitionHelpers.h b/panda/src/graph/multiTransitionHelpers.h new file mode 100644 index 0000000000..d1fbd580ba --- /dev/null +++ b/panda/src/graph/multiTransitionHelpers.h @@ -0,0 +1,127 @@ +// Filename: multiTransitionHelpers.h +// Created by: drose (15Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MULTITRANSITIONHELPERS_H +#define MULTITRANSITIONHELPERS_H + +#include + +//////////////////////////////////////////////////////////////////// +// Function: dmap_compose +// Description: Accepts two DirectionMaps, and builds a new +// list (another DirectionMap) which represents the +// memberwise composition of the two input maps. +//////////////////////////////////////////////////////////////////// +template +OutputIterator +dmap_compose(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result); + + +//////////////////////////////////////////////////////////////////// +// Function: dmap_invert_compose +// Description: Accepts two DirectionMaps, and builds a new +// list (another DirectionMap) which represents the +// memberwise result inverting and composing. +//////////////////////////////////////////////////////////////////// +template +OutputIterator +dmap_invert_compose(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result); + + +//////////////////////////////////////////////////////////////////// +// Function: dmap_invert +// Description: Accepts a DirectionMap, and builds a new list +// which represents the memberwise inversion of the +// input. Guarantees that the new list will have +// exactly the same length as the input list. +//////////////////////////////////////////////////////////////////// +template +OutputIterator +dmap_invert(InputIterator first, InputIterator last, + OutputIterator result); + + +//////////////////////////////////////////////////////////////////// +// Function: dmap_equiv +// Description: Accepts a pair of DirectionMaps, and returns +// true if they are equivalent, false otherwise. Two +// DirectionMaps are defined to be equivalent if +// all nonidentity members present in one set are +// present and equivalent in the other set, +//////////////////////////////////////////////////////////////////// +template +bool +dmap_equiv(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2); + +//////////////////////////////////////////////////////////////////// +// Function: dmap_compare +// Description: Accepts a pair of DirectionMaps, and returns +// < 0 if the first one sorts before the second one, > 0 +// if the first one sorts after, 0 if they are +// equivalent. +//////////////////////////////////////////////////////////////////// +template +int +dmap_compare(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2); + + +//////////////////////////////////////////////////////////////////// +// Function: dmap_is_identity +// Description: Accepts a DirectionMap, and returns true if all +// elements in the map correspond to the identity +// transition, false otherwise. +//////////////////////////////////////////////////////////////////// +template +bool +dmap_is_identity(InputIterator first, InputIterator last); + + +//////////////////////////////////////////////////////////////////// +// Function: bmap_equiv +// Description: Accepts a pair of BoolMaps, and returns true if they +// are equivalent, false otherwise. Two BoolMaps are +// defined to be equivalent if all 'on' members present +// in one set are present and equivalent in the other +// set, +//////////////////////////////////////////////////////////////////// +template +bool +bmap_equiv(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2); + +//////////////////////////////////////////////////////////////////// +// Function: bmap_compare +// Description: Accepts a pair of BoolMaps, and returns < 0 if the +// first one sorts first, > 0 if the second one sorts +// first, 0 if they are equivalent. +//////////////////////////////////////////////////////////////////// +template +int +bmap_compare(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2); + + +//////////////////////////////////////////////////////////////////// +// Function: bmap_apply +// Description: Accepts a BoolMap and a DirectionMap, and builds a +// new list (another BoolMap) which represents the +// memberwise application of the two input maps. +//////////////////////////////////////////////////////////////////// +template +OutputIterator +bmap_apply(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + bool complete_transition, TransitionDirection want_dirs, + OutputIterator result); + +#include "multiTransitionHelpers.I" + +#endif diff --git a/panda/src/graph/namedNode.I b/panda/src/graph/namedNode.I new file mode 100644 index 0000000000..66732d3fd3 --- /dev/null +++ b/panda/src/graph/namedNode.I @@ -0,0 +1,38 @@ +// Filename: namedNode.I +// Created by: drose (23May00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: NamedNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NamedNode:: +NamedNode(const string &initial_name) : + Namable(initial_name) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NamedNode::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NamedNode:: +NamedNode(const NamedNode ©) : + Node(copy), Namable(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NamedNode::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NamedNode:: +operator = (const NamedNode ©) { + Node::operator = (copy); + Namable::operator = (copy); +} diff --git a/panda/src/graph/namedNode.cxx b/panda/src/graph/namedNode.cxx new file mode 100644 index 0000000000..ccc011b7f4 --- /dev/null +++ b/panda/src/graph/namedNode.cxx @@ -0,0 +1,98 @@ +// Filename: namedNode.cxx +// Created by: drose (15Jan99) +// + +#include "namedNode.h" +#include +#include +#include +#include + +TypeHandle NamedNode::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: NamedNode::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Node that is a shallow copy +// of this one. It will be a different Node pointer, +// but its internal data may or may not be shared with +// that of the original Node. +//////////////////////////////////////////////////////////////////// +Node *NamedNode:: +make_copy() const { + return new NamedNode(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: NamedNode::Output +// Access: Public, Virtual +// Description: Writes a brief description of the node to the +// indicated output stream. This is invoked by the << +// operator. It may be overridden in derived classes to +// include some information relevant to the class. +//////////////////////////////////////////////////////////////////// +void NamedNode:: +output(ostream &out) const { + if (get_name().empty()) { + out << get_type(); + } else { + out << get_type() << " " << get_name(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NamedNode::write_object +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void NamedNode:: +write_datagram(BamWriter *manager, Datagram &me) +{ + Node::write_datagram(manager, me); + me.add_string(get_name()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NamedNode::make_NamedNode +// Access: Protected +// Description: Factory method to generate a node object +//////////////////////////////////////////////////////////////////// +TypedWriteable* NamedNode:: +make_NamedNode(const FactoryParams ¶ms) +{ + NamedNode *me = new NamedNode; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: NamedNode::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void NamedNode:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + Node::fillin(scan, manager); + set_name(scan.get_string()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NamedNode::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a node object +//////////////////////////////////////////////////////////////////// +void NamedNode:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_NamedNode); +} diff --git a/panda/src/graph/namedNode.h b/panda/src/graph/namedNode.h new file mode 100644 index 0000000000..64aa3640a0 --- /dev/null +++ b/panda/src/graph/namedNode.h @@ -0,0 +1,68 @@ +// Filename: namedNode.h +// Created by: drose (15Jan99) +// + +#ifndef NAMEDNODE_H +#define NAMEDNODE_H + +#include + +#include "node.h" +#include + +/////////////////////////////////////////////////////////////////// +// Class : NamedNode +// Description : A base class for all nodes which have names. This +// will be (almost?) all of them. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NamedNode : public Node, public Namable { +public: + INLINE NamedNode(const string &initial_name = ""); + INLINE NamedNode(const NamedNode ©); + INLINE void operator = (const NamedNode ©); + + virtual Node *make_copy() const; + virtual void output(ostream &out) const; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + + static TypedWriteable *make_NamedNode(const FactoryParams ¶ms); + + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Node::init_type(); + Namable::init_type(); + register_type(_type_handle, "NamedNode", + Node::get_class_type(), + Namable::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +// We need this operator to specify which output operator (from Node +// or Namable) that NamedNode uses. +INLINE ostream & +operator << (ostream &out, const NamedNode &node) { + node.output(out); + return out; +} + +#include "namedNode.I" + +#endif + diff --git a/panda/src/graph/node.I b/panda/src/graph/node.I new file mode 100644 index 0000000000..b8ca0fd9c8 --- /dev/null +++ b/panda/src/graph/node.I @@ -0,0 +1,23 @@ +// Filename: node.I +// Created by: drose (26Oct98) +// +//////////////////////////////////////////////////////////////////// + +#include + + +//////////////////////////////////////////////////////////////////// +// Function: remove_child +// Description: Finds the arc that connects the indicated nodes and +// removes it. Returns true if the arc was found and +// removed, false if it did not exist. +//////////////////////////////////////////////////////////////////// +INLINE bool +remove_child(Node *parent, Node *child, TypeHandle graph_type) { + NodeRelation *arc = find_arc(parent, child, graph_type); + if (arc != (NodeRelation *)NULL) { + remove_arc(arc); + return true; + } + return false; +} diff --git a/panda/src/graph/node.cxx b/panda/src/graph/node.cxx new file mode 100644 index 0000000000..a7e1c945ad --- /dev/null +++ b/panda/src/graph/node.cxx @@ -0,0 +1,509 @@ +// Filename: node.cxx +// Created by: drose (27Oct98) +// + +#include "node.h" +#include "nodeRelation.h" +#include +#include +#include +#include +#include + +TypeHandle Node::_type_handle; +Node* const Node::Null = (Node*)0L; + +//////////////////////////////////////////////////////////////////// +// Function: Node::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Node:: +Node() { +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Node:: +Node(const Node ©) : + TypedWriteable(copy), + BoundedObject(copy), + ReferenceCount(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Node:: +operator = (const Node ©) { + TypedWriteable::operator = (copy); + BoundedObject::operator = (copy); + ReferenceCount::operator = (copy); +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::Destructor +// Access: Public, Virtual +// Description: When a Node destructs, all of its arcs must be +// deleted as well. +//////////////////////////////////////////////////////////////////// +Node:: +~Node() { + // We'd better not have any arcs pointing into this node, since + // we're destructing it now. If we do, the destructor was called in + // error. + UpRelations::iterator uri; + for (uri = _parents.begin(); uri != _parents.end(); ++uri) { + nassertv((*uri).second.empty()); + } + + DownRelations::iterator dri; + for (dri = _children.begin(); dri != _children.end(); ++dri) { + DownRelationPointers &drp = (*dri).second; + + DownRelationPointers::iterator drpi; + for (drpi = drp.begin(); drpi != drp.end(); ++drpi) { + PT(NodeRelation) relation = (*drpi); + + // This deletes the arc and anything below it through the magic + // of reference-counting. + relation->detach_below(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Node that is a shallow copy +// of this one. It will be a different Node pointer, +// but its internal data may or may not be shared with +// that of the original Node. No children will be +// copied. +//////////////////////////////////////////////////////////////////// +Node *Node:: +make_copy() const { + return new Node(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::copy_subgraph +// Access: Public +// Description: Allocates and returns a complete copy of this node +// and the entire scene graph rooted at this node. Some +// data may still be shared from the original +// (e.g. vertex index tables), but nothing that will +// impede normal use of the node. +//////////////////////////////////////////////////////////////////// +Node *Node:: +copy_subgraph(TypeHandle graph_type) const { + InstanceMap inst_map; + return r_copy_subgraph(graph_type, inst_map); +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::safe_to_flatten +// Access: Public, Virtual +// Description: Returns true if it is generally safe to flatten out +// this particular kind of Node by duplicating +// instances, false otherwise (for instance, a Camera +// cannot be safely flattened, because the Camera +// pointer itself is meaningful). +//////////////////////////////////////////////////////////////////// +bool Node:: +safe_to_flatten() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::safe_to_transform +// Access: Public, Virtual +// Description: Returns true if it is generally safe to transform +// this particular kind of Node by calling the xform() +// method, false otherwise. For instance, it's usually +// a bad idea to attempt to xform a Character. +//////////////////////////////////////////////////////////////////// +bool Node:: +safe_to_transform() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::xform +// Access: Public, Virtual +// Description: Transforms the contents of this node by the indicated +// matrix, if it means anything to do so. For most +// kinds of nodes, this does nothing. +//////////////////////////////////////////////////////////////////// +void Node:: +xform(const LMatrix4f &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::transform_changed +// Access: Public, Virtual +// Description: Called whenever the transform matrix on one of the +// arcs directly above this node has changed. This is +// simply a hook so the node can do something +// appropriate. It does not get called when arcs far +// above the node change. +//////////////////////////////////////////////////////////////////// +void Node:: +transform_changed(NodeRelation *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::get_num_parents +// Access: Public +// Description: Returns the number of parent arcs of the indicated +// type (e.g. RenderRelation::get_class_type()) the node +// has. +//////////////////////////////////////////////////////////////////// +int Node:: +get_num_parents(TypeHandle type) const { + UpRelations::const_iterator uri; + uri = _parents.find(type); + if (uri == _parents.end()) { + return 0; + } + return (*uri).second.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::get_parent +// Access: Public +// Description: Returns the nth parent arc of the indicated type the +// node has. The index must be in the range 0 <= index +// < get_num_parents(type). +//////////////////////////////////////////////////////////////////// +NodeRelation *Node:: +get_parent(TypeHandle type, int index) const { + UpRelations::const_iterator uri; + uri = _parents.find(type); + nassertr(uri != _parents.end(), (NodeRelation *)NULL); + nassertr(index >= 0 && index < (*uri).second.size(), (NodeRelation *)NULL); + return (*uri).second[index]; +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::get_num_children +// Access: Public +// Description: Returns the number of child arcs of the indicated +// type (e.g. RenderRelation::get_class_type()) the node +// has. +//////////////////////////////////////////////////////////////////// +int Node:: +get_num_children(TypeHandle type) const { + DownRelations::const_iterator dri; + dri = _children.find(type); + if (dri == _children.end()) { + return 0; + } + return (*dri).second.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::get_child +// Access: Public +// Description: Returns the nth child arc of the indicated type the +// node has. The index must be in the range 0 <= index +// < get_num_children(type). +//////////////////////////////////////////////////////////////////// +NodeRelation *Node:: +get_child(TypeHandle type, int index) const { + DownRelations::const_iterator dri; + dri = _children.find(type); + nassertr(dri != _children.end(), (NodeRelation *)NULL); + nassertr(index >= 0 && index < (*dri).second.size(), (NodeRelation *)NULL); + return (*dri).second[index]; +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::sub_render +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool Node:: +sub_render(const AllAttributesWrapper &, AllTransitionsWrapper &, + GraphicsStateGuardianBase *) { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::has_sub_render +// Access: Public, Virtual +// Description: Should be redefined to return true if the function +// sub_render(), above, expects to be called during +// traversal. +//////////////////////////////////////////////////////////////////// +bool Node:: +has_sub_render() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::output +// Access: Public, Virtual +// Description: Writes a brief description of the node to the +// indicated output stream. This is invoked by the << +// operator. It may be overridden in derived classes to +// include some information relevant to the class. +//////////////////////////////////////////////////////////////////// +void Node:: +output(ostream &out) const { + out << get_type(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Node::propagate_stale_bound +// Access: Protected, Virtual +// Description: Called by BoundedObject::mark_bound_stale(), this +// should make sure that all bounding volumes that +// depend on this one are marked stale also. +//////////////////////////////////////////////////////////////////// +void Node:: +propagate_stale_bound() { + // Mark all of our parent arcs, in all graphs, stale as well. + UpRelations::const_iterator uri; + for (uri = _parents.begin(); uri != _parents.end(); ++uri) { + const UpRelationPointers &urp = (*uri).second; + + UpRelationPointers::const_iterator urpi; + for (urpi = urp.begin(); urpi != urp.end(); ++urpi) { + (*urpi)->mark_bound_stale(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::r_copy_subgraph +// Access: Protected, Virtual +// Description: This is the recursive implementation of copy_subgraph(). +// It returns a copy of the entire subgraph rooted at +// this node. +// +// Note that it includes the parameter inst_map, which +// is a map type, and is not (and cannot be) exported +// from PANDA.DLL. Thus, any derivative of Node that is +// not also a member of PANDA.DLL *cannot* access this +// map. +//////////////////////////////////////////////////////////////////// +Node *Node:: +r_copy_subgraph(TypeHandle graph_type, Node::InstanceMap &inst_map) const { + Node *copy = make_copy(); + nassertr(copy != (Node *)NULL, NULL); + if (copy->get_type() != get_type()) { + graph_cat.warning() + << "Don't know how to copy nodes of type " << get_type() << "\n"; + } + + copy->r_copy_children(this, graph_type, inst_map); + return copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::r_copy_children +// Access: Protected, Virtual +// Description: This is called by r_deep_copy(); the copy has already +// been made of this particular node (and this is the +// copy); this function's job is to copy all of the +// children from the original. +// +// Note that it includes the parameter inst_map, which +// is a map type, and is not (and cannot be) exported +// from PANDA.DLL. Thus, any derivative of Node that is +// not also a member of PANDA.DLL *cannot* access this +// map, and probably should not even override this +// function. +//////////////////////////////////////////////////////////////////// +void Node:: +r_copy_children(const Node *from, TypeHandle graph_type, + Node::InstanceMap &inst_map) { + DownRelations::const_iterator dri; + dri = from->_children.find(graph_type); + if (dri != from->_children.end()) { + const DownRelationPointers &drp = (*dri).second; + + DownRelationPointers::const_iterator drpi; + for (drpi = drp.begin(); drpi != drp.end(); ++drpi) { + NodeRelation *source_arc = (*drpi); + Node *source_child = source_arc->get_child(); + nassertv(source_child != (Node *)NULL); + + Node *dest_child; + + // Check to see if we have already copied this child. If we + // have, use the copy. In this way, a subgraph that contains + // instances will be correctly duplicated into another subgraph + // that also contains its own instances. + InstanceMap::const_iterator ci; + ci = inst_map.find(source_child); + if (ci != inst_map.end()) { + dest_child = (*ci).second; + } else { + dest_child = source_child->r_copy_subgraph(graph_type, inst_map); + inst_map[source_child] = dest_child; + } + + NodeRelation *dest_arc = + NodeRelation::create_typed_arc(graph_type, this, dest_child); + nassertv(dest_arc != (NodeRelation *)NULL); + nassertv(dest_arc->is_exact_type(graph_type)); + + dest_arc->copy_transitions_from(source_arc); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void Node:: +write_datagram(BamWriter *manager, Datagram &me) +{ + + //A node should not write out it's UpRelations, + //the rationale for this restriction is that if a + //a node is choosen in the middle of the graph, then + //most likely we only want to write it and it's children. + //if we did write out all of the UpRelations then, writing + //any node in a graph would cause the entire graph to be written + //out + + me.add_uint16(_children.size()); + + DownRelations::iterator dri; + for (dri = _children.begin(); dri != _children.end(); ++dri) + { + DownRelationPointers &drp = (*dri).second; + me.add_uint16(drp.size()); + + DownRelationPointers::iterator drpi; + for (drpi = drp.begin(); drpi != drp.end(); ++drpi) + { + PT(NodeRelation) relation = (*drpi); + + //Ask manager to write out the pointer for me + manager->write_pointer(me, relation); + } + } + +} + + +//////////////////////////////////////////////////////////////////// +// Function: Node::complete_pointers +// Access: Public +// Description: Takes in a vector of pointers to TypedWriteable +// objects that correspond to all the requests for +// pointers that this object made to BamReader. +//////////////////////////////////////////////////////////////////// +int Node:: +complete_pointers(vector_typedWriteable &, BamReader*) +{ + //Dummy function that BamReader expects if there are + //any read_pointer requests made by an object. But Node is + //a special case that only calls read_pointer to force its + //NodeRelations to be read. Those NodeRelations will add themselves + //into the Node, so don't attempt to do that as well here. + return _num_pointers; +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::make_Node +// Access: Protected +// Description: Factory method to generate a node object +//////////////////////////////////////////////////////////////////// +TypedWriteable* Node:: +make_Node(const FactoryParams ¶ms) +{ + Node *me = new Node; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void Node:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + PN_uint16 numRelations = scan.get_uint16(); + PN_uint16 numRelationPointers; + _num_pointers = 0; + + + while(numRelations > 0) + { + numRelationPointers = scan.get_uint16(); + while(numRelationPointers > 0) + { + manager->read_pointer(scan, this); + numRelationPointers--; + _num_pointers++; + } + numRelations--; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Node::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a node object +//////////////////////////////////////////////////////////////////// +void Node:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_Node); +} + +//////////////////////////////////////////////////////////////////// +// Function: find_arc +// Description: Searches for the arc of the indicated type that +// connects the two indicated nodes. Returns the arc if +// it exists, or NULL if does not. +//////////////////////////////////////////////////////////////////// +NodeRelation * +find_arc(Node *parent, Node *child, TypeHandle graph_type) { + // We must now walk the RelationPointers list, looking for the + // matching parent-child arc. We'll start in the child looking for + // the parent arc, on the assumption there are likely to be fewer + // parents than children. + + UpRelations::const_iterator uri = child->_parents.find(graph_type); + + if (uri != child->_parents.end()) { + const UpRelationPointers &urp = (*uri).second; + UpRelationPointers::const_iterator urpi; + for (urpi = urp.begin(); urpi != urp.end(); ++urpi) { + if ((*urpi)->get_parent() == parent && + (*urpi)->get_child() == child) { + return (*urpi); + } + } + } + + // There is no matching arc. + return (NodeRelation *)NULL; +} diff --git a/panda/src/graph/node.h b/panda/src/graph/node.h new file mode 100644 index 0000000000..df0f7cb826 --- /dev/null +++ b/panda/src/graph/node.h @@ -0,0 +1,176 @@ +// Filename: node.h +// Created by: drose (26Oct98) +// + +#ifndef NODE_H +#define NODE_H + +#include + +#include "nodeRelation.h" +#include "boundedObject.h" + +#include +#include +#include + +class NodeAttributes; +class GraphicsStateGuardianBase; +class AllAttributesWrapper; +class AllTransitionsWrapper; +class BamWriter; +class BamReader; +class Datagram; +class DatagramIterator; + +//////////////////////////////////////////////////////////////////// +// Class : Node +// Description : The base class for all scene graph nodes. A Node may +// be joined to any number of other nodes, as a parent +// or as a child, with any kind of relation arcs. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Node : public TypedWriteable, public BoundedObject, + virtual public ReferenceCount { + // We can't simply inherit from TypedWriteableReferenceCount, because we need + // to inherit virtually from ReferenceCount. +public: + static Node* const Null; + + Node(); + Node(const Node ©); + void operator = (const Node ©); + virtual ~Node(); + + virtual Node *make_copy() const; + Node *copy_subgraph(TypeHandle graph_type) const; + + virtual bool safe_to_flatten() const; + virtual bool safe_to_transform() const; + virtual void xform(const LMatrix4f &mat); + + virtual void transform_changed(NodeRelation *arc); + + int get_num_parents(TypeHandle type) const; + NodeRelation *get_parent(TypeHandle type, int index) const; + int get_num_children(TypeHandle type) const; + NodeRelation *get_child(TypeHandle type, int index) const; + + // These functions will be called when the node is visited during + // the indicated traversal. + virtual void app_traverse() { } + virtual void draw_traverse() { } + virtual void dgraph_traverse() { } + + // This function is similar to another function in NodeTransition. + // It may or may not intercept the render traversal. + virtual bool sub_render(const AllAttributesWrapper &attrib, + AllTransitionsWrapper &trans, + GraphicsStateGuardianBase *gsgbase); + virtual bool has_sub_render() const; + + virtual void output(ostream &out) const; + + // We reference-count the child pointer, but not the parent pointer, + // to avoid circular reference counting. + + // The following members are public to allow low-overhead traversal + // through the scene graph when necessary, but beware! It is not + // safe to access these members directly outside of PANDA.DLL. + // Instead, use the get_parent()/get_child() interface, above. + + UpRelations _parents; + DownRelations _children; + +protected: + virtual void propagate_stale_bound(); + + typedef map InstanceMap; + virtual Node *r_copy_subgraph(TypeHandle graph_type, + InstanceMap &inst_map) const; + virtual void r_copy_children(const Node *from, TypeHandle graph_type, + InstanceMap &inst_map); + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + virtual int complete_pointers(vector_typedWriteable &plist, + BamReader *manager); + + static TypedWriteable *make_Node(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +private: + //This variable is set inside fillin to the number of pointers + //that are "read" by Node. This needs to be set, even though + //node does nothing in complete_pointers, because it needs to + //return the number of pointer requests that it made. This + //is necessary because the vector of pointers to TypedWriteables + //that is given to it by BamReader, will be filled with those requests + //and any children of Node, need to be able to ignore those as well. + //So complete_pointers must return the correct number read + int _num_pointers; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedWriteable::init_type(); + BoundedObject::init_type(); + ReferenceCount::init_type(); + register_type(_type_handle, "Node", + TypedWriteable::get_class_type(), + BoundedObject::get_class_type(), + ReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +INLINE ostream & +operator << (ostream &out, const Node &node) { + node.output(out); + return out; +} + +EXPCL_PANDA NodeRelation * +find_arc(Node *parent, Node *child, TypeHandle graph_type); + +INLINE bool +remove_child(Node *parent, Node *child, TypeHandle graph_type); + + +#include "node.I" + +#endif + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/panda/src/graph/nodeAttribute.I b/panda/src/graph/nodeAttribute.I new file mode 100644 index 0000000000..4fae565881 --- /dev/null +++ b/panda/src/graph/nodeAttribute.I @@ -0,0 +1,149 @@ +// Filename: nodeAttribute.I +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeAttribute:: +NodeAttribute() { + _priority = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttribute::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeAttribute:: +NodeAttribute(const NodeAttribute ©) : + TypedReferenceCount(copy), + _priority(copy._priority) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttribute::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodeAttribute:: +operator = (const NodeAttribute ©) { + TypedReferenceCount::operator = (copy); + _priority = copy._priority; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttribute::Equality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodeAttribute:: +operator == (const NodeAttribute &other) const { + return compare_to(other) == 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttribute::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodeAttribute:: +operator != (const NodeAttribute &other) const { + return compare_to(other) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttribute::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodeAttribute:: +operator < (const NodeAttribute &other) const { + return compare_to(other) < 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttribute::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodeAttribute:: +operator <= (const NodeAttribute &other) const { + return compare_to(other) <= 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttribute::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodeAttribute:: +operator > (const NodeAttribute &other) const { + return compare_to(other) > 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttribute::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodeAttribute:: +operator >= (const NodeAttribute &other) const { + return compare_to(other) >= 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttribute::compare_to +// Access: Public +// Description: This function works like strcmp(): it compares the +// two attributes and returns a number less than zero +// if this attribute sorts before the other one, equal +// to zero if they are equivalent, or greater than zero +// if this attribute sorts after the other one. +// +// This imposes an arbitrary sorting order across all +// attributes, whose sole purpose is to allow grouping +// of equivalent attributes together in STL structures +// like maps and sets. +//////////////////////////////////////////////////////////////////// +INLINE int NodeAttribute:: +compare_to(const NodeAttribute &other) const { + TypeHandle my_handle = get_handle(); + TypeHandle other_handle = other.get_handle(); + + if (my_handle == other_handle) { + return internal_compare_to(&other); + + } else { + return + (my_handle < other_handle) ? -1 : 1; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttribute::set_priority +// Access: Public +// Description: Changes the priority associated with this attribute. +// The attribute will not be affected by transitions +// with a lower priority. +//////////////////////////////////////////////////////////////////// +INLINE void NodeAttribute:: +set_priority(int priority) { + _priority = priority; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttribute::get_priority +// Access: Public +// Description: Returns the priority associated with this attribute. +// Normally this is of limited value; the priority is +// meaningful primarily on the transitions. +//////////////////////////////////////////////////////////////////// +INLINE int NodeAttribute:: +get_priority() const { + return _priority; +} diff --git a/panda/src/graph/nodeAttribute.N b/panda/src/graph/nodeAttribute.N new file mode 100644 index 0000000000..4efaa99498 --- /dev/null +++ b/panda/src/graph/nodeAttribute.N @@ -0,0 +1 @@ +ignoremember get_handle diff --git a/panda/src/graph/nodeAttribute.cxx b/panda/src/graph/nodeAttribute.cxx new file mode 100644 index 0000000000..d991d6534c --- /dev/null +++ b/panda/src/graph/nodeAttribute.cxx @@ -0,0 +1,42 @@ +// Filename: nodeAttribute.cxx +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nodeAttribute.h" + +#include + +TypeHandle NodeAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttribute::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void NodeAttribute:: +output(ostream &out) const { + out << get_handle(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttribute::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void NodeAttribute:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << *this << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void NodeAttribute:: +issue(GraphicsStateGuardianBase *) { +} diff --git a/panda/src/graph/nodeAttribute.h b/panda/src/graph/nodeAttribute.h new file mode 100644 index 0000000000..9205930099 --- /dev/null +++ b/panda/src/graph/nodeAttribute.h @@ -0,0 +1,103 @@ +// Filename: nodeAttribute.h +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NODEATTRIBUTE_H +#define NODEATTRIBUTE_H + +#include + +#include + +class NodeTransition; +class GraphicsStateGuardianBase; + +//////////////////////////////////////////////////////////////////// +// Class : NodeAttribute +// Description : This is an abstract class defining a single +// Attribute, a state property such as color or texture +// that may be in effect when rendering nodes of the +// scene graph. +// +// In general, the scene graph represents state by +// encoding transitions between various states on the +// arcs of the graph. The attribute values themselves +// are not explicitly stored; they are computed by +// repeated application of the transitions. +// +// A NodeTransition (defined in nodeTransition.h) +// represents a potential change from any one state (for +// instance, the initial state) to any other. For +// example, it might represent the change from the +// untextured state to rendering with a particular +// texture. +// +// Any number of Transitions may be applied along the +// arcs leading from the top of the scene graph to a +// geometry node. The Attribute state that will be in +// effect when the geometry node is rendered will be +// that computed by the consecutive application of each +// Transition encountered to the initial state. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NodeAttribute : public TypedReferenceCount { +protected: + INLINE NodeAttribute(); + INLINE NodeAttribute(const NodeAttribute ©); + INLINE void operator = (const NodeAttribute ©); + +public: + INLINE bool operator == (const NodeAttribute &other) const; + INLINE bool operator != (const NodeAttribute &other) const; + INLINE bool operator < (const NodeAttribute &other) const; + INLINE bool operator <= (const NodeAttribute &other) const; + INLINE bool operator > (const NodeAttribute &other) const; + INLINE bool operator >= (const NodeAttribute &other) const; + + INLINE int compare_to(const NodeAttribute &other) const; + + INLINE void set_priority(int priority); + INLINE int get_priority() const; + + virtual NodeAttribute *make_copy() const=0; + virtual NodeAttribute *make_initial() const=0; + + virtual TypeHandle get_handle() const=0; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual int internal_compare_to(const NodeAttribute *other) const=0; + +protected: + int _priority; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "NodeAttribute", + TypedReferenceCount::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +INLINE ostream &operator << (ostream &out, const NodeAttribute &nab) { + nab.output(out); + return out; +} + +#include "nodeAttribute.I" + +#endif diff --git a/panda/src/graph/nodeAttributeWrapper.I b/panda/src/graph/nodeAttributeWrapper.I new file mode 100644 index 0000000000..01774acee8 --- /dev/null +++ b/panda/src/graph/nodeAttributeWrapper.I @@ -0,0 +1,132 @@ +// Filename: nodeAttributeWrapper.I +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributeWrapper::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeAttributeWrapper:: +NodeAttributeWrapper(TypeHandle handle) : _handle(handle) { + nassertv(_handle != TypeHandle::none()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributeWrapper::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeAttributeWrapper:: +NodeAttributeWrapper(const NodeAttributeWrapper ©) : + _handle(copy._handle), + _attrib(copy._attrib) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributeWrapper::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodeAttributeWrapper:: +operator = (const NodeAttributeWrapper ©) { + _handle = copy._handle; + _attrib = copy._attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributeWrapper::get_handle +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypeHandle NodeAttributeWrapper:: +get_handle() const { + return _handle; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributeWrapper::get_attrib +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeAttribute *NodeAttributeWrapper:: +get_attrib() const { + return _attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributeWrapper::set_attrib +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodeAttributeWrapper:: +set_attrib(NodeAttribute *attrib) { + _attrib = attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributeWrapper::is_initial +// Access: Public +// Description: Returns true if the wrapper represents an initial +// attribute. +//////////////////////////////////////////////////////////////////// +INLINE bool NodeAttributeWrapper:: +is_initial() const { + return (_attrib == (NodeAttribute *)NULL); + // || _attrib->is_initial() +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributeWrapper::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int NodeAttributeWrapper:: +compare_to(const NodeAttributeWrapper &other) const { + nassertr(_handle == other._handle, false); + if (_attrib == other._attrib) { + return 0; + } + if (_attrib == (NodeAttribute *)NULL) { + return -1; + } + if (other._attrib == (NodeAttribute *)NULL) { + return 1; + } + return _attrib->compare_to(*other._attrib); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributeWrapper::make_initial +// Access: Public +// Description: Resets the wrapper to the initial attribute. +//////////////////////////////////////////////////////////////////// +INLINE void NodeAttributeWrapper:: +make_initial() { + _attrib.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: get_attribute_into +// Description: This external template function is handy for +// extracting the attribute (of a known type) from the +// wrapper. If the attribute exists, it is +// automatically downcasted to the correct type and +// stored in the pointer given in the first parameter, +// and the return value is true. If the attribute does +// not exist, the pointer is filled with NULL and the +// return value is false. +//////////////////////////////////////////////////////////////////// +template +INLINE bool +get_attribute_into(Attribute *&ptr, const NodeAttributeWrapper &attrib) { + NodeAttribute *nt = attrib.get_attrib(); + if (nt == (NodeAttribute *)NULL) { + ptr = (Attribute *)NULL; + return false; + } + DCAST_INTO_R(ptr, nt, false); + return true; +} diff --git a/panda/src/graph/nodeAttributeWrapper.cxx b/panda/src/graph/nodeAttributeWrapper.cxx new file mode 100644 index 0000000000..b34e24cd8b --- /dev/null +++ b/panda/src/graph/nodeAttributeWrapper.cxx @@ -0,0 +1,60 @@ +// Filename: nodeAttributeWrapper.cxx +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nodeAttributeWrapper.h" +#include "nodeTransitionWrapper.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributeWrapper::init_from +// Access: Public, Static +// Description: This is a named constructor that creates an empty +// NodeAttributeWrapper ready to access the same type +// of NodeAttribute as the other. +//////////////////////////////////////////////////////////////////// +NodeAttributeWrapper NodeAttributeWrapper:: +init_from(const NodeTransitionWrapper &trans) { + return NodeAttributeWrapper(trans.get_handle()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributeWrapper::apply_in_place +// Access: Public +// Description: Modifies the attribute by applying the transition. +//////////////////////////////////////////////////////////////////// +void NodeAttributeWrapper:: +apply_in_place(const NodeTransitionWrapper &trans) { + nassertv(_handle == trans.get_handle()); + _attrib = NodeTransitionCacheEntry::apply(_attrib, trans._entry); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributeWrapper::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void NodeAttributeWrapper:: +output(ostream &out) const { + if (_attrib == (NodeAttribute *)NULL) { + out << "no " << _handle; + } else { + out << *_attrib; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributeWrapper::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void NodeAttributeWrapper:: +write(ostream &out, int indent_level) const { + if (_attrib == (NodeAttribute *)NULL) { + indent(out, indent_level) << "no " << _handle << "\n"; + } else { + _attrib->write(out, indent_level); + } +} diff --git a/panda/src/graph/nodeAttributeWrapper.h b/panda/src/graph/nodeAttributeWrapper.h new file mode 100644 index 0000000000..5e53448adf --- /dev/null +++ b/panda/src/graph/nodeAttributeWrapper.h @@ -0,0 +1,61 @@ +// Filename: nodeAttributeWrapper.h +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NODEATTRIBUTEWRAPPER_H +#define NODEATTRIBUTEWRAPPER_H + +#include + +#include "nodeAttribute.h" + +#include +#include + +class NodeTransitionWrapper; + +//////////////////////////////////////////////////////////////////// +// Class : NodeAttributeWrapper +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NodeAttributeWrapper { +public: + typedef NodeTransitionWrapper TransitionWrapper; + typedef NodeAttributeWrapper AttributeWrapper; + + INLINE NodeAttributeWrapper(TypeHandle handle); + INLINE NodeAttributeWrapper(const NodeAttributeWrapper ©); + INLINE void operator = (const NodeAttributeWrapper ©); + static NodeAttributeWrapper init_from(const NodeTransitionWrapper &trans); + + INLINE TypeHandle get_handle() const; + INLINE NodeAttribute *get_attrib() const; + INLINE void set_attrib(NodeAttribute *attrib); + + INLINE bool is_initial() const; + INLINE int compare_to(const NodeAttributeWrapper &other) const; + + INLINE void make_initial(); + void apply_in_place(const NodeTransitionWrapper &trans); + + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + +private: + TypeHandle _handle; + PT(NodeAttribute) _attrib; +}; + +INLINE ostream &operator << (ostream &out, const NodeAttributeWrapper &naw) { + naw.output(out); + return out; +} + +#include "nodeAttributeWrapper.I" + +template +INLINE bool +get_attribute_into(Attribute *&ptr, const NodeAttributeWrapper &trans); + +#endif diff --git a/panda/src/graph/nodeAttributes.I b/panda/src/graph/nodeAttributes.I new file mode 100644 index 0000000000..05053e5b0a --- /dev/null +++ b/panda/src/graph/nodeAttributes.I @@ -0,0 +1,125 @@ +// Filename: nodeAttributes.I +// Created by: drose (21Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeAttributes::size_type NodeAttributes:: +size() const { + return _attributes.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::begin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeAttributes::iterator NodeAttributes:: +begin() { + return _attributes.begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::end +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeAttributes::iterator NodeAttributes:: +end() { + return _attributes.end(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::begin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeAttributes::const_iterator NodeAttributes:: +begin() const { + return _attributes.begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::end +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeAttributes::const_iterator NodeAttributes:: +end() const { + return _attributes.end(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::insert +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeAttributes::iterator NodeAttributes:: +insert(NodeAttributes::iterator position, + const NodeAttributes::value_type &x) { + return _attributes.insert(position, x); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::erase +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodeAttributes:: +erase(NodeAttributes::iterator position) { + _attributes.erase(position); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::apply_in_place +// Access: Public +// Description: Modifies the current NodeAttributes object to reflect +// the application of the indicated transitions. +//////////////////////////////////////////////////////////////////// +INLINE void NodeAttributes:: +apply_in_place(const NodeTransitionCache &trans) { + apply_from(*this, trans); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::apply +// Access: Public +// Description: Allocates and returns a new NodeAttributes object +// that represents the application of this +// NodeAttributes to the indicated transition cache. +// This NodeAttributes object is not changed. +//////////////////////////////////////////////////////////////////// +INLINE NodeAttributes *NodeAttributes:: +apply(const NodeTransitionCache &trans) const { + NodeAttributes *na = new NodeAttributes; + na->apply_from(*this, trans); + return na; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_attribute_into +// Description: This external template function is handy for +// extracting a attribute of a particular type from the +// set. If the attribute exists, it is automatically +// downcasted to the correct type and stored in the +// pointer given in the first parameter, and the return +// value is true. If the attribute does not exist, the +// pointer is filled with NULL and the return value is +// false. +//////////////////////////////////////////////////////////////////// +template +INLINE bool +get_attribute_into(Attribute *&ptr, const NodeAttributes &attrib, + TypeHandle transition_type) { + NodeAttribute *nt = attrib.get_attribute(transition_type); + if (nt == (NodeAttribute *)NULL) { + ptr = (Attribute *)NULL; + return false; + } + DCAST_INTO_R(ptr, nt, false); + return true; +} diff --git a/panda/src/graph/nodeAttributes.N b/panda/src/graph/nodeAttributes.N new file mode 100644 index 0000000000..30b650b6cc --- /dev/null +++ b/panda/src/graph/nodeAttributes.N @@ -0,0 +1,2 @@ +ignoreinvolved iterator +ignoreinvolved const_iterator diff --git a/panda/src/graph/nodeAttributes.cxx b/panda/src/graph/nodeAttributes.cxx new file mode 100644 index 0000000000..f5a68e29b0 --- /dev/null +++ b/panda/src/graph/nodeAttributes.cxx @@ -0,0 +1,251 @@ +// Filename: nodeAttributes.cxx +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nodeAttributes.h" +#include "nodeTransitionCache.h" +#include "config_graph.h" +#include "setTransitionHelpers.h" + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttributes:: +NodeAttributes() { +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttributes:: +NodeAttributes(const NodeAttributes ©) : + _attributes(copy._attributes) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NodeAttributes:: +operator = (const NodeAttributes ©) { + _attributes = copy._attributes; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +NodeAttributes:: +~NodeAttributes() { +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::is_empty +// Access: Public +// Description: Returns true if there are no Attributes stored in +// the set, or false if there are any (even initial) +// Attributes. +//////////////////////////////////////////////////////////////////// +bool NodeAttributes:: +is_empty() const { + return _attributes.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::set_attribute +// Access: Public +// Description: Stores the indicated attribute pointer in the +// NodeAttributes set, according to the indicated +// TypeHandle. Node that attributes are stored +// according to the TypeHandle of the associated +// *transition*, not of the attributes' own TypeHandle. +// Thus, there cannot be a flavor of set_attribute() +// that automatically infers the correct TypeHandle +// based on the attribute type. +// +// The NodeAttribute may be NULL indicating that the +// attribute should be cleared. If the NodeAttribute is +// not NULL, it must match the type indicated by the +// TypeHandle. +// +// The return value is a pointer to the *previous* +// attribute in the set, if any, or NULL if there was +// none. +//////////////////////////////////////////////////////////////////// +PT(NodeAttribute) NodeAttributes:: +set_attribute(TypeHandle handle, NodeAttribute *attrib) { + if (attrib == (NodeAttribute *)NULL) { + return clear_attribute(handle); + + } else { + Attributes::iterator ti; + ti = _attributes.find(handle); + if (ti != _attributes.end()) { + PT(NodeAttribute) result = (*ti).second; + (*ti).second = attrib; + return result; + } + + _attributes.insert(Attributes::value_type(handle, attrib)); + return NULL; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::clear_attribute +// Access: Public +// Description: Removes any attribute associated with the indicated +// handle from the set. +// +// The return value is a pointer to the previous +// attribute in the set, if any, or NULL if there was +// none. +//////////////////////////////////////////////////////////////////// +PT(NodeAttribute) NodeAttributes:: +clear_attribute(TypeHandle handle) { + nassertr(handle != TypeHandle::none(), NULL); + + Attributes::iterator ti; + ti = _attributes.find(handle); + if (ti != _attributes.end()) { + PT(NodeAttribute) result = (*ti).second; + _attributes.erase(ti); + return result; + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::has_attribute +// Access: Public +// Description: Returns true if ab attribute associated with the +// indicated handle has been stored in the set (even if +// it is the initial attribute), or false otherwise. +//////////////////////////////////////////////////////////////////// +bool NodeAttributes:: +has_attribute(TypeHandle handle) const { + nassertr(handle != TypeHandle::none(), false); + return _attributes.count(handle) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::get_attribute +// Access: Public +// Description: Returns the attribute associated with the indicated +// handle, or NULL if no such attribute has been stored +// in the set. +//////////////////////////////////////////////////////////////////// +NodeAttribute *NodeAttributes:: +get_attribute(TypeHandle handle) const { + nassertr(handle != TypeHandle::none(), NULL); + Attributes::const_iterator ai; + ai = _attributes.find(handle); + if (ai != _attributes.end()) { + return (*ai).second; + } + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::clear +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NodeAttributes:: +clear() { + _attributes.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::is_initial +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool NodeAttributes:: +is_initial() const { + Attributes::const_iterator ai; + for (ai = _attributes.begin(); ai != _attributes.end(); ++ai) { + if ((*ai).second != (NodeAttribute *)NULL) { + // && !(*ai).second->is_initial() + return false; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int NodeAttributes:: +compare_to(const NodeAttributes &other) const { + return tmap_compare_attr(_attributes.begin(), _attributes.end(), + other._attributes.begin(), other._attributes.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::apply_from +// Access: Public +// Description: Modifies the current NodeAttributes object to reflect +// the application of the indicated NodeAttributes and +// the indicated transitions. The "other" +// NodeAttributes object may be the same as this. +//////////////////////////////////////////////////////////////////// +void NodeAttributes:: +apply_from(const NodeAttributes &other, const NodeTransitionCache &trans) { + Attributes temp; + + tmap_apply(other._attributes.begin(), other._attributes.end(), + trans._cache.begin(), trans._cache.end(), + inserter(temp, temp.begin())); + + _attributes.swap(temp); +} + + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NodeAttributes:: +output(ostream &out) const { + bool written_any = false; + + Attributes::const_iterator ai; + for (ai = _attributes.begin(); ai != _attributes.end(); ++ai) { + if ((*ai).second != (NodeAttribute *)NULL) { + if (written_any) { + out << " "; + } + out << *(*ai).second; + written_any = true; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NodeAttributes:: +write(ostream &out, int indent_level) const { + Attributes::const_iterator ai; + for (ai = _attributes.begin(); ai != _attributes.end(); ++ai) { + if ((*ai).second != (NodeAttribute *)NULL) { + (*ai).second->write(out, indent_level); + } + } +} diff --git a/panda/src/graph/nodeAttributes.h b/panda/src/graph/nodeAttributes.h new file mode 100644 index 0000000000..f0d4caa352 --- /dev/null +++ b/panda/src/graph/nodeAttributes.h @@ -0,0 +1,89 @@ +// Filename: nodeAttributes.h +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NODEATTRIBUTES_H +#define NODEATTRIBUTES_H + +#include + +#include "nodeAttribute.h" + +#include + +#include + +class NodeTransitionCache; + +//////////////////////////////////////////////////////////////////// +// Class : NodeAttributes +// Description : This represents a set of zero or more NodeAttribute +// pointers, organized by the attributes' get_handle() +// value. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NodeAttributes { +public: + NodeAttributes(); + NodeAttributes(const NodeAttributes ©); + void operator = (const NodeAttributes ©); + ~NodeAttributes(); + + bool is_empty() const; + PT(NodeAttribute) set_attribute(TypeHandle handle, NodeAttribute *attrib); + PT(NodeAttribute) clear_attribute(TypeHandle handle); + bool has_attribute(TypeHandle handle) const; + NodeAttribute *get_attribute(TypeHandle handle) const; + + void clear(); + + bool is_initial() const; + int compare_to(const NodeAttributes &other) const; + +private: + typedef map Attributes; +public: + // STL-like definitions to expose the map within NodeAttributes to + // external adjustment. Beware! These are not safe to use outside + // of PANDA.DLL. + typedef Attributes::iterator iterator; + typedef Attributes::const_iterator const_iterator; + typedef Attributes::value_type value_type; + typedef Attributes::size_type size_type; + + INLINE size_type size() const; + INLINE iterator begin(); + INLINE iterator end(); + INLINE const_iterator begin() const; + INLINE const_iterator end() const; + INLINE iterator insert(iterator position, const value_type &x); + INLINE void erase(iterator position); + +public: + INLINE void apply_in_place(const NodeTransitionCache &trans); + INLINE NodeAttributes *apply(const NodeTransitionCache &trans) const; + void apply_from(const NodeAttributes &other, + const NodeTransitionCache &trans); + +public: + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + +private: + Attributes _attributes; +friend class NodeAttributeCache; +}; + +INLINE ostream &operator << (ostream &out, const NodeAttributes &nas) { + nas.output(out); + return out; +} + +template +INLINE bool +get_attribute_into(Attribute *&ptr, const NodeAttributes &attrib, + TypeHandle transition_type); + +#include "nodeAttributes.I" + +#endif diff --git a/panda/src/graph/nodeRelation.I b/panda/src/graph/nodeRelation.I new file mode 100644 index 0000000000..527130651a --- /dev/null +++ b/panda/src/graph/nodeRelation.I @@ -0,0 +1,392 @@ +// Filename: nodeRelation.I +// Created by: drose (30Sep99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::Constructor +// Access: Public +// Description: Creates a new arc of the scene graph, and immediately +// attaches it. +//////////////////////////////////////////////////////////////////// +INLINE NodeRelation:: +NodeRelation(Node *parent, Node *to, int sort, TypeHandle type) : + _parent(parent), _child(to), _sort(sort), + _type(type), _num_transitions(0) +{ + _top_subtree = NULL; + _attached = false; + attach(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::Constructor +// Access: Protected +// Description: Creates a new, unattached arc. This constructor is +// only intended for passing through the factory to +// create an arc based on a particular type using +// create_typed_arc(), below. You shouldn't, in +// general, attempt to create an unattached arc. +//////////////////////////////////////////////////////////////////// +INLINE NodeRelation:: +NodeRelation(TypeHandle type) : + _type(type) +{ + _parent = NULL; + _child = NULL; + _sort = 0; + _top_subtree = NULL; + _attached = false; + _num_transitions = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::Constructor +// Access: Protected +// Description: Creates a new, unattached arc. This constructor is +// only intended for being called in make_NodeRelation. +// You shouldn't, in general, attempt to create an +// unattached arc. +//////////////////////////////////////////////////////////////////// +INLINE NodeRelation:: +NodeRelation(void) +{ + _parent = NULL; + _child = NULL; + _sort = 0; + _top_subtree = NULL; + _attached = false; + _type = get_class_type(); + _num_transitions = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: remove_arc +// Description: Removes the arc from the graph, which incidentally +// will also delete the arc unless there are some +// additional outstanding pointers to it. +//////////////////////////////////////////////////////////////////// +INLINE void +remove_arc(NodeRelation *arc) { + PT(NodeRelation) hold_arc = arc->detach(); + arc->_parent = NULL; + arc->_child = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::output_transitions +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodeRelation:: +output_transitions(ostream &out) const { + out << _transitions; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::write_transitions +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodeRelation:: +write_transitions(ostream &out, int indent_level) const { + _transitions.write(out, indent_level); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::get_parent +// Access: Public +// Description: Returns the node above the arc in the scene graph. +//////////////////////////////////////////////////////////////////// +INLINE Node *NodeRelation:: +get_parent() const { + return _parent; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::get_child +// Access: Public +// Description: Returns the node below the arc in the scene graph. +//////////////////////////////////////////////////////////////////// +INLINE Node *NodeRelation:: +get_child() const { + return _child; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::get_sort +// Access: Public +// Description: Returns the sorting index of the arc. This affects +// its apparent position among its list of siblings. +//////////////////////////////////////////////////////////////////// +INLINE int NodeRelation:: +get_sort() const { + return _sort; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::change_parent +// Access: Public +// Description: Changes the parent of the arc to a different node. +//////////////////////////////////////////////////////////////////// +INLINE void NodeRelation:: +change_parent(Node *parent) { + PT(NodeRelation) hold_arc = detach(); + _parent = parent; + attach(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::change_child +// Access: Public +// Description: Changes the child of the arc to a different node. +//////////////////////////////////////////////////////////////////// +INLINE void NodeRelation:: +change_child(Node *child) { + PT(NodeRelation) hold_arc = detach(); + _child = child; + attach(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::change_parent_and_child +// Access: Public +// Description: Simultaneously moves both the parent and child of the +// arc to different nodes. +//////////////////////////////////////////////////////////////////// +INLINE void NodeRelation:: +change_parent_and_child(Node *parent, Node *child) { + PT(NodeRelation) hold_arc = detach(); + _parent = parent; + _child = child; + attach(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::set_sort +// Access: Public +// Description: Changes the sorting index of the arc. This affects +// its apparent position among its list of siblings. +//////////////////////////////////////////////////////////////////// +INLINE void NodeRelation:: +set_sort(int sort) { + PT(NodeRelation) hold_arc = detach(); + _sort = sort; + attach(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::set_transition +// Access: Public +// Description: This flavor of set_transition() accepts a specific +// TypeHandle, indicating the type of transition that we +// are setting, and a NodeTransition pointer indicating +// the value of the transition. The NodeTransition may +// be NULL indicating that the transition should be +// cleared. If the NodeTransition is not NULL, it must +// match the type indicated by the TypeHandle. +// +// The return value is a pointer to the *previous* +// transition in the set, if any, or NULL if there was +// none. +//////////////////////////////////////////////////////////////////// +INLINE PT(NodeTransition) NodeRelation:: +set_transition(TypeHandle handle, NodeTransition *trans) { + PT(NodeTransition) old_trans = + _transitions.set_transition(handle, trans); + + if (old_trans != (NodeTransition *)NULL) { + old_trans->removed_from_arc(this); + } + if (trans != (NodeTransition *)NULL) { + trans->added_to_arc(this); + } + + changed_transition(handle); + return old_trans; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::set_transition +// Access: Public +// Description: This flavor of set_transition() accepts a pointer to +// a NodeTransition only. It infers the type of the +// NodeTransition from the pointer. However, it is not +// valid to pass a NULL pointer to this flavor of +// set_transition; if the pointer might be NULL, use the +// above flavor instead (or just call clear_transition). +// +// The return value is a pointer to the *previous* +// transition in the set, if any, or NULL if there was +// none. +//////////////////////////////////////////////////////////////////// +INLINE PT(NodeTransition) NodeRelation:: +set_transition(NodeTransition *trans) { + nassertr(trans != (NodeTransition *)NULL, NULL); + nassertr(trans->get_handle() != TypeHandle::none(), NULL); + return set_transition(trans->get_handle(), trans); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::set_transition +// Access: Public +// Description: This is a convenience flavor. It automatically +// applies the indicated priority to the given +// transition before setting the transition. Generally, +// this will be used for creating override transitions +// on-the-fly. +//////////////////////////////////////////////////////////////////// +INLINE PT(NodeTransition) NodeRelation:: +set_transition(NodeTransition *trans, int priority) { + nassertr(trans != (NodeTransition *)NULL, NULL); + nassertr(trans->get_handle() != TypeHandle::none(), NULL); + trans->set_priority(priority); + return set_transition(trans->get_handle(), trans); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::clear_transition +// Access: Public +// Description: Removes any transition associated with the indicated +// handle from the arc. +// +// The return value is a pointer to the previous +// transition in the set, if any, or NULL if there was +// none. +//////////////////////////////////////////////////////////////////// +INLINE PT(NodeTransition) NodeRelation:: +clear_transition(TypeHandle handle) { + PT(NodeTransition) old_trans = + _transitions.clear_transition(handle); + if (old_trans != (NodeTransition *)NULL) { + old_trans->removed_from_arc(this); + } + + changed_transition(handle); + return old_trans; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::has_transition +// Access: Public +// Description: Returns true if a transition associated with the +// indicated handle has been stored on the arc (even if +// it is the identity transition), or false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool NodeRelation:: +has_transition(TypeHandle handle) const { + return _transitions.has_transition(handle); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::has_any_transition +// Access: Public +// Description: Returns true if a any transition has been stored on +// the arc (even if it is the identity transition), or +// false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool NodeRelation:: +has_any_transition() const { + return !_transitions.is_empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::get_transition +// Access: Public +// Description: Returns the transition associated with the indicated +// handle, or NULL if no such transition has been stored +// on the arc. +//////////////////////////////////////////////////////////////////// +INLINE NodeTransition *NodeRelation:: +get_transition(TypeHandle handle) const { + return _transitions.get_transition(handle); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::compare_transitions_to +// Access: Public +// Description: Returns a negative number if the transitions on the +// other arc sort before those on this arc, positive if +// they sort after, or zero if the sets of transitions +// are equivalent. +//////////////////////////////////////////////////////////////////// +INLINE int NodeRelation:: +compare_transitions_to(const NodeRelation *arc) const { + return _transitions.compare_to(arc->_transitions); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::create_typed_arc +// Access: Public, Static +// Description: Creates a new arc of the scene graph of the indicated +// type, and immediately attaches it. Returns NULL if +// the type is unknown. +//////////////////////////////////////////////////////////////////// +INLINE NodeRelation *NodeRelation:: +create_typed_arc(TypeHandle type, Node *parent, Node *child) { + NodeRelation *arc = get_factory().make_instance(type); + if (arc != (NodeRelation *)NULL) { + arc->_parent = parent; + arc->_child = child; + arc->attach(); + } + return arc; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::register_with_factory +// Access: Public, Static +// Description: Registers the type NodeRelation with the factory. +// This is only intended to be called at initialization +// time; don't call it directly. +//////////////////////////////////////////////////////////////////// +INLINE void NodeRelation:: +register_with_factory() { + get_factory().register_factory(get_class_type(), make_arc); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::get_factory +// Access: Protected, Static +// Description: Returns a reference to the NodeRelation factory. +// Creates one if this is the first time this function +// has been called. This is necessary to guarantee +// ordering at static init time. +//////////////////////////////////////////////////////////////////// +INLINE Factory &NodeRelation:: +get_factory() { + if (_factory == (Factory *)NULL) { + _factory = new Factory; + } + return *_factory; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_transition_into +// Description: This external template function is handy for +// extracting a transition of a particular type from the +// arc. If the transition exists, it is automatically +// downcasted to the correct type and stored in the +// pointer given in the first parameter, and the return +// value is true. If the transition does not exist, the +// pointer is filled with NULL and the return value is +// false. +//////////////////////////////////////////////////////////////////// +template +INLINE bool +get_transition_into(Transition *&ptr, const NodeRelation *arc, + TypeHandle transition_type) { + NodeTransition *nt = arc->get_transition(transition_type); + if (nt == (NodeTransition *)NULL) { + ptr = (Transition *)NULL; + return false; + } + DCAST_INTO_R(ptr, nt, false); + return true; +} + +template +INLINE bool +get_transition_into(Transition *&ptr, const NodeRelation *arc) { + return get_transition_into(ptr, arc, Transition::get_class_type()); +} diff --git a/panda/src/graph/nodeRelation.N b/panda/src/graph/nodeRelation.N new file mode 100644 index 0000000000..1a62d38995 --- /dev/null +++ b/panda/src/graph/nodeRelation.N @@ -0,0 +1,2 @@ +ignoremember create_typed_arc +ignoremember register_with_factory diff --git a/panda/src/graph/nodeRelation.cxx b/panda/src/graph/nodeRelation.cxx new file mode 100644 index 0000000000..2274a443c7 --- /dev/null +++ b/panda/src/graph/nodeRelation.cxx @@ -0,0 +1,757 @@ +// Filename: nodeRelation.cxx +// Created by: drose (26Oct98) +// + +#include "nodeRelation.h" +#include "node.h" +#include "config_graph.h" + +#include +#include + + +TypeHandle NodeRelation::_type_handle; + +LastGraphUpdate last_graph_update; + +Factory *NodeRelation::_factory = NULL; + +// Following are a handful of local template functions that provide +// support for manipulating the list of arcs on nodes. They are +// template functions because they work as well on UpRelationArcs as +// as they do on DownRelationArcs, which are slightly different things +// (DownRelationArcs are reference-counting). + +//////////////////////////////////////////////////////////////////// +// Function: verify_arc_list +// Description: A local template function that verifies that the list +// of arcs (either UpRelationArcs or DownRelationArcs) +// is correctly sorted, if paranoid_graph is set. +// Otherwise, it does nothing. +//////////////////////////////////////////////////////////////////// +#ifdef NDEBUG +template +INLINE void +verify_arc_list(Iterator, Iterator) { +} +#else // NDEBUG +template +static void +verify_arc_list(Iterator begin, Iterator end) { + if (paranoid_graph) { + if (begin < end) { + Iterator i = begin; + int sort = (*i)->get_sort(); + ++i; + while (i < end) { + nassertv(sort <= (*i)->get_sort()); + sort = (*i)->get_sort(); + ++i; + } + } + } +} +#endif // NDEBUG + +//////////////////////////////////////////////////////////////////// +// Function: find_insert_position +// Description: A local template function that performs a binary +// search on the arcs list (either UpRelationArcs or +// DownRelationArcs) and finds the place to insert the +// indicated arc. +// +// This place will be at the end of the similarly-sorted +// arcs. +//////////////////////////////////////////////////////////////////// +template +static Iterator +r_find_insert_position(Iterator begin, Iterator end, NodeRelation *arc) { + if (begin == end) { + // The list is empty; the insert position is the end of the list. + return end; + } + + Iterator center = begin + (end - begin) / 2; + nassertr(center < end, end); + + if ((*center)->get_sort() > arc->get_sort()) { + // Insert before the center. + return r_find_insert_position(begin, center, arc); + + } else { // (*center)->get_sort() <= arc->get_sort(); + // Insert after the center. + return r_find_insert_position(center + 1, end, arc); + } +} + +template +static Iterator +find_insert_position(Iterator begin, Iterator end, NodeRelation *arc) { + Iterator result = r_find_insert_position(begin, end, arc); +#ifndef NDEBUG + // Verify the result. + if (paranoid_graph) { + // If there is a node before the indicated position, it must have + // a sort value either less than or equal to this arc's value. + if (begin < result) { + nassertr((*(result - 1))->get_sort() <= arc->get_sort(), result); + } + + // If there is a node after the indicated position, it must have a + // sort value greater than this arc's value. + if (result < end) { + nassertr((*result)->get_sort() > arc->get_sort(), result); + } + } +#endif + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: find_arc +// Description: A local template function that performs a binary +// search on the arcs list (either UpRelationArcs or +// DownRelationArcs) and finds the arc's position within +// the list. It returns end if the arc is not within +// the list. +//////////////////////////////////////////////////////////////////// +template +static Iterator +find_arc(Iterator begin, Iterator end, NodeRelation *arc) { + if (begin == end) { + // The list is empty; the arc is not on the list. + return end; + } + + Iterator center = begin + (end - begin) / 2; + nassertr(center < end, end); + + if ((*center)->get_sort() > arc->get_sort()) { + // It must be before the center. + return find_arc(begin, center, arc); + + } else if ((*center)->get_sort() < arc->get_sort()) { + // It must be after the center. + return find_arc(center + 1, end, arc); + + } else { + // The center's sort matches the arc's sort. It could be either + // before or after the center. First try after. + Iterator i = center; + while (i < end && (*i)->get_sort() == arc->get_sort()) { + if ((*i) == arc) { + return i; + } + ++i; + } + + // No, try before. + i = center; + --i; + while (i >= begin && (*i)->get_sort() == arc->get_sort()) { + if ((*i) == arc) { + return i; + } + --i; + } + + // No such arc! + return end; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: internal_insert_arc +// Description: A local template function that inserts the arc into +// its appropriate place in the list and returns true if +// successful, false if there was some kind of error. +//////////////////////////////////////////////////////////////////// +template +static bool +internal_insert_arc(ArcList &alist, NodeRelation *arc) { + nassertr(arc != (NodeRelation *)NULL, false); + + TYPENAME ArcList::iterator position = + find_insert_position(alist.begin(), alist.end(), arc); + nassertr(position >= alist.begin() && position <= alist.end(), false); + + /* + if (graph_cat.is_debug()) { + if (position == alist.end()) { + graph_cat.debug() + << "Inserting " << *arc << " at end\n"; + } else { + graph_cat.debug() + << "Inserting " << *arc << " before " << *(*position) << "\n"; + } + } + */ + + alist.insert(position, arc); + verify_arc_list(alist.begin(), alist.end()); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: internal_remove_arc +// Description: A local template function that removes the arc from +// its place in the list and returns true if successful, +// false if there was some kind of error. +//////////////////////////////////////////////////////////////////// +template +static bool +internal_remove_arc(ArcList &alist, NodeRelation *arc) { + nassertr(arc != (NodeRelation *)NULL, false); + + TYPENAME ArcList::iterator position = + find_arc(alist.begin(), alist.end(), arc); + nassertr(position >= alist.begin() && position <= alist.end(), false); + nassertr(position != alist.end(), false); + + /* + if (graph_cat.is_debug()) { + TYPENAME ArcList::iterator next = position + 1; + if (next == list.end()) { + graph_cat.debug() + << "Removing " << *arc << " from end\n"; + } else { + graph_cat.debug() + << "Removing " << *arc << " from before " << *(*next) << "\n"; + } + } + */ + + alist.erase(position); + verify_arc_list(alist.begin(), alist.end()); + + return true; +} + + + + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::Copy Constructor +// Access: Private +// Description: It's not legal to copy a NodeRelation. +//////////////////////////////////////////////////////////////////// +NodeRelation:: +NodeRelation(const NodeRelation &) { + graph_cat.error() + << "NodeRelation copy constructor called!\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::Copy Assignment Operator +// Access: Private +// Description: It's not legal to copy a NodeRelation. +//////////////////////////////////////////////////////////////////// +void NodeRelation:: +operator = (const NodeRelation &) { + graph_cat.error() + << "NodeRelation copy assignment operator called!\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeRelation:: +~NodeRelation() { + // An attached arc should never be deleted. If this assertion + // fails, it's most likely that someone attempted to explicitly + // delete an arc. You should use remove_arc() instead. + nassertv(!_attached); + + _transitions.remove_all_from_arc(this); +} + + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::output +// Access: Public +// Description: Writes a brief description of the arc to the +// indicated output stream. This function is called by +// the << operator. +//////////////////////////////////////////////////////////////////// +void NodeRelation:: +output(ostream &out) const { + if (_parent == (Node*)NULL) { + out << "(null)"; + } else { + out << *_parent; + } + out << " -> "; + if (_child == (Node*)NULL) { + out << "(null)"; + } else { + out << *_child; + } + + if (!_attached) { + out << " (unattached)"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::copy_transitions_from +// Access: Public +// Description: Copies all of the transitions stored on the other arc +// to this arc. Any existing transitions on this arc, +// for which there was not a corresponding transition of +// the same type on the other arc, are left undisturbed. +//////////////////////////////////////////////////////////////////// +void NodeRelation:: +copy_transitions_from(const NodeRelation *arc) { + copy_transitions_from(arc->_transitions); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::compose_transitions_from +// Access: Public +// Description: Similar to copy_transitions_from(), except that if +// the same type of transition exists on both arcs, the +// composition of the two is stored. The result +// represents the same set of transitions that would +// result from composing the two individual sets of +// transitions. +//////////////////////////////////////////////////////////////////// +void NodeRelation:: +compose_transitions_from(const NodeRelation *arc) { + compose_transitions_from(arc->_transitions); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::copy_transitions_from +// Access: Public +// Description: Copies all of the transitions stored in the indicated +// set to this arc. Any existing transitions on this +// arc, for which there was not a corresponding +// transition of the same type on the other arc, are +// left undisturbed. +//////////////////////////////////////////////////////////////////// +void NodeRelation:: +copy_transitions_from(const NodeTransitions &trans) { + if (!trans.is_empty()) { + _transitions.copy_transitions_from(trans, this); + + // Now mark that *all* transitions have changed, even though many + // of them might not have, because we're not really sure. + _net_transitions.clear(); + NodeTransitions::const_iterator ti; + for (ti = _transitions.begin(); ti != _transitions.end(); ++ti) { + changed_transition((*ti).first); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::compose_transitions_from +// Access: Public +// Description: Similar to copy_transitions_from(), except that if +// the same type of transition exists in both places, +// the composition of the two is stored. The result +// represents the same set of transitions that would +// result from composing the two individual sets of +// transitions. +//////////////////////////////////////////////////////////////////// +void NodeRelation:: +compose_transitions_from(const NodeTransitions &trans) { + if (!trans.is_empty()) { + _transitions.compose_transitions_from(trans, this); + + // Now mark that *all* transitions have changed, even though many + // of them might not have, because we're not really sure. + _net_transitions.clear(); + NodeTransitions::const_iterator ti; + for (ti = _transitions.begin(); ti != _transitions.end(); ++ti) { + changed_transition((*ti).first); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::sub_render_trans +// Access: Public +// Description: Calls sub_render() on each transition assigned to the +// arc. Returns true if all transitions returned true, +// false if any returned false. +//////////////////////////////////////////////////////////////////// +bool NodeRelation:: +sub_render_trans(const AllAttributesWrapper &attrib, + AllTransitionsWrapper &trans, + GraphicsStateGuardianBase *gsgbase) { + bool all_true = true; + NodeTransitions::const_iterator ti; + for (ti = _transitions.begin(); ti != _transitions.end(); ++ti) { + if (!(*ti).second->sub_render(this, attrib, trans, gsgbase)) { + all_true = false; + } + } + + return all_true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::has_sub_render_trans +// Access: Public +// Description: Returns true if any transition on the arc has a +// sub_render() function. +//////////////////////////////////////////////////////////////////// +bool NodeRelation:: +has_sub_render_trans() const { + NodeTransitions::const_iterator ti; + for (ti = _transitions.begin(); ti != _transitions.end(); ++ti) { + if ((*ti).second->has_sub_render()) { + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::make_arc +// Access: Public, Static +// Description: This function is passed to the Factory to make a new +// NodeRelation by type. Don't try to call this +// function directly. +//////////////////////////////////////////////////////////////////// +NodeRelation *NodeRelation:: +make_arc(const FactoryParams &) { + return new NodeRelation(NodeRelation::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::attach +// Access: Protected +// Description: Makes the arc an official part of the graph by +// informing the nodes that it connects of its +// existence. It is an error to attach an arc that has +// already been attached. +// +// It is also an error to attach an arc that is not +// grounded at both ends. +//////////////////////////////////////////////////////////////////// +void NodeRelation:: +attach() { + nassertv(_parent != (Node*)NULL); + nassertv(_child != (Node*)NULL); + nassertv(!_attached); + + _attached = true; + + bool inserted_one = internal_insert_arc(_parent->_children[_type], this); + bool inserted_two = internal_insert_arc(_child->_parents[_type], this); + nassertv(inserted_one && inserted_two); + + // Blow out the cache and increment the current update sequence. + _net_transitions.clear(); + ++last_graph_update[_type]; + + _parent->force_bound_stale(); + mark_bound_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::detach +// Access: Protected +// Description: Removes the arc from the graph. The arc remains and +// still refers to its connected nodes, but the nodes +// themselves no longer know about the arc. It is an +// error to detach an arc that is not already attached. +// +// detach() returns a PointerTo for the arc itself. +// This is useful not so much for the return value +// itself, but more to prevent the arc from destructing +// until detach() returns, since the arc will destruct +// when its last reference count is removed, and it is +// generally a bad idea to destruct a class within its +// own method. +//////////////////////////////////////////////////////////////////// +PT(NodeRelation) NodeRelation:: +detach() { + PT(NodeRelation) result = this; + + nassertr(_parent != (Node*)NULL, result); + nassertr(_child != (Node*)NULL, result); + nassertr(_attached, result); + + force_bound_stale(); + + bool removed_one = internal_remove_arc(_parent->_children[_type], this); + bool removed_two = internal_remove_arc(_child->_parents[_type], this); + + nassertr(removed_one, result); + nassertr(removed_two, result); + + _attached = false; + + // Blow out the cache and increment the current update sequence. + _net_transitions.clear(); + ++last_graph_update[_type]; + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::detach_below +// Access: Protected +// Description: This is a special method that is only called from the +// Node destructor. It detaches the arc, but does not +// remove it from its parent's arc list, which is +// presumably about to be destroyed anyway. +//////////////////////////////////////////////////////////////////// +PT(NodeRelation) NodeRelation:: +detach_below() { + PT(NodeRelation) result = this; + + nassertr(_parent != (Node*)NULL, result); + nassertr(_child != (Node*)NULL, result); + nassertr(_attached, result); + + force_bound_stale(); + + bool removed = internal_remove_arc(_child->_parents[_type], this); + + nassertr(removed, result); + + _attached = false; + + // Blow out the cache and increment the current update sequence. + _net_transitions.clear(); + ++last_graph_update[_type]; + + return result; +} + + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::changed_transition +// Access: Public, Virtual +// Description: This is called by set_transition() or +// clear_transition() whenever a transition is added, +// updated, or removed from the arc. It is just a +// callback to the arc so it can decide whether it needs +// to update any internal data as a response to this +// adjustment (for instance, by marking the bounding +// sphere stale). +//////////////////////////////////////////////////////////////////// +void NodeRelation:: +changed_transition(TypeHandle trans_type) { + if (_net_transitions != (NodeTransitionCache *)NULL) { + _net_transitions->clear_transition(trans_type); + } + last_graph_update[get_type()]++; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::propagate_stale_bound +// Access: Protected, Virtual +// Description: Called by BoundedObject::mark_bound_stale(), this +// should make sure that all bounding volumes that +// depend on this one are marked stale also. +//////////////////////////////////////////////////////////////////// +void NodeRelation:: +propagate_stale_bound() { + // Mark all of our parent arcs stale as well. + Node *node = _parent; + nassertv(node != (Node*)NULL); + + UpRelations::const_iterator uri; + uri = node->_parents.find(get_type()); + if (uri != node->_parents.end()) { + const UpRelationPointers &urp = (*uri).second; + + UpRelationPointers::const_iterator urpi; + for (urpi = urp.begin(); urpi != urp.end(); ++urpi) { + (*urpi)->mark_bound_stale(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::recompute_bound +// Access: Protected, Virtual +// Description: Recomputes the dynamic bounding volume for this arc +// (and all of its descendants). +//////////////////////////////////////////////////////////////////// +void NodeRelation:: +recompute_bound() { + // First, get ourselves a fresh, empty bounding volume. + BoundedObject::recompute_bound(); + nassertv(_bound != (BoundingVolume*)NULL); + + // Now actually compute the bounding volume by putting it around all + // of our child bounding volumes. + vector child_volumes; + + Node *node = _child; + nassertv(node != (Node*)NULL); + + child_volumes.push_back(&node->get_bound()); + + DownRelations::const_iterator dri; + dri = node->_children.find(get_type()); + if (dri != node->_children.end()) { + const DownRelationPointers &drp = (*dri).second; + + DownRelationPointers::const_iterator drpi; + for (drpi = drp.begin(); drpi != drp.end(); ++drpi) { + child_volumes.push_back(&(*drpi)->get_bound()); + } + } + + bool success = + _bound->around(child_volumes.begin(), child_volumes.end()); + +#ifndef NDEBUG + if (!success) { + graph_cat.error() + << "Unable to recompute bounding volume for " << *this << ":\n" + << "Cannot put " << _bound->get_type() << " around:\n"; + for (int i = 0; i < (int)child_volumes.size(); i++) { + graph_cat.error(false) + << " " << *child_volumes[i] << "\n"; + } + } +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void NodeRelation:: +write_datagram(BamWriter *manager, Datagram &me) +{ + //Write out the "dynamic" type + manager->write_handle(me, _type); + + //We should always be attached if we are trying to write out + nassertv(_attached); + //Neither the Child nor the Parent should be NULL + nassertv(get_parent() != Node::Null && get_child() != Node::Null); + + //Write out the pointer to my parent + manager->write_pointer(me, _parent); + //Write out the pointer to my child + manager->write_pointer(me, _child); + + //Write out the sort relation for this object + me.add_uint16(_sort); + + //Now write out all the Transitions on this arc + me.add_uint16(_transitions.size()); + NodeTransitions::iterator ci; + for(ci = _transitions.begin(); ci != _transitions.end(); ci++) + { + manager->write_pointer(me, (*ci).second); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::complete_pointers +// Access: Public +// Description: Takes in a vector of pointes to TypedWriteable +// objects that correspond to all the requests for +// pointers that this object made to BamReader. +//////////////////////////////////////////////////////////////////// +int NodeRelation:: +complete_pointers(vector_typedWriteable &plist, BamReader*) +{ + nassertr(plist[0] != TypedWriteable::Null && + plist[1] != TypedWriteable::Null, 0); + _parent = DCAST(Node, plist[0]); + _child = DCAST(Node, plist[1]); + + //Let attach do the work of connecting to the + //parent and child nodes, and telling them about + //myself + attach(); + + //The rest of this is the list of Transitions + for(int i = 2; i < _num_transitions + 2; i++) + { + //Ignore Null pointers. This SHOULD mean that + //we have received a Transition that the current + //version doesn't know about, so we want to be able + //to gracefully handle new functionality being thrown + //at old code + if (plist[i] == TypedWriteable::Null) + { + graph_cat->warning() << get_type().get_name() + << "Ignoring null Transition" << endl; + } + else + { + //Let set_transition do the work for storing + //a reference to this transition, determing it's + //exact type, telling the transition about me, etc... + set_transition(DCAST(NodeTransition, plist[i])); + } + } + return _num_transitions+2; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::make_NodeRelation +// Access: Protected +// Description: Factory method to generate a NodeRelation object +//////////////////////////////////////////////////////////////////// +TypedWriteable* NodeRelation:: +make_NodeRelation(const FactoryParams ¶ms) +{ + NodeRelation *me = new NodeRelation; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void NodeRelation:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + _type = manager->read_handle(scan); + //Read in my parent + manager->read_pointer(scan, this); + //Read in my child + manager->read_pointer(scan, this); + //Get my sort relation + _sort = scan.get_uint16(); + + //Now read in all of my transitions + _num_transitions = scan.get_uint16(); + for(int i = 0; i < _num_transitions; i++) + { + manager->read_pointer(scan, this); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a NodeRelation object +//////////////////////////////////////////////////////////////////// +void NodeRelation:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_NodeRelation); +} diff --git a/panda/src/graph/nodeRelation.h b/panda/src/graph/nodeRelation.h new file mode 100644 index 0000000000..593254149b --- /dev/null +++ b/panda/src/graph/nodeRelation.h @@ -0,0 +1,235 @@ +// Filename: nodeRelation.h +// Created by: drose (26Oct98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NODERELATION_H +#define NODERELATION_H + +#include + +#include "nodeTransitions.h" +#include "nodeTransitionCache.h" +#include "boundedObject.h" +#include "pt_Node.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +class Node; + +// This structure keeps a monotonically incrementing sequence number +// for each change made to the graph, for the purpose of invalidating +// wrt cache values. A different sequence number is kept for each +// type of graph, hence the map. +typedef map LastGraphUpdate; +extern LastGraphUpdate EXPCL_PANDA last_graph_update; + +/////////////////////////////////////////////////////////////////// +// Class : NodeRelation +// Description : The base class for all scene graph arcs. This is the +// glue between Nodes that defines the scene graph. +// There are no arcs of type NodeRelation per se, but +// there may be any number of types of arcs that inherit +// from NodeRelation. +// +// All arcs are directed binary relations, from a parent +// node to a child node, and may include any number of +// NodeTransitions which affect the attributes of the +// child node and later descendants. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NodeRelation : public TypedWriteableReferenceCount, public BoundedObject { +public: + INLINE NodeRelation(Node *from, Node *to, int sort, TypeHandle type); + +protected: + // Normally, this should only be used from derived classes for + // passing to the factory. Don't attempt to create an unattached + // arc directly. + INLINE NodeRelation(TypeHandle type); + + //make_NodeRelation needs to have a construct that takes nothing + //since it creates the object before it has any information + INLINE NodeRelation(void); + +private: + // It is an error to attempt to copy an arc. + NodeRelation(const NodeRelation ©); + void operator = (const NodeRelation ©); + +public: + // The destructor needs to be virtual so that we can delete the + // NodeRelations of various types when the Node they're attached to + // destructs. However, a NodeRelation should not generally be + // deleted directly, because it is reference-counted in the tree; + // instead, you should call remove_arc(). + virtual ~NodeRelation(); + + void output(ostream &out) const; + INLINE void output_transitions(ostream &out) const; + INLINE void write_transitions(ostream &out, int indent = 0) const; + + INLINE Node *get_parent() const; + INLINE Node *get_child() const; + INLINE int get_sort() const; + + INLINE void change_parent(Node *parent); + INLINE void change_child(Node *child); + INLINE void change_parent_and_child(Node *parent, Node *child); + INLINE void set_sort(int sort); + + INLINE PT(NodeTransition) set_transition(TypeHandle handle, + NodeTransition *trans); + INLINE PT(NodeTransition) set_transition(NodeTransition *trans); + INLINE PT(NodeTransition) set_transition(NodeTransition *trans, int priority); + INLINE PT(NodeTransition) clear_transition(TypeHandle handle); + INLINE bool has_transition(TypeHandle handle) const; + INLINE bool has_any_transition() const; + INLINE NodeTransition *get_transition(TypeHandle handle) const; + void copy_transitions_from(const NodeRelation *arc); + void compose_transitions_from(const NodeRelation *arc); + void copy_transitions_from(const NodeTransitions &trans); + void compose_transitions_from(const NodeTransitions &trans); + + INLINE int compare_transitions_to(const NodeRelation *arc) const; + + bool sub_render_trans(const AllAttributesWrapper &attrib, + AllTransitionsWrapper &trans, + GraphicsStateGuardianBase *gsgbase); + bool has_sub_render_trans() const; + +public: + // Factory stuff: to create a new NodeRelation based on its + // TypeHandle. + INLINE static NodeRelation * + create_typed_arc(TypeHandle type, Node *parent, Node *child); + + // This is just to be called at initialization time; don't try to + // call this directly. + INLINE static void register_with_factory(); + +protected: + INLINE static Factory &get_factory(); + +private: + static NodeRelation *make_arc(const FactoryParams ¶ms); + static Factory *_factory; + +protected: + void attach(); + PT(NodeRelation) detach(); + PT(NodeRelation) detach_below(); + +private: + // We reference-count the child pointer, but not the parent pointer, + // to avoid circular reference counting. + + Node *_parent; + PT_Node _child; + int _sort; + TypeHandle _type; + bool _attached; + +private: + // This is the set of transitions assigned to the arc. You should + // never access this directly; use set_transition() and friends + // instead. + NodeTransitions _transitions; + + // This stores the known net transitions from the _top_subtree node, + // which is either NULL to indicate the root of the graph, or a + // pointer to the nearest descendent with multiple parents. You + // should never even attempt to access it directly; it exists only + // to support caching in wrt(). + PT(NodeTransitionCache) _net_transitions; + Node *_top_subtree; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + virtual int complete_pointers(vector_typedWriteable &plist, + BamReader *manager); + + static TypedWriteable *make_NodeRelation(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +private: + //This value is only used for the process of re-construction + //from a binary source. DO NOT ACCESS. The value is only + //guaranteed to be accurate during that process + int _num_transitions; + +public: + virtual void changed_transition(TypeHandle transition_type); + +protected: + virtual void propagate_stale_bound(); + virtual void recompute_bound(); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedWriteableReferenceCount::init_type(); + BoundedObject::init_type(); + register_type(_type_handle, "NodeRelation", + TypedWriteableReferenceCount::get_class_type(), + BoundedObject::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend INLINE void remove_arc(NodeRelation *arc); + friend class Node; + friend class NodeTransitionWrapper; + friend class AllTransitionsWrapper; +}; + +INLINE ostream & +operator << (ostream &out, const NodeRelation &arc) { + arc.output(out); + return out; +} + +INLINE void remove_arc(NodeRelation *arc); + +template +INLINE bool +get_transition_into(Transition *&ptr, const NodeRelation *arc, + TypeHandle transition_type); + +template +INLINE bool +get_transition_into(Transition *&ptr, const NodeRelation *arc); + +typedef vector DownRelationPointers; +typedef map DownRelations; + +typedef vector UpRelationPointers; +typedef map UpRelations; + +#include "nodeRelation.I" + +// We include node.h here at the end, so we'll know that nodes are of +// type ReferenceCount and will be able to compile anything that uses +// a NodeRelation. We have to include it here at the end instead of +// the beginning because node.h also includes nodeRelation.h. +#include "node.h" + +#endif diff --git a/panda/src/graph/nodeTransition.I b/panda/src/graph/nodeTransition.I new file mode 100644 index 0000000000..7a827124e1 --- /dev/null +++ b/panda/src/graph/nodeTransition.I @@ -0,0 +1,180 @@ +// Filename: nodeTransition.I +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeTransition:: +NodeTransition() { + _priority = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeTransition:: +NodeTransition(const NodeTransition ©) : + TypedWriteableReferenceCount(copy), + _priority(copy._priority) +{ + // We specifically do *not* copy the _arcs list. +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransition:: +operator = (const NodeTransition ©) { + TypedWriteableReferenceCount::operator = (copy); + _priority = copy._priority; + // We specifically do *not* copy the _arcs list. +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::Equality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodeTransition:: +operator == (const NodeTransition &other) const { + return compare_to(other) == 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodeTransition:: +operator != (const NodeTransition &other) const { + return compare_to(other) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodeTransition:: +operator < (const NodeTransition &other) const { + return compare_to(other) < 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodeTransition:: +operator <= (const NodeTransition &other) const { + return compare_to(other) <= 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodeTransition:: +operator > (const NodeTransition &other) const { + return compare_to(other) > 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodeTransition:: +operator >= (const NodeTransition &other) const { + return compare_to(other) >= 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::compare_to +// Access: Public +// Description: This function works like strcmp(): it compares the +// two transitions and returns a number less than zero +// if this transition sorts before the other one, equal +// to zero if they are equivalent, or greater than zero +// if this transition sorts after the other one. +// +// This imposes an arbitrary sorting order across all +// transitions, whose sole purpose is to allow grouping +// of equivalent transitions together in STL structures +// like maps and sets. +//////////////////////////////////////////////////////////////////// +INLINE int NodeTransition:: +compare_to(const NodeTransition &other) const { + TypeHandle my_handle = get_handle(); + TypeHandle other_handle = other.get_handle(); + + if (my_handle == other_handle) { + return internal_compare_to(&other); + + } else { + return + (my_handle < other_handle) ? -1 : 1; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::set_priority +// Access: Public +// Description: Changes the priority associated with this transition. +// The transition will always override transitions with +// a lower priority. +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransition:: +set_priority(int priority) { + _priority = priority; + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::get_priority +// Access: Public +// Description: Returns the priority associated with this transition. +//////////////////////////////////////////////////////////////////// +INLINE int NodeTransition:: +get_priority() const { + return _priority; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::add_to_arc +// Access: Protected +// Description: Intended to be called only by the arc's +// set_transition() method to indicate that the +// transition has been assigned to the arc. Further +// state changes on the transition will now notify the +// arc. +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransition:: +added_to_arc(NodeRelation *arc) { + bool inserted = _arcs.insert(arc).second; + nassertv(inserted); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::add_to_arc +// Access: Protected +// Description: Intended to be called by the arc's +// set_transition() or clear_transition() method (or by +// the arc's destructor) to indicate that the transition +// is no longer assigned to the arc. Further state +// changes on the transition will *not* notify the arc. +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransition:: +removed_from_arc(NodeRelation *arc) { + size_t num_erased = _arcs.erase(arc); + nassertv(num_erased == 1); +} diff --git a/panda/src/graph/nodeTransition.N b/panda/src/graph/nodeTransition.N new file mode 100644 index 0000000000..4efaa99498 --- /dev/null +++ b/panda/src/graph/nodeTransition.N @@ -0,0 +1 @@ +ignoremember get_handle diff --git a/panda/src/graph/nodeTransition.cxx b/panda/src/graph/nodeTransition.cxx new file mode 100644 index 0000000000..28c90cb149 --- /dev/null +++ b/panda/src/graph/nodeTransition.cxx @@ -0,0 +1,120 @@ +// Filename: nodeTransition.cxx +// Created by: drose (26Oct98) +// +//////////////////////////////////////////////////////////////////// + +#include "nodeTransition.h" +#include "nodeTransitions.h" +#include "nodeRelation.h" + +#include + +TypeHandle NodeTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::get_handle +// Access: Public, Virtual +// Description: Returns the TypeHandle that is used to identify this +// particular transition (and its attribute) on the +// graph arcs. Normally this will be the same as the +// transition's type, e.g. get_type(), but certain +// transition types may want to redefine this. +//////////////////////////////////////////////////////////////////// +TypeHandle NodeTransition:: +get_handle() const { + return get_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::sub_render +// Access: Public, Virtual +// Description: This virtual function is normally a no-op. It is +// called during the render traversal to allow a special +// transition (e.g. a ShaderTransition) to intercept the +// normal render traversal with some fancy rendering +// process. +//////////////////////////////////////////////////////////////////// +bool NodeTransition:: +sub_render(NodeRelation *, const AllAttributesWrapper &, + AllTransitionsWrapper &, GraphicsStateGuardianBase *) { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::has_sub_render +// Access: Public, Virtual +// Description: Should be redefined to return true if the function +// sub_render(), above, expects to be called during +// traversal. +//////////////////////////////////////////////////////////////////// +bool NodeTransition:: +has_sub_render() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void NodeTransition:: +output(ostream &out) const { + out << get_handle(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void NodeTransition:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << *this << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::state_changed +// Access: Protected +// Description: This should be called by any internal method (like +// set_priority() or set_value()) that changes the state +// of the transition and may thus affect the state cache +// on the scene graph. It notifies all arcs that this +// transition has been applied to of the change. +//////////////////////////////////////////////////////////////////// +void NodeTransition:: +state_changed() { + TypeHandle handle = get_handle(); + + Arcs::const_iterator ai; + for (ai = _arcs.begin(); ai != _arcs.end(); ++ai) { + (*ai)->changed_transition(handle); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void NodeTransition:: +write_datagram(BamWriter *, Datagram &me) +{ + me.add_uint16(_priority); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransition::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void NodeTransition:: +fillin(DatagramIterator& scan, BamReader*) +{ + _priority = scan.get_uint16(); +} + + diff --git a/panda/src/graph/nodeTransition.h b/panda/src/graph/nodeTransition.h new file mode 100644 index 0000000000..fa8dfe69fc --- /dev/null +++ b/panda/src/graph/nodeTransition.h @@ -0,0 +1,132 @@ +// Filename: nodeTransition.h +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NODETRANSITION_H +#define NODETRANSITION_H + +#include + +#include + +class Node; +class NodeAttribute; +class NodeAttributes; +class NodeTransitions; +class NodeRelation; +class GraphicsStateGuardianBase; +class AllAttributesWrapper; +class AllTransitionsWrapper; +class BamWriter; +class BamReader; +class Datagram; +class DatagramIterator; + +//////////////////////////////////////////////////////////////////// +// Class : NodeTransition +// Description : This is an abstract class defining the basic +// interface to a Transition--the type of state change +// request that is stored on the arcs of the scene +// graph. +// +// See the comments at the beginning of NodeAttribute +// for a fuller description of the purpose of this +// class. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NodeTransition : public TypedWriteableReferenceCount { +protected: + INLINE NodeTransition(); + INLINE NodeTransition(const NodeTransition ©); + INLINE void operator = (const NodeTransition ©); + +public: + INLINE bool operator == (const NodeTransition &other) const; + INLINE bool operator != (const NodeTransition &other) const; + INLINE bool operator < (const NodeTransition &other) const; + INLINE bool operator <= (const NodeTransition &other) const; + INLINE bool operator > (const NodeTransition &other) const; + INLINE bool operator >= (const NodeTransition &other) const; + + INLINE int compare_to(const NodeTransition &other) const; + + INLINE void set_priority(int priority); + INLINE int get_priority() const; + + virtual NodeTransition *make_copy() const=0; + virtual NodeAttribute *make_attrib() const=0; + + virtual TypeHandle get_handle() const; + + virtual NodeTransition *compose(const NodeTransition *other) const=0; + virtual NodeTransition *invert() const=0; + virtual NodeAttribute *apply(const NodeAttribute *attrib) const=0; + + virtual bool sub_render(NodeRelation *arc, + const AllAttributesWrapper &attrib, + AllTransitionsWrapper &trans, + GraphicsStateGuardianBase *gsgbase); + virtual bool has_sub_render() const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeTransition *other) const=0; + + // And this is the internal function we'll call whenever our value + // changes. + void state_changed(); + +public: + // We need to keep a list of all of the arcs we're assigned to, so + // that if our value changes we can keep the cache up-to-date. + // These functions will be called by the arcs when appropriate. + // Don't attempt to call them directly; they're public only to make + // it convenient to call them from non-class template functions. + INLINE void added_to_arc(NodeRelation *arc); + INLINE void removed_from_arc(NodeRelation *arc); + +protected: + int _priority; + + typedef set Arcs; + Arcs _arcs; + +public: + virtual void write_datagram(BamWriter* manager, Datagram &me); + + //NodeTransition has no factory methods as it is not directly made + +protected: + virtual void fillin(DatagramIterator& scan, BamReader* manager); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedWriteableReferenceCount::init_type(); + register_type(_type_handle, "NodeTransition", + TypedWriteableReferenceCount::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class NodeRelation; + friend class NodeTransitions; + friend class NodeTransitionCache; +}; + +INLINE ostream &operator << (ostream &out, const NodeTransition &ntb) { + ntb.output(out); + return out; +} + +#include "nodeTransition.I" + +#endif diff --git a/panda/src/graph/nodeTransitionCache.I b/panda/src/graph/nodeTransitionCache.I new file mode 100644 index 0000000000..2aff26660a --- /dev/null +++ b/panda/src/graph/nodeTransitionCache.I @@ -0,0 +1,52 @@ +// Filename: nodeTransitionCache.I +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::set_transition +// Access: Public +// Description: This flavor of set_transition() accepts a pointer to +// a NodeTransition only. It infers the type of the +// NodeTransition from the pointer. However, it is not +// valid to pass a NULL pointer to this flavor of +// set_transition; if the pointer might be NULL, use the +// above flavor instead (or just call clear_transition). +//////////////////////////////////////////////////////////////////// +INLINE PT(NodeTransition) NodeTransitionCache:: +set_transition(NodeTransition *trans) { + nassertr(trans != (NodeTransition *)NULL, NULL); + nassertr(trans->get_handle() != TypeHandle::none(), NULL); + return set_transition(trans->get_handle(), trans); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeTransitionCache::size_type NodeTransitionCache:: +size() const { + return _cache.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::begin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeTransitionCache::const_iterator NodeTransitionCache:: +begin() const { + return _cache.begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::end +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeTransitionCache::const_iterator NodeTransitionCache:: +end() const { + return _cache.end(); +} diff --git a/panda/src/graph/nodeTransitionCache.cxx b/panda/src/graph/nodeTransitionCache.cxx new file mode 100644 index 0000000000..b4c9800c4f --- /dev/null +++ b/panda/src/graph/nodeTransitionCache.cxx @@ -0,0 +1,495 @@ +// Filename: nodeTransitionCache.cxx +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nodeTransitionCache.h" +#include "nodeTransitions.h" +#include "config_graph.h" +#include "setTransitionHelpers.h" + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +NodeTransitionCache:: +NodeTransitionCache() { +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::Constructor from NodeTransitions +// Access: Public +// Description: Adds an entry into the cache for each non-identity +// Transition in the NodeTransitions set. +//////////////////////////////////////////////////////////////////// +NodeTransitionCache:: +NodeTransitionCache(const NodeTransitions &nt) { + NodeTransitions::Transitions::const_iterator ti; + for (ti = nt._transitions.begin(); + ti != nt._transitions.end(); + ++ti) { + TypeHandle handle = (*ti).first; + NodeTransition *trans = (*ti).second; + + if (trans != (NodeTransition *)NULL) { + _cache[handle] = NodeTransitionCacheEntry(trans); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +NodeTransitionCache:: +~NodeTransitionCache() { +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::is_identity +// Access: Public +// Description: Returns true if all of the transitions in the cache +// are the identity transition. +//////////////////////////////////////////////////////////////////// +bool NodeTransitionCache:: +is_identity() const { + Cache::const_iterator ci; + for (ci = _cache.begin(); ci != _cache.end(); ++ci) { + if (!(*ci).second.is_identity()) { + return false; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::compare_to +// Access: Public +// Description: This only compares the set of transitions in the +// cache; it does not compare timestamps. +//////////////////////////////////////////////////////////////////// +int NodeTransitionCache:: +compare_to(const NodeTransitionCache &other) const { + return tmap_compare_cache(_cache.begin(), _cache.end(), + other._cache.begin(), other._cache.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::is_empty +// Access: Public +// Description: Returns true if there are no Transitions stored in +// the cache, or false if there are any (even identity) +// Transitions. +//////////////////////////////////////////////////////////////////// +bool NodeTransitionCache:: +is_empty() const { + return _cache.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::set_transition +// Access: Public +// Description: This flavor of set_transition() accepts a specific +// TypeHandle, indicating the type of transition that we +// are setting, and a NodeTransition pointer indicating +// the value of the transition. The NodeTransition may +// be NULL indicating that the transition should be +// cleared. If the NodeTransition is not NULL, it must +// match the type indicated by the TypeHandle. +// +// The return value is a pointer to the *previous* +// transition in the cache, if any, or NULL if there was +// none. +//////////////////////////////////////////////////////////////////// +PT(NodeTransition) NodeTransitionCache:: +set_transition(TypeHandle handle, NodeTransition *trans) { + if (trans == (NodeTransition *)NULL) { + return clear_transition(handle); + + } else { + Cache::iterator ci; + ci = _cache.find(handle); + if (ci != _cache.end()) { + PT(NodeTransition) result = (*ci).second.get_trans(); + (*ci).second = NodeTransitionCacheEntry(trans); + return result; + } + + _cache.insert(Cache::value_type(handle, NodeTransitionCacheEntry(trans))); + return NULL; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::clear_transition +// Access: Public +// Description: Removes any transition associated with the indicated +// handle from the set. +// +// The return value is a pointer to the previous +// transition in the set, if any, or NULL if there was +// none. +//////////////////////////////////////////////////////////////////// +PT(NodeTransition) NodeTransitionCache:: +clear_transition(TypeHandle handle) { + nassertr(handle != TypeHandle::none(), NULL); + + Cache::iterator ci; + ci = _cache.find(handle); + if (ci != _cache.end()) { + PT(NodeTransition) result = (*ci).second.get_trans(); + _cache.erase(ci); + return result; + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::has_transition +// Access: Public +// Description: Returns true if a transition associated with the +// indicated handle has been stored in the cache, or +// false otherwise. +//////////////////////////////////////////////////////////////////// +bool NodeTransitionCache:: +has_transition(TypeHandle handle) const { + nassertr(handle != TypeHandle::none(), false); + Cache::const_iterator ci; + ci = _cache.find(handle); + if (ci != _cache.end()) { + return (*ci).second.has_trans(); + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::get_transition +// Access: Public +// Description: Returns the transition associated with the indicated +// handle, or NULL if no such transition has been stored +// in the cache. +//////////////////////////////////////////////////////////////////// +NodeTransition *NodeTransitionCache:: +get_transition(TypeHandle handle) const { + nassertr(handle != TypeHandle::none(), NULL); + Cache::const_iterator ci; + ci = _cache.find(handle); + if (ci != _cache.end()) { + return (*ci).second.get_trans(); + } + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::clear +// Access: Public +// Description: Blows away all entries in the cache. +//////////////////////////////////////////////////////////////////// +void NodeTransitionCache:: +clear() { + _cache.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::lookup_entry +// Access: Public +// Description: Looks up the indicated entry in the cache. If the +// entry is defined in the cache, copies it to the +// 'entry' parameter and returns true; otherwise, +// reinitializes the 'entry' parameter and returns +// false. +//////////////////////////////////////////////////////////////////// +bool NodeTransitionCache:: +lookup_entry(TypeHandle handle, NodeTransitionCacheEntry &entry) const { + Cache::const_iterator ci; + ci = _cache.find(handle); + if (ci != _cache.end()) { + entry = (*ci).second; + return true; + } + + entry.clear(); + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::store_entry +// Access: Public +// Description: Adds or updates an entry in the cache for a +// particular Transition type. +//////////////////////////////////////////////////////////////////// +void NodeTransitionCache:: +store_entry(TypeHandle handle, const NodeTransitionCacheEntry &entry) { + _cache[handle] = entry; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::store_to +// Access: Public, Static +// Description: Adds a transition to the NodeTransitions set for each +// entry in the cache, and tells the transition that it +// has been added to the indicated arc. +//////////////////////////////////////////////////////////////////// +void NodeTransitionCache:: +store_to(const NodeTransitionCache *a, NodeRelation *arc, + NodeTransitions &nt) { + if (a == (NodeTransitionCache *)NULL || a->_cache.empty()) { + // a is empty: no change. + return; + } + + // First, tell all of the entries in the cache that they're about + // the be added to the arc. + Cache::const_iterator ci; + for (ci = a->_cache.begin(); ci != a->_cache.end(); ++ci) { + NodeTransition *trans = (*ci).second.get_trans(); + trans->added_to_arc(arc); + } + + // And now actually add them. + NodeTransitions temp; + tmap_union(nt._transitions.begin(), nt._transitions.end(), + a->_cache.begin(), a->_cache.end(), + inserter(temp._transitions, temp._transitions.begin())); + nt._transitions.swap(temp._transitions); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::union +// Access: Public, Static +// Description: Returns a pointer to a new NodeTransitionCache (or +// perhaps a pointer to one of the two source caches) +// that represents the union of the two caches. Either +// or both pointers may be NULL, indicating identity, +// and the return value might also be NULL. +//////////////////////////////////////////////////////////////////// +NodeTransitionCache *NodeTransitionCache:: +c_union(const NodeTransitionCache *a, const NodeTransitionCache *b) { + if (a == (NodeTransitionCache *)NULL || a->_cache.empty()) { + // a is empty, therefore identity: the result is the same as b. + return (NodeTransitionCache *)b; + + } else if (b == (NodeTransitionCache *)NULL || b->_cache.empty()) { + // b is empty, therefore identity: the result is the same as a. + return (NodeTransitionCache *)a; + + } else { + // Neither is empty. Build and return a new list. + NodeTransitionCache *result = new NodeTransitionCache; + + tmap_union(a->_cache.begin(), a->_cache.end(), + b->_cache.begin(), b->_cache.end(), + inserter(result->_cache, result->_cache.begin())); + + return result; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::compose +// Access: Public, Static +// Description: Returns a pointer to a new NodeTransitionCache (or +// perhaps a pointer to one of the two source caches) +// that represents the memberwise composition of the two +// caches. Either or both pointers may be NULL, +// indicating identity, and the return value might also +// be NULL. +//////////////////////////////////////////////////////////////////// +NodeTransitionCache *NodeTransitionCache:: +compose(const NodeTransitionCache *a, const NodeTransitionCache *b) { + // Since the absence of a transition from the object implies the + // identity transition, we can make a quick special case for either + // object being empty: + + if (a == (NodeTransitionCache *)NULL || a->_cache.empty()) { + // a is empty, therefore identity: the result is the same as b. + return (NodeTransitionCache *)b; + + } else if (b == (NodeTransitionCache *)NULL || b->_cache.empty()) { + // b is empty, therefore identity: the result is the same as a. + return (NodeTransitionCache *)a; + + } else { + // Neither is empty. Build and return a new list. + NodeTransitionCache *result = new NodeTransitionCache; + + tmap_compose(a->_cache.begin(), a->_cache.end(), + b->_cache.begin(), b->_cache.end(), + inserter(result->_cache, result->_cache.begin())); + + return result; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::invert +// Access: Public, Static +// Description: Returns a pointer to a new NodeTransitionCache (or +// perhaps a pointer to the source caches) that +// represents the memberwise inversion of the caches. +// The source pointer may be NULL, indicating identity, +// and the return value might also be NULL. +//////////////////////////////////////////////////////////////////// +NodeTransitionCache *NodeTransitionCache:: +invert(const NodeTransitionCache *a) { + if (a == (NodeTransitionCache *)NULL || a->_cache.empty()) { + // a is empty, therefore identity: the result is identity. + return NULL; + } + + // Build and return a new list. + NodeTransitionCache *result = new NodeTransitionCache; + + tmap_invert(a->_cache.begin(), a->_cache.end(), + inserter(result->_cache, result->_cache.begin())); + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::invert_compose +// Access: Public, Static +// Description: Returns a pointer to a new NodeTransitionCache (or +// perhaps a pointer to one of the two source caches) +// that represents the memberwise invert-compose of the +// two caches. Either or both pointers may be NULL, +// indicating identity, and the return value might also +// be NULL. +//////////////////////////////////////////////////////////////////// +NodeTransitionCache *NodeTransitionCache:: +invert_compose(const NodeTransitionCache *a, const NodeTransitionCache *b) { + // Since the absence of a transition from the object implies the + // identity transition, we can make a quick special case for either + // object being empty: + + if (a == (NodeTransitionCache *)NULL || a->_cache.empty()) { + // a is empty, therefore identity: the result is the same as b. + return (NodeTransitionCache *)b; + + } else if (b == (NodeTransitionCache *)NULL || b->_cache.empty()) { + // b is empty, therefore identity: the result is the inverse of a. + return invert(a); + + } else { + // Neither is empty. Build and return a new list. + NodeTransitionCache *result = new NodeTransitionCache; + + tmap_invert_compose(a->_cache.begin(), a->_cache.end(), + b->_cache.begin(), b->_cache.end(), + inserter(result->_cache, result->_cache.begin())); + + return result; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::cached_compose +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +NodeTransitionCache *NodeTransitionCache:: +cached_compose(const NodeTransitionCache *a, + const NodeTransitionCache *cache, + const NodeTransitionCache *b, + UpdateSeq now) { + // Since the absence of a transition from the object implies the + // identity transition, we can make a quick special case for either + // object being empty: + + if (a == (NodeTransitionCache *)NULL || a->_cache.empty()) { + // a is empty, therefore identity: the result is the same as b. + return set_computed_verified(b, now); + + } else if (b == (NodeTransitionCache *)NULL || b->_cache.empty()) { + // b is empty, therefore identity: the result is the same as a. + return set_computed_verified(a, now); + + } else { + // Neither is empty. Build and return a new list. + NodeTransitionCache *result = new NodeTransitionCache; + + if (cache == (NodeTransitionCache *)NULL) { + // If the cache is NULL, we must send in an empty list for the + // cache list. We'll use [a.begin .. a.begin). + tmap_cached_compose(a->_cache.begin(), a->_cache.end(), + a->_cache.begin(), a->_cache.begin(), + b->_cache.begin(), b->_cache.end(), + now, + inserter(result->_cache, result->_cache.begin())); + + } else { + tmap_cached_compose(a->_cache.begin(), a->_cache.end(), + cache->_cache.begin(), cache->_cache.end(), + b->_cache.begin(), b->_cache.end(), + now, + inserter(result->_cache, result->_cache.begin())); + } + + return result; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::set_computed_verified +// Access: Public, Static +// Description: Returns a new pointer that represents the adjustment +// of the given cache to set each computed and verified +// value to the current now value. This may modify the +// source cache only if there is only one pointer to it. +//////////////////////////////////////////////////////////////////// +NodeTransitionCache *NodeTransitionCache:: +set_computed_verified(const NodeTransitionCache *a, UpdateSeq now) { + if (a == (NodeTransitionCache *)NULL || a->_cache.empty()) { + return NULL; + } + + NodeTransitionCache *result = (NodeTransitionCache *)a; + if (a->get_count() > 1) { + // Copy-on-write. + result = new NodeTransitionCache(*a); + } + + Cache::iterator ci; + for (ci = result->_cache.begin(); ci != result->_cache.end(); ++ci) { + (*ci).second.set_computed_verified(now); + } + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NodeTransitionCache:: +output(ostream &out) const { + bool written_any = false; + Cache::const_iterator ci; + for (ci = _cache.begin(); ci != _cache.end(); ++ci) { + if (!(*ci).second.is_identity()) { + if (written_any) { + out << " "; + } + out << (*ci).second; + written_any = true; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCache::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NodeTransitionCache:: +write(ostream &out, int indent_level) const { + Cache::const_iterator ci; + for (ci = _cache.begin(); ci != _cache.end(); ++ci) { + (*ci).second.write(out, indent_level); + } +} diff --git a/panda/src/graph/nodeTransitionCache.h b/panda/src/graph/nodeTransitionCache.h new file mode 100644 index 0000000000..8cb22b5336 --- /dev/null +++ b/panda/src/graph/nodeTransitionCache.h @@ -0,0 +1,110 @@ +// Filename: nodeTransitionCache.h +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NODETRANSITIONCACHE_H +#define NODETRANSITIONCACHE_H + +#include + +#include "nodeTransitionCacheEntry.h" + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : NodeTransitionCache +// Description : This represents the cached values of accumulated +// NodeTransitions values, associated with a particular +// arc. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NodeTransitionCache : public ReferenceCount { +public: + NodeTransitionCache(); + NodeTransitionCache(const NodeTransitions &nt); + ~NodeTransitionCache(); + + bool is_identity() const; + int compare_to(const NodeTransitionCache &other) const; + + bool is_empty() const; + PT(NodeTransition) set_transition(TypeHandle handle, NodeTransition *trans); + INLINE PT(NodeTransition) set_transition(NodeTransition *trans); + PT(NodeTransition) clear_transition(TypeHandle handle); + bool has_transition(TypeHandle handle) const; + NodeTransition *get_transition(TypeHandle handle) const; + + void clear(); + + bool + lookup_entry(TypeHandle handle, NodeTransitionCacheEntry &entry) const; + + void + store_entry(TypeHandle handle, const NodeTransitionCacheEntry &entry); + +private: + typedef map Cache; +public: + // STL-like definitions to allow read-only traversal of the + // individual transitions. Note that each of these is a + // NodeTransitionCacheEntry, not simply a PT(NodeTransition). + // Beware! These are not safe to use outside of PANDA.DLL. + typedef Cache::const_iterator iterator; + typedef Cache::const_iterator const_iterator; + typedef Cache::value_type value_type; + typedef Cache::size_type size_type; + + INLINE size_type size() const; + INLINE const_iterator begin() const; + INLINE const_iterator end() const; + + +public: + // The following functions are support functions for + // AllTransitionsWrapper; they have limited utility elsewhere. + + static void + store_to(const NodeTransitionCache *a, NodeRelation *arc, + NodeTransitions &nt); + + static NodeTransitionCache * + c_union(const NodeTransitionCache *a, const NodeTransitionCache *b); + + static NodeTransitionCache * + compose(const NodeTransitionCache *a, const NodeTransitionCache *b); + + static NodeTransitionCache * + invert(const NodeTransitionCache *a); + + static NodeTransitionCache * + invert_compose(const NodeTransitionCache *a, const NodeTransitionCache *b); + + static NodeTransitionCache * + cached_compose(const NodeTransitionCache *a, + const NodeTransitionCache *cache, + const NodeTransitionCache *b, + UpdateSeq now); + + static NodeTransitionCache * + set_computed_verified(const NodeTransitionCache *a, UpdateSeq now); + + +public: + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + +private: + Cache _cache; + friend class NodeAttributes; +}; + +INLINE ostream &operator << (ostream &out, const NodeTransitionCache &ntc) { + ntc.output(out); + return out; +} + +#include "nodeTransitionCache.I" + +#endif diff --git a/panda/src/graph/nodeTransitionCacheEntry.I b/panda/src/graph/nodeTransitionCacheEntry.I new file mode 100644 index 0000000000..d6aab9de46 --- /dev/null +++ b/panda/src/graph/nodeTransitionCacheEntry.I @@ -0,0 +1,343 @@ +// Filename: nodeTransitionCacheEntry.I +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_graph.h" + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeTransitionCacheEntry:: +NodeTransitionCacheEntry(NodeTransition *trans) : _trans(trans) { +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeTransitionCacheEntry:: +NodeTransitionCacheEntry(const NodeTransitionCacheEntry ©) : + _trans(copy._trans), + _computed(copy._computed), + _verified(copy._verified) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransitionCacheEntry:: +operator = (const NodeTransitionCacheEntry ©) { + _trans = copy._trans; + _computed = copy._computed; + _verified = copy._verified; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::is_identity +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodeTransitionCacheEntry:: +is_identity() const { + return (_trans == (NodeTransition *)NULL); + // || _trans->is_identity(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int NodeTransitionCacheEntry:: +compare_to(const NodeTransitionCacheEntry &other) const { + bool this_ident = is_identity(); + bool other_ident = other.is_identity(); + if (this_ident && other_ident) { + return 0; + } + if (this_ident) { + return -1; + } + if (other_ident) { + return 1; + } + + // This should be guaranteed by the above logic. + nassertr(_trans != (NodeTransition *)NULL && + other._trans != (NodeTransition *)NULL, false); + + return _trans->compare_to(*other._trans); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::clear +// Access: Public +// Description: Reinitializes the cache entry to its original state. +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransitionCacheEntry:: +clear() { + _trans.clear(); + _computed.clear(); + _verified.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::set_trans +// Access: Public +// Description: Changes the transition associated with the cache +// entry without changing the associated time stamps. +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransitionCacheEntry:: +set_trans(NodeTransition *trans) { + _trans = trans; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::clear_trans +// Access: Public +// Description: Removes the transitoin associated with the cache +// entry without changing the associated time stamps. +// This is of limited usefulness. +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransitionCacheEntry:: +clear_trans() { + _trans.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::has_trans +// Access: Public +// Description: Returns true if the cache entry has an associated +// NodeTransition, false if it doesn't (and the +// transition should be assumed to be identity). +//////////////////////////////////////////////////////////////////// +INLINE bool NodeTransitionCacheEntry:: +has_trans() const { + return _trans != (NodeTransition *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::get_trans +// Access: Public +// Description: Returns the NodeTransition associated with the cache +// entry, or NULL if there is no associated +// NodeTransition (in which case it should be assumed to +// be identity). +//////////////////////////////////////////////////////////////////// +INLINE NodeTransition *NodeTransitionCacheEntry:: +get_trans() const { + return _trans; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::is_cache_verified +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodeTransitionCacheEntry:: +is_cache_verified(UpdateSeq now) const { + return _verified == now; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::is_freshly_computed +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodeTransitionCacheEntry:: +is_freshly_computed(UpdateSeq changed) const { + return changed < _computed; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::set_computed_verified +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransitionCacheEntry:: +set_computed_verified(UpdateSeq now) { + _computed = now; + _verified = now; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::Typecast Operator +// Access: Public +// Description: This typecast operator allows the cache entry to be +// directly assigned to a NodeTransition pointer. It's +// primarily useful for using the code in +// setTransitionHelpers.h interchangeably between +// NodeTransitions and NodeTransitionCaches. +//////////////////////////////////////////////////////////////////// +INLINE NodeTransitionCacheEntry:: +operator const PT(NodeTransition) &() const { + return _trans; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::get_computed +// Access: Public +// Description: Returns the timestamp at which this cache entry was +// computed. +//////////////////////////////////////////////////////////////////// +INLINE UpdateSeq NodeTransitionCacheEntry:: +get_computed() const { + return _computed; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::get_verified +// Access: Public +// Description: Returns the last timestamp at which this cache entry +// was verified; i.e. when the graph was last walked up +// to the root to ensure the cache entry is still valid. +//////////////////////////////////////////////////////////////////// +INLINE UpdateSeq NodeTransitionCacheEntry:: +get_verified() const { + return _verified; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::invert +// Access: Public, Static +// Description: Returns a new cache entry that represents the inverse +// of a. +//////////////////////////////////////////////////////////////////// +INLINE NodeTransitionCacheEntry NodeTransitionCacheEntry:: +invert(const NodeTransitionCacheEntry &a) { + if (a.is_identity()) { + return a; + } + return NodeTransitionCacheEntry(a._trans->invert()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::compose +// Access: Public, Static +// Description: Returns a new cache entry that represents the +// composition of a and b. +//////////////////////////////////////////////////////////////////// +INLINE NodeTransitionCacheEntry NodeTransitionCacheEntry:: +compose(const NodeTransitionCacheEntry &a, + const NodeTransitionCacheEntry &b) { + if (a.is_identity()) { + return b; + } + if (b.is_identity()) { + return a; + } + return NodeTransitionCacheEntry(a._trans->compose(b._trans)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::invert_compose +// Access: Public, Static +// Description: Returns a new cache entry that represents the +// composition of invert(a) and b. +//////////////////////////////////////////////////////////////////// +INLINE NodeTransitionCacheEntry NodeTransitionCacheEntry:: +invert_compose(const NodeTransitionCacheEntry &a, + const NodeTransitionCacheEntry &b) { + if (a.is_identity()) { + return b; + } + if (b.is_identity()) { + return NodeTransitionCacheEntry(a._trans->invert()); + } + if (a._trans->compare_to(*b._trans) == 0) { + return NodeTransitionCacheEntry(); + } + + PT(NodeTransition) inv = a._trans->invert(); + if (inv == (NodeTransition *)NULL) { + return b; + } + + // We don't care about priority for this operation. + + // This is broken! We don't have the right the change the priority + // on this node. For now, leave this out, since we aren't using + // priority on any transitions for which we're computing wrt() at + // the moment. Fix me soon. + // inv->set_priority(b._trans->get_priority()); + + return NodeTransitionCacheEntry(inv->compose(b._trans)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::cached_compose +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeTransitionCacheEntry NodeTransitionCacheEntry:: +cached_compose(const NodeTransitionCacheEntry &a, + const NodeTransitionCacheEntry &cache, + const NodeTransitionCacheEntry &b, + UpdateSeq now) { +#ifndef NDEBUG + if (wrt_cat.is_debug()) { + wrt_cat.debug() + << "Composing:\n" + << " a) " << a << " = " << a._computed << "\n" + << " cache) " << cache << " = " << cache._computed << "\n" + << " b) " << b << " = " << b._computed << "\n"; + } +#endif + if (cache._computed == UpdateSeq::initial() || + cache._computed < a._computed) { + NodeTransitionCacheEntry result = compose(a, b); + result.set_computed_verified(now); +#ifndef NDEBUG + if (wrt_cat.is_debug()) { + wrt_cat.debug() << "computed result is " << result << "\n"; + } +#endif + return result; + + } else { + NodeTransitionCacheEntry result = cache; + result._verified = now; +#ifndef NDEBUG + if (wrt_cat.is_debug()) { + wrt_cat.debug() << "cached result is " << result << "\n"; + } +#endif + return result; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::apply +// Access: Public, Static +// Description: Returns a new NodeAttribute (or possibly the same +// NodeAttribute) that represents the indicated +// transition applied to the attribute. +//////////////////////////////////////////////////////////////////// +INLINE NodeAttribute *NodeTransitionCacheEntry:: +apply(const NodeAttribute *a, const NodeTransitionCacheEntry &b) { + if (b.is_identity()) { + return (NodeAttribute *)a; + } + + return b._trans->apply(a); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::make_attrib +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeAttribute *NodeTransitionCacheEntry:: +make_attrib() const { + if (_trans == (NodeTransition *)NULL) { + return NULL; + } + return _trans->make_attrib(); +} diff --git a/panda/src/graph/nodeTransitionCacheEntry.cxx b/panda/src/graph/nodeTransitionCacheEntry.cxx new file mode 100644 index 0000000000..75bbf0a4e2 --- /dev/null +++ b/panda/src/graph/nodeTransitionCacheEntry.cxx @@ -0,0 +1,33 @@ +// Filename: nodeTransitionCacheEntry.cxx +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nodeTransitionCacheEntry.h" + +#include + + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NodeTransitionCacheEntry:: +output(ostream &out) const { + if (_trans != (NodeTransition *)NULL) { + out << *_trans; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionCacheEntry::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NodeTransitionCacheEntry:: +write(ostream &out, int indent_level) const { + if (_trans != (NodeTransition *)NULL) { + _trans->write(out, indent_level); + } +} diff --git a/panda/src/graph/nodeTransitionCacheEntry.h b/panda/src/graph/nodeTransitionCacheEntry.h new file mode 100644 index 0000000000..140a32a2a7 --- /dev/null +++ b/panda/src/graph/nodeTransitionCacheEntry.h @@ -0,0 +1,92 @@ +// Filename: nodeTransitionCacheEntry.h +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NODETRANSITIONCACHEENTRY_H +#define NODETRANSITIONCACHEENTRY_H + +#include + +#include "nodeTransition.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : NodeTransitionCacheEntry +// Description : This is a single cached accumulated NodeTransition +// value, representing the net Transition for one +// particular transition type on an arc. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NodeTransitionCacheEntry { +public: + INLINE NodeTransitionCacheEntry(NodeTransition *trans = NULL); + INLINE NodeTransitionCacheEntry(const NodeTransitionCacheEntry ©); + INLINE void operator = (const NodeTransitionCacheEntry ©); + + INLINE bool is_identity() const; + INLINE int compare_to(const NodeTransitionCacheEntry &other) const; + + INLINE void clear(); + + INLINE void set_trans(NodeTransition *trans); + INLINE void clear_trans(); + INLINE bool has_trans() const; + INLINE NodeTransition *get_trans() const; + + INLINE bool is_cache_verified(UpdateSeq now) const; + INLINE bool is_freshly_computed(UpdateSeq changed) const; + INLINE void set_computed_verified(UpdateSeq now); + + INLINE operator const PT(NodeTransition) &() const; + + INLINE UpdateSeq get_computed() const; + INLINE UpdateSeq get_verified() const; + +public: + // The following functions perform the basic transition operations + // on the entry's _trans pointer. In general, they don't bother to + // keep the _computed and _verified members consistent across these + // operations. + + INLINE static NodeTransitionCacheEntry + invert(const NodeTransitionCacheEntry &a); + + INLINE static NodeTransitionCacheEntry + compose(const NodeTransitionCacheEntry &a, + const NodeTransitionCacheEntry &b); + + INLINE static NodeTransitionCacheEntry + invert_compose(const NodeTransitionCacheEntry &a, + const NodeTransitionCacheEntry &b); + + INLINE static NodeTransitionCacheEntry + cached_compose(const NodeTransitionCacheEntry &a, + const NodeTransitionCacheEntry &cache, + const NodeTransitionCacheEntry &b, + UpdateSeq now); + + INLINE static NodeAttribute * + apply(const NodeAttribute *a, const NodeTransitionCacheEntry &b); + + INLINE NodeAttribute *make_attrib() const; + +public: + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + +private: + PT(NodeTransition) _trans; + UpdateSeq _computed; + UpdateSeq _verified; +}; + +INLINE ostream &operator << (ostream &out, const NodeTransitionCacheEntry &e) { + e.output(out); + return out; +} + +#include "nodeTransitionCacheEntry.I" + +#endif diff --git a/panda/src/graph/nodeTransitionWrapper.I b/panda/src/graph/nodeTransitionWrapper.I new file mode 100644 index 0000000000..82d82633ca --- /dev/null +++ b/panda/src/graph/nodeTransitionWrapper.I @@ -0,0 +1,266 @@ +// Filename: nodeTransitionWrapper.I +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nodeRelation.h" +#include "nodeTransitionCacheEntry.h" + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeTransitionWrapper:: +NodeTransitionWrapper(TypeHandle handle) : _handle(handle) { + nassertv(_handle != TypeHandle::none()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeTransitionWrapper:: +NodeTransitionWrapper(const NodeTransitionWrapper ©) : + _handle(copy._handle), + _entry(copy._entry) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransitionWrapper:: +operator = (const NodeTransitionWrapper ©) { + _handle = copy._handle; + _entry = copy._entry; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::init_from +// Access: Public, Static +// Description: This is a named constructor that creates an empty +// NodeTransitionWrapper ready to access the same type +// of NodeTransition as the other. +//////////////////////////////////////////////////////////////////// +INLINE NodeTransitionWrapper NodeTransitionWrapper:: +init_from(const NodeTransitionWrapper &other) { + return NodeTransitionWrapper(other._handle); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::get_handle +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypeHandle NodeTransitionWrapper:: +get_handle() const { + return _handle; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::has_trans +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodeTransitionWrapper:: +has_trans() const { + return _entry.has_trans(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::get_trans +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeTransition *NodeTransitionWrapper:: +get_trans() const { + return _entry.get_trans(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::is_identity +// Access: Public +// Description: Returns true if the wrapper represents an identity +// transition. +//////////////////////////////////////////////////////////////////// +INLINE bool NodeTransitionWrapper:: +is_identity() const { + return _entry.is_identity(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int NodeTransitionWrapper:: +compare_to(const NodeTransitionWrapper &other) const { + return _entry.compare_to(other._entry); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::make_identity +// Access: Public +// Description: Resets the wrapper to the identity transition. +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransitionWrapper:: +make_identity() { + _entry.clear_trans(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::extract_from +// Access: Public +// Description: Sets the wrapper to the transition contained on the +// arc. +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransitionWrapper:: +extract_from(const NodeRelation *arc) { + nassertv(arc != (NodeRelation *)NULL); + nassertv(_handle != TypeHandle::none()); + _entry.set_trans(arc->get_transition(_handle)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::store_to +// Access: Public +// Description: Stores the transition in the wrapper to the arc. +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransitionWrapper:: +store_to(NodeRelation *arc) const { + nassertv(arc != (NodeRelation *)NULL); + arc->set_transition(_handle, _entry.get_trans()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::compose_in_place +// Access: Public +// Description: Sets this transition to the composition of this +// transition and the following one. +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransitionWrapper:: +compose_in_place(const NodeTransitionWrapper &other) { + nassertv(_handle == other._handle); + _entry = NodeTransitionCacheEntry::compose(_entry, other._entry); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::invert_in_place +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransitionWrapper:: +invert_in_place() { + _entry = NodeTransitionCacheEntry::invert(_entry); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::invert_compose_in_place +// Access: Public +// Description: Sets this transition to the composition of this +// transition and the following one. +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransitionWrapper:: +invert_compose_in_place(const NodeTransitionWrapper &other) { + _entry = NodeTransitionCacheEntry::invert_compose(_entry, other._entry); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::extract_from_cache +// Access: Public +// Description: Sets this transition to the value indicated in the +// cache on the arc. Also returns the arc's top_subtree +// indication. +//////////////////////////////////////////////////////////////////// +INLINE Node *NodeTransitionWrapper:: +extract_from_cache(const NodeRelation *arc) { + nassertr(_handle != TypeHandle::none(), NULL); + + if (arc->_net_transitions == (NodeTransitionCache *)NULL) { + _entry.clear(); + } else { + arc->_net_transitions->lookup_entry(_handle, _entry); + } + return arc->_top_subtree; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::store_to_cache +// Access: Public +// Description: Stores this transition into the arc's cache, and +// updates the arc's top_subtree. +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransitionWrapper:: +store_to_cache(NodeRelation *arc, Node *top_subtree) { + arc->_top_subtree = top_subtree; + if (arc->_net_transitions == (NodeTransitionCache *)NULL) { + arc->_net_transitions = new NodeTransitionCache; + + } else if (arc->_net_transitions->get_count() > 1) { + // Copy-on-write. + arc->_net_transitions = new NodeTransitionCache(*arc->_net_transitions); + } + + arc->_net_transitions->store_entry(_handle, _entry); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::is_cache_verified +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodeTransitionWrapper:: +is_cache_verified(UpdateSeq now) const { + return _entry.is_cache_verified(now); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::set_computed_verified +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransitionWrapper:: +set_computed_verified(UpdateSeq now) { + _entry.set_computed_verified(now); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::cached_compose +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodeTransitionWrapper:: +cached_compose(const NodeTransitionWrapper &cache, + const NodeTransitionWrapper &value, + UpdateSeq now) { + _entry = + NodeTransitionCacheEntry::cached_compose(_entry, cache._entry, + value._entry, now); +} + +//////////////////////////////////////////////////////////////////// +// Function: get_transition_into +// Description: This external template function is handy for +// extracting the transition (of a known type) from the +// wrapper. If the transition exists, it is +// automatically downcasted to the correct type and +// stored in the pointer given in the first parameter, +// and the return value is true. If the transition does +// not exist, the pointer is filled with NULL and the +// return value is false. +//////////////////////////////////////////////////////////////////// +template +INLINE bool +get_transition_into(Transition *&ptr, const NodeTransitionWrapper &trans) { + NodeTransition *nt = trans.get_trans(); + if (nt == (NodeTransition *)NULL) { + ptr = (Transition *)NULL; + return false; + } + DCAST_INTO_R(ptr, nt, false); + return true; +} diff --git a/panda/src/graph/nodeTransitionWrapper.cxx b/panda/src/graph/nodeTransitionWrapper.cxx new file mode 100644 index 0000000000..1e11d667db --- /dev/null +++ b/panda/src/graph/nodeTransitionWrapper.cxx @@ -0,0 +1,50 @@ +// Filename: nodeTransitionWrapper.cxx +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nodeTransitionWrapper.h" +#include "nodeAttributeWrapper.h" +#include "nodeRelation.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::init_from +// Access: Public, Static +// Description: This is a named constructor that creates an empty +// NodeTransitionWrapper ready to access the same type +// of NodeTransition as the other. +//////////////////////////////////////////////////////////////////// +NodeTransitionWrapper NodeTransitionWrapper:: +init_from(const NodeAttributeWrapper &attrib) { + return NodeTransitionWrapper(attrib.get_handle()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void NodeTransitionWrapper:: +output(ostream &out) const { + if (!_entry.has_trans()) { + out << "identity-" << _handle; + } else { + out << *_entry.get_trans(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitionWrapper::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void NodeTransitionWrapper:: +write(ostream &out, int indent_level) const { + if (!_entry.has_trans()) { + indent(out, indent_level) << "identity-" << _handle << "\n"; + } else { + _entry.get_trans()->write(out, indent_level); + } +} diff --git a/panda/src/graph/nodeTransitionWrapper.h b/panda/src/graph/nodeTransitionWrapper.h new file mode 100644 index 0000000000..527000cb79 --- /dev/null +++ b/panda/src/graph/nodeTransitionWrapper.h @@ -0,0 +1,96 @@ +// Filename: nodeTransitionWrapper.h +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NODETRANSITIONWRAPPER_H +#define NODETRANSITIONWRAPPER_H + +// +// There are several flavors of TransitionWrappers (and their +// corresponding AttributeWrappers). These are classes that represent +// one or a number of transitions (or attributes) simultaneously and +// are passed to template functions like df_traverse() and wrt() so +// that the same functions can be used to operate on either one +// transition type or a number of them. +// +// Since these classes are used as template parameters, they do not +// need to use inheritance or virtual functions; but they all must +// have a similar interface. +// + +#include + +#include "nodeTransition.h" +#include "nodeTransitionCacheEntry.h" + +class Node; +class NodeRelation; +class NodeAttribute; +class NodeAttributeWrapper; + +//////////////////////////////////////////////////////////////////// +// Class : NodeTransitionWrapper +// Description : This is a wrapper around a single transition type, +// selected at runtime by its handle. It is useful when +// computing a wrt() based on one transition type only +// (for instance, a MatrixTransition). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NodeTransitionWrapper { +public: + typedef NodeTransitionWrapper TransitionWrapper; + typedef NodeAttributeWrapper AttributeWrapper; + + INLINE NodeTransitionWrapper(TypeHandle handle); + INLINE NodeTransitionWrapper(const NodeTransitionWrapper ©); + INLINE void operator = (const NodeTransitionWrapper ©); + + INLINE static NodeTransitionWrapper + init_from(const NodeTransitionWrapper &other); + static NodeTransitionWrapper init_from(const NodeAttributeWrapper &attrib); + + INLINE TypeHandle get_handle() const; + INLINE bool has_trans() const; + INLINE NodeTransition *get_trans() const; + + INLINE bool is_identity() const; + INLINE int compare_to(const NodeTransitionWrapper &other) const; + + INLINE void make_identity(); + INLINE void extract_from(const NodeRelation *arc); + INLINE void store_to(NodeRelation *arc) const; + + INLINE void compose_in_place(const NodeTransitionWrapper &other); + INLINE void invert_in_place(); + INLINE void invert_compose_in_place(const NodeTransitionWrapper &other); + + INLINE Node *extract_from_cache(const NodeRelation *arc); + INLINE void store_to_cache(NodeRelation *arc, Node *top_subtree); + INLINE bool is_cache_verified(UpdateSeq now) const; + INLINE void set_computed_verified(UpdateSeq now); + + INLINE void cached_compose(const NodeTransitionWrapper &cache, + const NodeTransitionWrapper &value, + UpdateSeq now); + + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + +private: + TypeHandle _handle; + NodeTransitionCacheEntry _entry; +friend class NodeAttributeWrapper; +}; + +INLINE ostream &operator << (ostream &out, const NodeTransitionWrapper &ntw) { + ntw.output(out); + return out; +} + +template +INLINE bool +get_transition_into(Transition *&ptr, const NodeTransitionWrapper &trans); + +#include "nodeTransitionWrapper.I" + +#endif diff --git a/panda/src/graph/nodeTransitions.I b/panda/src/graph/nodeTransitions.I new file mode 100644 index 0000000000..ba0721cabd --- /dev/null +++ b/panda/src/graph/nodeTransitions.I @@ -0,0 +1,81 @@ +// Filename: nodeTransitions.I +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::set_transition +// Access: Public +// Description: This flavor of set_transition() accepts a pointer to +// a NodeTransition only. It infers the type of the +// NodeTransition from the pointer. However, it is not +// valid to pass a NULL pointer to this flavor of +// set_transition; if the pointer might be NULL, use the +// above flavor instead (or just call clear_transition). +//////////////////////////////////////////////////////////////////// +INLINE PT(NodeTransition) NodeTransitions:: +set_transition(NodeTransition *trans) { + nassertr(trans != (NodeTransition *)NULL, NULL); + nassertr(trans->get_handle() != TypeHandle::none(), NULL); + return set_transition(trans->get_handle(), trans); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeTransitions::size_type NodeTransitions:: +size() const { + return _transitions.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::begin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeTransitions::const_iterator NodeTransitions:: +begin() const { + return _transitions.begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::end +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeTransitions::const_iterator NodeTransitions:: +end() const { + return _transitions.end(); +} + +//////////////////////////////////////////////////////////////////// +// Function: get_transition_into +// Description: This external template function is handy for +// extracting a transition of a particular type from the +// set. If the transition exists, it is automatically +// downcasted to the correct type and stored in the +// pointer given in the first parameter, and the return +// value is true. If the transition does not exist, the +// pointer is filled with NULL and the return value is +// false. +//////////////////////////////////////////////////////////////////// +template +INLINE bool +get_transition_into(Transition *&ptr, const NodeTransitions &trans, + TypeHandle transition_type) { + NodeTransition *nt = trans.get_transition(transition_type); + if (nt == (NodeTransition *)NULL) { + ptr = (Transition *)NULL; + return false; + } + DCAST_INTO_R(ptr, nt, false); + return true; +} + +template +INLINE bool +get_transition_into(Transition *&ptr, const NodeTransitions &trans) { + return get_transition_into(ptr, trans, Transition::get_class_type()); +} diff --git a/panda/src/graph/nodeTransitions.cxx b/panda/src/graph/nodeTransitions.cxx new file mode 100644 index 0000000000..27f6a22c53 --- /dev/null +++ b/panda/src/graph/nodeTransitions.cxx @@ -0,0 +1,278 @@ +// Filename: nodeTransitions.cxx +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nodeTransitions.h" +#include "config_graph.h" +#include "setTransitionHelpers.h" +#include "nodeRelation.h" + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +NodeTransitions:: +NodeTransitions() { +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +NodeTransitions:: +NodeTransitions(const NodeTransitions ©) : + _transitions(copy._transitions) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NodeTransitions:: +operator = (const NodeTransitions ©) { + _transitions = copy._transitions; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +NodeTransitions:: +~NodeTransitions() { +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::is_empty +// Access: Public +// Description: Returns true if there are no Transitions stored in +// the set, or false if there are any (even identity) +// Transitions. +//////////////////////////////////////////////////////////////////// +bool NodeTransitions:: +is_empty() const { + return _transitions.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::set_transition +// Access: Public +// Description: This flavor of set_transition() accepts a specific +// TypeHandle, indicating the type of transition that we +// are setting, and a NodeTransition pointer indicating +// the value of the transition. The NodeTransition may +// be NULL indicating that the transition should be +// cleared. If the NodeTransition is not NULL, it must +// match the type indicated by the TypeHandle. +// +// The return value is a pointer to the *previous* +// transition in the set, if any, or NULL if there was +// none. +//////////////////////////////////////////////////////////////////// +PT(NodeTransition) NodeTransitions:: +set_transition(TypeHandle handle, NodeTransition *trans) { + if (trans == (NodeTransition *)NULL) { + return clear_transition(handle); + + } else { + Transitions::iterator ti; + ti = _transitions.find(handle); + if (ti != _transitions.end()) { + PT(NodeTransition) result = (*ti).second; + (*ti).second = trans; + return result; + } + + _transitions.insert(Transitions::value_type(handle, trans)); + return NULL; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::clear_transition +// Access: Public +// Description: Removes any transition associated with the indicated +// handle from the set. +// +// The return value is a pointer to the previous +// transition in the set, if any, or NULL if there was +// none. +//////////////////////////////////////////////////////////////////// +PT(NodeTransition) NodeTransitions:: +clear_transition(TypeHandle handle) { + nassertr(handle != TypeHandle::none(), NULL); + + Transitions::iterator ti; + ti = _transitions.find(handle); + if (ti != _transitions.end()) { + PT(NodeTransition) result = (*ti).second; + _transitions.erase(ti); + return result; + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::has_transition +// Access: Public +// Description: Returns true if a transition associated with the +// indicated handle has been stored in the set (even if +// it is the identity transition), or false otherwise. +//////////////////////////////////////////////////////////////////// +bool NodeTransitions:: +has_transition(TypeHandle handle) const { + nassertr(handle != TypeHandle::none(), false); + return _transitions.count(handle) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::get_transition +// Access: Public +// Description: Returns the transition associated with the indicated +// handle, or NULL if no such transition has been stored +// in the set. +//////////////////////////////////////////////////////////////////// +NodeTransition *NodeTransitions:: +get_transition(TypeHandle handle) const { + nassertr(handle != TypeHandle::none(), NULL); + Transitions::const_iterator ti; + ti = _transitions.find(handle); + if (ti != _transitions.end()) { + return (*ti).second; + } + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::clear +// Access: Public +// Description: Removes all transitions from the set. +//////////////////////////////////////////////////////////////////// +void NodeTransitions:: +clear() { + _transitions.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::copy_transitions_from +// Access: Public +// Description: Copies all of the transitions stored in the other set +// to this set. Any existing transitions in this set, +// for which there was not a corresponding transition of +// the same type in the other set, are left undisturbed. +//////////////////////////////////////////////////////////////////// +void NodeTransitions:: +copy_transitions_from(const NodeTransitions &other) { + Transitions temp; + tmap_union(_transitions.begin(), _transitions.end(), + other._transitions.begin(), other._transitions.end(), + inserter(temp, temp.begin())); + _transitions.swap(temp); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::copy_transitions_from +// Access: Public +// Description: As above, but updates the transitions on the way to +// indicated they are being attached to (or removed +// from) the indicated arc. This is intended only to be +// called from NodeRelation::copy_transitions(); don't +// call it directly. +//////////////////////////////////////////////////////////////////// +void NodeTransitions:: +copy_transitions_from(const NodeTransitions &other, + NodeRelation *to_arc) { + Transitions temp; + tmap_arc_union(_transitions.begin(), _transitions.end(), + other._transitions.begin(), other._transitions.end(), + to_arc, inserter(temp, temp.begin())); + _transitions.swap(temp); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::compose_transitions_from +// Access: Public +// Description: Similar to copy_transitions_from(), except that if +// the same type of transition exists on both arcs, the +// composition of the two is stored. The result +// represents the same set of transitions that would +// result from composing the two individual sets of +// transitions. +//////////////////////////////////////////////////////////////////// +void NodeTransitions:: +compose_transitions_from(const NodeTransitions &other, + NodeRelation *to_arc) { + Transitions temp; + tmap_arc_compose(_transitions.begin(), _transitions.end(), + other._transitions.begin(), other._transitions.end(), + to_arc, inserter(temp, temp.begin())); + _transitions.swap(temp); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int NodeTransitions:: +compare_to(const NodeTransitions &other) const { + return tmap_compare_trans(_transitions.begin(), _transitions.end(), + other._transitions.begin(), other._transitions.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::remove_all_from_arc +// Access: Public +// Description: Marks all of the transitions in the set as removed +// from the indicated arc. This is intended to be +// called only by the arc's destructor. +//////////////////////////////////////////////////////////////////// +void NodeTransitions:: +remove_all_from_arc(NodeRelation *arc) { + Transitions::iterator ti; + for (ti = _transitions.begin(); ti != _transitions.end(); ++ti) { + nassertv((*ti).second != (NodeTransition *)NULL); + (*ti).second->removed_from_arc(arc); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NodeTransitions:: +output(ostream &out) const { + bool written_any = false; + Transitions::const_iterator ti; + for (ti = _transitions.begin(); ti != _transitions.end(); ++ti) { + if ((*ti).second != (NodeTransition *)NULL) { + if (written_any) { + out << " "; + } + out << *(*ti).second; + written_any = true; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodeTransitions::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NodeTransitions:: +write(ostream &out, int indent_level) const { + Transitions::const_iterator ti; + for (ti = _transitions.begin(); ti != _transitions.end(); ++ti) { + if ((*ti).second != (NodeTransition *)NULL) { + (*ti).second->write(out, indent_level); + } + } +} diff --git a/panda/src/graph/nodeTransitions.h b/panda/src/graph/nodeTransitions.h new file mode 100644 index 0000000000..fc950803de --- /dev/null +++ b/panda/src/graph/nodeTransitions.h @@ -0,0 +1,91 @@ +// Filename: nodeTransitions.h +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NODETRANSITIONS_H +#define NODETRANSITIONS_H + +#include + +#include "nodeTransition.h" + +#include + +#include + +class NodeRelation; + +//////////////////////////////////////////////////////////////////// +// Class : NodeTransitions +// Description : This represents a set of zero or more NodeTransition +// pointers, organized by the transitions' get_handle() +// value. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NodeTransitions { +public: + NodeTransitions(); + NodeTransitions(const NodeTransitions ©); + void operator = (const NodeTransitions ©); + ~NodeTransitions(); + + bool is_empty() const; + PT(NodeTransition) set_transition(TypeHandle handle, NodeTransition *trans); + INLINE PT(NodeTransition) set_transition(NodeTransition *trans); + PT(NodeTransition) clear_transition(TypeHandle handle); + bool has_transition(TypeHandle handle) const; + NodeTransition *get_transition(TypeHandle handle) const; + void copy_transitions_from(const NodeTransitions &other); + void copy_transitions_from(const NodeTransitions &other, + NodeRelation *to_arc); + void compose_transitions_from(const NodeTransitions &other, + NodeRelation *to_arc); + + void clear(); + + int compare_to(const NodeTransitions &other) const; + + void remove_all_from_arc(NodeRelation *arc); + +private: + typedef map Transitions; + +public: + // STL-like definitions to allow read-only traversal of the + // individual transitions. Beware! These are not safe to use + // outside of PANDA.DLL. + typedef Transitions::const_iterator iterator; + typedef Transitions::const_iterator const_iterator; + typedef Transitions::value_type value_type; + typedef Transitions::size_type size_type; + + INLINE size_type size() const; + INLINE const_iterator begin() const; + INLINE const_iterator end() const; + +public: + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + +private: + Transitions _transitions; + friend class NodeTransitionCache; +}; + +INLINE ostream &operator << (ostream &out, const NodeTransitions &nts) { + nts.output(out); + return out; +} + +template +INLINE bool +get_transition_into(Transition *&ptr, const NodeTransitions &trans, + TypeHandle transition_type); + +template +INLINE bool +get_transition_into(Transition *&ptr, const NodeTransitions &trans); + +#include "nodeTransitions.I" + +#endif diff --git a/panda/src/graph/nullAttributeWrapper.I b/panda/src/graph/nullAttributeWrapper.I new file mode 100644 index 0000000000..a2b448ecd7 --- /dev/null +++ b/panda/src/graph/nullAttributeWrapper.I @@ -0,0 +1,97 @@ +// Filename: nullAttributeWrapper.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: NullAttributeWrapper::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NullAttributeWrapper:: +NullAttributeWrapper() { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullAttributeWrapper::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NullAttributeWrapper:: +NullAttributeWrapper(const NullAttributeWrapper &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullAttributeWrapper::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NullAttributeWrapper:: +operator = (const NullAttributeWrapper &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullAttributeWrapper::init_from +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NullAttributeWrapper NullAttributeWrapper:: +init_from(const NullTransitionWrapper &) { + return NullAttributeWrapper(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NullAttributeWrapper::is_initial +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NullAttributeWrapper:: +is_initial() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NullAttributeWrapper::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int NullAttributeWrapper:: +compare_to(const NullAttributeWrapper &) const { + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NullAttributeWrapper::make_initial +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NullAttributeWrapper:: +make_initial() { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullAttributeWrapper::apply_in_place +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NullAttributeWrapper:: +apply_in_place(const NullTransitionWrapper &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullAttributeWrapper::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NullAttributeWrapper:: +output(ostream &) const { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullAttributeWrapper::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NullAttributeWrapper:: +write(ostream &, int) const { +} diff --git a/panda/src/graph/nullAttributeWrapper.cxx b/panda/src/graph/nullAttributeWrapper.cxx new file mode 100644 index 0000000000..8f6a810bdf --- /dev/null +++ b/panda/src/graph/nullAttributeWrapper.cxx @@ -0,0 +1,6 @@ +// Filename: nullAttributeWrapper.cxx +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nullAttributeWrapper.h" diff --git a/panda/src/graph/nullAttributeWrapper.h b/panda/src/graph/nullAttributeWrapper.h new file mode 100644 index 0000000000..19b2d8eb51 --- /dev/null +++ b/panda/src/graph/nullAttributeWrapper.h @@ -0,0 +1,50 @@ +// Filename: nullAttributeWrapper.h +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NULLATTRIBUTEWRAPPER_H +#define NULLATTRIBUTEWRAPPER_H + +#include + +#include "nodeAttribute.h" + +#include +#include + +class NullTransitionWrapper; + +//////////////////////////////////////////////////////////////////// +// Class : NullAttributeWrapper +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NullAttributeWrapper { +public: + typedef NullTransitionWrapper TransitionWrapper; + typedef NullAttributeWrapper AttributeWrapper; + + INLINE NullAttributeWrapper(); + INLINE NullAttributeWrapper(const NullAttributeWrapper ©); + INLINE void operator = (const NullAttributeWrapper ©); + INLINE static NullAttributeWrapper + init_from(const NullTransitionWrapper &trans); + + INLINE bool is_initial() const; + INLINE int compare_to(const NullAttributeWrapper &other) const; + + INLINE void make_initial(); + INLINE void apply_in_place(const NullTransitionWrapper &trans); + + INLINE void output(ostream &out) const; + INLINE void write(ostream &out, int indent_level = 0) const; +}; + +INLINE ostream &operator << (ostream &out, const NullAttributeWrapper &naw) { + naw.output(out); + return out; +} + +#include "nullAttributeWrapper.I" + +#endif diff --git a/panda/src/graph/nullLevelState.cxx b/panda/src/graph/nullLevelState.cxx new file mode 100644 index 0000000000..e4030667f6 --- /dev/null +++ b/panda/src/graph/nullLevelState.cxx @@ -0,0 +1,6 @@ +// Filename: nullLevelState.cxx +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nullLevelState.h" diff --git a/panda/src/graph/nullLevelState.h b/panda/src/graph/nullLevelState.h new file mode 100644 index 0000000000..8d71a3524c --- /dev/null +++ b/panda/src/graph/nullLevelState.h @@ -0,0 +1,25 @@ +// Filename: nullLevelState.h +// Created by: drose (17Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NULLLEVELSTATE_H +#define NULLLEVELSTATE_H + +#include + +/////////////////////////////////////////////////////////////////// +// Class : NullLevelState +// Description : This is an empty class designed to be passed to a +// traverser that doesn't care about tracking states +// between levels. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NullLevelState { +public: + INLINE NullLevelState() { } + INLINE NullLevelState(const NullLevelState &) { } + INLINE void operator = (const NullLevelState &) { } +}; + +#endif + diff --git a/panda/src/graph/nullTransitionWrapper.I b/panda/src/graph/nullTransitionWrapper.I new file mode 100644 index 0000000000..7aae1d1061 --- /dev/null +++ b/panda/src/graph/nullTransitionWrapper.I @@ -0,0 +1,196 @@ +// Filename: nullTransitionWrapper.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nodeRelation.h" +#include "nodeTransitionCacheEntry.h" + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NullTransitionWrapper:: +NullTransitionWrapper() { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NullTransitionWrapper:: +NullTransitionWrapper(const NullTransitionWrapper &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NullTransitionWrapper:: +operator = (const NullTransitionWrapper &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::init_from +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NullTransitionWrapper NullTransitionWrapper:: +init_from(const NullTransitionWrapper &) { + return NullTransitionWrapper(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::init_from +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NullTransitionWrapper NullTransitionWrapper:: +init_from(const NullAttributeWrapper &) { + return NullTransitionWrapper(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::is_identity +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NullTransitionWrapper:: +is_identity() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int NullTransitionWrapper:: +compare_to(const NullTransitionWrapper &) const { + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::make_identity +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NullTransitionWrapper:: +make_identity() { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::extract_from +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NullTransitionWrapper:: +extract_from(const NodeRelation *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::store_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NullTransitionWrapper:: +store_to(NodeRelation *) const { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::compose_in_place +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NullTransitionWrapper:: +compose_in_place(const NullTransitionWrapper &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::invert_in_place +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NullTransitionWrapper:: +invert_in_place() { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::invert_compose_in_place +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NullTransitionWrapper:: +invert_compose_in_place(const NullTransitionWrapper &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::extract_from_cache +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Node *NullTransitionWrapper:: +extract_from_cache(const NodeRelation *) { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::store_to_cache +// Access: Public +// Description: Stores this transition into the arc's cache, and +// updates the arc's top_subtree. +//////////////////////////////////////////////////////////////////// +INLINE void NullTransitionWrapper:: +store_to_cache(NodeRelation *, Node *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::is_cache_verified +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NullTransitionWrapper:: +is_cache_verified(UpdateSeq) const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::set_computed_verified +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NullTransitionWrapper:: +set_computed_verified(UpdateSeq) { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::cached_compose +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NullTransitionWrapper:: +cached_compose(const NullTransitionWrapper &, + const NullTransitionWrapper &, + UpdateSeq) { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NullTransitionWrapper:: +output(ostream &) const { +} + +//////////////////////////////////////////////////////////////////// +// Function: NullTransitionWrapper::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NullTransitionWrapper:: +write(ostream &, int) const { +} diff --git a/panda/src/graph/nullTransitionWrapper.cxx b/panda/src/graph/nullTransitionWrapper.cxx new file mode 100644 index 0000000000..4fd81881ec --- /dev/null +++ b/panda/src/graph/nullTransitionWrapper.cxx @@ -0,0 +1,7 @@ +// Filename: nullTransitionWrapper.cxx +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nullTransitionWrapper.h" + diff --git a/panda/src/graph/nullTransitionWrapper.h b/panda/src/graph/nullTransitionWrapper.h new file mode 100644 index 0000000000..83781925c1 --- /dev/null +++ b/panda/src/graph/nullTransitionWrapper.h @@ -0,0 +1,86 @@ +// Filename: nullTransitionWrapper.h +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NULLTRANSITIONWRAPPER_H +#define NULLTRANSITIONWRAPPER_H + +// +// There are several flavors of TransitionWrappers (and their +// corresponding AttributeWrappers). These are classes that represent +// one or a number of transitions (or attributes) simultaneously and +// are passed to template functions like df_traverse() and wrt() so +// that the same functions can be used to operate on either one +// transition type or a number of them. +// +// Since these classes are used as template parameters, they do not +// need to use inheritance or virtual functions; but they all must +// have a similar interface. +// + +#include + +#include "nodeTransition.h" +#include "nodeTransitionCacheEntry.h" + +class Node; +class NodeRelation; +class NodeAttribute; +class NullAttributeWrapper; + +//////////////////////////////////////////////////////////////////// +// Class : NullTransitionWrapper +// Description : This is a wrapper around *no* transitions at all. It +// does absolutely nothing. It's pointless to pass to +// wrt(), but it's useful for passing to df_traverse() +// to perform a traversal without bothering to keep +// track of state. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NullTransitionWrapper { +public: + typedef NullTransitionWrapper TransitionWrapper; + typedef NullAttributeWrapper AttributeWrapper; + + INLINE NullTransitionWrapper(); + INLINE NullTransitionWrapper(const NullTransitionWrapper ©); + INLINE void operator = (const NullTransitionWrapper ©); + + INLINE static NullTransitionWrapper + init_from(const NullTransitionWrapper &other); + INLINE static NullTransitionWrapper + init_from(const NullAttributeWrapper &attrib); + + INLINE bool is_identity() const; + INLINE int compare_to(const NullTransitionWrapper &other) const; + + INLINE void make_identity(); + INLINE void extract_from(const NodeRelation *arc); + INLINE void store_to(NodeRelation *arc) const; + + INLINE void compose_in_place(const NullTransitionWrapper &other); + INLINE void invert_in_place(); + INLINE void invert_compose_in_place(const NullTransitionWrapper &other); + + INLINE Node *extract_from_cache(const NodeRelation *arc); + INLINE void store_to_cache(NodeRelation *arc, Node *top_subtree); + INLINE bool is_cache_verified(UpdateSeq now) const; + INLINE void set_computed_verified(UpdateSeq now); + + INLINE void cached_compose(const NullTransitionWrapper &cache, + const NullTransitionWrapper &value, + UpdateSeq now); + + INLINE void output(ostream &out) const; + INLINE void write(ostream &out, int indent_level = 0) const; +}; + +INLINE ostream &operator << (ostream &out, const NullTransitionWrapper &ntw) { + ntw.output(out); + return out; +} + +#include "nullTransitionWrapper.I" + +#endif + diff --git a/panda/src/graph/onAttribute.I b/panda/src/graph/onAttribute.I new file mode 100644 index 0000000000..140cad5d66 --- /dev/null +++ b/panda/src/graph/onAttribute.I @@ -0,0 +1,34 @@ +// Filename: onAttribute.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: OnAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE OnAttribute:: +OnAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: OnAttribute::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE OnAttribute:: +OnAttribute(const OnAttribute ©) : + NodeAttribute(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: OnAttribute::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void OnAttribute:: +operator = (const OnAttribute ©) { + NodeAttribute::operator = (copy); +} diff --git a/panda/src/graph/onAttribute.cxx b/panda/src/graph/onAttribute.cxx new file mode 100644 index 0000000000..f0f972c1bb --- /dev/null +++ b/panda/src/graph/onAttribute.cxx @@ -0,0 +1,47 @@ +// Filename: onAttribute.cxx +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "onAttribute.h" +#include "onTransition.h" + +#include + +TypeHandle OnAttribute::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: OnAttribute::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void OnAttribute:: +output(ostream &out) const { + output_value(out); +} + +//////////////////////////////////////////////////////////////////// +// Function: OnAttribute::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void OnAttribute:: +write(ostream &out, int indent_level) const { + write_value(out, indent_level); +} + +//////////////////////////////////////////////////////////////////// +// Function: OnAttribute::internal_compare_to +// Access: Protected, Virtual +// Description: Returns a number < 0 if this attribute sorts before +// the other attribute, > 0 if it sorts after, 0 if +// they are equivalent (except for priority). +//////////////////////////////////////////////////////////////////// +int OnAttribute:: +internal_compare_to(const NodeAttribute *other) const { + const OnAttribute *ot; + DCAST_INTO_R(ot, other, false); + + return compare_values(ot); +} diff --git a/panda/src/graph/onAttribute.h b/panda/src/graph/onAttribute.h new file mode 100644 index 0000000000..aa1cd7ca84 --- /dev/null +++ b/panda/src/graph/onAttribute.h @@ -0,0 +1,58 @@ +// Filename: onAttribute.h +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ONATTRIBUTE_H +#define ONATTRIBUTE_H + +#include + +#include "nodeAttribute.h" + +class OnTransition; + +//////////////////////////////////////////////////////////////////// +// Class : OnAttribute +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA OnAttribute : public NodeAttribute { +protected: + INLINE OnAttribute(); + INLINE OnAttribute(const OnAttribute ©); + INLINE void operator = (const OnAttribute ©); + +public: + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeAttribute *other) const; + + virtual void set_value_from(const OnTransition *other)=0; + virtual int compare_values(const OnAttribute *other) const=0; + virtual void output_value(ostream &out) const=0; + virtual void write_value(ostream &out, int indent_level) const=0; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeAttribute::init_type(); + register_type(_type_handle, "OnAttribute", + NodeAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +friend class OnTransition; +}; + +#include "onAttribute.I" + +#endif diff --git a/panda/src/graph/onOffAttribute.I b/panda/src/graph/onOffAttribute.I new file mode 100644 index 0000000000..ff88de476c --- /dev/null +++ b/panda/src/graph/onOffAttribute.I @@ -0,0 +1,89 @@ +// Filename: onOffAttribute.I +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: OnOffAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE OnOffAttribute:: +OnOffAttribute(bool is_on) { + _is_on = is_on; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffAttribute::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE OnOffAttribute:: +OnOffAttribute(const OnOffAttribute ©) : + NodeAttribute(copy), + _is_on(copy._is_on) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffAttribute::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void OnOffAttribute:: +operator = (const OnOffAttribute ©) { + NodeAttribute::operator = (copy); + _is_on = copy._is_on; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffAttribute::set_on +// Access: Public +// Description: Changes the attribute to an 'on' attribute; +// this turns on the attribute for all nodes at this +// point and below. However, this particular function +// does not change the attribute value itself, and is +// thus only appropriate for derived attribute types +// that do not actually carry an attribute value. +// Derived types that *do* have an attribute value +// should override this function to accept a value +// parameter of the appropriate type. +//////////////////////////////////////////////////////////////////// +INLINE void OnOffAttribute:: +set_on() { + _is_on = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffAttribute::set_off +// Access: Public +// Description: Changes the attribute to an 'off' attribute; this +// turns off the attribute for all nodes at this point +// and below. +//////////////////////////////////////////////////////////////////// +INLINE void OnOffAttribute:: +set_off() { + _is_on = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffAttribute::is_on +// Access: Public +// Description: Returns true if the attribute is on, false if it is +// not. +//////////////////////////////////////////////////////////////////// +INLINE bool OnOffAttribute:: +is_on() const { + return _is_on; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffAttribute::is_off +// Access: Public +// Description: Returns true if the attribute is off, false if it is +// not. +//////////////////////////////////////////////////////////////////// +INLINE bool OnOffAttribute:: +is_off() const { + return !_is_on; +} diff --git a/panda/src/graph/onOffAttribute.cxx b/panda/src/graph/onOffAttribute.cxx new file mode 100644 index 0000000000..a2525dbb1c --- /dev/null +++ b/panda/src/graph/onOffAttribute.cxx @@ -0,0 +1,105 @@ +// Filename: onOffAttribute.cxx +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "onOffAttribute.h" +#include "onOffTransition.h" + +#include + +TypeHandle OnOffAttribute::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: OnOffAttribute::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void OnOffAttribute:: +output(ostream &out) const { + if (_is_on) { + out << "on-" << get_handle() << ":"; + output_value(out); + } else { + out << "off-" << get_handle(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffAttribute::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void OnOffAttribute:: +write(ostream &out, int indent_level) const { + if (_is_on) { + indent(out, indent_level) << "on-" << get_handle() << ":\n"; + write_value(out, indent_level + 2); + } else { + indent(out, indent_level) << "off-" << get_handle() << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffAttribute::internal_compare_to +// Access: Protected, Virtual +// Description: Returns a number < 0 if this attribute sorts before +// the other attribute, > 0 if it sorts after, 0 if +// they are equivalent (except for priority). +//////////////////////////////////////////////////////////////////// +int OnOffAttribute:: +internal_compare_to(const NodeAttribute *other) const { + const OnOffAttribute *ot; + DCAST_INTO_R(ot, other, false); + + if (_is_on != ot->_is_on) { + return (_is_on < ot->_is_on) ? -1 : 1; + } + if (_is_on) { + return compare_values(ot); + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffAttribute::set_value_from +// Access: Protected, Virtual +// Description: Assigns the value from the corresponding value of the +// other attribute, which is assumed to be of an +// equivalent type. +//////////////////////////////////////////////////////////////////// +void OnOffAttribute:: +set_value_from(const OnOffTransition *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffAttribute::is_value_equivalent +// Access: Protected, Virtual +// Description: Returns 0 if the two transitions share the same +// value, or < 0 or > 0 otherwise. It is given that +// they are of equivalent types and that they are both +// "on" transitions. +//////////////////////////////////////////////////////////////////// +int OnOffAttribute:: +compare_values(const OnOffAttribute *) const { + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffAttribute::output_value +// Access: Protected, Virtual +// Description: Outputs the value on one line. +//////////////////////////////////////////////////////////////////// +void OnOffAttribute:: +output_value(ostream &) const { +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffAttribute::write_value +// Access: Protected, Virtual +// Description: Outputs the value on multiple lines. +//////////////////////////////////////////////////////////////////// +void OnOffAttribute:: +write_value(ostream &, int) const { +} diff --git a/panda/src/graph/onOffAttribute.h b/panda/src/graph/onOffAttribute.h new file mode 100644 index 0000000000..156719954c --- /dev/null +++ b/panda/src/graph/onOffAttribute.h @@ -0,0 +1,67 @@ +// Filename: onOffAttribute.h +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ONOFFATTRIBUTE_H +#define ONOFFATTRIBUTE_H + +#include + +#include "nodeAttribute.h" + +class OnOffTransition; + +//////////////////////////////////////////////////////////////////// +// Class : OnOffAttribute +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA OnOffAttribute : public NodeAttribute { +protected: + INLINE OnOffAttribute(bool is_on = false); + INLINE OnOffAttribute(const OnOffAttribute ©); + INLINE void operator = (const OnOffAttribute ©); + +public: + INLINE void set_on(); + INLINE void set_off(); + + INLINE bool is_on() const; + INLINE bool is_off() const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeAttribute *other) const; + + virtual void set_value_from(const OnOffTransition *other); + virtual int compare_values(const OnOffAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + +private: + bool _is_on; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeAttribute::init_type(); + register_type(_type_handle, "OnOffAttribute", + NodeAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +friend class OnOffTransition; +}; + +#include "onOffAttribute.I" + +#endif diff --git a/panda/src/graph/onOffTransition.I b/panda/src/graph/onOffTransition.I new file mode 100644 index 0000000000..337d89f32c --- /dev/null +++ b/panda/src/graph/onOffTransition.I @@ -0,0 +1,117 @@ +// Filename: onOffTransition.I +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE OnOffTransition:: +OnOffTransition(TransitionDirection direction) { + _direction = direction; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE OnOffTransition:: +OnOffTransition(const OnOffTransition ©) : + NodeTransition(copy), + _direction(copy._direction) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void OnOffTransition:: +operator = (const OnOffTransition ©) { + NodeTransition::operator = (copy); + _direction = copy._direction; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::set_identity +// Access: Public +// Description: Changes the transition to an identity transition; +// this has no effect on the attribute for any nodes at +// this point and below. +//////////////////////////////////////////////////////////////////// +INLINE void OnOffTransition:: +set_identity() { + _direction = TD_identity; + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::set_on +// Access: Public +// Description: Changes the transition to an 'on' transition; +// this turns on the attribute for all nodes at this +// point and below. However, this particular function +// does not change the attribute value itself, and is +// thus only appropriate for derived transition types +// that do not actually carry an attribute value. +// Derived types that *do* have an attribute value +// should override this function to accept a value +// parameter of the appropriate type. +//////////////////////////////////////////////////////////////////// +INLINE void OnOffTransition:: +set_on() { + _direction = TD_on; + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::set_off +// Access: Public +// Description: Changes the transition to an 'off' transition; this +// turns off the attribute for all nodes at this point +// and below. +//////////////////////////////////////////////////////////////////// +INLINE void OnOffTransition:: +set_off() { + _direction = TD_off; + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::is_identity +// Access: Public +// Description: Returns true if this is the identity transition, +// false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool OnOffTransition:: +is_identity() const { + return _direction == TD_identity; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::is_on +// Access: Public +// Description: Returns true if this transition is 'on', false +// otherwise. If the derived transition type carries a +// value, it should define a get_value() function which +// allows you to query the on value. +//////////////////////////////////////////////////////////////////// +INLINE bool OnOffTransition:: +is_on() const { + return _direction == TD_on; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::is_off +// Access: Public +// Description: Returns true if this transition is the 'off' +// transition, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool OnOffTransition:: +is_off() const { + return _direction == TD_off; +} diff --git a/panda/src/graph/onOffTransition.cxx b/panda/src/graph/onOffTransition.cxx new file mode 100644 index 0000000000..0707f04acc --- /dev/null +++ b/panda/src/graph/onOffTransition.cxx @@ -0,0 +1,263 @@ +// Filename: onOffTransition.cxx +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "onOffTransition.h" +#include "onOffAttribute.h" + +#include +#include +#include +#include +#include + +TypeHandle OnOffTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::compose +// Access: Public, Virtual +// Description: Returns a new transition that corresponds to the +// composition of this transition with the second +// transition (which must be of an equivalent type). +// This may return the same pointer as either source +// transition. Applying the transition returned from +// this function to an attribute attribute will produce +// the same effect as applying each transition +// separately. +//////////////////////////////////////////////////////////////////// +NodeTransition *OnOffTransition:: +compose(const NodeTransition *other) const { + const OnOffTransition *ot; + DCAST_INTO_R(ot, other, NULL); + + if (ot->_priority < _priority || ot->_direction == TD_identity) { + // The other transition is identity or too low-priority; the + // result is unchanged. + return (OnOffTransition *)this; + } + + // In any other case, for an OnOffTransition, the result is the same + // as the second operand. + return (OnOffTransition *)ot; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::invert +// Access: Public, Virtual +// Description: Returns a new transition that corresponds to the +// inverse of this transition. If the transition was +// identity, this may return the same pointer. Returns +// NULL if the transition cannot be inverted. +//////////////////////////////////////////////////////////////////// +NodeTransition *OnOffTransition:: +invert() const { + switch (_direction) { + case TD_identity: + return (OnOffTransition *)this; + + case TD_on: + OnOffTransition *result; + DCAST_INTO_R(result, make_copy(), NULL); + result->_direction = TD_off; + return result; + + case TD_off: + return NULL; + } + + // Invalid direction flag. + nassertr(false, NULL); + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::apply +// Access: Public, Virtual +// Description: Returns a new attribute (or possibly the same +// attribute) that represents the effect of applying this +// indicated transition to the indicated attribute. The +// source attribute may be NULL, indicating the initial +// attribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *OnOffTransition:: +apply(const NodeAttribute *attrib) const { + if (_direction == TD_identity) { + // The transition is identity; it has no effect. + return (NodeAttribute *)attrib; + } + + OnOffAttribute *result; + if (attrib == (const NodeAttribute *)NULL) { + DCAST_INTO_R(result, make_attrib(), NULL); + } else { + DCAST_INTO_R(result, (NodeAttribute *)attrib, NULL); + } + + if (_priority < result->_priority || _direction == TD_identity) { + // The priority is too low to affect the attribute, or the + // transition is identity. + return result; + } + + if (result->get_count() > 1) { + // Copy on write. + DCAST_INTO_R(result, result->make_copy(), NULL); + } + + result->_priority = _priority; + + if (_direction == TD_on) { + result->_is_on = true; + result->set_value_from(this); + } else { + result->_is_on = false; + } + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void OnOffTransition:: +output(ostream &out) const { + switch (_direction) { + case TD_identity: + out << "identity-" << get_handle(); + return; + + case TD_on: + out << "on-" << get_handle() << ":"; + output_value(out); + return; + + case TD_off: + out << "off-" << get_handle(); + return; + } + + // Invalid direction flag. + nassertv(false); +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void OnOffTransition:: +write(ostream &out, int indent_level) const { + switch (_direction) { + case TD_identity: + indent(out, indent_level) << "identity-" << get_handle() << "\n"; + return; + + case TD_on: + indent(out, indent_level) << "on-" << get_handle() << ":\n"; + write_value(out, indent_level + 2); + return; + + case TD_off: + indent(out, indent_level) << "off-" << get_handle() << "\n"; + return; + } + + // Invalid direction flag. + nassertv(false); +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::internal_compare_to +// Access: Protected, Virtual +// Description: Returns a number < 0 if this transition sorts before +// the other transition, > 0 if it sorts after, 0 if +// they are equivalent (except for priority). +//////////////////////////////////////////////////////////////////// +int OnOffTransition:: +internal_compare_to(const NodeTransition *other) const { + const OnOffTransition *ot; + DCAST_INTO_R(ot, other, false); + + if (_direction != ot->_direction) { + return (_direction < ot->_direction) ? -1 : 1; + } + if (_direction == TD_on) { + return compare_values(ot); + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::set_value_from +// Access: Protected, Virtual +// Description: Assigns the value from the corresponding value of the +// other transition, which is assumed to be of an +// equivalent type. +//////////////////////////////////////////////////////////////////// +void OnOffTransition:: +set_value_from(const OnOffTransition *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::compare_values +// Access: Protected, Virtual +// Description: Returns 0 if the two transitions share the same +// value, or < 0 or > 0 otherwise. It is given that +// they are of equivalent types and that they are both +// "on" transitions. +//////////////////////////////////////////////////////////////////// +int OnOffTransition:: +compare_values(const OnOffTransition *) const { + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::output_value +// Access: Protected, Virtual +// Description: Outputs the value on one line. +//////////////////////////////////////////////////////////////////// +void OnOffTransition:: +output_value(ostream &) const { +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::write_value +// Access: Protected, Virtual +// Description: Outputs the value on multiple lines. +//////////////////////////////////////////////////////////////////// +void OnOffTransition:: +write_value(ostream &, int) const { +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void OnOffTransition:: +write_datagram(BamWriter *manager, Datagram &me) +{ + NodeTransition::write_datagram(manager, me); + me.add_uint8(_direction); +} + +//////////////////////////////////////////////////////////////////// +// Function: OnOffTransition::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void OnOffTransition:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + NodeTransition::fillin(scan, manager); + _direction = (enum TransitionDirection) scan.get_uint8(); +} + + + diff --git a/panda/src/graph/onOffTransition.h b/panda/src/graph/onOffTransition.h new file mode 100644 index 0000000000..9d0a3c1353 --- /dev/null +++ b/panda/src/graph/onOffTransition.h @@ -0,0 +1,100 @@ +// Filename: onOffTransition.h +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ONOFFTRANSITION_H +#define ONOFFTRANSITION_H + +#include + +#include "nodeTransition.h" +#include "transitionDirection.h" + +class NodeAttribute; +class NodeRelation; + +//////////////////////////////////////////////////////////////////// +// Class : OnOffTransition +// Description : This is an abstract class that encapsulates a family +// of transitions for states that are either on or off +// (and, when on, may or may not have some particular +// value). This is typically useful for things like +// Texture, which have a definite value when on, and +// also have a definite off condition. +// +// See also OnTransition, which is used for states that +// are always on in some sense, or whose value itself +// can encode the case of being off. +// +// The transition may be either 'on', turning on a +// particular state, 'off', turning *off* a state that +// was previously turned on, or 'identity', not +// affecting the state. If two 'on' transitions are +// composed together, the second one overrides the +// first. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA OnOffTransition : public NodeTransition { +protected: + INLINE OnOffTransition(TransitionDirection direction = TD_identity); + INLINE OnOffTransition(const OnOffTransition ©); + INLINE void operator = (const OnOffTransition ©); + +public: + INLINE void set_identity(); + INLINE void set_on(); + INLINE void set_off(); + + INLINE bool is_identity() const; + INLINE bool is_on() const; + INLINE bool is_off() const; + + virtual NodeTransition *compose(const NodeTransition *other) const; + virtual NodeTransition *invert() const; + virtual NodeAttribute *apply(const NodeAttribute *attrib) const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeTransition *other) const; + + virtual void set_value_from(const OnOffTransition *other); + virtual int compare_values(const OnOffTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + +private: + TransitionDirection _direction; + +public: + //No Read/Write factory methods are defined since this is + //only an abstract clas + virtual void write_datagram(BamWriter* manager, Datagram &me); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeTransition::init_type(); + register_type(_type_handle, "OnOffTransition", + NodeTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + +friend class OnOffAttribute; +}; + +#include "onOffTransition.I" + +#endif diff --git a/panda/src/graph/onTransition.I b/panda/src/graph/onTransition.I new file mode 100644 index 0000000000..d93ff18c4e --- /dev/null +++ b/panda/src/graph/onTransition.I @@ -0,0 +1,34 @@ +// Filename: onTransition.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: OnTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE OnTransition:: +OnTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: OnTransition::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE OnTransition:: +OnTransition(const OnTransition ©) : + NodeTransition(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: OnTransition::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void OnTransition:: +operator = (const OnTransition ©) { + NodeTransition::operator = (copy); +} diff --git a/panda/src/graph/onTransition.cxx b/panda/src/graph/onTransition.cxx new file mode 100644 index 0000000000..25a8430d3e --- /dev/null +++ b/panda/src/graph/onTransition.cxx @@ -0,0 +1,120 @@ +// Filename: onTransition.cxx +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "onTransition.h" +#include "onAttribute.h" + +#include + +TypeHandle OnTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: OnTransition::compose +// Access: Public, Virtual +// Description: Returns a new transition that corresponds to the +// composition of this transition with the second +// transition (which must be of an equivalent type). +// This may return the same pointer as either source +// transition. Applying the transition returned from +// this function to an attribute attribute will produce +// the same effect as applying each transition +// separately. +//////////////////////////////////////////////////////////////////// +NodeTransition *OnTransition:: +compose(const NodeTransition *other) const { + const OnTransition *ot; + DCAST_INTO_R(ot, other, NULL); + + if (ot->_priority < _priority) { + // The other transition is too low-priority; the result is + // unchanged. + return (OnTransition *)this; + } + + // In any other case, for an OnTransition, the result is the same + // as the second operand. + return (OnTransition *)ot; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnTransition::invert +// Access: Public, Virtual +// Description: Returns a new transition that corresponds to the +// inverse of this transition. If the transition was +// identity, this may return the same pointer. Returns +// NULL if the transition cannot be inverted. +//////////////////////////////////////////////////////////////////// +NodeTransition *OnTransition:: +invert() const { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnTransition::apply +// Access: Public, Virtual +// Description: Returns a new attribute (or possibly the same +// attribute) that represents the effect of applying this +// indicated transition to the indicated attribute. The +// source attribute may be NULL, indicating the initial +// attribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *OnTransition:: +apply(const NodeAttribute *attrib) const { + OnAttribute *result; + if (attrib == (const NodeAttribute *)NULL) { + DCAST_INTO_R(result, make_attrib(), NULL); + } else { + DCAST_INTO_R(result, (NodeAttribute *)attrib, NULL); + } + + if (_priority < result->_priority) { + // The priority is too low to affect the attribute. + return result; + } + + if (result->get_count() > 1) { + // Copy on write. + DCAST_INTO_R(result, result->make_copy(), NULL); + } + + result->_priority = _priority; + result->set_value_from(this); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: OnTransition::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void OnTransition:: +output(ostream &out) const { + output_value(out); +} + +//////////////////////////////////////////////////////////////////// +// Function: OnTransition::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void OnTransition:: +write(ostream &out, int indent_level) const { + write_value(out, indent_level); +} + +//////////////////////////////////////////////////////////////////// +// Function: OnTransition::internal_compare_to +// Access: Protected, Virtual +// Description: Returns a number < 0 if this transition sorts before +// the other transition, > 0 if it sorts after, 0 if +// they are equivalent (except for priority). +//////////////////////////////////////////////////////////////////// +int OnTransition:: +internal_compare_to(const NodeTransition *other) const { + const OnTransition *ot; + DCAST_INTO_R(ot, other, false); + + return compare_values(ot); +} diff --git a/panda/src/graph/onTransition.h b/panda/src/graph/onTransition.h new file mode 100644 index 0000000000..a5506b0865 --- /dev/null +++ b/panda/src/graph/onTransition.h @@ -0,0 +1,77 @@ +// Filename: onTransition.h +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ONTRANSITION_H +#define ONTRANSITION_H + +#include + +#include "nodeTransition.h" + +class NodeAttribute; +class NodeRelation; + +//////////////////////////////////////////////////////////////////// +// Class : OnTransition +// Description : This is an abstract class that encapsulates a family +// of transitions for states that are always on, in one +// state or another. It's similar to OnOffTransition, +// except the state can never be 'off'; it is always on +// in some sense. +// +// Most scene graph attributes that have an enumerated +// set of states fall into this category. +// +// There is no explicit identity transition. Each +// transition always changes the state. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA OnTransition : public NodeTransition { +protected: + INLINE OnTransition(); + INLINE OnTransition(const OnTransition ©); + INLINE void operator = (const OnTransition ©); + +public: + virtual NodeTransition *compose(const NodeTransition *other) const; + virtual NodeTransition *invert() const; + virtual NodeAttribute *apply(const NodeAttribute *attrib) const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeTransition *other) const; + + virtual void set_value_from(const OnTransition *other)=0; + virtual int compare_values(const OnTransition *other) const=0; + virtual void output_value(ostream &out) const=0; + virtual void write_value(ostream &out, int indent_level) const=0; + + //No new data is defined in OnTransition, and OnTransition is only + //an Abstract class, so no Read/Write methods are defined + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeTransition::init_type(); + register_type(_type_handle, "OnTransition", + NodeTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + +friend class OnAttribute; +}; + +#include "onTransition.I" + +#endif diff --git a/panda/src/graph/pointerNameClass.h b/panda/src/graph/pointerNameClass.h new file mode 100644 index 0000000000..cb3333807b --- /dev/null +++ b/panda/src/graph/pointerNameClass.h @@ -0,0 +1,27 @@ +// Filename: pointerNameClass.h +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef POINTERNAMECLASS_H +#define POINTERNAMECLASS_H + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PointerNameClass +// Description : This is a stupid little class that's used by +// MultiNodeTransition to define the name of its +// PT(Node) class, so the MultiTransition it +// inherits from can initialize itself properly. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PointerNameClass { +public: + static string get_class_name() { + return "PT(Node)"; + } +}; + +#endif + + diff --git a/panda/src/graph/pt_NamedNode.N b/panda/src/graph/pt_NamedNode.N new file mode 100644 index 0000000000..80c260bf74 --- /dev/null +++ b/panda/src/graph/pt_NamedNode.N @@ -0,0 +1,3 @@ +ignorefile boundedObject.h + +forcetype PT_NamedNode diff --git a/panda/src/graph/pt_NamedNode.cxx b/panda/src/graph/pt_NamedNode.cxx new file mode 100644 index 0000000000..34b8fb1d4c --- /dev/null +++ b/panda/src/graph/pt_NamedNode.cxx @@ -0,0 +1,11 @@ +// Filename: pt_NamedNode.cxx +// Created by: drose (16May00) +// +//////////////////////////////////////////////////////////////////// + +#include "pt_NamedNode.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/graph/pt_NamedNode.h b/panda/src/graph/pt_NamedNode.h new file mode 100644 index 0000000000..bc8b594dbd --- /dev/null +++ b/panda/src/graph/pt_NamedNode.h @@ -0,0 +1,34 @@ +// Filename: pt_NamedNode.h +// Created by: drose (16May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PT_NAMEDNODE_H +#define PT_NAMEDNODE_H + +#include + +#include "namedNode.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PT_NamedNode +// Description : A PT(NamedNode). This is defined here solely we can +// explicitly export the template class. It's not +// strictly necessary, but it doesn't hurt. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToBase) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerTo) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, ConstPointerTo) + +typedef PointerTo PT_NamedNode; +typedef ConstPointerTo CPT_NamedNode; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/graph/pt_Node.N b/panda/src/graph/pt_Node.N new file mode 100644 index 0000000000..71e110d3de --- /dev/null +++ b/panda/src/graph/pt_Node.N @@ -0,0 +1,3 @@ +ignorefile boundedObject.h + +forcetype PT_Node diff --git a/panda/src/graph/pt_Node.cxx b/panda/src/graph/pt_Node.cxx new file mode 100644 index 0000000000..4f9c2fb05d --- /dev/null +++ b/panda/src/graph/pt_Node.cxx @@ -0,0 +1,12 @@ +// Filename: pt_Node.cxx +// Created by: drose (16May00) +// +//////////////////////////////////////////////////////////////////// + +#include "node.h" +#include "pt_Node.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/graph/pt_Node.h b/panda/src/graph/pt_Node.h new file mode 100644 index 0000000000..36fe8d5663 --- /dev/null +++ b/panda/src/graph/pt_Node.h @@ -0,0 +1,37 @@ +// Filename: pt_Node.h +// Created by: drose (16May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PT_NODE_H +#define PT_NODE_H + +#include + +// We can't include node.h, because we need to include this in +// nodeRelation.h. + +#include + +class Node; + +//////////////////////////////////////////////////////////////////// +// Class : PT_Node +// Description : A PT(Node). This is defined here solely we can +// explicitly export the template class. It's not +// strictly necessary, but it doesn't hurt. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToBase) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerTo) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, ConstPointerTo) + +typedef PointerTo PT_Node; +typedef ConstPointerTo CPT_Node; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/graph/setTransitionHelpers.I b/panda/src/graph/setTransitionHelpers.I new file mode 100644 index 0000000000..31c80dbf5a --- /dev/null +++ b/panda/src/graph/setTransitionHelpers.I @@ -0,0 +1,1060 @@ +// Filename: setTransitionHelpers.I +// Created by: drose (25Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "nodeTransitionCacheEntry.h" +#include "config_graph.h" + +//////////////////////////////////////////////////////////////////// +// Function: tmap_get_interest +// Description: Returns the NodeTransition elements of interest to +// us. These are the elements of the first sequence--a +// NodeTransition map--whose keys appear in the second +// sequence, a sorted list of NodeTransition +// TypeHandles. +//////////////////////////////////////////////////////////////////// +template +OutputIterator +tmap_get_interest(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result) { + while (first1 != last1 && first2 != last2) { + if ((*first1).first < *first2) { + ++first1; + ++result; + } else if (*first2 < (*first1).first) { + ++first2; + ++result; + } else { + *result = *first1; + ++first1; + ++first2; + ++result; + } + } + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: tmap_union +// Description: Accepts two NodeTransition or NodeAttribute maps, and +// builds a new map which is the union of the first two. +// If an element appears in both maps, the entry from +// the second is preferred. +//////////////////////////////////////////////////////////////////// +template +OutputIterator +tmap_union(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result) { + while (first1 != last1 && first2 != last2) { + if ((*first1).first < (*first2).first) { + *result = *first1; + ++first1; + ++result; + } else if ((*first2).first < (*first1).first) { + *result = *first2; + ++first2; + ++result; + } else { + *result = *first2; + ++first1; + ++first2; + ++result; + } + } + + while (first1 != last1) { + *result = *first1; + ++first1; + ++result; + } + + while (first2 != last2) { + *result = *first2; + ++first2; + ++result; + } + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: tmap_arc_union +// Description: As above, but updates the transitions on the way to +// indicate they are being attached to (or removed from) +// the indicated arc. +//////////////////////////////////////////////////////////////////// +template +OutputIterator +tmap_arc_union(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + NodeRelation *to_arc, OutputIterator result) { + while (first1 != last1 && first2 != last2) { + if ((*first1).first < (*first2).first) { + *result = *first1; + ++first1; + ++result; + } else if ((*first2).first < (*first1).first) { + (*first2).second->added_to_arc(to_arc); + *result = *first2; + ++first2; + ++result; + } else { + (*first1).second->removed_from_arc(to_arc); + (*first2).second->added_to_arc(to_arc); + *result = *first2; + ++first1; + ++first2; + ++result; + } + } + + while (first1 != last1) { + *result = *first1; + ++first1; + ++result; + } + + while (first2 != last2) { + (*first2).second->added_to_arc(to_arc); + *result = *first2; + ++first2; + ++result; + } + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: tmap_arc_compose +// Description: +//////////////////////////////////////////////////////////////////// +template +OutputIterator +tmap_arc_compose(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + NodeRelation *to_arc, OutputIterator result) { + while (first1 != last1 && first2 != last2) { + if ((*first1).first < (*first2).first) { + *result = *first1; + ++first1; + ++result; + } else if ((*first2).first < (*first1).first) { + (*first2).second->added_to_arc(to_arc); + *result = *first2; + ++first2; + ++result; + } else { + (*first1).second->removed_from_arc(to_arc); + + PT(NodeTransition) c = (*first1).second->compose((*first2).second); + if (c != (NodeTransition *)NULL) { + c->added_to_arc(to_arc); + *result = pair((*first1).first, c); + ++result; + } + ++first1; + ++first2; + } + } + + while (first1 != last1) { + *result = *first1; + ++first1; + ++result; + } + + while (first2 != last2) { + (*first2).second->added_to_arc(to_arc); + *result = *first2; + ++first2; + ++result; + } + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: tmap_arc_compose +// Description: +//////////////////////////////////////////////////////////////////// +template +OutputIterator +tmap_arc_compose(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result) { + while (first1 != last1 && first2 != last2) { + if ((*first1).first < (*first2).first) { + *result = *first1; + ++first1; + ++result; + } else if ((*first2).first < (*first1).first) { + *result = *first2; + ++first2; + ++result; + } else { + NodeTransition *c = (*first1).second->compose((*first2).second); + if (c != (NodeTransition *)NULL) { + *result = pair + ((*first1).first, c); + ++result; + } + ++first1; + ++first2; + } + } + + while (first1 != last1) { + *result = *first1; + ++first1; + ++result; + } + + while (first2 != last2) { + *result = *first2; + ++first2; + ++result; + } + return result; +} + + +//////////////////////////////////////////////////////////////////// +// Function: tmap_compose +// Description: Accepts two NodeTransition maps, and builds a new +// list (a NodeTransition map) which represents the +// memberwise composition of the two input maps. +//////////////////////////////////////////////////////////////////// +template +OutputIterator +tmap_compose(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result) { + while (first1 != last1 && first2 != last2) { + if ((*first1).first < (*first2).first) { + *result = *first1; + ++first1; + ++result; + } else if ((*first2).first < (*first1).first) { + *result = *first2; + ++first2; + ++result; + } else { + *result = pair + ((*first1).first, + NodeTransitionCacheEntry::compose((*first1).second, (*first2).second)); + ++first1; + ++first2; + ++result; + } + } + + while (first1 != last1) { + *result = *first1; + ++first1; + ++result; + } + + while (first2 != last2) { + *result = *first2; + ++first2; + ++result; + } + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: tmap_invert_compose +// Description: Accepts two NodeTransition maps, and builds a new +// list (a NodeTransition map) which represents the +// memberwise result of invert_compose of the two input +// maps. +//////////////////////////////////////////////////////////////////// +template +OutputIterator +tmap_invert_compose(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result) { + while (first1 != last1 && first2 != last2) { + if ((*first1).first < (*first2).first) { + *result = pair + ((*first1).first, NodeTransitionCacheEntry::invert((*first1).second)); + ++first1; + ++result; + } else if ((*first2).first < (*first1).first) { + *result = *first2; + ++first2; + ++result; + } else { + NodeTransitionCacheEntry ic = + NodeTransitionCacheEntry::invert_compose((*first1).second, + (*first2).second); + if (!ic.is_identity()) { + // Only bother to store the result if it's not identity. + *result = pair((*first1).first, ic); + ++result; + } + ++first1; + ++first2; + } + } + + while (first1 != last1) { + *result = pair + ((*first1).first, NodeTransitionCacheEntry::invert((*first1).second)); + ++first1; + ++result; + } + + while (first2 != last2) { + *result = *first2; + ++first2; + ++result; + } + return result; +} + +// This macro is used in the various cached_compose functions to +// possibly output debug statements about which pieces we're +// composing. + +#ifndef NDEBUG +#define OUTPUT_CC_ELEM(desc, type) \ +{ \ + if (wrt_cat.is_debug()) { \ + wrt_cat.debug() << "in " << desc << ": " << (type) << "\n"; \ + } \ +} +#else +#define OUTPUT_CC_ELEM(desc, type) +#endif + +//////////////////////////////////////////////////////////////////// +// Function: tmap_cached_compose_not_1 +// Description: One of several support functions for +// tmap_cached_compose(), below, this handles the case +// of tmap_cached_compose() for an NodeTransition that +// is already known not to be present in list 1, but may +// be in one or both of lists 2 and 3. +//////////////////////////////////////////////////////////////////// +template +void +tmap_cached_compose_not_1(InputIterator2 &first2, InputIterator3 &first3, + UpdateSeq now, OutputIterator &result) { + if ((*first2).first < (*first3).first) { + // Here's an element in list 2 that is not in lists 1 or 3. + OUTPUT_CC_ELEM("2", (*first2).first); + + *result = pair + ((*first2).first, + NodeTransitionCacheEntry::cached_compose + (NodeTransitionCacheEntry(), + (*first2).second, + NodeTransitionCacheEntry(), + now)); + ++first2; + ++result; + + } else if ((*first3).first < (*first2).first) { + // Here's an element in list 3 that is not lists 2 or 1. + OUTPUT_CC_ELEM("3", (*first3).first); + + *result = pair + ((*first3).first, + NodeTransitionCacheEntry::cached_compose + (NodeTransitionCacheEntry(), + NodeTransitionCacheEntry(), + (*first3).second, + now)); + ++first3; + ++result; + + } else { // (*first2).first == (*first3).first + // Here's an element in lists 2 and 3 that is not in list 1. + OUTPUT_CC_ELEM("2, 3", (*first2).first); + + *result = pair + ((*first2).first, + NodeTransitionCacheEntry::cached_compose + (NodeTransitionCacheEntry(), + (*first2).second, + (*first3).second, + now)); + ++first2; + ++first3; + ++result; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: tmap_cached_compose_not_2 +// Description: One of several support functions for +// tmap_cached_compose(), below, this handles the case +// of tmap_cached_compose() for an NodeTransition that +// is already known not to be present in list 2, but may +// be in one or both of lists 1 and 3. +//////////////////////////////////////////////////////////////////// +template +void +tmap_cached_compose_not_2(InputIterator1 &first1, InputIterator3 &first3, + UpdateSeq now, OutputIterator &result) { + if ((*first1).first < (*first3).first) { + // Here's an element in list 1 that is not in lists 2 or 3. + OUTPUT_CC_ELEM("1", (*first1).first); + + *result = pair + ((*first1).first, + NodeTransitionCacheEntry::cached_compose + ((*first1).second, + NodeTransitionCacheEntry(), + NodeTransitionCacheEntry(), + now)); + ++first1; + ++result; + + } else if ((*first3).first < (*first1).first) { + // Here's an element in list 3 that is not lists 1 or 2. + OUTPUT_CC_ELEM("3", (*first3).first); + + *result = pair + ((*first3).first, + NodeTransitionCacheEntry::cached_compose + (NodeTransitionCacheEntry(), + NodeTransitionCacheEntry(), + (*first3).second, + now)); + ++first3; + ++result; + + } else { // (*first1).first == (*first3).first + // Here's an element in lists 1 and 3 that is not in list 2. + OUTPUT_CC_ELEM("1, 3", (*first1).first); + + *result = pair + ((*first1).first, + NodeTransitionCacheEntry::cached_compose + ((*first1).second, + NodeTransitionCacheEntry(), + (*first3).second, + now)); + ++first1; + ++first3; + ++result; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: tmap_cached_compose_not_3 +// Description: One of several support functions for +// tmap_cached_compose(), below, this handles the case +// of tmap_cached_compose() for an NodeTransition that +// is already known not to be present in list 3, but may +// be in one or both of lists 1 and 2. +//////////////////////////////////////////////////////////////////// +template +void +tmap_cached_compose_not_3(InputIterator1 &first1, InputIterator2 &first2, + UpdateSeq now, OutputIterator &result) { + if ((*first1).first < (*first2).first) { + // Here's an element in list 1 that is not in lists 2 or 3. + OUTPUT_CC_ELEM("1", (*first1).first); + + *result = pair + ((*first1).first, + NodeTransitionCacheEntry::cached_compose + ((*first1).second, + NodeTransitionCacheEntry(), + NodeTransitionCacheEntry(), + now)); + ++first1; + ++result; + + } else if ((*first2).first < (*first1).first) { + // Here's an element in list 2 that is not lists 1 or 3. + OUTPUT_CC_ELEM("2", (*first2).first); + + *result = pair + ((*first2).first, + NodeTransitionCacheEntry::cached_compose + (NodeTransitionCacheEntry(), + (*first2).second, + NodeTransitionCacheEntry(), + now)); + ++first2; + ++result; + + } else { // (*first1).first == (*first2).first + // Here's an element in lists 1 and 2 that is not in list 3. + OUTPUT_CC_ELEM("1, 2", (*first1).first); + + *result = pair + ((*first1).first, + NodeTransitionCacheEntry::cached_compose + ((*first1).second, + (*first2).second, + NodeTransitionCacheEntry(), + now)); + ++first1; + ++first2; + ++result; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: tmap_cached_compose_12 +// Description: One of several support functions for +// tmap_cached_compose(), below, this handles the case +// of tmap_cached_compose() for an NodeTransition that +// is already known to be in both lists 1 and 2, and may +// or may not also be in list 3. +//////////////////////////////////////////////////////////////////// +template +void +tmap_cached_compose_12(InputIterator1 &first1, InputIterator2 &first2, + InputIterator3 &first3, + UpdateSeq now, OutputIterator &result) { + if ((*first1).first < (*first3).first) { + // Here's an element in lists 1 and 2 that is not in list 3. + OUTPUT_CC_ELEM("1, 2", (*first1).first); + + *result = pair + ((*first1).first, + NodeTransitionCacheEntry::cached_compose + ((*first1).second, + (*first2).second, + NodeTransitionCacheEntry(), + now)); + ++first1; + ++first2; + ++result; + + } else if ((*first3).first < (*first1).first) { + // Here's an element in list 3 that is not lists 1 or 2. + OUTPUT_CC_ELEM("3", (*first3).first); + + *result = pair + ((*first3).first, + NodeTransitionCacheEntry::cached_compose + (NodeTransitionCacheEntry(), + NodeTransitionCacheEntry(), + (*first3).second, + now)); + ++first3; + ++result; + + } else { // (*first1).first == (*first3).first + // Here's an element in lists 1, 2, and 3. + OUTPUT_CC_ELEM("1, 2, 3", (*first1).first); + + *result = pair + ((*first1).first, + NodeTransitionCacheEntry::cached_compose + ((*first1).second, + (*first2).second, + (*first3).second, + now)); + ++first1; + ++first2; + ++first3; + ++result; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: tmap_cached_compose +// Description: Calls dynamic_cached_compose() on each matching +// element of [first1 .. last1), [cached_first +// .. cached_last), and [value_first .. value_last). +// +// This function is big and complicated because it has +// to do a parallel merge on three separate lists. Any +// NodeTransition type may appear in any or all of the +// lists; if a particular type appears in any list but +// is absent from the others, an implicit identity +// transition should be inferred where it is absent. +//////////////////////////////////////////////////////////////////// +template +OutputIterator +tmap_cached_compose(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + InputIterator3 first3, InputIterator3 last3, + UpdateSeq now, OutputIterator result) { + while (first1 != last1 && first2 != last2 && first3 != last3) { + if ((*first1).first < (*first2).first) { + tmap_cached_compose_not_2(first1, first3, now, result); + + } else if ((*first2).first < (*first1).first) { + tmap_cached_compose_not_1(first2, first3, now, result); + + } else { // (*first1).first == (*first2).first + tmap_cached_compose_12(first1, first2, first3, now, result); + } + } + + // Now pick up everything else. + if (first1 == last1) { + while (first2 != last2 && first3 != last3) { + tmap_cached_compose_not_1(first2, first3, now, result); + } + + } else if (first2 == last2) { + while (first1 != last1 && first3 != last3) { + tmap_cached_compose_not_2(first1, first3, now, result); + } + + } else if (first3 == last3) { + while (first1 != last1 && first2 != last2) { + tmap_cached_compose_not_3(first1, first2, now, result); + } + } + + while (first1 != last1) { + // Here's an element in list 1 that is not in lists 2 or 3. + OUTPUT_CC_ELEM("1", (*first1).first); + + *result = pair + ((*first1).first, + NodeTransitionCacheEntry::cached_compose + ((*first1).second, + NodeTransitionCacheEntry(), + NodeTransitionCacheEntry(), + now)); + ++first1; + ++result; + } + + while (first2 != last2) { + // Here's an element in list 2 that is not in lists 1 or 3. + OUTPUT_CC_ELEM("2", (*first2).first); + + *result = pair + ((*first2).first, + NodeTransitionCacheEntry::cached_compose + (NodeTransitionCacheEntry(), + (*first2).second, + NodeTransitionCacheEntry(), + now)); + ++first2; + ++result; + } + + while (first3 != last3) { + // Here's an element in list 3 that is not lists 1 or 2. + OUTPUT_CC_ELEM("3", (*first3).first); + + *result = pair + ((*first3).first, + NodeTransitionCacheEntry::cached_compose + (NodeTransitionCacheEntry(), + NodeTransitionCacheEntry(), + (*first3).second, + now)); + ++first3; + ++result; + } + + return result; +} + + +//////////////////////////////////////////////////////////////////// +// Function: tmap_apply +// Description: Accepts a NodeAttribute map and a NodeTransition map, +// and builds a new list (a NodeAttribute map) which +// represents the memberwise application of the two +// input maps. +//////////////////////////////////////////////////////////////////// +template +OutputIterator +tmap_apply(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result) { + while (first1 != last1 && first2 != last2) { + if ((*first1).first < (*first2).first) { + *result = *first1; + ++first1; + ++result; + } else if ((*first2).first < (*first1).first) { + *result = pair + ((*first2).first, + NodeTransitionCacheEntry::apply(NULL, (*first2).second)); + ++first2; + ++result; + } else { + *result = pair + ((*first1).first, + NodeTransitionCacheEntry::apply((*first1).second, (*first2).second)); + ++first1; + ++first2; + ++result; + } + } + + while (first1 != last1) { + *result = *first1; + ++first1; + ++result; + } + + while (first2 != last2) { + *result = pair + ((*first2).first, + NodeTransitionCacheEntry::apply(NULL, (*first2).second)); + ++first2; + ++result; + } + return result; +} + + +//////////////////////////////////////////////////////////////////// +// Function: tmap_invert +// Description: Accepts a NodeTransition map, and builds a new list +// which represents the memberwise inversion of the +// input. Guarantees that the new list will have +// exactly the same length as the input list. +//////////////////////////////////////////////////////////////////// +template +OutputIterator +tmap_invert(InputIterator first, InputIterator last, + OutputIterator result) { + while (first != last) { + *result = pair + ((*first).first, NodeTransitionCacheEntry::invert((*first).second)); + ++first; + ++result; + } + return result; +} + + +//////////////////////////////////////////////////////////////////// +// Function: tmap_equiv_trans +// Description: Accepts a pair of NodeTransition maps, and returns +// true if they are equivalent, false otherwise. Two +// NodeTransition maps are defined to be equivalent if +// all nonidentity members present in one set are +// present and equivalent in the other set, +//////////////////////////////////////////////////////////////////// +template +bool +tmap_equiv_trans(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) { + while (first1 != last1 && first2 != last2) { + if ((*first1).first < (*first2).first) { + if (!(*first1).second.is_identity()) { + return false; + } + ++first1; + } else if ((*first2).first < (*first1).first) { + if (!(*first2).second.is_identity()) { + return false; + } + ++first2; + } else { + if (!(*first1).second.compare_to((*first2).second) == 0) { + return false; + } + ++first1; + ++first2; + } + } + + while (first1 != last1) { + if (!(*first1).second.is_identity()) { + return false; + } + ++first1; + } + + while (first2 != last2) { + if (!(*first2).second.is_identity()) { + return false; + } + ++first2; + } + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: tmap_equiv_attr +// Description: Accepts a pair of NodeAttribute maps, and returns +// true if they are equivalent, false otherwise. Two +// NodeAttributes maps are defined to be equivalent if +// all non-NULL members present in one set are present +// and equivalent in the other set, +//////////////////////////////////////////////////////////////////// +template +bool +tmap_equiv_attr(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) { + while (first1 != last1 && first2 != last2) { + if ((*first1).first < (*first2).first) { + if ((*first1).second != (NodeAttribute *)NULL) { + return false; + } + ++first1; + } else if ((*first2).first < (*first1).first) { + if ((*first2).second != (NodeAttribute *)NULL) { + return false; + } + ++first2; + } else { + if ((*first1).second != (*first2).second) { + if ((*first1).second == (NodeAttribute *)NULL || + (*first2).second == (NodeAttribute *)NULL || + (*first1).second->compare_to((*first2).second) != 0) { + return false; + } + } + ++first1; + ++first2; + } + } + + while (first1 != last1) { + if ((*first1).second != (NodeAttribute *)NULL) { + return false; + } + ++first1; + } + + while (first2 != last2) { + if ((*first2).second != (NodeAttribute *)NULL) { + return false; + } + ++first2; + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: tmap_compare_cache +// Description: Accepts a pair of NodeTransition maps, and returns +// -1 if the first one sorts before the second one, +// 1 if it sorts after, or 0 if they are equivalent. +//////////////////////////////////////////////////////////////////// +template +int +tmap_compare_cache(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) { + while (first1 != last1 && first2 != last2) { + if ((*first1).first < (*first2).first) { + if (!(*first1).second.is_identity()) { + return 1; + } + ++first1; + } else if ((*first2).first < (*first1).first) { + if (!(*first2).second.is_identity()) { + return -1; + } + ++first2; + } else { + int result; + result = (*first1).second.compare_to((*first2).second); + if (result != 0) { + return result; + } + ++first1; + ++first2; + } + } + + while (first1 != last1) { + if (!(*first1).second.is_identity()) { + // list1 is longer. + return -1; + } + ++first1; + } + + while (first2 != last2) { + if (!(*first2).second.is_identity()) { + // list2 is longer. + return 1; + } + ++first2; + } + + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: tmap_compare_trans +// Description: Accepts a pair of NodeTransition maps, and returns +// -1 if the first one sorts before the second one, +// 1 if it sorts after, or 0 if they are equivalent. +//////////////////////////////////////////////////////////////////// +template +int +tmap_compare_trans(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) { + while (first1 != last1 && first2 != last2) { + if ((*first1).first < (*first2).first) { + if ((*first1).second != (NodeTransition *)NULL) { + return 1; + } + ++first1; + } else if ((*first2).first < (*first1).first) { + if ((*first2).second != (NodeTransition *)NULL) { + return -1; + } + ++first2; + } else { + if ((*first1).second != (*first2).second) { + if ((*first1).second == (NodeTransition *)NULL) { + return -1; + } else if ((*first2).second == (NodeTransition *)NULL) { + return 1; + } else { + int result; + result = (*first1).second->compare_to(*(*first2).second); + if (result != 0) { + return result; + } + } + } + ++first1; + ++first2; + } + } + + while (first1 != last1) { + if ((*first1).second != (NodeTransition *)NULL) { + // list1 is longer. + return -1; + } + ++first1; + } + + while (first2 != last2) { + if ((*first2).second != (NodeTransition *)NULL) { + // list2 is longer. + return 1; + } + ++first2; + } + + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: tmap_compare_attr +// Description: Accepts a pair of NodeAttribute maps, and returns +// -1 if the first one sorts before the second one, +// 1 if it sorts after, or 0 if they are equivalent. +//////////////////////////////////////////////////////////////////// +template +int +tmap_compare_attr(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) { + while (first1 != last1 && first2 != last2) { + if ((*first1).first < (*first2).first) { + if ((*first1).second != (NodeAttribute *)NULL) { + return 1; + } + ++first1; + } else if ((*first2).first < (*first1).first) { + if ((*first2).second != (NodeAttribute *)NULL) { + return -1; + } + ++first2; + } else { + if ((*first1).second != (*first2).second) { + if ((*first1).second == (NodeAttribute *)NULL) { + return -1; + } else if ((*first2).second == (NodeAttribute *)NULL) { + return 1; + } else { + int result; + result = (*first1).second->compare_to(*(*first2).second); + if (result != 0) { + return result; + } + } + } + ++first1; + ++first2; + } + } + + while (first1 != last1) { + if ((*first1).second != (NodeAttribute *)NULL) { + // list1 is longer. + return -1; + } + ++first1; + } + + while (first2 != last2) { + if ((*first2).second != (NodeAttribute *)NULL) { + // list2 is longer. + return 1; + } + ++first2; + } + + return 0; +} + + + +//////////////////////////////////////////////////////////////////// +// Function: tmap_is_identity +// Description: Accepts a NodeTransition map, and returns true if all +// elements in the map correspond to the identity +// transition, false otherwise. +//////////////////////////////////////////////////////////////////// +template +bool +tmap_is_identity(InputIterator first, InputIterator last) { + while (first != last) { + if (!(*first).second.is_identity()) { + return false; + } + ++first; + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: tmap_is_initial +// Description: Accepts a NodeAttribute map, and returns true if all +// elements in the map correspond to the initial +// attribute, false otherwise. +//////////////////////////////////////////////////////////////////// +template +bool +tmap_is_initial(InputIterator first, InputIterator last) { + while (first != last) { + if (!(*first).second.is_initial()) { + return false; + } + ++first; + } + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: tmap_output +// Description: Accepts a NodeTransition or NodeAttribute map and +// writes each element to the given output stream with a +// single space separating them. +//////////////////////////////////////////////////////////////////// +template +ostream & +tmap_output(InputIterator first, InputIterator last, ostream &out) { + if (first != last) { + out << *((*first).second); + ++first; + while (first != last) { + out << " " << *((*first).second); + ++first; + } + } + return out; +} diff --git a/panda/src/graph/setTransitionHelpers.h b/panda/src/graph/setTransitionHelpers.h new file mode 100644 index 0000000000..6453a4b6d4 --- /dev/null +++ b/panda/src/graph/setTransitionHelpers.h @@ -0,0 +1,125 @@ +// Filename: setTransitionHelpers.h +// Created by: drose (25Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SETTRANSITIONHELPERS_H +#define SETTRANSITIONHELPERS_H + +#include + +#include + + +//////////////////////////////////////////////////////////////////// +// +// tmap_* functions +// +// The SetTransition and SetAttribute classes are implemented with the +// help of a handful of template functions that operate on +// NodeTransition and NodeAttribute maps. +// +// Each of these follows the standard STL calling conventions for +// operating on one or two sequences, and/or storing the results into +// an output sequence. +// +// Some of the following functions can work on either a NodeTransition +// or a NodeAttribute map; most are specialized for one or the other. +// +//////////////////////////////////////////////////////////////////// + +template +OutputIterator +tmap_get_interest(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result); + +template +OutputIterator +tmap_union(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result); + +template +OutputIterator +tmap_arc_union(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + NodeRelation *to_arc, OutputIterator result); + +template +OutputIterator +tmap_arc_compose(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + NodeRelation *to_arc, OutputIterator result); + +template +OutputIterator +tmap_compose(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result); + +template +OutputIterator +tmap_invert_compose(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result); + +template +OutputIterator +tmap_cached_compose(InputIterator1 first1, InputIterator1 last1, + InputIterator2 cached_first, InputIterator2 cached_last, + InputIterator3 value_first, InputIterator3 value_last, + UpdateSeq now, OutputIterator result); + +template +OutputIterator +tmap_apply(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result); + +template +OutputIterator +tmap_invert(InputIterator first, InputIterator last, + OutputIterator result); + +template +bool +tmap_equiv_trans(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2); + +template +bool +tmap_equiv_attr(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2); + +template +int +tmap_compare_cache(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2); + +template +int +tmap_compare_trans(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2); + +template +int +tmap_compare_attr(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2); + +template +bool +tmap_is_identity(InputIterator first, InputIterator last); + +template +bool +tmap_is_initial(InputIterator first, InputIterator last); + +template +ostream & +tmap_output(InputIterator first, InputIterator last, ostream &out); + +#include "setTransitionHelpers.I" + +#endif diff --git a/panda/src/graph/test_graph.cxx b/panda/src/graph/test_graph.cxx new file mode 100644 index 0000000000..f7781c6dc0 --- /dev/null +++ b/panda/src/graph/test_graph.cxx @@ -0,0 +1,746 @@ +// Filename: test_graph.cxx +// Created by: drose (02Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "nodeRelation.h" +#include "onOffTransition.h" +#include "onOffAttribute.h" +#include "onTransition.h" +#include "onAttribute.h" +#include "namedNode.h" +#include "pt_NamedNode.h" +#include "wrt.h" +#include "nodeTransitionWrapper.h" +#include "nodeAttributeWrapper.h" +#include "allTransitionsWrapper.h" +#include "allAttributesWrapper.h" +#include "nullTransitionWrapper.h" +#include "nullAttributeWrapper.h" +#include "traverserVisitor.h" +#include "dftraverser.h" +#include "multiTransition.h" +#include "nullLevelState.h" + +#include + +typedef int TestType; + +//#define USE_ONOFF + +#ifdef USE_ONOFF +typedef OnOffTransition BaseTransition; +typedef OnOffAttribute BaseAttribute; +#else +typedef OnTransition BaseTransition; +typedef OnAttribute BaseAttribute; +#endif + +class TestTransition : public BaseTransition { +public: + TestTransition(); + TestTransition(TestType value); +#ifdef USE_ONOFF + static TestTransition off(); +#endif + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + + TestType get_value() const; + +protected: + virtual void set_value_from(const BaseTransition *other); + virtual int compare_values(const BaseTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + TestType _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + BaseTransition::init_type(); + register_type(_type_handle, "TestTransition", + BaseTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +class TestAttribute : public BaseAttribute { +public: + virtual TypeHandle get_handle() const; + + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + +protected: + virtual void set_value_from(const BaseTransition *other); + virtual int compare_values(const BaseAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + TestType _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + BaseAttribute::init_type(); + register_type(_type_handle, "TestAttribute", + BaseAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +class Test2Transition : public TestTransition { +public: + Test2Transition(); + Test2Transition(TestType value); +#ifdef USE_ONOFF + static Test2Transition off(); +#endif + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TestTransition::init_type(); + register_type(_type_handle, "Test2Transition", + TestTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +class Test2Attribute : public TestAttribute { +public: + virtual TypeHandle get_handle() const; + + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TestAttribute::init_type(); + register_type(_type_handle, "Test2Attribute", + TestAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +class TestMName { +public: + static string get_class_name() { + return "TestType"; + } +}; + +class TestMAttribute : public MultiAttribute { +protected: + virtual NodeAttribute *make_copy() const { + return new TestMAttribute(*this); + } + virtual NodeAttribute *make_initial() const { + return new TestMAttribute(); + } + + virtual TypeHandle get_handle() const; + + virtual void output_property(ostream &out, const TestType &prop) const { + out << prop; + } + virtual void write_property(ostream &out, const TestType &prop, + int indent_level) const { + indent(out, indent_level) << prop << "\n"; + } + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MultiAttribute::init_type(); + register_type(_type_handle, "TestMAttribute", + MultiAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +class TestMTransition : public MultiTransition { +protected: + + virtual NodeTransition *make_copy() const { + return new TestMTransition(*this); + } + virtual NodeAttribute *make_attrib() const { + return new TestMAttribute; + } + virtual NodeTransition *make_identity() const { + return new TestMTransition; + } + virtual void output_property(ostream &out, const TestType &prop) const { + out << prop; + } + virtual void write_property(ostream &out, const TestType &prop, + int indent_level) const { + indent(out, indent_level) << prop << "\n"; + } + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MultiTransition::init_type(); + register_type(_type_handle, "TestMTransition", + MultiTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +TypeHandle TestMAttribute::get_handle() const { + return TestMTransition::get_class_type(); +} + +template +class PrintNodes : public TraverserVisitor { +public: + PrintNodes() { + _indent_level = 0; + } + bool reached_node(Node *node, AttributeWrapper &state, NullLevelState &) { + indent(nout, _indent_level) + << "Reached " << *node << ", state is " << state << "\n"; + return true; + } + bool forward_arc(NodeRelation *arc, TransitionWrapper &trans, + AttributeWrapper &pre, AttributeWrapper &post, + NullLevelState &) { + // indent(nout, _indent_level + 1) + // << "Passing " << *arc << ", trans is " << trans << "\n"; + _indent_level += 2; + return true; + } + void backward_arc(NodeRelation *arc, TransitionWrapper &trans, + AttributeWrapper &pre, AttributeWrapper &post, + const NullLevelState &) { + _indent_level -= 2; + } + int _indent_level; +}; + + +TestTransition:: +TestTransition() { +} + +TestTransition:: +TestTransition(TestType value) { + _value = value; +#ifdef USE_ONOFF + set_on(); +#endif +} + +#ifdef USE_ONOFF +TestTransition TestTransition:: +off() { + TestTransition tt; + tt._direction = D_off; + return tt; +} +#endif + +NodeTransition *TestTransition:: +make_copy() const { + return new TestTransition(*this); +} + +NodeAttribute *TestTransition:: +make_attrib() const { + return new TestAttribute; +} + +TestType TestTransition:: +get_value() const { + return _value; +} + +void TestTransition:: +set_value_from(const BaseTransition *other) { + const TestTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +int TestTransition:: +compare_values(const BaseTransition *other) const { + const TestTransition *ot; + DCAST_INTO_R(ot, other, false); + return _value - ot->_value; +} + +void TestTransition:: +output_value(ostream &out) const { + out << _value; +} + +void TestTransition:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} + +TypeHandle TestAttribute:: +get_handle() const { + return TestTransition::get_class_type(); +} + +NodeAttribute *TestAttribute:: +make_copy() const { + return new TestAttribute(*this); +} + +NodeAttribute *TestAttribute:: +make_initial() const { + return new TestAttribute; +} + +void TestAttribute:: +set_value_from(const BaseTransition *other) { + const TestTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->get_value(); +} + +int TestAttribute:: +compare_values(const BaseAttribute *other) const { + const TestAttribute *ot; + DCAST_INTO_R(ot, other, false); + return _value - ot->_value; +} + +void TestAttribute:: +output_value(ostream &out) const { + out << _value; +} + +void TestAttribute:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} + +Test2Transition:: +Test2Transition() { +} + +Test2Transition:: +Test2Transition(TestType value) : TestTransition(value) { +} + +#ifdef USE_ONOFF +Test2Transition Test2Transition:: +off() { + Test2Transition tt; + tt._direction = D_off; + return tt; +} +#endif USE_ONOFF + +NodeTransition *Test2Transition:: +make_copy() const { + return new Test2Transition(*this); +} + +NodeAttribute *Test2Transition:: +make_attrib() const { + return new Test2Attribute; +} + +TypeHandle Test2Attribute:: +get_handle() const { + return Test2Transition::get_class_type(); +} + +NodeAttribute *Test2Attribute:: +make_copy() const { + return new Test2Attribute(*this); +} + +NodeAttribute *Test2Attribute:: +make_initial() const { + return new Test2Attribute; +} + +TypeHandle TestTransition::_type_handle; +TypeHandle TestAttribute::_type_handle; +TypeHandle Test2Transition::_type_handle; +TypeHandle Test2Attribute::_type_handle; +TypeHandle TestMTransition::_type_handle; +TypeHandle TestMAttribute::_type_handle; + + +int main() { + TestTransition::init_type(); + TestAttribute::init_type(); + Test2Transition::init_type(); + Test2Attribute::init_type(); + TestMTransition::init_type(); + TestMAttribute::init_type(); + + PT_NamedNode r = new NamedNode("r"); + + PT_NamedNode a = new NamedNode("a"); + PT_NamedNode b = new NamedNode("b"); + + PT_NamedNode aa = new NamedNode("aa"); + PT_NamedNode ab = new NamedNode("ab"); + PT_NamedNode ba = new NamedNode("ba"); + + NodeRelation *r_a = + new NodeRelation(r, a, 0, NodeRelation::get_class_type()); + NodeRelation *r_b = + new NodeRelation(r, b, 0, NodeRelation::get_class_type()); + + NodeRelation *a_aa = + new NodeRelation(a, aa, 0, NodeRelation::get_class_type()); + NodeRelation *a_ab = + new NodeRelation(a, ab, 0, NodeRelation::get_class_type()); + NodeRelation *b_ba = + new NodeRelation(b, ba, 0, NodeRelation::get_class_type()); + + r_a->set_transition(new TestTransition(1)); + // a_aa->set_transition(new TestTransition(2)); + a_aa->set_transition(new Test2Transition(3)); + +#ifdef USE_ONOFF + a_aa->set_transition(new Test2Transition()); + a_ab->set_transition(new Test2Transition(Test2Transition::off())); +#endif + + /* + TestMTransition *tm = new TestMTransition; + tm->set_on(101); + tm->set_off(102); + tm->set_identity(103); + r_a->set_transition(tm); + + tm = new TestMTransition; + tm->set_on(102); + a_aa->set_transition(tm); + + tm = new TestMTransition; + tm->set_complete(true); + a_ab->set_transition(tm); + */ + + nout << "\nr to a has "; + r_a->output_transitions(nout); + nout << "\nr to b has "; + r_b->output_transitions(nout); + nout << "\na to aa has "; + a_aa->output_transitions(nout); + nout << "\na to ab has "; + a_ab->output_transitions(nout); + nout << "\nb to ba has "; + b_ba->output_transitions(nout); + nout << "\n"; + +#if 0 + { + nout << "\n"; + PrintNodes pn; + df_traverse(r, pn, + NullAttributeWrapper(), + NullLevelState(), + NodeRelation::get_class_type()); + nout << "\n"; + + NullTransitionWrapper result; + + wrt(r, aa, result, NodeRelation::get_class_type()); + nout << "wrt of r to aa is " << result << "\n"; + + wrt(aa, r, result, NodeRelation::get_class_type()); + nout << "wrt of aa to r is " << result << "\n"; + + wrt(r, ab, result, NodeRelation::get_class_type()); + nout << "wrt of r to ab is " << result << "\n"; + + wrt(ab, r, result, NodeRelation::get_class_type()); + nout << "wrt of ab to r is " << result << "\n"; + + wrt(ab, aa, result, NodeRelation::get_class_type()); + nout << "wrt of ab to aa is " << result << "\n"; + + wrt(aa, ab, result, NodeRelation::get_class_type()); + nout << "wrt of aa to ab is " << result << "\n"; + + wrt(r, ba, result, NodeRelation::get_class_type()); + nout << "wrt of r to ba is " << result << "\n"; + + wrt(ba, r, result, NodeRelation::get_class_type()); + nout << "wrt of ba to r is " << result << "\n"; + + wrt(aa, ba, result, NodeRelation::get_class_type()); + nout << "wrt of aa to ba is " << result << "\n"; + + wrt(ba, aa, result, NodeRelation::get_class_type()); + nout << "wrt of ba to aa is " << result << "\n"; + + wrt(ab, ba, result, NodeRelation::get_class_type()); + nout << "wrt of ab to ba is " << result << "\n"; + + wrt(ba, ab, result, NodeRelation::get_class_type()); + nout << "wrt of ba to ab is " << result << "\n"; + } + + { + nout << "\n"; + PrintNodes pn; + df_traverse(r, pn, + NodeAttributeWrapper(TestTransition::get_class_type()), + NullLevelState(), + NodeRelation::get_class_type()); + nout << "\n"; + + NodeTransitionWrapper result(TestTransition::get_class_type()); + + wrt(r, aa, result, NodeRelation::get_class_type()); + nout << "wrt of r to aa is " << result << "\n"; + + wrt(aa, r, result, NodeRelation::get_class_type()); + nout << "wrt of aa to r is " << result << "\n"; + + wrt(r, ab, result, NodeRelation::get_class_type()); + nout << "wrt of r to ab is " << result << "\n"; + + wrt(ab, r, result, NodeRelation::get_class_type()); + nout << "wrt of ab to r is " << result << "\n"; + + wrt(ab, aa, result, NodeRelation::get_class_type()); + nout << "wrt of ab to aa is " << result << "\n"; + + wrt(aa, ab, result, NodeRelation::get_class_type()); + nout << "wrt of aa to ab is " << result << "\n"; + + wrt(r, ba, result, NodeRelation::get_class_type()); + nout << "wrt of r to ba is " << result << "\n"; + + wrt(ba, r, result, NodeRelation::get_class_type()); + nout << "wrt of ba to r is " << result << "\n"; + + wrt(aa, ba, result, NodeRelation::get_class_type()); + nout << "wrt of aa to ba is " << result << "\n"; + + wrt(ba, aa, result, NodeRelation::get_class_type()); + nout << "wrt of ba to aa is " << result << "\n"; + + wrt(ab, ba, result, NodeRelation::get_class_type()); + nout << "wrt of ab to ba is " << result << "\n"; + + wrt(ba, ab, result, NodeRelation::get_class_type()); + nout << "wrt of ba to ab is " << result << "\n"; + } + + { + nout << "\n"; + PrintNodes pn; + df_traverse(r, pn, + NodeAttributeWrapper(Test2Transition::get_class_type()), + NullLevelState(), + NodeRelation::get_class_type()); + nout << "\n"; + + NodeTransitionWrapper result(Test2Transition::get_class_type()); + + wrt(r, aa, result, NodeRelation::get_class_type()); + nout << "wrt of r to aa is " << result << "\n"; + + wrt(aa, r, result, NodeRelation::get_class_type()); + nout << "wrt of aa to r is " << result << "\n"; + + wrt(r, ab, result, NodeRelation::get_class_type()); + nout << "wrt of r to ab is " << result << "\n"; + + wrt(ab, r, result, NodeRelation::get_class_type()); + nout << "wrt of ab to r is " << result << "\n"; + + wrt(ab, aa, result, NodeRelation::get_class_type()); + nout << "wrt of ab to aa is " << result << "\n"; + + wrt(aa, ab, result, NodeRelation::get_class_type()); + nout << "wrt of aa to ab is " << result << "\n"; + + wrt(r, ba, result, NodeRelation::get_class_type()); + nout << "wrt of r to ba is " << result << "\n"; + + wrt(ba, r, result, NodeRelation::get_class_type()); + nout << "wrt of ba to r is " << result << "\n"; + + wrt(aa, ba, result, NodeRelation::get_class_type()); + nout << "wrt of aa to ba is " << result << "\n"; + + wrt(ba, aa, result, NodeRelation::get_class_type()); + nout << "wrt of ba to aa is " << result << "\n"; + + wrt(ab, ba, result, NodeRelation::get_class_type()); + nout << "wrt of ab to ba is " << result << "\n"; + + wrt(ba, ab, result, NodeRelation::get_class_type()); + nout << "wrt of ba to ab is " << result << "\n"; + } + + { + nout << "\n"; + PrintNodes pn; + df_traverse(r, pn, + NodeAttributeWrapper(TestMTransition::get_class_type()), + NullLevelState(), + NodeRelation::get_class_type()); + nout << "\n"; + + NodeTransitionWrapper result(TestMTransition::get_class_type()); + + wrt(r, aa, result, NodeRelation::get_class_type()); + nout << "wrt of r to aa is " << result << "\n"; + + wrt(aa, r, result, NodeRelation::get_class_type()); + nout << "wrt of aa to r is " << result << "\n"; + + wrt(r, ab, result, NodeRelation::get_class_type()); + nout << "wrt of r to ab is " << result << "\n"; + + wrt(ab, r, result, NodeRelation::get_class_type()); + nout << "wrt of ab to r is " << result << "\n"; + + wrt(ab, aa, result, NodeRelation::get_class_type()); + nout << "wrt of ab to aa is " << result << "\n"; + + wrt(aa, ab, result, NodeRelation::get_class_type()); + nout << "wrt of aa to ab is " << result << "\n"; + + wrt(r, ba, result, NodeRelation::get_class_type()); + nout << "wrt of r to ba is " << result << "\n"; + + wrt(ba, r, result, NodeRelation::get_class_type()); + nout << "wrt of ba to r is " << result << "\n"; + + wrt(aa, ba, result, NodeRelation::get_class_type()); + nout << "wrt of aa to ba is " << result << "\n"; + + wrt(ba, aa, result, NodeRelation::get_class_type()); + nout << "wrt of ba to aa is " << result << "\n"; + + wrt(ab, ba, result, NodeRelation::get_class_type()); + nout << "wrt of ab to ba is " << result << "\n"; + + wrt(ba, ab, result, NodeRelation::get_class_type()); + nout << "wrt of ba to ab is " << result << "\n"; + } + + { + nout << "\n"; + PrintNodes pn; + df_traverse(r, pn, + AllAttributesWrapper(), + NullLevelState(), + NodeRelation::get_class_type()); + nout << "\n"; + + AllTransitionsWrapper result; + + wrt(r, aa, result, NodeRelation::get_class_type()); + nout << "wrt of r to aa is " << result << "\n"; + + wrt(aa, r, result, NodeRelation::get_class_type()); + nout << "wrt of aa to r is " << result << "\n"; + + wrt(r, ab, result, NodeRelation::get_class_type()); + nout << "wrt of r to ab is " << result << "\n"; + + wrt(ab, r, result, NodeRelation::get_class_type()); + nout << "wrt of ab to r is " << result << "\n"; + + wrt(ab, aa, result, NodeRelation::get_class_type()); + nout << "wrt of ab to aa is " << result << "\n"; + + wrt(aa, ab, result, NodeRelation::get_class_type()); + nout << "wrt of aa to ab is " << result << "\n"; + + wrt(r, ba, result, NodeRelation::get_class_type()); + nout << "wrt of r to ba is " << result << "\n"; + + wrt(ba, r, result, NodeRelation::get_class_type()); + nout << "wrt of ba to r is " << result << "\n"; + + wrt(aa, ba, result, NodeRelation::get_class_type()); + nout << "wrt of aa to ba is " << result << "\n"; + + wrt(ba, aa, result, NodeRelation::get_class_type()); + nout << "wrt of ba to aa is " << result << "\n"; + + wrt(ab, ba, result, NodeRelation::get_class_type()); + nout << "wrt of ab to ba is " << result << "\n"; + + wrt(ba, ab, result, NodeRelation::get_class_type()); + nout << "wrt of ba to ab is " << result << "\n"; + } +#endif + + AllTransitionsWrapper result; + + wrt(aa, r, result, NodeRelation::get_class_type()); + nout << "wrt of aa to r is " << result << "\n"; + a_aa->set_transition(new Test2Transition(4)); + + wrt(aa, r, result, NodeRelation::get_class_type()); + nout << "wrt of aa to r is now " << result << "\n"; + + return 0; +} diff --git a/panda/src/graph/test_graphRead.cxx b/panda/src/graph/test_graphRead.cxx new file mode 100644 index 0000000000..e61c3b3170 --- /dev/null +++ b/panda/src/graph/test_graphRead.cxx @@ -0,0 +1,49 @@ +// Filename: test_graphRead.cxx +// Created by: jason (15Jun00) + +#include "nodeRelation.h" +#include "namedNode.h" +#include "pt_NamedNode.h" +#include +#include + +#include + +int main() { + string test_file = "graphTest.out"; + datagram_file stream(test_file); + BamReader manager(&stream); + stream.open(file::FILE_READ); + + manager.init(); + + PT_NamedNode r = DCAST(NamedNode, manager.read_object()); + PT_NamedNode a = DCAST(NamedNode, manager.read_object()); + PT_NamedNode b = DCAST(NamedNode, manager.read_object()); + PT_NamedNode aa = DCAST(NamedNode, manager.read_object()); + PT_NamedNode ab = DCAST(NamedNode, manager.read_object()); + PT_NamedNode ba = DCAST(NamedNode, manager.read_object()); + + NodeRelation *r_a = DCAST(NodeRelation, manager.read_object()); + NodeRelation *r_b = DCAST(NodeRelation, manager.read_object()); + NodeRelation *a_aa = DCAST(NodeRelation, manager.read_object()); + NodeRelation *a_ab = DCAST(NodeRelation, manager.read_object()); + NodeRelation *b_ba = DCAST(NodeRelation, manager.read_object()); + + manager.resolve(); + + r_a->output(nout); + nout << endl; + r_b->output(nout); + nout << endl; + a_aa->output(nout); + nout << endl; + a_ab->output(nout); + nout << endl; + b_ba->output(nout); + nout << endl; + + stream.close(); + + return 0; +} diff --git a/panda/src/graph/test_graphWrite.cxx b/panda/src/graph/test_graphWrite.cxx new file mode 100644 index 0000000000..bf6c1f878f --- /dev/null +++ b/panda/src/graph/test_graphWrite.cxx @@ -0,0 +1,68 @@ +// Filename: test_graphWrite.cxx +// Created by: jason (15Jun00) + +#include "nodeRelation.h" +#include "namedNode.h" +#include "pt_NamedNode.h" +#include +#include + +#include + +int main() { + string test_file = "graphTest.out"; + datagram_file stream(test_file); + BamWriter manager(&stream); + stream.open(file::FILE_WRITE); + + PT_NamedNode r = new NamedNode("r"); + + PT_NamedNode a = new NamedNode("a"); + PT_NamedNode b = new NamedNode("b"); + + PT_NamedNode aa = new NamedNode("aa"); + PT_NamedNode ab = new NamedNode("ab"); + PT_NamedNode ba = new NamedNode("ba"); + + NodeRelation *r_a = + new NodeRelation(r, a, 0, NodeRelation::get_class_type()); + NodeRelation *r_b = + new NodeRelation(r, b, 0, NodeRelation::get_class_type()); + + NodeRelation *a_aa = + new NodeRelation(a, aa, 0, NodeRelation::get_class_type()); + NodeRelation *a_ab = + new NodeRelation(a, ab, 0, NodeRelation::get_class_type()); + NodeRelation *b_ba = + new NodeRelation(b, ba, 0, NodeRelation::get_class_type()); + + r_a->output(nout); + nout << endl; + r_b->output(nout); + nout << endl; + a_aa->output(nout); + nout << endl; + a_ab->output(nout); + nout << endl; + b_ba->output(nout); + nout << endl; + + if (manager.init()) + { + manager.write_object(r); + manager.write_object(a); + manager.write_object(b); + manager.write_object(aa); + manager.write_object(ab); + manager.write_object(ba); + + manager.write_object(r_a); + manager.write_object(r_b); + manager.write_object(a_aa); + manager.write_object(a_ab); + manager.write_object(b_ba); + } + + stream.close(); + return 0; +} diff --git a/panda/src/graph/transitionDirection.h b/panda/src/graph/transitionDirection.h new file mode 100644 index 0000000000..0c44c55330 --- /dev/null +++ b/panda/src/graph/transitionDirection.h @@ -0,0 +1,16 @@ +// Filename: transitionDirection.h +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TRANSITIONDIRECTION_H +#define TRANSITIONDIRECTION_H + +enum TransitionDirection { + TD_identity, + TD_on, + TD_off +}; + +#endif + diff --git a/panda/src/graph/traverserVisitor.I b/panda/src/graph/traverserVisitor.I new file mode 100644 index 0000000000..e8fd9a6a16 --- /dev/null +++ b/panda/src/graph/traverserVisitor.I @@ -0,0 +1,57 @@ +// Filename: traverserVisitor.I +// Created by: drose (20Mar00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: TraverserVisitor::reached_node +// Access: Public +// Description: Called for each node visited. It may return true if +// the traversal is to be continued, or false if it +// should be pruned at this node. +//////////////////////////////////////////////////////////////////// +template +INLINE bool TraverserVisitor:: +reached_node(Node *, + TraverserVisitor::AttributeWrapper &, + LevelState &) { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: TraverserVisitor::reached_node +// Access: Public +// Description: Called each time an arc is traversed in the forward +// direction. As above, it may return true if the +// traversal should continue, or false if it should be +// pruned and not reach the destination node. +//////////////////////////////////////////////////////////////////// +template +INLINE bool TraverserVisitor:: +forward_arc(NodeRelation *, + TraverserVisitor::TransitionWrapper &, + TraverserVisitor::AttributeWrapper &, + TraverserVisitor::AttributeWrapper &, + LevelState &) { + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: TraverserVisitor::reached_node +// Access: Public +// Description: Called each time an arc is traversed in the backward +// direction. It is guaranteed to be paired stack-wise +// with the corresponding call to forward_arc, so that +// each call to backward_arc matches the previous +// unmatched call to forward_arc. +//////////////////////////////////////////////////////////////////// +template +INLINE void TraverserVisitor:: +backward_arc(NodeRelation *, + TraverserVisitor::TransitionWrapper &, + TraverserVisitor::AttributeWrapper &, + TraverserVisitor::AttributeWrapper &, + const LevelState &) { +} diff --git a/panda/src/graph/traverserVisitor.h b/panda/src/graph/traverserVisitor.h new file mode 100644 index 0000000000..f1688e6878 --- /dev/null +++ b/panda/src/graph/traverserVisitor.h @@ -0,0 +1,41 @@ +// Filename: traverserVisitor.h +// Created by: drose (26Oct98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TRAVERSERVISITOR_H +#define TRAVERSERVISITOR_H + +#include + +class Node; +class NodeRelation; + +template +class EXPCL_PANDA TraverserVisitor { +public: + typedef TW TransitionWrapper; + typedef TYPENAME TransitionWrapper::AttributeWrapper AttributeWrapper; + + INLINE bool reached_node(Node *node, + AttributeWrapper &render_state, + LevelState &level_state); + + // Some traversers (notably DFTraverser) will also call the + // following two functions to mark the crossing of arcs. This will + // allow the Visitor to maintain its own internal state as needed. + + INLINE bool forward_arc(NodeRelation *arc, TransitionWrapper &trans, + AttributeWrapper &pre, AttributeWrapper &post, + LevelState &level_state); + INLINE void backward_arc(NodeRelation *arc, TransitionWrapper &trans, + AttributeWrapper &pre, AttributeWrapper &post, + const LevelState &level_state); +}; + +#include "traverserVisitor.I" + +#endif + + + diff --git a/panda/src/graph/vector_PT_Node.cxx b/panda/src/graph/vector_PT_Node.cxx new file mode 100644 index 0000000000..23dcb4722c --- /dev/null +++ b/panda/src/graph/vector_PT_Node.cxx @@ -0,0 +1,11 @@ +// Filename: vector_PT_Node.cxx +// Created by: drose (16May00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_PT_Node.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/graph/vector_PT_Node.h b/panda/src/graph/vector_PT_Node.h new file mode 100644 index 0000000000..9a3846003c --- /dev/null +++ b/panda/src/graph/vector_PT_Node.h @@ -0,0 +1,33 @@ +// Filename: vector_PT_Node.h +// Created by: drose (16May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_PT_NODE_H +#define VECTOR_PT_NODE_H + +#include + +#include "node.h" +#include "pt_Node.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_PT_Node +// Description : A vector of PT(Node)'s. This class is defined once +// here, and exported to PANDA.DLL; other packages that +// want to use a vector of this type (whether they need +// to export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, std::vector) +typedef vector vector_PT_Node; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/graph/wrt.I b/panda/src/graph/wrt.I new file mode 100644 index 0000000000..32b304b181 --- /dev/null +++ b/panda/src/graph/wrt.I @@ -0,0 +1,532 @@ +// Filename: wrt.cxx +// Created by: drose (26Oct98) +// +//////////////////////////////////////////////////////////////////// + +#include "wrt.h" +#include "nodeRelation.h" +#include "node.h" +#include "config_graph.h" + +//////////////////////////////////////////////////////////////////// +// Function: get_cached_net_transition +// Description: Returns the net transition from the root of the +// graph, or the nearest ancestor with multiple parents, +// to and including the indicated arc. This is a +// support function for wrt() and should not be called +// directly. +// +// This flavor of get_cached_net_transition() gets the +// transition up to an arc, instead of a node, and does +// not necessarily start at the root of the scene graph. +// By starting at the nearest ancestor with multiple +// parents, we allow ambiguous paths in the scene graph +// without interfering with state caching. Since there +// are no nodes with multiple parents in the chain of +// transitions returned by this function, the result is +// always unambiguous; any wrt() ambiguity is resolved +// later. +//////////////////////////////////////////////////////////////////// +template +void +get_cached_net_transition(NodeRelation *arc, Node *&root, UpdateSeq now, + TransitionWrapper &net, TypeHandle graph_type) { + TransitionWrapper cur_cache = TransitionWrapper::init_from(net); + Node *top_subtree = cur_cache.extract_from_cache(arc); + + if (cur_cache.is_cache_verified(now)) { + // This arc's cached value has recently been verified, so we don't + // even need to go anywhere--we trust it's still good. + root = top_subtree; + net = cur_cache; + + } else { + // This arc's cache hasn't recently been verified, and we need to + // verify it now. This will entail at least walking up the scene + // graph to the root, and possibly recomputing the cache as we go. + + // Get the node above the arc. + Node *node = arc->get_parent(); + + UpRelations::const_iterator uri; + uri = node->_parents.find(graph_type); + + if (uri == node->_parents.end() || (*uri).second.size() != 1) { + // The node has zero or multiple parents. It's thus either the + // top of the scene graph, or it's the first ancestor with + // multiple parents; in either case, it's as far as we can take + // the caching. Stop here. + + if (uri == node->_parents.end() || (*uri).second.empty()) { + // The node is the very top of the scene graph. We'll treat + // this as a special case by setting the node to NULL. This + // will tell our calling function that there's no need to look + // further. + node = NULL; + } + + root = node; + net.extract_from(arc); + net.set_computed_verified(now); + net.store_to_cache(arc, node); + + } else { + // The node has exactly one parent. Carry on. + + NodeRelation *parent = *(*uri).second.begin(); + get_cached_net_transition(parent, root, now, net, graph_type); + + // Now recompute our own cache. + TransitionWrapper cur_value = TransitionWrapper::init_from(net); + cur_value.extract_from(arc); + + net.cached_compose(cur_cache, cur_value, now); + net.store_to_cache(arc, root); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: get_cached_net_transition +// Description: Returns the net transition from the root of the graph +// to the indicated node. This is a support function +// for wrt() and should not be called directly. +// +// If [arc_list_begin..arc_list_end) is nonempty, it +// contains a list of NodeRelation pointers to resolve +// ambiguities when a node with multiple parents is +// encountered. +//////////////////////////////////////////////////////////////////// +template +void +get_cached_net_transition(const Node *node, + InputIterator arc_list_begin, + InputIterator arc_list_end, + UpdateSeq now, + TransitionWrapper &result, + TypeHandle graph_type) { + if (node == NULL) { + // the NULL node is by convention equivalent to the root. + result.make_identity(); + return; + } + + UpRelations::const_iterator uri; + uri = node->_parents.find(graph_type); + + if (uri == node->_parents.end()) { + // This node has no parents. Stop here. + result.make_identity(); + return; + } + + const UpRelationPointers &urp = (*uri).second; + if (urp.empty()) { + // Again, this node has no parents. + result.make_identity(); + return; + } + + const NodeRelation *parent_arc = (const NodeRelation *)NULL; + + if (urp.size() == 1) { + // There is only one parent, so there's no doubt as to which arc + // to choose. + parent_arc = *(urp.begin()); + + } else { + // The node has multiple parents, and we have a list of arcs in + // arc_list_begin .. arc_list_end. If any of the parents is + // mentioned in the list, we must choose that parent. + for (InputIterator ri = arc_list_begin; + parent_arc == (NodeRelation *)NULL && ri != arc_list_end; + ++ri) { + if ((*ri)->get_child() == node) { + parent_arc = (*ri); + } + } + + if (parent_arc == (NodeRelation *)NULL) { + // No, it wasn't mentioned. Issue a warning and use the first + // one. + graph_cat.warning() + << *node << " has multiple parents; wrt() ambiguous.\n"; + parent_arc = *(urp.begin()); + } + } + + // Get the net transition leading into this node. + Node *root; + get_cached_net_transition((NodeRelation *)parent_arc, root, now, result, + graph_type); + + if (root != NULL) { + // There's more to get. The above function call was forced to + // stop at a node with multiple parents. + TransitionWrapper more = TransitionWrapper::init_from(result); + get_cached_net_transition(root, arc_list_begin, arc_list_end, + now, more, graph_type); + more.compose_in_place(result); + result = more; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: get_uncached_net_transition +// Description: Returns the net transition from the root of the graph +// to the indicated node. This is a support function +// for wrt() and should not be called directly. +// +// If [arc_list_begin..arc_list_end) is nonempty, it +// contains a list of NodeRelation pointers to resolve +// ambiguities when a node with multiple parents is +// encountered. +// +// This variant of get_cached_net_transition() does not +// respect or update the cache values stored in the +// arcs. It's intended only as a debugging doublecheck +// against the normal get_cached_net_transition() +// function that does do this. +//////////////////////////////////////////////////////////////////// +template +void +get_uncached_net_transition(const Node *node, + InputIterator arc_list_begin, + InputIterator arc_list_end, + TransitionWrapper &result, + TypeHandle graph_type) { + if (node == NULL) { + // the NULL node is by convention equivalent to the root. + result.make_identity(); + return; + } + + UpRelations::const_iterator uri; + uri = node->_parents.find(graph_type); + + if (uri == node->_parents.end()) { + // This node has no parents. Stop here. + result.make_identity(); + return; + } + + const UpRelationPointers &urp = (*uri).second; + if (urp.empty()) { + // Again, this node has no parents. + result.make_identity(); + return; + } + + const NodeRelation *parent_arc = (const NodeRelation *)NULL; + + if (urp.size() == 1) { + // There is only one parent, so there's no doubt as to which arc + // to choose. + parent_arc = *(urp.begin()); + + } else { + // The node has multiple parents, and we have a list of arcs in + // arc_list_begin .. arc_list_end. If any of the parents is + // mentioned in the list, we must choose that parent. + for (InputIterator ri = arc_list_begin; + parent_arc == (NodeRelation *)NULL && ri != arc_list_end; + ++ri) { + if ((*ri)->get_child() == node) { + parent_arc = (*ri); + } + } + + if (parent_arc == (NodeRelation *)NULL) { + // No, it wasn't mentioned. Issue a warning and use the first + // one. + graph_cat.warning() + << *node << " has multiple parents; wrt() ambiguous.\n"; + parent_arc = *(urp.begin()); + } + } + + get_uncached_net_transition(parent_arc->get_parent(), + arc_list_begin, arc_list_end, + result, + graph_type); + + TransitionWrapper next = TransitionWrapper::init_from(result); + next.extract_from(parent_arc); + + result.compose_in_place(next); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: cached_wrt_base +// Description: The implementation of wrt_base, below, but always +// uses the cached value. This is the normal behavior. +//////////////////////////////////////////////////////////////////// +template +void +cached_wrt_base(const Node *from, + InputIterator1 from_arcs_begin, InputIterator1 from_arcs_end, + const Node *to, + InputIterator2 to_arcs_begin, InputIterator2 to_arcs_end, + TransitionWrapper &result, + TypeHandle graph_type) { + + UpdateSeq now = last_graph_update[graph_type]; + + TransitionWrapper net_from_trans = TransitionWrapper::init_from(result); + + get_cached_net_transition(from, from_arcs_begin, from_arcs_end, now, + net_from_trans, graph_type); + get_cached_net_transition(to, to_arcs_begin, to_arcs_end, now, + result, graph_type); + +#ifndef NDEBUG + if (paranoid_wrt) { + // We don't entirely trust the caching to work flawlessly. Go + // ahead and compute the uncached transition just to doublecheck. + TransitionWrapper check_from_trans = + TransitionWrapper::init_from(result); + TransitionWrapper check_to_trans = + TransitionWrapper::init_from(result); + get_uncached_net_transition(from, from_arcs_begin, from_arcs_end, + check_from_trans, graph_type); + if (check_from_trans.compare_to(net_from_trans) != 0) { + graph_cat.warning() + << "WRT cache from " << *from << " is invalid!\n" + << " cached value is " << net_from_trans << "\n" + << " should be " << check_from_trans << "\n"; + net_from_trans = check_from_trans; + } + + get_uncached_net_transition(to, to_arcs_begin, to_arcs_end, + check_to_trans, graph_type); + if (check_to_trans.compare_to(result) != 0) { + graph_cat.warning() + << "WRT cache to " << *to << " is invalid!\n" + << " cached value is " << result << "\n" + << " should be " << check_to_trans << "\n"; + result = check_to_trans; + } + } +#endif + + result.invert_compose_in_place(net_from_trans); +} + +//////////////////////////////////////////////////////////////////// +// Function: uncached_wrt_base +// Description: The implementation of wrt_base, below, but never +// cached. +//////////////////////////////////////////////////////////////////// +template +void +uncached_wrt_base(const Node *from, + InputIterator1 from_arcs_begin, InputIterator1 from_arcs_end, + const Node *to, + InputIterator2 to_arcs_begin, InputIterator2 to_arcs_end, + TransitionWrapper &result, + TypeHandle graph_type) { + + UpdateSeq now = last_graph_update[graph_type]; + + TransitionWrapper net_from_trans = TransitionWrapper::init_from(result); + get_uncached_net_transition(from, from_arcs_begin, from_arcs_end, + net_from_trans, graph_type); + get_uncached_net_transition(to, to_arcs_begin, to_arcs_end, + result, graph_type); + result.invert_compose_in_place(net_from_trans); +} + +//////////////////////////////////////////////////////////////////// +// Function: wrt_base +// Description: Returns the net transition from the node "to" +// to the node "from". +// +// If [from_arcs_begin..from_arcs_end) is nonempty, it +// contains a list of NodeRelation pointers to resolve +// ambiguities when a node with multiple parents is +// encountered as an ancestor of the from node. If any +// of the parents is listed, that parent will be chosen. +// +// Similarly with [to_arcs_begin..to_arcs_end). +// +// wrt_base() is a low-level implementation; use the +// various wrt() wrappers instead. +//////////////////////////////////////////////////////////////////// +template +INLINE void +wrt_base(const Node *from, + InputIterator1 from_arcs_begin, InputIterator1 from_arcs_end, + const Node *to, + InputIterator2 to_arcs_begin, InputIterator2 to_arcs_end, + TransitionWrapper &result, + TypeHandle graph_type) { + if (cache_wrt) { + cached_wrt_base(from, from_arcs_begin, from_arcs_end, + to, to_arcs_begin, to_arcs_end, + result, graph_type); + } else { + uncached_wrt_base(from, from_arcs_begin, from_arcs_end, + to, to_arcs_begin, to_arcs_end, + result, graph_type); + } +} + +template +INLINE void +wrt(const Node *from, const Node *to, + TransitionWrapper &result, TypeHandle graph_type) { + // In the normal wrt() interface, we have no from_arcs or to_arcs + // list. Supply empty lists for both. + const NodeRelation *arc = NULL; + wrt_base(from, &arc, &arc, to, &arc, &arc, result, graph_type); +} + +template +INLINE void +wrt(const Node *from, + InputIterator from_arcs_begin, InputIterator from_arcs_end, + const Node *to, + TransitionWrapper &result, TypeHandle graph_type) { + const NodeRelation *arc = NULL; + wrt_base(from, from_arcs_begin, from_arcs_end, to, &arc, &arc, + result, graph_type); +} + +template +INLINE void +wrt(const Node *from, + const Node *to, + InputIterator to_arcs_begin, InputIterator to_arcs_end, + TransitionWrapper &result, TypeHandle graph_type) { + const NodeRelation *arc = NULL; + wrt_base(from, &arc, &arc, to, to_arcs_begin, to_arcs_end, + result, graph_type); +} + +template +INLINE void +wrt(const Node *from, + InputIterator1 from_arcs_begin, InputIterator1 from_arcs_end, + const Node *to, + InputIterator2 to_arcs_begin, InputIterator2 to_arcs_end, + TransitionWrapper &result, TypeHandle graph_type) { + wrt_base(from, from_arcs_begin, from_arcs_end, + to, to_arcs_begin, to_arcs_end, + result, graph_type); +} + +template +INLINE void +uncached_wrt(const Node *from, const Node *to, + TransitionWrapper &result, TypeHandle graph_type) { + const NodeRelation *arc = NULL; + uncached_wrt_base(from, &arc, &arc, to, &arc, &arc, result, graph_type); +} + +template +INLINE void +uncached_wrt(const Node *from, + InputIterator from_arcs_begin, InputIterator from_arcs_end, + const Node *to, + TransitionWrapper &result, TypeHandle graph_type) { + const NodeRelation *arc = NULL; + uncached_wrt_base(from, from_arcs_begin, from_arcs_end, to, &arc, &arc, + result, graph_type); +} + +template +INLINE void +uncached_wrt(const Node *from, + const Node *to, + InputIterator to_arcs_begin, InputIterator to_arcs_end, + TransitionWrapper &result, TypeHandle graph_type) { + const NodeRelation *arc = NULL; + uncached_wrt_base(from, &arc, &arc, to, to_arcs_begin, to_arcs_end, + result, graph_type); +} + +template +INLINE void +uncached_wrt(const Node *from, + InputIterator1 from_arcs_begin, InputIterator1 from_arcs_end, + const Node *to, + InputIterator2 to_arcs_begin, InputIterator2 to_arcs_end, + TransitionWrapper &result, TypeHandle graph_type) { + uncached_wrt_base(from, from_arcs_begin, from_arcs_end, + to, to_arcs_begin, to_arcs_end, + result, graph_type); +} + +template +Node * +wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result, + TypeHandle graph_type) { + if (!cache_wrt) { + // If we aren't caching wrt, do this the hard way. + uncached_wrt(arc->get_child(), &arc, &arc + 1, to, result, graph_type); + return to; + } + + UpdateSeq now = last_graph_update[graph_type]; + + // First, determine the net transition up to the top of the current + // subtree. + Node *top_subtree; + get_cached_net_transition(arc, top_subtree, now, result, graph_type); + + if (top_subtree == to || to == (Node *)NULL) { + // If the top of the subtree is the node we asked to wrt to, + // excellent! Stop here. + + } else { + + // Otherwise, it must be the case that the node we want to wrt to is + // some descendent of the top_subtree node. It also therefore + // follows that this node has exactly one parent. + + nassertr(to->get_num_parents(graph_type) == 1, NULL); + NodeRelation *to_arc = to->get_parent(graph_type, 0); + + // Save the result from the first pass. + TransitionWrapper net_from_trans = result; + + // Now determine the net transition to the top of the subtree from + // this arc. It had better be the same subtree! + Node *top_subtree_2; + get_cached_net_transition(to_arc, top_subtree_2, now, result, graph_type); + nassertr(top_subtree == top_subtree_2, NULL); + + // And now compute the actual wrt. + result.invert_compose_in_place(net_from_trans); + } + +#ifndef NDEBUG + if (paranoid_wrt) { + // Now check the results. + TransitionWrapper check_trans = + TransitionWrapper::init_from(result); + uncached_wrt(arc->get_child(), &arc, &arc + 1, to, check_trans, + graph_type); + + if (check_trans.compare_to(result) != 0) { + graph_cat.warning() + << "WRT subtree cache from " << *arc->get_child() << " to "; + if (to == (Node *)NULL) { + graph_cat.warning(false) << "(top)"; + } else { + graph_cat.warning(false) << *to; + } + graph_cat.warning(false) + << " is invalid!\n" + << " cached value is " << result << "\n" + << " should be " << check_trans << "\n"; + result = check_trans; + } + } +#endif + + return top_subtree; +} diff --git a/panda/src/graph/wrt.h b/panda/src/graph/wrt.h new file mode 100644 index 0000000000..71fee63484 --- /dev/null +++ b/panda/src/graph/wrt.h @@ -0,0 +1,123 @@ +// Filename: wrt.h +// Created by: drose (26Oct98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef WRT_H +#define WRT_H + +#include + +#include + +class Node; + +// The normal wrt() call, with-respect-to, computes the transition +// necessary to convert from the state at "to" to the state at "from". +// (If this seems backwards, think of it as returning the state at +// "from" with respect to the node "to", or in other words, the node +// "from" as seen from "to".) + +// It's most useful for TransformTransitions, but it is meaningful to +// use wrt() with any kind of NodeTransition at all. + +// The various additional flavors of wrt() are provided to resolve +// ambiguities in the case of a node having multiple parents in the +// scene graph. If a given node in the ancestry of node "from" has +// multiple parents, and one of those parents is in the set +// from_arcs_begin..from_arcs_end, that arc is chosen. Otherwise, a +// warning is issued and an arc is chosen arbitrarily. Similarly for +// multiple parenthood in the ancestry of node "to" and +// to_arcs_begin..to_arcs_end. + +template +INLINE void +wrt(const Node *from, const Node *to, + TransitionWrapper &result, TypeHandle graph_type); + +template +INLINE void +wrt(const Node *from, + InputIterator from_arcs_begin, InputIterator from_arcs_end, + const Node *to, + TransitionWrapper &result, TypeHandle graph_type); + +template +INLINE void +wrt(const Node *from, + const Node *to, + InputIterator to_arcs_begin, InputIterator to_arcs_end, + TransitionWrapper &result, TypeHandle graph_type); + +template +INLINE void +wrt(const Node *from, + InputIterator1 from_arcs_begin, InputIterator1 from_arcs_end, + const Node *to, + InputIterator2 to_arcs_begin, InputIterator2 to_arcs_end, + TransitionWrapper &result, TypeHandle graph_type); + +// Similar to the above, but always uncached. Useful mainly for +// debugging, or when you suspect the cache is invalid. Also note +// that you can configure 'cache-wrt' or 'paranoid-wrt' to disable or +// force verification of the cache implicitly. +template +INLINE void +uncached_wrt(const Node *from, const Node *to, + TransitionWrapper &result, TypeHandle graph_type); + +template +INLINE void +uncached_wrt(const Node *from, + InputIterator from_arcs_begin, InputIterator from_arcs_end, + const Node *to, + TransitionWrapper &result, TypeHandle graph_type); + +template +INLINE void +uncached_wrt(const Node *from, + const Node *to, + InputIterator to_arcs_begin, InputIterator to_arcs_end, + TransitionWrapper &result, TypeHandle graph_type); + +template +INLINE void +uncached_wrt(const Node *from, + InputIterator1 from_arcs_begin, InputIterator1 from_arcs_end, + const Node *to, + InputIterator2 to_arcs_begin, InputIterator2 to_arcs_end, + TransitionWrapper &result, TypeHandle graph_type); + + +// The following function is a bit different. Rather than computing +// the relative transform between two nodes, it computes the net +// transform along the shortest unambigous path from the indicated arc +// towards the root. That is, this is the wrt between the child of +// the indicated arc and the closest ancestor node that has multiple +// parents, or the root of the scene graph if the arc only appears +// once in the scene graph. The return value is the particular node +// with multiple parents at which the wrt stopped, or NULL if it went +// all the way to the root. + +// This is extended just a bit further by allowing the user to specify +// a "to" node. This must be either NULL, or the expected top node, +// or some node in between. If it is a node in between, it indicates +// the point at which the relative computation should stop. In +// effect, this makes wrt_subtree(arc, to) equivalent to +// wrt(arc->get_child(), to), except that it must be true that "to" is +// a linear ancestor of arc. + +// This concept is important within the computation of wrt itself to +// manage cached state between multiple instances, but is of limited +// general utility; it is of primary interest to code (like the +// CullTraverser) that needs to cache a wrt-type value for many nodes +// across the entire tree. +template +Node * +wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result, + TypeHandle graph_type); + + +#include "wrt.I" + +#endif diff --git a/panda/src/grutil/Sources.pp b/panda/src/grutil/Sources.pp new file mode 100644 index 0000000000..cb30e37213 --- /dev/null +++ b/panda/src/grutil/Sources.pp @@ -0,0 +1,18 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET grutil + #define LOCAL_LIBS \ + sgraph gobj linmath putil + + #define SOURCES \ + config_grutil.cxx config_grutil.h lineSegs.I lineSegs.cxx \ + lineSegs.h + + #define INSTALL_HEADERS \ + lineSegs.I lineSegs.h + + #define IGATESCAN all + +#end lib_target + diff --git a/panda/src/grutil/config_grutil.cxx b/panda/src/grutil/config_grutil.cxx new file mode 100644 index 0000000000..668e531065 --- /dev/null +++ b/panda/src/grutil/config_grutil.cxx @@ -0,0 +1,15 @@ +// Filename: config_grutil.cxx +// Created by: drose (24May00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_grutil.h" + +#include + +Configure(config_grutil); +NotifyCategoryDef(grutil, ""); + +ConfigureFn(config_grutil) { +} + diff --git a/panda/src/grutil/config_grutil.h b/panda/src/grutil/config_grutil.h new file mode 100644 index 0000000000..0b40925acf --- /dev/null +++ b/panda/src/grutil/config_grutil.h @@ -0,0 +1,16 @@ +// Filename: config_grutil.h +// Created by: drose (24May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_GRUTIL_H +#define CONFIG_GRUTIL_H + +#include +#include + +NotifyCategoryDecl(grutil, EXPCL_PANDA, EXPTP_PANDA); + +#endif + + diff --git a/panda/src/grutil/lineSegs.I b/panda/src/grutil/lineSegs.I new file mode 100644 index 0000000000..bc787e39e8 --- /dev/null +++ b/panda/src/grutil/lineSegs.I @@ -0,0 +1,225 @@ +// Filename: lineSegs.I +// Created by: drose (24May00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::Point::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LineSegs::Point:: +Point() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::Point::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LineSegs::Point:: +Point(const Vertexf &point, const Colorf &color) : + _point(point), + _color(color) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::Point::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LineSegs::Point:: +Point(const LineSegs::Point ©) : + _point(copy._point), + _color(copy._color) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::Point::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void LineSegs::Point:: +operator = (const LineSegs::Point ©) { + _point = copy._point; + _color = copy._color; +} + + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::set_color +// Access: Public +// Description: Establishes the color that will be assigned to all +// vertices created by future calls to move_to() and +// draw_to(). +//////////////////////////////////////////////////////////////////// +INLINE void LineSegs:: +set_color(float r, float g, float b, float a) { + _color.set(r, g, b, a); +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::set_color +// Access: Public +// Description: Establishes the color that will be assigned to all +// vertices created by future calls to move_to() and +// draw_to(). +//////////////////////////////////////////////////////////////////// +INLINE void LineSegs:: +set_color(const Colorf &color) { + _color = color; +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::set_thickness +// Access: Public +// Description: Establishes the line thickness or point size in +// pixels that will be assigned to all lines and points +// created by future calls to create(). +//////////////////////////////////////////////////////////////////// +INLINE void LineSegs:: +set_thickness(float thick) { + _thick = thick; +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::move_to +// Access: Public +// Description: Moves the pen to the given point without drawing a +// line. When followed by draw_to(), this marks the +// first point of a line segment; when followed by +// move_to() or create(), this creates a single point. +//////////////////////////////////////////////////////////////////// +INLINE void LineSegs:: +move_to(float x, float y, float z) { + move_to(Vertexf(x, y, z)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::draw_to +// Access: Public +// Description: Draws a line segment from the pen's last position +// (the last call to move_to or draw_to) to the +// indicated point. move_to() and draw_to() only update +// tables; the actual drawing is performed when create() +// is called. +//////////////////////////////////////////////////////////////////// +INLINE void LineSegs:: +draw_to(float x, float y, float z) { + draw_to(Vertexf(x, y, z)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::create +// Access: Public +// Description: Creates a new GeomNode that will render the series of +// line segments and points described via calls to +// move_to() and draw_to(). The lines and points are +// created with the color and thickness established by +// calls to set_color() and set_thick(). +// +// If frame_accurate is true, the line segments will be +// created as a frame-accurate index, so that later +// calls to set_vertex or set_vertex_color will be +// visually correct. +//////////////////////////////////////////////////////////////////// +INLINE GeomNode *LineSegs:: +create(bool frame_accurate) { + GeomNode *gnode = new GeomNode; + return create(gnode, frame_accurate); +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::get_num_vertices +// Access: Public +// Description: Returns the total number of line segment and point +// vertices generated by the last call to create(). The +// positions of these vertices may be read and adjusted +// through get_vertex() and set_vertex(). +//////////////////////////////////////////////////////////////////// +INLINE int LineSegs:: +get_num_vertices() const { + return _created_verts.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::get_vertex +// Access: Public +// Description: Returns the nth point or vertex of the line segment +// sequence generated by the last call to create(). The +// first move_to() generates vertex 0; subsequent +// move_to() and draw_to() calls generate consecutively +// higher vertex numbers. +//////////////////////////////////////////////////////////////////// +INLINE Vertexf LineSegs:: +get_vertex(int vertex) const { + nassertr(vertex >= 0 && vertex < _created_verts.size(), + Vertexf(0.0, 0.0, 0.0)); + return _created_verts[vertex]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::set_vertex +// Access: Public +// Description: Moves the nth point or vertex of the line segment +// sequence generated by the last call to create(). The +// first move_to() generates vertex 0; subsequent +// move_to() and draw_to() calls generate consecutively +// higher vertex numbers. +//////////////////////////////////////////////////////////////////// +INLINE void LineSegs:: +set_vertex(int vertex, const Vertexf &vert) { + nassertv(vertex >= 0 && vertex < _created_verts.size()); + _created_verts[vertex] = vert; +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::set_vertex +// Access: Public +// Description: Moves the nth point or vertex of the line segment +// sequence generated by the last call to create(). The +// first move_to() generates vertex 0; subsequent +// move_to() and draw_to() calls generate consecutively +// higher vertex numbers. +//////////////////////////////////////////////////////////////////// +INLINE void LineSegs:: +set_vertex(int vertex, float x, float y, float z) { + set_vertex(vertex, Vertexf(x, y, z)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::get_vertex_color +// Access: Public +// Description: Returns the color of the nth point or vertex/ +//////////////////////////////////////////////////////////////////// +INLINE Colorf LineSegs:: +get_vertex_color(int vertex) const { + nassertr(vertex >= 0 && vertex < _created_colors.size(), + Colorf(0.0, 0.0, 0.0, 0.0)); + return _created_colors[vertex]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::set_vertex_color +// Access: Public +// Description: Changes the vertex color of the nth point or vertex. +// See set_vertex(). +//////////////////////////////////////////////////////////////////// +INLINE void LineSegs:: +set_vertex_color(int vertex, const Colorf &color) { + nassertv(vertex >= 0 && vertex < _created_verts.size()); + _created_colors[vertex] = color; +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::set_vertex_color +// Access: Public +// Description: Changes the vertex color of the nth point or vertex. +// See set_vertex(). +//////////////////////////////////////////////////////////////////// +INLINE void LineSegs:: +set_vertex_color(int vertex, float r, float g, float b, float a) { + set_vertex_color(vertex, Colorf(r, g, b, a)); +} diff --git a/panda/src/grutil/lineSegs.cxx b/panda/src/grutil/lineSegs.cxx new file mode 100644 index 0000000000..4d0c04389c --- /dev/null +++ b/panda/src/grutil/lineSegs.cxx @@ -0,0 +1,215 @@ +// Filename: lineSegs.cxx +// Created by: (24May00) +// +//////////////////////////////////////////////////////////////////// + +#include "lineSegs.h" + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::Constructor +// Access: Public +// Description: Constructs a LineSegs object, which can be used to +// create any number of disconnected lines or points of +// various thicknesses and colors through the visible +// scene. After creating the object, call move_to() and +// draw_to() repeatedly to describe the path, then call +// create() to create a GeomNode which will render the +// described path. +//////////////////////////////////////////////////////////////////// +LineSegs:: +LineSegs() { + _color.set(1.0, 1.0, 1.0, 1.0); + _thick = 1.0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::Destructor +// Access: Public +//////////////////////////////////////////////////////////////////// +LineSegs:: +~LineSegs() { +} + + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::reset +// Access: Public +// Description: Removes any lines in progress and resets to the +// initial empty state. +//////////////////////////////////////////////////////////////////// +void LineSegs:: +reset() { + _list.clear(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::move_to +// Access: Public +// Description: Moves the pen to the given point without drawing a +// line. When followed by draw_to(), this marks the +// first point of a line segment; when followed by +// move_to() or create(), this creates a single point. +//////////////////////////////////////////////////////////////////// +void LineSegs:: +move_to(const Vertexf &v) { + // We create a new SegmentList with the initial point in it. + SegmentList segs; + segs.push_back(Point(v, _color)); + + // And add this list to the list of segments. + _list.push_back(segs); +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::draw_to +// Access: Public +// Description: Draws a line segment from the pen's last position +// (the last call to move_to or draw_to) to the +// indicated point. move_to() and draw_to() only update +// tables; the actual drawing is performed when create() +// is called. +//////////////////////////////////////////////////////////////////// +void LineSegs:: +draw_to(const Vertexf &v) { + if (_list.empty()) { + // Let our first call to draw_to() be an implicit move_to(). + move_to(v); + + } else { + // Get the current SegmentList, which was the last one we added to + // the LineList. + SegmentList &segs = _list.back(); + + // Add the new point. + segs.push_back(Point(v, _color)); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::empty +// Access: Public +// Description: Returns true if move_to() or draw_to() have not been +// called since the last reset() or create(), false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool LineSegs:: +is_empty() { + return _list.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::get_current_position +// Access: Public +// Description: Returns the pen's current position. The next call to +// draw_to() will draw a line segment from this point. +//////////////////////////////////////////////////////////////////// +const Vertexf &LineSegs:: +get_current_position() { + if (_list.empty()) { + // Our pen isn't anywhere. We'll put it somewhere. + move_to(Vertexf(0.0, 0.0, 0.0)); + } + + return _list.back().back()._point; +} + +//////////////////////////////////////////////////////////////////// +// Function: LineSegs::create +// Access: Public +// Description: Appends to an existing GeomNode a new pfGeoSet that +// will render the series of line segments and points +// described via calls to move_to() and draw_to(). The +// lines and points are created with the color and +// thickness established by calls to set_color() and +// set_thick(). +// +// If frame_accurate is true, the line segments will be +// created as a frame-accurate index, so that later +// calls to set_vertex or set_vertex_color will be +// visually correct. +//////////////////////////////////////////////////////////////////// +GeomNode *LineSegs:: +create(GeomNode *previous, bool) { + if (!_list.empty()) { + _created_verts.clear(); + _created_colors.clear(); + + // One array each for the indices into these arrays for points + // and lines, and one for our line-segment lengths array. + PTA_ushort point_index; + PTA_ushort line_index; + PTA_int lengths; + + // Now fill up the arrays. + int v = 0; + LineList::const_iterator ll; + SegmentList::const_iterator sl; + + for (ll = _list.begin(); ll != _list.end(); ll++) { + const SegmentList &segs = (*ll); + + if (segs.size() < 2) { + point_index.push_back(v); + } else { + lengths.push_back(segs.size()); + } + + for (sl = segs.begin(); sl != segs.end(); sl++) { + if (segs.size() >= 2) { + line_index.push_back(v); + } + _created_verts.push_back((*sl)._point); + _created_colors.push_back((*sl)._color); + v++; + nassertr(v == _created_verts.size(), previous); + } + } + + // Now create the lines. + if (line_index.size() > 0) { + // Create a new Geom and add the line segments. + Geom *geom; + if (line_index.size() <= 2) { + // Here's a special case: just one line segment. + GeomLine *geom_line = new GeomLine; + geom_line->set_num_prims(1); + geom_line->set_width(_thick); + geom = geom_line; + + } else { + // The more normal case: multiple line segments, connected + // end-to-end like a series of linestrips. + GeomLinestrip *geom_linestrip = new GeomLinestrip; + geom_linestrip->set_num_prims(lengths.size()); + geom_linestrip->set_lengths(lengths); + geom_linestrip->set_width(_thick); + geom = geom_linestrip; + } + + geom->set_colors(_created_colors, G_PER_VERTEX, line_index); + geom->set_coords(_created_verts, G_PER_VERTEX, line_index); + + previous->add_geom(geom); + } + + // And now create the points. + if (point_index.size() > 0) { + // Create a new Geom and add the points. + GeomPoint *geom = new GeomPoint; + + geom->set_num_prims(point_index.size()); + geom->set_size(_thick); + geom->set_colors(_created_colors, G_PER_VERTEX, point_index); + geom->set_coords(_created_verts, G_PER_VERTEX, point_index); + + previous->add_geom(geom); + } + + // And reset for next time. + reset(); + } + + return previous; +} diff --git a/panda/src/grutil/lineSegs.h b/panda/src/grutil/lineSegs.h new file mode 100644 index 0000000000..12bd927c9f --- /dev/null +++ b/panda/src/grutil/lineSegs.h @@ -0,0 +1,86 @@ +// Filename: lineSegs.h +// Created by: drose (24May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINESEGS_H +#define LINESEGS_H + +#include + +#include +#include +#include +#include +#include +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : LineSegs +// Description : Encapsulates creation of a series of connected or +// disconnected line segments or points, for drawing +// paths or rays. This class doesn't attempt to be the +// smartest it could possibly be; it's intended +// primarily as a visualization and editing tool. +//////////////////////////////////////////////////////////////////// +class LineSegs { +public: + LineSegs(); + ~LineSegs(); + + void reset(); + INLINE void set_color(float r, float g, float b, float a = 1.0); + INLINE void set_color(const Colorf &color); + INLINE void set_thickness(float thick); + + INLINE void move_to(float x, float y, float z); + void move_to(const Vertexf &v); + + INLINE void draw_to(float x, float y, float z); + void draw_to(const Vertexf &v); + + const Vertexf &get_current_position(); + bool is_empty(); + + INLINE GeomNode *create(bool frame_accurate = false); + GeomNode *create(GeomNode *previous, bool frame_accurate = false); + + // Functions to move the line vertices after they have been created. + INLINE int get_num_vertices() const; + + INLINE Vertexf get_vertex(int vertex) const; + INLINE void set_vertex(int vertex, const Vertexf &vert); + INLINE void set_vertex(int vertex, float x, float y, float z); + + INLINE Colorf get_vertex_color(int vertex) const; + INLINE void set_vertex_color(int vertex, const Colorf &color); + INLINE void set_vertex_color(int vertex, float r, float g, float b, float a = 1.0); + +private: + class Point { + public: + INLINE Point(); + INLINE Point(const Vertexf &point, const Colorf &color); + INLINE Point(const Point ©); + INLINE void operator = (const Point ©); + + Vertexf _point; + Colorf _color; + }; + + typedef vector SegmentList; + typedef vector LineList; + + LineList _list; + Colorf _color; + float _thick; + + PTA_Vertexf _created_verts; + PTA_Colorf _created_colors; +}; + +#include "lineSegs.I" + +#endif diff --git a/panda/src/gsgbase/Sources.pp b/panda/src/gsgbase/Sources.pp new file mode 100644 index 0000000000..3af3450cd0 --- /dev/null +++ b/panda/src/gsgbase/Sources.pp @@ -0,0 +1,28 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET gsgbase + #define LOCAL_LIBS \ + putil graph + + #define SOURCES \ + config_gsgbase.cxx config_gsgbase.h graphicsStateGuardianBase.cxx \ + graphicsStateGuardianBase.h + + #define INSTALL_HEADERS \ + graphicsStateGuardianBase.h + + #define IGATESCAN all + +#end lib_target + +#begin test_bin_target + #define TARGET test_gsgbase + #define LOCAL_LIBS \ + gsgbase + + #define SOURCES \ + test_gsgbase.cxx + +#end test_bin_target + diff --git a/panda/src/gsgbase/config_gsgbase.cxx b/panda/src/gsgbase/config_gsgbase.cxx new file mode 100644 index 0000000000..b317db1543 --- /dev/null +++ b/panda/src/gsgbase/config_gsgbase.cxx @@ -0,0 +1,15 @@ +// Filename: config_gsgbase.cxx +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_gsgbase.h" +#include "graphicsStateGuardianBase.h" + +#include + +Configure(config_gsgbase); + +ConfigureFn(config_gsgbase) { + GraphicsStateGuardianBase::init_type(); +} diff --git a/panda/src/gsgbase/config_gsgbase.h b/panda/src/gsgbase/config_gsgbase.h new file mode 100644 index 0000000000..0e5ca7c7d1 --- /dev/null +++ b/panda/src/gsgbase/config_gsgbase.h @@ -0,0 +1,11 @@ +// Filename: config_gsgbase.h +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_GSGBASE_H +#define CONFIG_GSGBASE_H + +// No config variables to define yet for this package. + +#endif diff --git a/panda/src/gsgbase/graphicsStateGuardianBase.cxx b/panda/src/gsgbase/graphicsStateGuardianBase.cxx new file mode 100644 index 0000000000..6d7009487e --- /dev/null +++ b/panda/src/gsgbase/graphicsStateGuardianBase.cxx @@ -0,0 +1,8 @@ +// Filename: graphicsStateGuardianBase.cxx +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "graphicsStateGuardianBase.h" + +TypeHandle GraphicsStateGuardianBase::_type_handle; diff --git a/panda/src/gsgbase/graphicsStateGuardianBase.h b/panda/src/gsgbase/graphicsStateGuardianBase.h new file mode 100644 index 0000000000..bb6df3fa30 --- /dev/null +++ b/panda/src/gsgbase/graphicsStateGuardianBase.h @@ -0,0 +1,189 @@ +// Filename: graphicsStateGuardianBase.h +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GRAPHICSSTATEGUARDIANBASE_H +#define GRAPHICSSTATEGUARDIANBASE_H + +#include + +#include +#include +#include + +// A handful of forward references. + +class RenderBuffer; +class GraphicsWindow; + +class GeomPoint; +class GeomLine; +class GeomLinestrip; +class GeomSprite; +class GeomPolygon; +class GeomQuad; +class GeomTri; +class GeomTristrip; +class GeomTrifan; +class GeomSphere; + +class TextureContext; +class Texture; +class PixelBuffer; + +class Material; +class Fog; + +class TransformAttribute; +class ColorMatrixAttribute; +class AlphaTransformAttribute; +class TexMatrixAttribute; +class ColorAttribute; +class TextureAttribute; +class LightAttribute; +class MaterialAttribute; +class RenderModeAttribute; +class ColorBlendAttribute; +class TextureApplyAttribute; +class ColorMaskAttribute; +class DepthTestAttribute; +class DepthWriteAttribute; +class TexGenAttribute; +class CullFaceAttribute; +class StencilAttribute; +class ClipPlaneAttribute; +class TransparencyAttribute; +class FogAttribute; +class LinesmoothAttribute; +class PointShapeAttribute; +class PolygonOffsetAttribute; + +class Node; +class GeomNode; +class PointLight; +class DirectionalLight; +class Spotlight; +class AmbientLight; + +class DisplayRegion; +class Projection; +class ProjectionNode; + +//////////////////////////////////////////////////////////////////// +// Class : GraphicsStateGuardianBase +// Description : This is a base class for the GraphicsStateGuardian +// class, which is itself a base class for the various +// GSG's for different platforms. This class contains +// all the function prototypes to support the +// double-dispatch of GSG to geoms, attributes, etc. It +// lives in a separate class in its own package so we +// can avoid circular build dependency problems. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GraphicsStateGuardianBase : public TypedReferenceCount { +public: + // These functions will be queried by the GeomIssuer to determine if + // it should issue normals, texcoords, and/or colors, based on the + // GSG's current state. + virtual bool wants_normals(void) const=0; + virtual bool wants_texcoords(void) const=0; + virtual bool wants_colors(void) const=0; + + + // Defined here are some internal interface functions for the + // GraphicsStateGuardian. These are here to support + // double-dispatching from Geoms and NodeAttributes, and are + // intended to be invoked only directly by the appropriate Geom and + // NodeAttribute types. They're public only because it would be too + // inconvenient to declare each of those types to be friends of this + // class. + + virtual void draw_point(const GeomPoint *geom)=0; + virtual void draw_line(const GeomLine *geom)=0; + virtual void draw_linestrip(const GeomLinestrip *geom)=0; + virtual void draw_sprite(const GeomSprite *geom)=0; + virtual void draw_polygon(const GeomPolygon *geom)=0; + virtual void draw_quad(const GeomQuad *geom)=0; + virtual void draw_tri(const GeomTri *geom)=0; + virtual void draw_tristrip(const GeomTristrip *geom)=0; + virtual void draw_trifan(const GeomTrifan *geom)=0; + virtual void draw_sphere(const GeomSphere *geom)=0; + + virtual TextureContext *prepare_texture(Texture *tex)=0; + virtual void apply_texture(TextureContext *tc)=0; + virtual void release_texture(TextureContext *tc)=0; + + virtual void copy_texture(TextureContext *tc, const DisplayRegion *dr)=0; + virtual void copy_texture(TextureContext *tc, const DisplayRegion *dr, + const RenderBuffer &rb)=0; + virtual void draw_texture(TextureContext *tc, const DisplayRegion *dr)=0; + virtual void draw_texture(TextureContext *tc, const DisplayRegion *dr, + const RenderBuffer &rb)=0; + + virtual void texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb)=0; + virtual void texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb, + const DisplayRegion *dr)=0; + + virtual void copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr)=0; + virtual void copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const RenderBuffer &rb)=0; + virtual void draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const NodeAttributes& na=NodeAttributes())=0; + virtual void draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const RenderBuffer &rb, + const NodeAttributes& na=NodeAttributes())=0; + + virtual void apply_material(Material *material)=0; + virtual void apply_fog(Fog *fog)=0; + + virtual void apply_light(PointLight *light)=0; + virtual void apply_light(DirectionalLight *light)=0; + virtual void apply_light(Spotlight *light)=0; + virtual void apply_light(AmbientLight *light)=0; + + virtual void issue_transform(const TransformAttribute *) { } + virtual void issue_color_transform(const ColorMatrixAttribute *) { } + virtual void issue_alpha_transform(const AlphaTransformAttribute *) { } + virtual void issue_tex_matrix(const TexMatrixAttribute *) { } + virtual void issue_color(const ColorAttribute *) { } + virtual void issue_texture(const TextureAttribute *) { } + virtual void issue_light(const LightAttribute *) { } + virtual void issue_material(const MaterialAttribute *) { } + virtual void issue_render_mode(const RenderModeAttribute *) { } + virtual void issue_color_blend(const ColorBlendAttribute *) { } + virtual void issue_texture_apply(const TextureApplyAttribute *) { } + virtual void issue_color_mask(const ColorMaskAttribute *) { } + virtual void issue_depth_test(const DepthTestAttribute *) { } + virtual void issue_depth_write(const DepthWriteAttribute *) { } + virtual void issue_tex_gen(const TexGenAttribute *) { } + virtual void issue_cull_face(const CullFaceAttribute *) { } + virtual void issue_stencil(const StencilAttribute *) { } + virtual void issue_clip_plane(const ClipPlaneAttribute *) { } + virtual void issue_transparency(const TransparencyAttribute *) { } + virtual void issue_fog(const FogAttribute *) { } + virtual void issue_linesmooth(const LinesmoothAttribute *) { } + virtual void issue_point_shape(const PointShapeAttribute *) { } + virtual void issue_polygon_offset(const PolygonOffsetAttribute *) { } + + virtual void begin_decal(GeomNode *) { } + virtual void end_decal(GeomNode *) { } + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "GraphicsStateGuardianBase", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/gsgbase/pptmpNGk8yj b/panda/src/gsgbase/pptmpNGk8yj new file mode 100644 index 0000000000..afed374916 --- /dev/null +++ b/panda/src/gsgbase/pptmpNGk8yj @@ -0,0 +1,67 @@ +#### Makefile for binaries. Any fields commented out are optional. +#### Generated automatically by ppremake 0.20 from Sources.pp. +################################# DO NOT EDIT ########################### + +#### Target's name: +TARGET = test_gsgbase + +# Standard .o file conversion information. + +#### Pull in standard .o make variables +include $(DTOOL)/inc/Makefile.o.vars + +#### Lex files +LFILES = +LEX = flex +LFLAGS = -olex.yy.c +LEXTENSION = yy.cxx +# LSUBST = + +#### Yacc files +YFILES = +YACC = bison +YFLAGS = -y -d +YEXTENSION = tab.cxx +# YSUBST = + +#### C files +CFILES = +# CFLAGS = + +#### C++ files +C++FILES = test_gsgbase.cxx +C++FLAGS += + +#### Pull in deferred-target files built in other packages +DEFERRED_FILES = framework + +#### Additional search directories for C/C++ header files: +# IPATH := + +#### Location to put .o files: +# ODIR = + +#### Source file dependencies (unnecessary with clearmake) +# foo.c: foo.h + +#### The .o action is here. +include $(DTOOL)/inc/Makefile.o.rules + +#### Pull in standard binary make variables. +include $(DTOOL)/inc/Makefile.bin.vars + +#### Other files and lib. Include $(ODIR) in any .o names. +# OFILES = +WHEN_NO_DEFER_LIBS = -lgsgbase +WHEN_DEFER_LIBS = fillinhere +LIBS = -linterrogatedb -ldconfig -ldtoolutil -ldtoolbase +SYSLIBS = + +#### Additional search directories for lib: +# LPATH := + +#### Other linker flags. +# LDFLAGS = + +#### The bin action is here. +include $(DTOOL)/inc/Makefile.bin.rules diff --git a/panda/src/gsgbase/test_gsgbase.cxx b/panda/src/gsgbase/test_gsgbase.cxx new file mode 100644 index 0000000000..3c2f649347 --- /dev/null +++ b/panda/src/gsgbase/test_gsgbase.cxx @@ -0,0 +1,10 @@ +// Filename: test_gsgbase.cxx +// Created by: shochet (02Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "graphicsStateGuardianBase.h" + +int main() { + return 0; +} diff --git a/panda/src/gsgmisc/Sources.pp b/panda/src/gsgmisc/Sources.pp new file mode 100644 index 0000000000..ca542f594c --- /dev/null +++ b/panda/src/gsgmisc/Sources.pp @@ -0,0 +1,15 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET gsgmisc + #define LOCAL_LIBS \ + putil gobj gsgbase graph mathutil + + #define SOURCES \ + geomIssuer.I geomIssuer.cxx geomIssuer.h + + #define INSTALL_HEADERS \ + geomIssuer.I geomIssuer.h + +#end lib_target + diff --git a/panda/src/gsgmisc/geomIssuer.I b/panda/src/gsgmisc/geomIssuer.I new file mode 100644 index 0000000000..54fcd13972 --- /dev/null +++ b/panda/src/gsgmisc/geomIssuer.I @@ -0,0 +1,59 @@ +// Filename: geomIssuer.I +// Created by: drose (04Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: GeomIssuer::issue_vertex +// Access: Public +// Description: Uses the function pointers set up by the constructor +// to actually issue the vertex to the rendering +// backend. If the vertex does not have the indicated +// bind type, nothing is done. +//////////////////////////////////////////////////////////////////// +INLINE void GeomIssuer:: +issue_vertex(GeomBindType bind, Geom::VertexIterator &i) { + _vertex_command[bind](_geom, i); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomIssuer::issue_normal +// Access: Public +// Description: Uses the function pointers set up by the constructor +// to actually issue the normal to the rendering +// backend. If the normal does not have the indicated +// bind type, nothing is done. +//////////////////////////////////////////////////////////////////// +INLINE void GeomIssuer:: +issue_normal(GeomBindType bind, Geom::NormalIterator &i) { + _normal_command[bind](_geom, i); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomIssuer::issue_texcoord +// Access: Public +// Description: Uses the function pointers set up by the constructor +// to actually issue the texCoord to the rendering +// backend. If the texCoord does not have the indicated +// bind type, nothing is done. +//////////////////////////////////////////////////////////////////// +INLINE void GeomIssuer:: +issue_texcoord(GeomBindType bind, Geom::TexCoordIterator &i) { + _texcoord_command[bind](_geom, i); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomIssuer::issue_color +// Access: Public +// Description: Uses the function pointers set up by the constructor +// to actually issue the color to the rendering +// backend. If the color does not have the indicated +// bind type, nothing is done. +//////////////////////////////////////////////////////////////////// +INLINE void GeomIssuer:: +issue_color(GeomBindType bind, Geom::ColorIterator &i) { + _color_command[bind](_geom, i, _gsg); +} + + diff --git a/panda/src/gsgmisc/geomIssuer.cxx b/panda/src/gsgmisc/geomIssuer.cxx new file mode 100644 index 0000000000..c7436d028e --- /dev/null +++ b/panda/src/gsgmisc/geomIssuer.cxx @@ -0,0 +1,92 @@ +// Filename: geomIssuer.cxx +// Created by: drose (03Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "geomIssuer.h" +#include +#include + +static void +issue_vertex_noop(const Geom *, Geom::VertexIterator &) { +} + +static void +issue_normal_noop(const Geom *, Geom::NormalIterator &) { +} + +static void +issue_texcoord_noop(const Geom *, Geom::TexCoordIterator &) { +} + +static void +issue_color_noop(const Geom *, Geom::ColorIterator &, const GraphicsStateGuardianBase *) { +} + +static GeomIssuer noop_issuer; + + +//////////////////////////////////////////////////////////////////// +// Function: GeomIssuer::Constructor +// Access: Public +// Description: The default constructor makes a no-op GeomIssuer. +// It's primarily intended to create the static +// _noop_issuer once and only once; normally, the real +// constructor, below, will be used. +//////////////////////////////////////////////////////////////////// +GeomIssuer:: +GeomIssuer() { + for (int i = 0; i < num_GeomBindTypes; i++) { + _vertex_command[i] = issue_vertex_noop; + _normal_command[i] = issue_normal_noop; + _texcoord_command[i] = issue_texcoord_noop; + _color_command[i] = issue_color_noop; + } + _geom = NULL; +} + + +//////////////////////////////////////////////////////////////////// +// Function: GeomIssuer::Constructor +// Access: Public +// Description: This is the real constructor. Given a Geom, the +// current gsg, and a series of functions that, when +// called, will actually issue the vertex/normal/whatnot +// to the rendering engine. It will construct a +// GeomIssuer with these pointers in the appropriate +// places to either issue the component or do nothing, +// according to the requirements of the geom and of the +// current state of the gsg. +//////////////////////////////////////////////////////////////////// +GeomIssuer:: +GeomIssuer(const Geom *geom, + const GraphicsStateGuardianBase *gsg, + IssueVertex *vertex, + IssueNormal *normal, + IssueTexCoord *texcoord, + IssueColor *color) { + memcpy(this, &noop_issuer, sizeof(GeomIssuer)); + _geom = geom; + _gsg = gsg; + + // Issue vertices by default (we might not want to if we're doing + // performance analysis) + if (vertex != NULL) { + _vertex_command[geom->get_binding(G_COORD)] = vertex; + } + + // Issue normals only if we have them and the gsg says we should. + if (normal != NULL && gsg->wants_normals()) { + _normal_command[geom->get_binding(G_NORMAL)] = normal; + } + + // Issue texcoords if we have them and the gsg wants them. + if (texcoord != NULL && gsg->wants_texcoords()) { + _texcoord_command[geom->get_binding(G_TEXCOORD)] = texcoord; + } + + // And ditto for colors. + if (color != NULL && gsg->wants_colors()) { + _color_command[geom->get_binding(G_COLOR)] = color; + } +} diff --git a/panda/src/gsgmisc/geomIssuer.h b/panda/src/gsgmisc/geomIssuer.h new file mode 100644 index 0000000000..a19738ecb0 --- /dev/null +++ b/panda/src/gsgmisc/geomIssuer.h @@ -0,0 +1,74 @@ +// Filename: geomIssuer.h +// Created by: drose (03Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMISSUER_H +#define GEOMISSUER_H + +#include + +#include +#include +#include +#include + +class GraphicsStateGuardianBase; + +//////////////////////////////////////////////////////////////////// +// Class : GeomIssuer +// Description : This is a utility class used by the various +// GraphicsStateGuardians to issue the vertex/normal/etc +// commands to the rendering engine. Given a geom and a +// gsg, as well as a set of functions that actually do +// the work of issuing vertices etc. to the rendering +// backend, it configures itself so that subsequent +// calls to issue_normal() (for instance) will either do +// nothing or issue a normal, depending on whether the +// requested binding type matches the geom's actual +// binding type for normals, and on whether vertices are +// required by the current state. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomIssuer { +public: + + // Declare some function types. This declares several typenames + // which are pointers to function types--these are not themselves + // functions. A function pointed to by a variable of this type, + // when given a Geom and an associated iterator, will issue the + // vertex (or whatever) referenced by the iterator to the rendering + // backend, and increment the iterator. + typedef void IssueVertex(const Geom *, Geom::VertexIterator &); + typedef void IssueNormal(const Geom *, Geom::NormalIterator &); + typedef void IssueTexCoord(const Geom *, Geom::TexCoordIterator &); + typedef void IssueColor(const Geom *, Geom::ColorIterator &, const GraphicsStateGuardianBase *gsg); + + GeomIssuer(); + GeomIssuer(const Geom *geom, + const GraphicsStateGuardianBase *gsg, + IssueVertex *vertex, + IssueNormal *normal, + IssueTexCoord *texcoord, + IssueColor *color); + + INLINE void issue_vertex(GeomBindType bind, + Geom::VertexIterator &i); + INLINE void issue_normal(GeomBindType bind, + Geom::NormalIterator &i); + INLINE void issue_texcoord(GeomBindType bind, + Geom::TexCoordIterator &i); + INLINE void issue_color(GeomBindType bind, + Geom::ColorIterator &i); + +protected: + const Geom *_geom; + const GraphicsStateGuardianBase *_gsg; + IssueVertex *_vertex_command[num_GeomBindTypes]; + IssueNormal *_normal_command[num_GeomBindTypes]; + IssueTexCoord *_texcoord_command[num_GeomBindTypes]; + IssueColor *_color_command[num_GeomBindTypes]; +}; + +#include "geomIssuer.I" + +#endif diff --git a/panda/src/ipc/Sources.pp b/panda/src/ipc/Sources.pp new file mode 100644 index 0000000000..ec3522fba9 --- /dev/null +++ b/panda/src/ipc/Sources.pp @@ -0,0 +1,86 @@ +#define LOCAL_LIBS express pandabase +#define OTHER_LIBS interrogatedb dconfig dtoolutil + +#begin lib_target + #define TARGET ipc + + #define SOURCES \ + ipc.cxx ipc_atomics.h ipc_condition.h ipc_file.I ipc_file.h \ + ipc_library.h ipc_mach_traits.h ipc_mutex.h ipc_nspr_traits.h \ + ipc_nt_traits.h ipc_posix_traits.h \ + ipc_ps2_traits.h ipc_semaphore.h ipc_solaris_traits.h ipc_thread.h \ + ipc_traits.cxx ipc_traits.h + + #define INSTALL_HEADERS \ + ipc_mutex.h ipc_condition.h ipc_semaphore.h ipc_thread.h \ + ipc_traits.h ipc_mach_traits.h ipc_nt_traits.h ipc_posix_traits.h \ + ipc_solaris_traits.h ipc_nspr_traits.h ipc_ps2_traits.h \ + ipc_atomics.h ipc_library.h ipc_file.h ipc_file.I + +#end lib_target + +// There needs to be a way to compile this into the ipc library only +// when appropriate. Perhaps as simple as compiling it all the time, +// but protecting the code itself within #ifdefs. +#define EXTRA_DIST ipc_nt_traits.cxx + +// test_lib_target is not implemented yet. This is a bogus +// test_bin_target until then. +#begin test_bin_target + #define TARGET loom + + #define SOURCES \ + loom.cxx loom.h loom_internal.h + + #define LOCAL_LIBS ipc $[LOCAL_LIBS] +#end test_bin_target + + +#begin test_bin_target + #define TARGET test_diners + #define SOURCES test_diners.cxx + #define LOCAL_LIBS ipc $[LOCAL_LIBS] +#end test_bin_target + +#begin test_bin_target + #define TARGET test_priority + #define SOURCES test_priority.cxx + #define LOCAL_LIBS ipc $[LOCAL_LIBS] +#end test_bin_target + +#begin test_bin_target + #define TARGET test_prodcons + #define SOURCES test_prodcons.cxx + #define LOCAL_LIBS ipc $[LOCAL_LIBS] +#end test_bin_target + +#begin test_bin_target + #define TARGET test_threaddata + #define SOURCES test_threaddata.cxx + #define LOCAL_LIBS ipc $[LOCAL_LIBS] +#end test_bin_target + +#begin test_bin_target + #define TARGET loom_main + #define SOURCES loom_main.cxx + #define LOCAL_LIBS loom ipc $[LOCAL_LIBS] +#end test_bin_target + +#begin test_bin_target + #define TARGET test_file + #define SOURCES test_file.cxx + #define LOCAL_LIBS loom ipc $[LOCAL_LIBS] +#end test_bin_target + +// Oops, these are test .so's +#begin test_bin_target + #define TARGET loom_test1 + #define SOURCES loom_test1.cxx + #define LOCAL_LIBS loom ipc $[LOCAL_LIBS] +#end test_bin_target + +#begin test_bin_target + #define TARGET loom_test2 + #define SOURCES loom_test2.cxx + #define LOCAL_LIBS loom ipc $[LOCAL_LIBS] +#end test_bin_target diff --git a/panda/src/ipc/ipc.cxx b/panda/src/ipc/ipc.cxx new file mode 100644 index 0000000000..9615fde0d3 --- /dev/null +++ b/panda/src/ipc/ipc.cxx @@ -0,0 +1,77 @@ +// Filename: ipc.cxx +// Created by: frang (06Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "ipc_condition.h" + +base_condition_variable* const base_condition_variable::Null = + (base_condition_variable*)0L; + +#include "ipc_library.h" + +base_library* const base_library::Null = (base_library*)0L; + +#include "ipc_file.h" + +base_file* const base_file::Null = (base_file*)0L; + +#include "ipc_mutex.h" + +base_mutex* const base_mutex::Null = (base_mutex*)0L; + +#include "ipc_semaphore.h" + +base_semaphore* const base_semaphore::Null = (base_semaphore*)0L; + +#include "ipc_thread.h" + +base_thread* const base_thread::Null = (base_thread*)0L; +base_thread::mutex* base_thread::_next_id_mutex = base_thread::mutex::Null; + +int base_thread::_next_id = 0; + +void* base_thread::thread_wrapper(void* data) +{ + base_thread* me = (base_thread *) data; + + me->_thread->start_in(); + + // now invoke the thread functin with the given argument + + if (me->_fn_void != NULL) + { + (*me->_fn_void)(me->_thread_arg); + base_thread::exit(); + } + + if (me->_fn_ret != NULL) + { + void* return_value = (*me->_fn_ret)(me->_thread_arg); + base_thread::exit(return_value); + } + + if (me->_detached) + { + me->run(me->_thread_arg); + base_thread::exit(); + } + else + { + void* return_value = me->run_undetached(me->_thread_arg); + base_thread::exit(return_value); + } + + // we should never get here, but this makes the compilers happy + + return (void *) NULL; +} + +void base_thread::run(void*) { +} + +void* base_thread::run_undetached(void*) { + return (void*)0L; +} + + diff --git a/panda/src/ipc/ipc_atomics.h b/panda/src/ipc/ipc_atomics.h new file mode 100644 index 0000000000..2f97f2262b --- /dev/null +++ b/panda/src/ipc/ipc_atomics.h @@ -0,0 +1,25 @@ +// Filename: ipc_atomics.h +// Created by: frang (10Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __IPC_ATOMICS_H__ +#define __IPC_ATOMICS_H__ + +#include + +#include "ipc_traits.h" + +INLINE int set_atomic(int& var, int val) { + return ipc_traits::set_atomic(var, val); +} + +INLINE int inc_atomic(int& var) { + return ipc_traits::inc_atomic(var); +} + +INLINE int dec_atomic(int& var) { + return ipc_traits::dec_atomic(var); +} + +#endif /* __IPC_ATOMICS_H__ */ diff --git a/panda/src/ipc/ipc_condition.h b/panda/src/ipc/ipc_condition.h new file mode 100644 index 0000000000..e0a00cb5fc --- /dev/null +++ b/panda/src/ipc/ipc_condition.h @@ -0,0 +1,39 @@ +// Filename: ipc_condition.h +// Created by: cary (16Sep98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __IPC_CONDITION_H__ +#define __IPC_CONDITION_H__ + +#include + +#include "ipc_traits.h" +#include "ipc_mutex.h" + +class EXPCL_PANDAEXPRESS base_condition_variable { + public: + typedef ipc_traits traits; + typedef traits::condition_class condition_class; + typedef base_mutex mutex; + + static base_condition_variable* const Null; + INLINE base_condition_variable(mutex& m) : _mutex(m), + _condition(traits::make_condition(m.get_mutex())) {} + INLINE ~base_condition_variable(void) { delete _condition; } + INLINE void wait(void) { _condition->wait(); } + INLINE int timedwait(const unsigned long secs, + const unsigned long nsecs = 0) { + return _condition->timedwait(secs, nsecs); + } + INLINE void signal(void) { _condition->signal(); } + INLINE void broadcast(void) { _condition->broadcast(); } + INLINE condition_class* get_condition(void) { return _condition; } + private: + mutex& _mutex; + condition_class *_condition; +}; + +typedef base_condition_variable condition_variable; + +#endif /* __IPC_CONDITION_H__ */ diff --git a/panda/src/ipc/ipc_file.I b/panda/src/ipc/ipc_file.I new file mode 100644 index 0000000000..e56b2b07b0 --- /dev/null +++ b/panda/src/ipc/ipc_file.I @@ -0,0 +1,96 @@ +// Filename: ipc_file.I +// Created by: jason (07Jun00) +// + +#include +#include +#include + +INLINE bool datagram_file:: +open(file_mode mode, string header){ + //By definition a file should not be opened for both + //reading and writing + nassertr(mode != FILE_BOTH, false); + _mode = mode; + + if (!base_file::open(mode)) return false; + + if (!header.empty()) + { + if (mode == FILE_READ) + { + string file_header; + if (readin(file_header,header.size()) != (int)header.size()) return false; + if (file_header != header) + { + //Note! Using nout until a config_ipc file is made and therefore + //ipc_cat is made + nout << "File is either damaged or not valid" << endl; + return false; + } + } + else + { + if (writeout(header) != (int)header.size()) return false; + } + } + + _valid = true; + return true; +} + +INLINE bool datagram_file:: +is_valid(void) +{ + return _valid; +} + +INLINE bool datagram_file:: +empty(void){ + return file::empty(); +} + +INLINE bool datagram_file:: +get_datagram(Datagram& dataBlock) { + //Only perform reads when mode is set to + //FILE_READ. This is because datagram_file + //can only read or write. + nassertr(_mode == FILE_READ, false); + + string header, packet; + + if (readin(header,sizeof(PN_uint32)) != sizeof(PN_uint32)) return false; + + Datagram size(header.data(), header.size()); + DatagramIterator sizescan(size); + + PN_uint32 packetsize = sizescan.get_uint32(); + + if (readin(packet, packetsize) != (int)packetsize) return false; + dataBlock.append_data(packet.data(), packetsize); + + return true; +} + +INLINE bool datagram_file:: +put_datagram(const Datagram& dataBlock) { + //Only perform writes when mode is set to + //FILE_READ or FILE_APPEND. This is because datagram_file + //can only read or write. + nassertr(_mode == FILE_APPEND || _mode == FILE_WRITE, false); + DatagramIterator scanin(dataBlock); + Datagram out; + int datalen = dataBlock.get_length(); + + //Determine the size of the datagram and add it into the data to be + //written to the file. Then append the message. + out.add_uint32(datalen); + out.append_data(scanin.get_remaining_bytes().data(), datalen); + + DatagramIterator scanout(out); + if (writeout(scanout.get_remaining_bytes()) != (int)out.get_length()) return false; + + return true; +} + + diff --git a/panda/src/ipc/ipc_file.h b/panda/src/ipc/ipc_file.h new file mode 100644 index 0000000000..8f18365e59 --- /dev/null +++ b/panda/src/ipc/ipc_file.h @@ -0,0 +1,63 @@ +// Filename: ipc_file.h +// Created by: jason (05Jun00) +// + +#ifndef __IPC_FILE_H__ +#define __IPC_FILE_H__ + +#include +#include +#include +#include "ipc_traits.h" + +class base_file { +public: + enum file_mode {FILE_READ, FILE_WRITE, FILE_BOTH, FILE_APPEND}; + + typedef ipc_traits traits; + typedef traits::file_class file_class; + + static base_file* const Null; + + INLINE base_file(const string& file = "") : _file(traits::make_file(file)) {} + virtual ~base_file(void) { delete _file; }; + INLINE void setFile(const string& file) {_file->setFile(file);} + INLINE bool open(file_mode mode) {return _file->open(mode);} + INLINE void close(void) {_file->close();} + INLINE bool empty(void) { return _file->empty();} + INLINE int readin(string& buffer, int numBytes) {return _file->readin(buffer, numBytes);} + INLINE int writeout(const string& buffer) {return _file->writeout(buffer);} +private: + file_class* _file; +}; + +class datagram_file : public DatagramGenerator, public DatagramSink, public base_file { +public: + INLINE datagram_file() : base_file(), _valid(false) {} + INLINE datagram_file(const string& file) : base_file(file) {} + virtual ~datagram_file(void){}; + + INLINE bool open(file_mode mode, string header = ""); + INLINE bool empty(void); + + INLINE bool get_datagram(Datagram& dataBlock); + INLINE bool put_datagram(const Datagram& dataBlock); + INLINE bool is_valid(void); + +protected: + //When someone has a datagram_file, they shouldn't be directly reading from or + //writing to the file + INLINE int readin(string& buffer, int numBytes) {return base_file::readin(buffer, numBytes);} + INLINE int writeout(const string& buffer) {return base_file::writeout(buffer);} +private: + file_mode _mode; + bool _valid; +}; + +typedef base_file file; +typedef file::file_mode file_mode; + +#include "ipc_file.I" + +#endif /* __IPC_FILE_H__ */ + diff --git a/panda/src/ipc/ipc_library.h b/panda/src/ipc/ipc_library.h new file mode 100644 index 0000000000..abdd8be1dc --- /dev/null +++ b/panda/src/ipc/ipc_library.h @@ -0,0 +1,28 @@ +// Filename: ipc_library.h +// Created by: frang (10Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __IPC_LIBRARY_H__ +#define __IPC_LIBRARY_H__ + +#include + +#include "ipc_traits.h" + +class base_library { +public: + typedef ipc_traits traits; + typedef traits::library_class library_class; + + static base_library* const Null; + INLINE base_library(std::string& lib) : _lib(traits::make_library(lib)) {} + INLINE ~base_library(void) { delete _lib; } + INLINE void* get_symbol(std::string& sym) { return _lib->get_symbol(sym); } +private: + library_class* _lib; +}; + +typedef base_library library; + +#endif /* __IPC_LIBRARY_H__ */ diff --git a/panda/src/ipc/ipc_mach_traits.h b/panda/src/ipc/ipc_mach_traits.h new file mode 100644 index 0000000000..11a7f74bdb --- /dev/null +++ b/panda/src/ipc/ipc_mach_traits.h @@ -0,0 +1,284 @@ +// Filename: ipc_mach_traits.h +// Created by: cary (16Sep98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __IPC_MACH_TRAITS_H__ +#define __IPC_MACH_TRAITS_H__ + +#include + +#include +#include +#include +#include + +class EXPCL_PANDAEXPRESS ipc_traits { + public: + class mutex_class; + class condition_class; + class semaphore_class; + class thread_class; + class library_class; + class mutex_class { + private: + struct mutex _mutex; + public: + // interface to mutex + static const mutex_class* Null; + INLINE mutex_class(void) { mutex_init(&_mutex); } + INLINE ~mutex_class(void) { mutex_clear(&_mutex); } + INLINE void lock(void) { mutex_lock(&_mutex); } + INLINE void unlock(void) { mutex_unlock(&_mutex); } + INLINE struct mutex* get_mutex(void) { return &_mutex; } + }; + class condition_class { + private: + struct condition _condition; + mutex_class *_mutex; + typedef struct alarmclock_args { + unsigned long secs; + unsigned long nsecs; + bool wakeup; + condition_class* condition; + mutex_class* mutex; + }; + INLINE any_t alarmclock(any_t arg) { + alarmclock_args* alarm = (alarmclock_args*)arg; + thread_ipc_traits::sleep(alarm->secs, alarm->nsecs); + alarm->mutex->lock(); + alarm->wakeup = TRUE; + alarm->condition->signal(); + alarm->mutex->unlock(); + return (any_t)TRUE; + } + public: + // interface to condition variables + static const condition_class* Null; + INLINE condition_class(mutex_class* m) : _mutex(m) { + condition_init(&_condition); + } + INLINE ~condition_class(void) { condition_clear(&_condition); } + INLINE void wait(void) { + condition_wait(&_condition, _mutex->get_mutex()); + } + INLINE int timedwait(const unsigned long secs, + const unsigned long nsecs) { + alarmclock_args alarm; + + thread_ipc_traits::get_time(&alarm.secs, &alarm.nsecs, 0, 0); + if (secs < alarm.secs || (secs == alarm.secs && + nsecs <= alarm.nsecs)) + return ETIMEDOUT; + alarm.secs = secs - alarm.secs; + if (nsecs <= alarm.nsecs) { + alarm.nsecs = 1000000 - alarm.nsecs + nsecs; + --alarm.secs; + } else + alarm.nsecs = nsecs - alarm.nsecs; + alarm.mutex = _mutex; + alarm.condition = this; + alarm.wakeup = FALSE; + cthread_t ct = cthread_fork((cthread_fn_t)alarmclock, + (any_t)&alarm); + cthread_detach(ct); + + condition_wait(&_condition, _mutex->get_mutex()); + + if (alarm.wakeup) + return 0; + + // interrupt the alarmclock thread sleep + cthread_abort(ct); + + // wait until it has signalled the condition + condition_wait(&_condition, _mutex->get_mutex()); + return 1; + } + INLINE void signal(void) { condition_signal(&_condition); } + INLINE void broadcast(void) { condition_signal(&_condition); } + }; + class semaphore_class { + private: + mutex_class _mutex; + condition_class _condition; + int _value; + public: + // interface to semaphores + static const semaphore_class* Null; + INLINE semaphore_class(const unsigned int initial) : _mutex(), + _condition(&_mutex), _value(initial) {} + INLINE ~semaphore_class(void) {} + INLINE void wait(void) { + _mutex->lock(); + while (_value == 0) + _condition.wait(&_mutex); + --_value; + _mutex->unlock(); + } + INLINE int trywait(void) { + _mutex->lock(); + if (_value == 0) { + _mutex->unlock(); + return 0; + } + --_value; + _mutex->unlock(); + return 1; + } + INLINE void post(void) { + _mutex->lock(); + if (_value == 0) + _condition.signal(); + ++_value; + _mutex->unlock(); + } + }; + class thread_class { + private: + chread_d _thread; + void* _b_ptr; // a handle to hang a pointer back to the high-level + // thread class on. Gross, but I couldn't think of + // a better way to do this reverse lookup. + void* (*_fn)(void*); + INLINE int priority_map(const int pri) { + switch (pri) { + case 0: + return 0; + case 1: + return normal_priority; + case 2: + return high_priority; + default: + return -1; + } + } + static void* thread_wrapper(void*); + public: + // interface to threads + static const thread_class* Null; + INLINE thread_class(void* data) : _b_ptr(data) {} + INLINE ~thread_class(void) {} + INLINE void manual_init(void) { + _thread = cthread_self(); + } + INLINE void start_pre(void* (*fn)(void*), const bool, const int) { + _fn = fn; + _thread = cthread_fork(thread_wrapper, (any_t)this); + } + INLINE void start_in(void) { + cthread_set_data(cthread_self(), (any_t)this); + } + INLINE void start_post(const bool det, const int) { + if (det) + cthread_detach(_thread); + } + INLINE void join(void** status) { + *status = cthread_join(_thread); + } + INLINE void set_priority(const int pri) { + kern_return_t rc = cthread_priority(_thread, priority_map(pri), + FALSE); + if (rc != KERN_SUCCESS) + throw thread_fatal(errno); + } + INLINE void* back_ptr(void) { return _b_ptr; } + static INLINE void exit(void* return_value) { + cthread_exit(return_value); + } + static INLINE thread_class* self(void) { + thread_class* me = (thread_class*)cthread_data(cthread_self()); + // if (me == thread_class::Null) not one of ours + return me; + } + static INLINE void yield(void) { + cthread_yield(); + } + }; + class library_class { + public: + static library_class* const Null; + INLINE library_class(str&) { + throw lib_load_invalid(); + } + INLINE ~library_class(void) { + throw lib_load_invalid(); + } + INLINE void* get_symbol(str&) { + throw lib_load_invalid(); + } + }; + static INLINE mutex_class *make_mutex(void) { + return new mutex_class; + } + static INLINE condition_class *make_condition(mutex_class* m) { + return new condition_class(m); + } + static INLINE semaphore_class *make_semaphore(const unsigned int initial) { + return new semaphore_class(initial); + } + static INLINE thread_class *make_thread(void* data) { + return new thread_class(data); + } + static INLINE library_class *make_library(str&) { + throw lib_load_invalid(); + } + static INLINE void sleep(const unsigned long secs, + const unsigned long nsecs = 0) { + const unsigned MAX_SLEEP_SECONDS=(unsigned)4294966; // (2**32-2)/1000 + if (secs <= MAX_SLEEP_SECONDS) { + thread_switch(THREAD_NULL, SWITCH_OPTION_WAIT, + secs * 1000 + nsecs / 1000000); + return; + } + unsigned no_of_max_sleeps = secs / MAX_SLEEP_SECONDS; + for (unsigned i=0; i + +#include "ipc_traits.h" + +class EXPCL_PANDAEXPRESS base_mutex { + public: + typedef ipc_traits traits; + typedef traits::mutex_class mutex_class; + + static base_mutex* const Null; + INLINE base_mutex(void) : _mutex(traits::make_mutex()) {} + INLINE ~base_mutex(void) { delete _mutex; } + INLINE void lock(void) { _mutex->lock(); } + INLINE void unlock(void) { _mutex->unlock(); } + INLINE mutex_class* get_mutex(void) { return _mutex; } + private: + mutex_class* _mutex; +}; + +class base_mutex_lock { + public: + typedef base_mutex mutex; + INLINE base_mutex_lock(mutex& m) : _mutex(m) { _mutex.lock(); } + INLINE ~base_mutex_lock(void) { _mutex.unlock(); } + private: + mutex& _mutex; +}; + +typedef base_mutex mutex; +typedef base_mutex_lock mutex_lock; + +#endif /* __IPC_MUTEX_H__ */ diff --git a/panda/src/ipc/ipc_nspr_traits.h b/panda/src/ipc/ipc_nspr_traits.h new file mode 100644 index 0000000000..25562672fd --- /dev/null +++ b/panda/src/ipc/ipc_nspr_traits.h @@ -0,0 +1,331 @@ +// Filename: ipc_nspr_traits.h +// Created by: cary (12Jan00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __IPC_NSPR_TRAITS_H__ +#define __IPC_NSPR_TRAITS_H__ + +#include +#include + +#include "datagram.h" + +class EXPCL_PANDAEXPRESS ipc_traits { +public: + class mutex_class; + class condition_class; + class semaphore_class; + class thread_class; + class library_class; + + class EXPCL_PANDAEXPRESS mutex_class { + private: + PRLock* _mutex; + public: + // interface to mutex + static mutex_class* const Null; + INLINE mutex_class(void) : _mutex(PR_NewLock()) { + if (_mutex == (PRLock*)0L) + cerr << "COULD NOT GET A MUTEX!" << endl; + } + INLINE ~mutex_class(void) { + PR_DestroyLock(_mutex); + } + INLINE void lock(void) { + PR_Lock(_mutex); + } + INLINE void unlock(void) { + PR_Unlock(_mutex); + } + INLINE PRLock* get_mutex(void) { + return _mutex; + } + }; + class EXPCL_PANDAEXPRESS condition_class { + private: + mutex_class* _mutex; + PRCondVar* _condition; + public: + static condition_class* const Null; + INLINE condition_class(mutex_class* m) + : _mutex(m), _condition(PR_NewCondVar(m->get_mutex())) {} + INLINE ~condition_class(void) { + PR_DestroyCondVar(_condition); + } + INLINE void wait(void) { + PR_WaitCondVar(_condition, PR_INTERVAL_NO_TIMEOUT); + } + INLINE int timedwait(const unsigned long secs, const unsigned long nsecs) { + unsigned long s, n; + ipc_traits::get_time(s, n); + PRIntervalTime i = PR_SecondsToInterval(secs - s) + + PR_MicrosecondsToInterval((nsecs - n) / 1000); + PR_WaitCondVar(_condition, i); + return 1; + } + INLINE void signal(void) { + PR_NotifyCondVar(_condition); + } + INLINE void broadcast(void) { + PR_NotifyAllCondVar(_condition); + } + }; + class EXPCL_PANDAEXPRESS semaphore_class { + private: + mutex_class _mutex; + condition_class _condition; + int _value; + class EXPCL_PANDAEXPRESS mutex_class_lock { + public: + INLINE mutex_class_lock(mutex_class& m) : _mutex(m) { + _mutex.lock(); + } + INLINE ~mutex_class_lock(void) { + _mutex.unlock(); + } + private: + mutex_class& _mutex; + }; + public: + static semaphore_class* const Null; + INLINE semaphore_class(const unsigned int initial) + : _mutex(), _condition(&_mutex), _value(initial) {} + INLINE ~semaphore_class(void) {} + INLINE void wait(void) { + mutex_class_lock l(_mutex); + while (_value == 0) + _condition.wait(); + --_value; + } + INLINE int trywait(void) { + mutex_class_lock l(_mutex); + if (_value == 0) + return 0; + --_value; + return 1; + } + INLINE void post(void) { + mutex_class_lock l(_mutex); + if (_value == 0) + _condition.signal(); + ++_value; + } + }; + class EXPCL_PANDAEXPRESS thread_class { + private: + PRThread* _thread; + void* _b_ptr; // a handle to hang a pointer back to the high-level + // thread class on. Gross, but I couldn't think of a + // better way to do this reverse lookup. + void* (*_fn)(void*); + void* _return_value; + INLINE PRThreadPriority priority_map(const int pri) { + switch (pri) { + case 0: + return PR_PRIORITY_LOW; + case 1: + return PR_PRIORITY_NORMAL; + case 2: + return PR_PRIORITY_HIGH; + } + return PR_PRIORITY_NORMAL; + } + static void thread_wrapper(void*); + public: + static thread_class* const Null; + INLINE thread_class(void* data) : _b_ptr(data) {} + INLINE ~thread_class(void) {} + INLINE void manual_init(void) { + _thread = PR_GetCurrentThread(); + } + INLINE void start_pre(void* (*fn)(void*), const bool det, const int pri) { + _fn = fn; + // create the thread, decide it's priority, if it's detached, and if it's + // a kernel thread, etc + _thread = PR_CreateThread(PR_USER_THREAD, thread_wrapper, this, + priority_map(pri), PR_GLOBAL_BOUND_THREAD, + det?PR_UNJOINABLE_THREAD:PR_JOINABLE_THREAD, + 256*1024); + } + INLINE void start_in(void) { + PR_SetThreadPrivate(__get_data_index(), this); + } + INLINE void start_post(const bool, const int) {} + INLINE void join(void** status) { + PR_JoinThread(_thread); + if (status) + *status = _return_value; + } + INLINE void set_priority(const int pri) { + PR_SetThreadPriority(_thread, priority_map(pri)); + } + INLINE void* back_ptr(void) { return _b_ptr; } + static INLINE void exit(void* return_value) { + thread_class* me = self(); + if (me != thread_class::Null) + me->_return_value = return_value; + // This gives us a seg fault upon exit - NSPR wants us to return from + // the callback function rather than exit() + //exit(0); // assuming that this is really a kernel thread. NSPR offers + // no 'thread exit' facility + } + static INLINE thread_class* self(void) { + return (thread_class*)PR_GetThreadPrivate(__get_data_index()); + } + static INLINE void yield(void) { + PR_Sleep(PR_INTERVAL_NO_WAIT); + } + }; + class EXPCL_PANDAEXPRESS library_class { + private: + PRLibrary* _lib; + public: + static library_class* const Null; + INLINE library_class(const std::string& lib) { + _lib = PR_LoadLibrary(lib.c_str()); + if (_lib == (PRLibrary*)0L) { + nout << "failed to load library '" << lib << "'" << endl; + } + } + INLINE ~library_class(void) { + if (_lib != (PRLibrary*)0L) { + PRStatus stat = PR_UnloadLibrary(_lib); + if (stat == PR_FAILURE) + nout << "failed to unload library" << endl; + } + } + INLINE void* get_symbol(const std::string& sym) { + if (_lib == (PRLibrary*)0L) { + nout << "library not correctly loaded" << endl; + return (void *)0L; + } + return PR_FindSymbol(_lib, sym.c_str()); + } + }; + class EXPCL_PANDAEXPRESS file_class { + //interface to file IO + public: + INLINE file_class(const std::string& file = "") : _filename(file), _fhandle((PRFileDesc*)NULL){} + INLINE ~file_class() { close(); } + INLINE void setFile(const std::string& file) { _filename = file; close(); } + INLINE bool open(const int mode) { + close(); + switch(mode){ + case 0: _fhandle = PR_Open(_filename.c_str(), PR_RDONLY, 00666); break; + case 1: _fhandle = PR_Open(_filename.c_str(), + PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 00666); break; + case 2: _fhandle = PR_Open(_filename.c_str(), + PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 00666); break; + case 3: _fhandle = PR_Open(_filename.c_str(), PR_APPEND | PR_CREATE_FILE, 00666); break; + default: _fhandle = PR_Open(_filename.c_str(), PR_RDONLY, 00666); break; + } + return (_fhandle != (PRFileDesc *)NULL); + } + INLINE void close(void) { + if (_fhandle != (PRFileDesc *)NULL) { PR_Close(_fhandle); _fhandle = (PRFileDesc *)NULL; } + } + INLINE bool empty(void){ + return (PR_Available(_fhandle) <= 0); + } + INLINE int readin(string &block, int numBytes) { + //Don't try and read less than nothing + nassertr(numBytes >= 0, 0); + int numRead; + char* temp = new char[numBytes]; + if (_fhandle != (PRFileDesc *)NULL){ + numRead = PR_Read(_fhandle, temp, numBytes); + block.assign(temp, numRead); + delete temp; + return numRead; + } + return 0; + } + INLINE int writeout(const string &block) { + //Make sure there is something to write out + nassertr(block.size() > 0, 0); + if (_fhandle != (PRFileDesc *)NULL) { + return PR_Write(_fhandle, (void*)block.data(), block.size()); + } + return 0; + } + private: + string _filename; + PRFileDesc* _fhandle; + }; + + static INLINE mutex_class* make_mutex(void) { + return new mutex_class; + } + static INLINE condition_class* make_condition(mutex_class* m) { + return new condition_class(m); + } + static INLINE semaphore_class* make_semaphore(const unsigned int initial) { + return new semaphore_class(initial); + } + static INLINE thread_class* make_thread(void* data) { + return new thread_class(data); + } + static INLINE library_class *make_library(const std::string& lib) { + return new library_class(lib); + } + static INLINE file_class *make_file(const std::string& file) { + return new file_class(file); + } + static INLINE void sleep(const unsigned long secs, + const unsigned long nsecs = 0) { + PRIntervalTime i = PR_SecondsToInterval(secs) + + PR_MicrosecondsToInterval(nsecs / 1000); + PR_Sleep(i); + } + static INLINE void get_time(unsigned long& abs_secs, + unsigned long& abs_nsecs, + const unsigned long rel_secs = 0, + const unsigned long rel_nsecs = 0) { + PRTime t = PR_Now(); + PRTime t_offset; + PRTime usec_convert; + PRTime rel_time; + PRTime w, x, y; + PRTime s, n; + + // really this step should only ever be done once + LL_UI2L(usec_convert, PR_USEC_PER_SEC); + + LL_UI2L(w, rel_secs); + LL_UI2L(x, (rel_nsecs / 1000)); + LL_MUL(y, usec_convert, w); + LL_ADD(rel_time, x, y); // the PRTime representing the rel componant + + LL_ADD(t_offset, t, rel_time); + + LL_DIV(s, t_offset, usec_convert); + LL_MOD(n, t_offset, usec_convert); + + LL_L2UI(abs_secs, s); + LL_L2UI(abs_nsecs, n); + abs_nsecs *= 1000; + } + static INLINE int set_atomic(int& var, int val) { + return PR_AtomicSet(&var, val); + } + static INLINE int inc_atomic(int& var) { + return PR_AtomicIncrement(&var); + } + static INLINE int dec_atomic(int& var) { + return PR_AtomicDecrement(&var); + } + // this really should be private, but it's annoying to make config a + // friend + static INLINE void __set_data_index(PRUintn d) { + _data_index = d; + } + static INLINE PRUintn __get_data_index(void) { + return _data_index; + } +private: + // other data specific to this implementation + static PRUintn _data_index; +}; + +#endif /* __IPC_NSPR_TRAITS_H__ */ diff --git a/panda/src/ipc/ipc_nt_traits.cxx b/panda/src/ipc/ipc_nt_traits.cxx new file mode 100644 index 0000000000..bb1f3d0219 --- /dev/null +++ b/panda/src/ipc/ipc_nt_traits.cxx @@ -0,0 +1,17 @@ +// Filename: ipc_nt_traits.cxx +// Created by: mike (23Oct98) +// +//////////////////////////////////////////////////////////////////// + +#ifdef WIN32_VC + +#include "ipc_nt_traits.h" + +const int ipc_traits::days_in_preceeding_months[12] = { 0, 31, 59, 90, + 120, 151, 181, 212, + 243, 273, 304, 334 }; +const int ipc_traits::days_in_preceeding_months_leap[12] = { 0, 31, 60, + 91, 121, 152, 182, 213, + 244, 274, 305, 335 }; + +#endif diff --git a/panda/src/ipc/ipc_nt_traits.h b/panda/src/ipc/ipc_nt_traits.h new file mode 100644 index 0000000000..fea61fe7c3 --- /dev/null +++ b/panda/src/ipc/ipc_nt_traits.h @@ -0,0 +1,556 @@ +// Filename: ipc_nt_traits.h +// Created by: cary (16Sep98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __IPC_NT_TRAITS_H__ +#define __IPC_NT_TRAITS_H__ + +#include + +#include +#include +#define WINDOWS_LEAN_AND_MEAN +#include +#undef WINDOWS_LEAN_AND_MEAN +#include + +#ifdef WIN32_VC +const unsigned long SEMAPHORE_MAX = 0x7fffffff; +#endif + +// much of the *next and *prev linked list stuff in here should probably be +// done with STL containers. Examine this when time permits. +class EXPCL_PANDAEXPRESS ipc_traits { + public: + class mutex_class; + class condition_class; + class semaphore_class; + class thread_class; + class library_class; + class file_class; + class mutex_class { + private: + CRITICAL_SECTION _mutex; + public: + // interface to mutex + static const mutex_class* Null; + INLINE mutex_class(void) { InitializeCriticalSection(&_mutex); } + INLINE ~mutex_class(void) { DeleteCriticalSection(&_mutex); } + INLINE void lock(void) { EnterCriticalSection(&_mutex); } + INLINE void unlock(void) { LeaveCriticalSection(&_mutex); } + }; + class thread_class { + private: + HANDLE _handle; + DWORD _id; + void* _return_value; + HANDLE _condition; + thread* _tnext; + thread* _tprev; + BOOL _waiting; + void* _b_ptr; // a handle to hang a pointer back to the high-level + // thread class on. Gross, but I couldn't think of + // a better way to do this reverse lookup. + void* (*_fn)(void*); + INLINE int priority_map(const int pri) { + switch (pri) { + case 0: + return THREAD_PRIORITY_LOWEST; + case 1: + return THREAD_PRIORITY_NORMAL; + case 2: + return THREAD_PRIORITY_HIGHEST; + } + throw thread_invalid(); + // never gets here, but keeps the compiler happy + return 0; + } +#ifndef __BCPLUSPLUS__ + static unsigned _stdcall thread_wrapper(void*); +#else + static void _USERENTRY thread_wrapper(void*); +#endif + public: + // interface to threads + static const thread_class* Null; + INLINE thread_class(void* data) : _b_ptr(data), + _tnext(thread_class::Null), _tprev(thread_class::Null), + _waiting(FALSE), _handle(NULL) { + _condition = CreateSemaphore(NULL, 0, SEMAPHORE_MAX, NULL); + if (_condition == NULL) + throw thread_fatal(GetLastError()); + } + INLINE ~thread_class(void) { + if (!CloseHandle(_handle)) + throw thread_fatal(GetLastError()); + if (!CloseHandle(_condition)) + throw thread_fatal(GetLastError()); + } + INLINE void manual_init(void) { + if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), &_handle, 0, FALSE, + DUPLICATE_SAME_ACCESS)) + throw thread_fatal(GetLastError()); + _id = GetCurrentThreadId(); + if (!SetThreadPriority(_handle, priority_map(PRIORITY_NORMAL))) + throw thread_fatal(GetLastError()); + } + INLINE void start_pre(void* (*fn)(void*), const bool, const int) { + _fn = fn; +#ifndef __BCPLUSPLUS__ + // MSVC++ or compatable + unsigned int t; + _handle = (HANDLE)_beginthreadex(NULL, 0, thread_wrapper, + (LPVOID)this, CREATE_SUSPENDED, + &t); + _id = t; + if (_handle == NULL) + throw thread_fatal(GetLastError()); +#else + // Borland C++ + _handle = (HANDLE)_beginthreadNT(thread_wrapper, 0, (void*)this, + NULL, CREATE_SUSPENDED, &_id); + if (_handle == INVALID_HANDLE_VALUE) + throw thread_fatal(errno); +#endif + } + INLINE void start_in(void) { + if (!TlsSetValue(self_tls_index, (LPVOID)this)) + throw thread_fatal(GetLastError()); + } + INLINE void start_post(const bool, const int pri) { + if (!SetThreadPriority(_handle, pri)) + throw thread_fatal(GetLastError()); + if (ResumeThread(_handle) == 0xffffffff) + throw thread_fatal(GetLastError()); + } + INLINE void join(void** status) { + if (WaitForSingleObject(_handle, INFINITE) != WAIT_OBJECT_0) + throw thread_fatal(GetLastError()); + if (status) + *status = _return_value; + } + INLINE void set_priority(const int pri) { + if (!SetThreadPriority(_handle, priority_map(pri))) + throw thread_fatal(GetLastError()); + } + INLINE void* back_ptr(void) { return _b_ptr; } + static INLINE void exit(void* return_value) { + thread_class* me = self(); + if (me != thread_class::Null) + me->_return_val = return_value; +#ifndef __BCPLUSPLUS__ + _endthreadex(0); +#else + _endthread(); +#endif + } + static INLINE thread_class* self(void) { + LPVOID me = TlsGetValue(self_tls_index); + if (me == NULL) + return thread_class::Null; + return (thread_class*)me; + } + static INLINE void yield(void) { + Sleep(0); + } + }; + // Condition variables are rather annoying to implement using NT + // synchronisation primitives, since none of them have the atomic + // "release mutex and wait to be signalled" which is central to the + // idea of a condition variable. To get around this the solution is to + // record which threads are waiting and explicitly wake up those threads. + // + // Here we implement a condition variable using a list of waiting threads + // (protected by a critical section), and a per-thread semaphore (which + // actually only needs to be a binary semaphore). + // + // To wait on the cv, a thread puts itself on the list of waiting threads + // for that xv, then releases the mutex and waits on its own personal + // semaphore. A signalling thread simply takes a thread from the head of + // the list and kicks that thread's semaphore. Broadcast is simply + // implemented by kicking the semaphore of each waiting thread. + // + // The only other tricky part comes when a thread gets a timeout from a + // timed wait on it's semaphore. Between returning with a timeout from + // the wait and entering the critical section, a signalling thread could + // get in, kick the waiting thread's semaphore and remove it from the + // list. If this happens, the waiting thread's semaphore is now out of + // step so it needs resetting, and the thread should indicate that it was + // signalled reather than that it timed out. + // + // It is possible that the thread calling wait or timedwait is not one + // of our threads. In this case we have to provide a temporary data + // structure, i.e. for the duration of the call, for the thread to link + // itself on the list of waiting threads. _internal_thread_dummy + // provides such a data structure and _internal_thread_helper is a helper + // class to deal with this special case for wait() and timedwait(). Once + // created, the _internal_thread_dummy is cached for use by the next + // wait() or timedwait() call from a thread that is not one of ours. + // This is probably worth doing because creating a Semaphore is quite + // heavy weight. + class condition_class { + private: + CRITICAL_SECTION _condition; + thread_class* waiting_head; + thread_class* waiting_tail; + mutex_class* _mutex; + class _internal_thread_helper; + class _internal_thread_dummy : public thread_class { + public: + INLINE _internal_thread_dummy(void) : + next((_internal_thread_dummy*)0L) {} + INLINE ~_internal_thread_dummy(void) {} + friend class _internal_thread_helper; + private: + _internal_thread_dummy* next; + }; + class _internal_thread_helper { + public: + INLINE _internal_thread_helper(void) : + d((_internal_thread_dummy*)0L), + t(thread_class::self()) { + if (cachelock == mutex_class::Null) + cachelock = new mutex_class; + if (t == thread_class::Null) { + cachelock->lock(); + if (cache) { + d = cache; + cache = cache->next; + } else + d = new _internal_tread_dummy; + t = d; + cachelock->unlock(); + } + } + INLINE ~_internal_thread_helper(void) { + if (d != (_internal_thread_dummy*)0L) { + cachelock->lock(); + d->next = cache; + cache = d; + cachelock->unlock(); + } + } + INLINE operator thread_class*(void) { return t; } + INLINE thread_class* operator->(void) { return t; } + static _internal_thread_dummy* cache; + static mutex_class* cachelock; + private: + _internal_thread_dummy* d; + thread_class* t; + }; + public: + // interface to condition variables + static const condition_class* Null; + INLINE condition_class(mutex_class*) : _mutex(m) { + InitializeCriticalSection(&_condition); + waiting_head = waiting_tail = thread_class::Null; + } + INLINE ~condition_class(void) { + DeleteCriticalSection(&_condition); + if (waiting_head != thread_class::Null) + DEBUG() << "thread_ipc_traits<\"nt\">::condition_class::~condition_class : list of waiting threads is not empty" << nend; + } + INLINE void wait(void) { + _internal_thread_helper me; + EnterCriticalSection(&_condition); + + me->cond_next = thread_class::Null; + me->cond_prev = waiting_tail; + if (waiting_head == thread_class::Null) + waiting_head = me; + else + waiting_tail->cond_next = me; + waiting_tail = me; + me->cond_waiting = TRUE; + + LeaveCriticalSection(&_condition); + _mutex->unlock(); + DWORD result = WaitForSingleObject(me->cond_semaphore, INFINITE); + _mutex->lock(); + if (result != WAIT_OBJECT_0) + throw condition_fatal(GetLastError()); + } + INLINE int timedwait(const unsigned long secs, + const unsigned long nsecs) { + _internal_thread_helper me; + EnterCriticalSection(&_condition); + + me->cond_next = thread_class::Null; + me->cond_pref = waiting_tail; + if (waiting_tail == thread_class::Null) + waiting_head = me; + else + waiting_tail->cond_next = me; + waiting_tail = me; + me->cond_waiting = TRUE; + + LeaveCriticalSection(&_condition); + _mutex->unlock(); + unsigned long now_sec, now_nsec; + thread_ipc_traits::get_time_now(now_sec, now_nsec); + DWORD timeout = (secs - now_sec) * 1000 + + (nsecs - now_nsecs) / 1000000; + if ((secs <= new_sec) && ((secs < now_sec) || + (nsecs < now_nsecs))) + timeout = 0; + DWORD result = WaitForSingleObject(me->cond_semaphore, timeout); + if (result == WAIT_TIMEOUT) { + EnterCriticalSection(&_condition); + if (me->cond_waiting) { + if (me->cond_prev != thread_class::Null) + me->cond_prev->cond_next = me->cond_next; + else + waiting_head = me->cond_next; + if (me->cond_next != thread_class::Null) + me->cond_next->cond_prev = me->cond_prev; + else + waiting_tail = me->cond_prev; + me->cond_waiting = FALSE; + LeaveCriticalSection(&_condition); + _mutex->lock(); + return 0; + } + // We timed out but another thread still signalled us. Wait + // for the semaphore (it _must_ have been signalled) to + // decrement it again. Return that we were signalled, not + // that we timed out. + LeaveCriticalSection(&_condition); + result = WaitForSingleObject(me->cond_semaphore, INFINITE); + } + if (result != WAIT_OBJECT_0) + throw condition_fatal(GetLastError()); + _mutex->lock(); + return 1; + } + INLINE void signal(void) { + EnterCriticalSection(&_condition); + if (waiting_head != thread_class::Null) { + thread_class* t = waiting_head; + waiting_head = t->cond_next; + if (waiting_head == thread_class::Null) + waiting_tail = thread_class::Null; + else + waiting_head->cond_prev = thread_class::Null; + t->cond_waiting = FALSE; + if (!ReleaseSemaphore(t->cond_semaphore, 1, NULL)) { + int rc = GetLastError(); + LeaveCriticalSection(&_condition); + throw condition_fatal(rc); + } + } + LeaveCriticalSection(&_condition); + } + INLINE void broadcast(void) { + EnterCriticalSection(&_condition); + while (waiting_head != thread_class::Null) { + thread_class* t = waiting_head; + waiting_head = t->cond_next; + if (waiting_head == thread_class::Null) + waiting_tail = thread_class::Null; + else + waiting_head->cond_prev = thread_class::Null; + t->cond_waiting = FALSE; + if (!ReleaseSemaphore(t->cond_semaphore, 1, NULL)) { + int rc = GetLastError(); + LeaveCriticalSection(&_condition); + throw condition_fatal(rc); + } + } + LeaveCriticalSection(&_condition); + } + }; + class semaphore_class { + private: + HANDLE _semaphore; +#ifndef WIN32_VC + const unsigned long SEMAPHORE_MAX = 0x7fffffff; +#endif + public: + // interface to semaphores + static const semaphore_class* Null; + INLINE semaphore_class(const unsigned int initial) : + _semaphore(CreateSemaphore(NULL, initial, SEMAPHORE_MAX, NULL)) { + if (_semaphore == NULL) { + DEBUG() << "thread_ipc_traits<\"nt\">::semaphore_class::semaphore_class : CreateSemaphore error " + << GetLastError() << nend; + throw semaphore_fatal(GetLastError()); + } + } + INLINE ~semaphore_class(void) { + if (!CloseHandle(_semaphore)) { + DEBUG() << "thread_ipc_traits<\"nt\">::semaphore_class::~semaphore_class : CloseHandle error " + << GetLastError() << nend; + throw semaphore_fatal(GetLastError()); + } + } + INLINE void wait(void) { + if (WaitForSingleObject(_semaphore, INFINITE) != WAIT_OBJECT_0) + throw semaphore_fatal(GetLastError()); + } + INLINE int trywait(void) { + switch (WaitForSingleObject(_semaphore, 0)) { + case WAIT_OBJECT_0: return 1; + case WAIT_TIMEOUT: return 0; + } + throw semaphore_fatal(GetLastError()); + return 0; // never really gets here, but the compilers like it + } + INLINE void post(void) { + if (!ReleaseSemaphore(_semaphore, 1, NULL)) + throw semaphore_fatal(GetLastError()); + } + }; + class library_class { + public: + // interface to dynamically loaded libraries + static library_class* const Null; + INLINE library_class(std::string&) { + throw lib_load_invalid(); + } + INLINE ~library_class(void) { + throw lib_load_invalid(); + } + INLINE void* get_symbol(std::string&) { + throw lib_load_invalid(); + } + }; + // BROKEN!! NOT FINISHED + class file_class { + //interface to file IO + public: + INLINE file_class(std::string& file) : _filename(file), _fhandle(NULL){}; + INLINE file_class() : _filename(NULL), _fhandle(NULL) {}; + INLINE ~file_class() { close(); } + INLINE void setFile(std::string& file) { _filename = file; close(); } + INLINE bool open(const int mode) { + close(); + if (_filename.data() != NULL) { + switch(mode){ + case 0: _fhandle = fopen(_filename.data(), "rb"); break; + case 1: _fhandle = fopen(_filename.data(), "wb"); break; + case 2: _fhandle = fopen(_filename.data(), "rwb"); break; + default: _fhandle = fopen(_filename.data(), "rb"); break; + } + if (_fhandle != NULL) return true; + } + return false; + } + INLINE void close(void) { + if (_fhandle != NULL) { fclose(_fhandle); _fhandle = NULL; } + } + /* + INLINE bool readin(MemBuf& buffer, int numBytes) { + BYTE* block = new BYTE[numBytes]; + if (_fhandle != NULL){ + fread(block, sizeof(BYTE), numBytes, _fhandle); + if (!ferror(_fhandle)){ buffer.addBack(block); delete [] block; return true; } + } + delete [] block; + return false; + } + INLINE bool writeout(MemBuf& buffer, int numBytes) { + BYTE* block = buffer.getBlock(0, numBytes); + if (_fhandle != NULL) { + fwrite(block, sizeof(BYTE), numBytes, _fhandle); + if (!ferror(_fhandle)) { delete [] block; return true; } + } + delete [] block; + return false; + } + INLINE bool readin(BYTE* block, int numBytes) { + if (_fhandle != NULL){ + fread(block, sizeof(BYTE), numBytes, _fhandle); + if (!ferror(_fhandle)){ return true; } + } + return false; + } + INLINE bool writeout(BYTE* block, int numBytes) { + if (_fhandle != NULL) { + fwrite(block, sizeof(BYTE), numBytes, _fhandle); + if (!ferror(_fhandle)) { return true; } + } + return false; + } + */ + private: + string _filename; + FILE* fhandle; + }; + static INLINE mutex_class *make_mutex(void) { + return new mutex_class; + } + static INLINE condition_class *make_condition(mutex_class* m) { + return new condition_class(m); + } + static INLINE semaphore_class *make_semaphore(const unsigned int initial) { + return new semaphore_class(initial); + } + static INLINE thread_class *make_thread(void* data) { + return new thread_class(data); + } + static INLINE library_class *make_thread(std::string&) { + throw lib_load_invalid(); + } + static INLINE file_class *make_file(std::string& file) { + return new file_class(file); + } + static INLINE void sleep(const unsigned long secs, + const unsigned long nsecs = 0) { + const DWORD MAX_SLEEP_SECONDS = (DWORD)4294966; // (2**32-2)/1000 + if (secs <= MAX_SLEEP_SECONDS) { + Sleep(secs * 1000 + nsecs / 1000000); + return; + } + DWORD no_of_max_sleeps = secs / MAX_SLEEP_SECONDS; + for (DWORD i=0; i + +#if defined(__alpha__) && defined(__osf1__) || defined(__hpux__) +// stop unnecessary definitions of TRY, etc on OSF +#ifndef EXC_HANDLING +#define EXC_HANDLING +#endif /* EXC_HANDLING */ +#endif /* __alpha__, __hpux__ */ + +// hack alert! +#ifdef __GNUC__ +#define _MIT_POSIX_THREADS 1 +#define PthreadDraftVersion 7 +#define NoNanoSleep + +#include +/* +extern "C" { +void usleep(unsigned long); +} +*/ +#endif /* __GNUC__ */ + +#include +#include +#include +#include + +#if (defined(__GLIBC__) && __GLIBC__ >= 2) +// typedef of struct timeval and gettimeofday(); +#include +#include +#endif /* __GLIBC__ */ + +// #if defined(__linux__) && defined(_MIT_POSIX_THREADS) +// #include +// #endif /* __linux__ & _MIT_POSIX_THREADS */ + +#if defined(__sgi) +#define PthreadSupportThreadPriority +// this next is just a guess +#define PthreadDraftVersion 10 +#include +#endif + +#define THROW_FATAL(x, t) throw t##_fatal(x) +#if (PthreadDraftVersion <= 6) +#define ERRNO(x) (((x) != 0) ? (errno) : 0) +#ifdef __VMS +// pthread_setprio returns old priority on success (draft version 4: +// OpenVms version < 7) +#define THROW_ERRORS(x, t) { if ((x) == -1) THROW_FATAL(errno, t); } +#else +#define THROW_ERRORS(x, t) { if ((x) != 0) THROW_FATAL(errno, t); } +#endif /* __VMS */ +#else +#define ERRNO(x) (x) +#define THROW_ERRORS(x, t) { int rc = (x); \ + if (rc != 0) THROW_FATAL(rc, t); } +#endif /* PthreadDraftVersion 6 */ +#define THROW_ERRORS_G(x) THROW_ERRORS(x, ipc) +#define THROW_ERRORS_M(x) THROW_ERRORS(x, mutex) +#define THROW_ERRORS_C(x) THROW_ERRORS(x, condition) +#define THROW_ERRORS_S(x) THROW_ERRORS(x, semaphore) +#define THROW_ERRORS_T(x) THROW_ERRORS(x, thread) + +class EXPCL_PANDAEXPRESS ipc_traits { + public: + class mutex_class; + class condition_class; + class semaphore_class; + class thread_class; + class library_class; + class mutex_class { + private: + pthread_mutex_t _mutex; + public: + // interface to mutex + static mutex_class* const Null; + INLINE mutex_class(void) { +#if (PthreadDraftVersion == 4) + THROW_ERRORS_M(pthread_mutex_init(&_mutex, pthread_mutexattr_default)); +#else + THROW_ERRORS_M(pthread_mutex_init(&_mutex, 0)); +#endif + } + INLINE ~mutex_class(void) { + THROW_ERRORS_M(pthread_mutex_destroy(&_mutex)); + } + INLINE void lock(void) { + THROW_ERRORS_M(pthread_mutex_lock(&_mutex)); + } + INLINE void unlock(void) { + THROW_ERRORS_M(pthread_mutex_unlock(&_mutex)); + } + INLINE pthread_mutex_t* get_mutex(void) { return &_mutex; } + }; + class condition_class { + private: + pthread_cond_t _condition; + mutex_class* _mutex; + public: + // interface to condition variables + static condition_class* const Null; + INLINE condition_class(mutex_class* m) : _mutex(m) { +#if (PthreadDraftVersion == 4) + THROW_ERRORS_C(pthread_cond_init(&_condition, pthread_condattr_default)); +#else + THROW_ERRORS_C(pthread_cond_init(&_condition, 0)); +#endif + } + INLINE ~condition_class(void) { + THROW_ERRORS_C(pthread_cond_destroy(&_condition)); + } + INLINE void wait(void) { + THROW_ERRORS_C(pthread_cond_wait(&_condition, + _mutex->get_mutex())); + } + INLINE int timedwait(const unsigned long secs, + const unsigned long nsecs) { + timespec rqts = { (long)secs, (long)nsecs }; + int rc = ERRNO(pthread_cond_timedwait(&_condition, + _mutex->get_mutex(), + &rqts)); + if (rc == 0) + return 1; +#if (PthreadsDraftVersion <= 6) + if (rc == EAGAIN) + return 0; +#endif + if (rc == ETIMEDOUT) + return 0; + throw condition_fatal(rc); + } + INLINE void signal(void) { + THROW_ERRORS_C(pthread_cond_signal(&_condition)); + } + INLINE void broadcast(void) { + THROW_ERRORS_C(pthread_cond_broadcast(&_condition)); + } + }; + class semaphore_class { + private: + mutex_class _mutex; + condition_class _condition; + int _value; + class mutex_class_lock { + public: + INLINE mutex_class_lock(mutex_class& m) : _mutex(m) { + _mutex.lock(); + } + INLINE ~mutex_class_lock(void) { _mutex.unlock(); } + private: + mutex_class& _mutex; + }; + public: + // interface to semaphores + static semaphore_class* const Null; + INLINE semaphore_class(const unsigned int initial) : _mutex(), + _condition(&_mutex), _value(initial) {} + INLINE ~semaphore_class(void) {} + INLINE void wait(void) { + mutex_class_lock l(_mutex); + while (_value == 0) + _condition.wait(&_mutex); + --_value; + } + INLINE int trywait(void) { + mutex_class_lock l(_mutex); + if (_value == 0) { + return 0; + } + --_value; + return 1; + } + INLINE void post(void) { + mutex_class_lock l(_mutex); + if (_value == 0) + _condition.signal(); + ++_value; + } + }; + class thread_class { + private: + pthread_t _thread; + void* _b_ptr; // a handle to hang a pointer back to the high-level + // thread class on. Gross, but I couldn't think of + // a better way to do this reverse lookup. + void* (*_fn)(void*); + INLINE int priority_map(const int pri) { +#ifdef PthreadSupportThreadPriority + switch (pri) { + case 0: + return lowest_priority; + case 1: + return normal_priority; + case 2: + return highest_priority; + } +#endif /* PthreadSupportThreadPriority */ + throw thread_invalid(); + } + static void* thread_wrapper(void*); + public: + // interface to threads + static thread_class* const Null; + INLINE thread_class(void* data) : _b_ptr(data) {} + INLINE ~thread_class(void) {} + INLINE void manual_init(void) { + _thread = pthread_self(); +#ifdef PthreadSupportPriority +#if (PthreadDraftVersion == 4) + THROW_ERRORS_T(pthread_setprio(_thread, + priority_map(PRIORITY_NORMAL))); +#elif (PthreadDraftVersion == 6) + pthread_attr_t attr; + pthread_attr_init(&attr); + THROW_ERRORS_T(pthread_attr_setprio(&attr, + priority_map(PRIORITY_NORMAL))); + THROW_ERRORS_T(pthread_setschedattr(_thread, attr)); +#else + struct sched_param sparam; + sparam.sched_priority = priority_map(PRIORITY_NORMAL); + THROW_ERRORS_T(pthread_setschedparam(_thread, SCHED_OTHER, + ¶m)); +#endif /* PthreadDraftVersion */ +#endif /* PthreadSupportPriority */ + } + INLINE void start_pre(void* (*fn)(void*), const bool, + const int pri) { + _fn = fn; + pthread_attr_t attr; +#if (PthreadDraftVersion == 4) + pthread_attr_create(&attr); +#else + pthread_attr_init(&attr); +#endif +#if (PthreadDraftVersion == 8) + pthread_attr_setdetachedstate(&attr, PTHREAD_CREATE_UNDETACHED); +#endif +#ifdef PthreadSupportThreadPriority +#if (PthreadDraftVersion <= 6) + THROW_ERRORS_T(pthread_attr_setprio(&attr, priority_map(pri))); +#else + struct sched_param sparam; + sparam.sched_priority = priority_map(pri); + THROW_ERRORS_T(pthread_attr_setschedparam(&attr, &sparam)); +#endif +#endif /* PthreadSupportThreadPriority */ +#if defined(__osf1__) && defined(__alpha__) || defined(__VMS) + // we're going to require a larger stack size then the default + // (21120) on OSF/1 + THROW_ERRORS_T(pthread_attr_setstacksize(&attr, 32768)); +#endif +#if (PthreadDraftVersion == 4) + THROW_ERRORS_T(pthread_create(&_thread, attr, thread_wrapper, + (void*)this)); + pthread_attr_delete(&attr); +#else + THROW_ERRORS_T(pthread_create(&_thread, &attr, thread_wrapper, + (void*)this)); + pthread_attr_destroy(&attr); +#endif + } + INLINE void start_in(void) { + THROW_ERRORS_T(pthread_setspecific(self_key, this)); + } + INLINE void start_post(const bool det, const int) { + if (det) { +#if (PthreadDraftVersion <= 6) + THROW_ERRORS_T(pthread_detach(&_thread)); +#else + THROW_ERRORS_T(pthread_detach(_thread)); +#endif + } + } + INLINE void join(void** status) { + THROW_ERRORS_T(pthread_join(_thread, status)); +#if (PthreadDraftVersion == 4) + // With draft 4 pthreads implementations (HPUS 10.x and Digital + // Unix 3.2), you have to detach the thread after join. If not, + // the storage for the thread will not be reclaimed. + THROW_ERRORS_T(pthread_detach(&_thread)); +#endif + } + INLINE void set_priority(const int pri) { +#ifdef PthreadSupportThreadPriority +#if (PthreadDraftVersion == 4) + THROW_ERRORS_T(pthread_setprio(_thread, priority_map(pri))); +#elif (PthreadDraftVersion == 6) + pthread_attr_t attr; + pthread_attr_init(&attr); + THROW_ERRORS_T(pthread_attr_setprio(&attr, priority_map(pri))); + THROW_ERRORS_T(pthread_setschedattr(_thread, attr)); +#else + struct sched_param sparam; + sparam.sched_priority = priority_map(pri); + THROW_ERRORS_T(pthread_setschedparam(_thread, SCHED_OTHER, + &sparam)); +#endif +#endif /* PthreadSupportThreadPriority */ + } + INLINE void* back_ptr(void) { return _b_ptr; } + static INLINE void exit(void* return_value) { + pthread_exit(return_value); + } + static INLINE thread_class* self(void) { + thread_class* me(thread_class::Null); +#if (PthreadDraftVersion <= 6) + THROW_ERRORS_T(pthread_getspecific(self_key, (void**)&me)); +#else + me = (thread_class*)pthread_getspecific(self_key); +#endif + return me; + } + static INLINE void yield(void) { +#if (PthreadDraftVersion == 6) + pthread_yield(NULL); +#elif (PthreadDraftVersion < 9) + pthread_yield(); +#else + THROW_ERRORS_T(sched_yield()); +#endif + } + }; + class library_class { + public: + static library_class* const Null; + INLINE library_class(ConfigString&) { + throw lib_load_invalid(); + } + INLINE ~library_class(void) { + throw lib_load_invalid(); + } + INLINE void* get_symbol(ConfigString&) { + throw lib_load_invalid(); + } + }; + static INLINE mutex_class *make_mutex(void) { + return new mutex_class; + } + static INLINE condition_class *make_condition(mutex_class* m) { + return new condition_class(m); + } + static INLINE semaphore_class *make_semaphore(const unsigned int initial) { + return new semaphore_class(initial); + } + static INLINE thread_class *make_thread(void* data) { + return new thread_class(data); + } + static INLINE library_class *make_library(ConfigString&) { + throw lib_load_invalid(); + } + static INLINE void sleep(const unsigned long secs, + const unsigned long nsecs = 0) { + timespec rqts = { (long)secs, (long)nsecs}; +#ifndef NoNanoSleep + if (nanosleep(&rqts, (timespec*)NULL) != 0) { + throw ipc_fatal(errno); + } +#else +#if defined(__osf1__) && defined(__alpha__) || defined(__hpux__) && (__OSVERSION__ == 10) || defined(__VMS) || defined(__SINIX__) + if (pthread_delay_np(&rqts) != 0) { + throw ipc_fatal(errno); + } +#elif defined(__linux__) || defined(__aix__) + // this seems terribly inaccurate, and should be re-examined + if (secs > 2000) + sleep(secs); + else + usleep(secs * 1000000 + (nsecs / 1000)); +#else + throw ipc_invalid(); +#endif +#endif /* NoNanoSleep */ + } + static INLINE void get_time(unsigned long& abs_secs, + unsigned long& abs_nsecs, + const unsigned long rel_secs = 0, + const unsigned long rel_nsecs = 0) { + timespec abs; +#if defined(__osf1__) && defined(__alpha__) || defined(__hpux__) && (__OSVERSION__ == 10) || defined(__VMS) || defined(__SINIX__) + timespec rel; + rel.tv_sec = rel_secs; + rel.tv_nsec = rel_nsecs; + THROW_ERRORS_G(pthread_get_expiration_np(&rel, &abs)); +#else +#if defined(__linux__) || defined(__aix__) + struct timeval tv; + gettimeofday(&tv, NULL); + abs.tv_sec = tv.tv_sec; + abs.tv_nsec = tv.tv_usec * 1000; +#else + clock_gettime(CLOCK_REALTIME, &abs); +#endif /* __linux__, __aix__, ... */ + abs.tv_nsec += rel_nsecs; + abs.tv_sec += rel_secs + abs.tv_nsec / 1000000000; + abs.tv_nsec = abs.tv_nsec & 1000000000; +#endif /* __osf1__, __alpha__, ... */ + abs_secs = abs.tv_sec; + abs_nsecs = abs.tv_nsec; + } + static INLINE int set_atomic(int&, int) { + throw set_atomic_invalid(); + } + static INLINE int inc_atomic(int&) { + throw inc_atomic_invalid(); + } + static INLINE int dec_atomic(int&) { + throw dec_atomic_invalid(); + } + // These should be private, but is annoying to make config a + // friend + static INLINE void __set_self_key(pthread_key_t k) { + self_key = k; + } + static INLINE void __set_lowest_priority(int p) { + lowest_priority = p; + } + static INLINE void __set_normal_priority(int p) { + normal_priority = p; + } + static INLINE void __set_highest_priority(int p) { + highest_priority = p; + } + private: + // other data specific to this implementation + static pthread_key_t self_key; +#ifdef PthreadSupportThreadPriority + static int lowest_priority; + static int normal_priority; + static int highest_priority; +#endif /* PthreadSupportThreadPriority */ +}; + +#endif /* __IPC_POSIX_TRAITS_H__ */ diff --git a/panda/src/ipc/ipc_ps2_traits.h b/panda/src/ipc/ipc_ps2_traits.h new file mode 100644 index 0000000000..5672dbd89e --- /dev/null +++ b/panda/src/ipc/ipc_ps2_traits.h @@ -0,0 +1,474 @@ +// Filename: ipc_ps2_traits.h +// created by: charles (07jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __ipc_ps2_traits_h__ +#define __ipc_ps2_traits_h__ + +#include +#include +#include +#include +#include +#include +#include + +// OK. The deal with the PS2 is that there is native library thread support +// for threads and semaphors. Nothing else. This is why mutex and condition +// are based off of semaphore_class- there's no other way to have threads sleep +// (instead of spin). + +// Spinning is VERY BAD on the PS2. There's no time-sharing- it's based off of +// priority. Threads are tasked out when they enter WAIT (sleep) mode. Threads +// are NOT tasked out when they're banging a while loop. If a thread is waiting +// for a variable, but not sleeping, deadlock. + +class EXPCL_PANDAEXPRESS ipc_traits + { + public: + + // prototype all of the classes here + + class mutex_class; + class condition_class; + class semaphore_class; + class thread_class; + class library_class; + + //// semaphore_class definition + + class semaphore_class + { + private: + + SemaParam m_sema_param; + int m_sema_id; + + static const int sema_max_size = 32; + + public: + + static semaphore_class* const Null; + + INLINE semaphore_class(const unsigned int initial = 1) { + m_sema_param.initCount = initial; + m_sema_param.maxCount = sema_max_size; + m_sema_param.option = 0; + + m_sema_id = CreateSema(&m_sema_param); + + if (m_sema_id < 0) + throw semaphore_fatal(-1); + } + + INLINE ~semaphore_class(void) { + DeleteSema(m_sema_id); + } + + INLINE void wait(void) { + int result; + + result = WaitSema(m_sema_id); + + // see if we overflowed the semaphore's queue + + if (result != m_sema_id) + throw semaphore_fatal(-1); + } + + INLINE int trywait(void) { + SemaParam sema_param; + + // query the semaphor in question directly + // we can't use poll here, because if the semaphor is + // grabbable, poll will grab it. + + ReferSemaStatus(m_sema_id, &sema_param); + + if (sema_param.currentCount == 0) + return 1; + + return 0; + } + + INLINE void post(void) { + SignalSema(m_sema_id); + } + }; + + //// mutex_class definition + + class mutex_class + { + private: + + semaphore_class m_semaphore; + + public: + + static mutex_class* const Null; + + inline mutex_class(void) {} + inline ~mutex_class(void) {} + + inline void lock(void) { + m_semaphore.wait(); + } + + inline void unlock(void) { + m_semaphore.post(); + } + }; + + //// condition_class definition + + class condition_class + { + private: + + // for an explanation of this bloody mess, see ipc_nt_traits.h + // cary's rant is more than sufficient. + + class WaitingThread + { + public: + + // note that the semaphor is being initialized to 0, + // meaning that a wait() on it will fail until someone + // else posts to it. + + int semaphore_id; + int thread_id; + }; + + list waiting_thread_list; + mutex_class list_access_lock; + mutex_class* _mutex; + + public: + + static condition_class* const Null; + + INLINE condition_class(mutex_class *m) : _mutex(m) {} + INLINE ~condition_class(void) {} + + INLINE void wait(void) + { + WaitingThread waiting_thread; + SemaParam sp; + + sp.initCount = 0; + sp.maxCount = 1; + + waiting_thread.semaphore_id = CreateSema(&sp); + waiting_thread.thread_id = GetThreadId(); + + // get the lock and add the current thread to the list. + + list_access_lock.lock(); + waiting_thread_list.push_back(waiting_thread); + list_access_lock.unlock(); + + // lock this thread away + + _mutex->unlock(); + WaitSema(waiting_thread.semaphore_id); + _mutex->lock(); + + // the thread is now sitting. + // + // + // when it gets here, it will have been released by signal/broadcast + + DeleteSema(waiting_thread.semaphore_id); + } + + INLINE int timedwait(const unsigned long secs, + const unsigned long nsecs) { + return -1; + } + + INLINE void signal(void) + { + WaitingThread first_in_line = waiting_thread_list.front(); + waiting_thread_list.pop_front(); + + // release this node. + + SignalSema(first_in_line.semaphore_id); + } + + INLINE void broadcast(void) + { + list::iterator begin = waiting_thread_list.begin(); + list::iterator end = waiting_thread_list.end(); + list::iterator cur = begin; + + int s_id; + + while (cur != end) + { + // touch/release this node + + s_id = (*cur).semaphore_id; + SignalSema(s_id); + + cur++; + } + + // and now clear the list out. + + waiting_thread_list.erase(begin, end); + } + }; + + //// thread_class definition + + class thread_class + { + private: + + void *_b_ptr; // for reverse lookup + void *(*_fn)(void *); + + struct ThreadParam m_thread_param; + int m_thread_id; + int m_priority; + + static const int m_thread_stacksize = 16384; + char m_thread_stack[m_thread_stacksize] __attribute__ ((aligned(16))); + + INLINE int priority_map(const int pri) { + switch (pri) { + case 2: + return 0; + + case 0: + return 2; + + case 1: + default: + return 1; + } + } + + class pointer_lookup + { + public: + + int thread_id; + thread_class *thread_ptr; + }; + + static vector thread_addr_vector; + static void thread_wrapper(void *); + + public: + + static thread_class *m_root_thread; + + static void BuildRootThread(void) + { + static bool initialized = false; + + // this will run once, and only once. Config calls it. + + if (initialized == true) + return; + + // create the new root_thread placeholder + + m_root_thread = new thread_class(NULL); + + m_root_thread->_fn = NULL; + m_root_thread->m_thread_id = 1; + m_root_thread->m_priority = 0; + + // now add it to the pointer lookup table + + pointer_lookup root_pl; + root_pl.thread_id = 1; + root_pl.thread_ptr = m_root_thread; + + thread_addr_vector.push_back(root_pl); + initialized = true; + } + + static thread_class* const Null; + + INLINE thread_class(void *data) : _b_ptr(data) {} + + INLINE ~thread_class(void) { + cout.flush(); + DeleteThread(GetThreadId()); + } + + INLINE void manual_init(void) + { + m_thread_id = GetThreadId(); + set_priority(1); + } + + INLINE void start_pre(void *(*fn)(void *), const bool, const int pri) + { + _fn = fn; + m_priority = priority_map(pri); + + // set up the thread_param structure + + m_thread_param.entry = thread_wrapper; + m_thread_param.stack = m_thread_stack; + m_thread_param.stackSize = m_thread_stacksize; + m_thread_param.initPriority = m_priority; + m_thread_param.gpReg = &_gp; + + // write this info back to the cache. + + FlushCache(0); + + // create and register the thread + + m_thread_id = CreateThread(&m_thread_param); + + if (m_thread_id < 0) + throw thread_fatal(-1); + + pointer_lookup pl; + pl.thread_id = m_thread_id; + pl.thread_ptr = this; + + thread_addr_vector.push_back(pl); + + // start the thread + + StartThread(m_thread_id, (void *) this); + } + + INLINE void start_in(void) {} + INLINE void start_post(const bool det, const int) {} + INLINE void *back_ptr(void) { return _b_ptr; } + + static INLINE void exit(void *) + { + int id = GetThreadId(); + + vector::iterator cur; + + for (cur = thread_addr_vector.begin(); cur != thread_addr_vector.end(); + cur++) + { + if ((*cur).thread_id == id) + { + thread_addr_vector.erase(cur); + break; + } + } + + ExitThread(); + } + + // WRITE ME (or maybe ... don't?) + + INLINE void join(void **status) {} + + INLINE void set_priority(const int pri) + { + m_priority = priority_map(pri); + ChangeThreadPriority(m_thread_id, m_priority); + } + + static INLINE thread_class *self(void) + { + int id, result; + + struct ThreadParam thread_param; + vector::iterator cur; + + id = GetThreadId(); + + for (cur = thread_addr_vector.begin(); cur != thread_addr_vector.end(); + cur++) + { + if ((*cur).thread_id == id) + return (*cur).thread_ptr; + } + + return NULL; + } + + static INLINE void yield(void) + { + int id, result, priority; + struct ThreadParam thread_param; + + id = GetThreadId(); + result = ReferThreadStatus(id, &thread_param); + + // make sure this thread isn't thrashed. + + if (result != id) + throw thread_fatal(-1); + + priority = thread_param.currentPriority; + RotateThreadReadyQueue(priority); + } + }; + + //// library_class definition + + class library_class + { + public: + + static library_class* const Null; + + INLINE library_class(ConfigString&) { + throw lib_load_invalid(); + } + + INLINE ~library_class(void) { + throw lib_load_invalid(); + } + + INLINE void* get_symbol(ConfigString&) { + throw lib_load_invalid(); + } + }; + + //// timing stuff + + static INLINE void sleep(const unsigned long secs, + const unsigned long nsecs) { + } + + static INLINE void get_time(unsigned long& abs_secs, + unsigned long& abs_nsecs, + const unsigned long rel_secs = 0, + const unsigned long rel_nsecs = 0) { + } + + //// generator stuff + + static INLINE mutex_class *make_mutex(void) { + return new mutex_class; + } + + static INLINE condition_class *make_condition(mutex_class *m) { + return new condition_class(m); + } + + static INLINE semaphore_class *make_semaphore(unsigned int initial) { + return new semaphore_class(initial); + } + + static INLINE thread_class *make_thread(void *data) { + return new thread_class(data); + } + + static INLINE library_class *make_library(ConfigString& str) { + return new library_class(str); + } +}; + +#endif // __IPC_PS2_TRAITS_H__ diff --git a/panda/src/ipc/ipc_semaphore.h b/panda/src/ipc/ipc_semaphore.h new file mode 100644 index 0000000000..d685d2e754 --- /dev/null +++ b/panda/src/ipc/ipc_semaphore.h @@ -0,0 +1,44 @@ +// Filename: ipc_semaphore.h +// Created by: cary (16Sep98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __IPC_SEMAPHORE_H__ +#define __IPC_SEMAPHORE_H__ + +#include + +#include "ipc_traits.h" + +class EXPCL_PANDAEXPRESS base_semaphore { + public: + typedef ipc_traits traits; + typedef ipc_traits::semaphore_class semaphore_class; + + static base_semaphore* const Null; + INLINE base_semaphore(const unsigned int initial = 1) : + _semaphore(traits::make_semaphore(initial)) {} + INLINE ~base_semaphore(void) { delete _semaphore; } + INLINE void wait(void) { _semaphore->wait(); } + INLINE int trywait(void) { return _semaphore->trywait(); } + INLINE void post(void) { _semaphore->post(); } + INLINE semaphore_class* get_semaphore(void) { return _semaphore; } + private: + semaphore_class *_semaphore; +}; + +class base_semaphore_lock { + public: + typedef base_semaphore semaphore; + INLINE base_semaphore_lock(semaphore& s) : _semaphore(s) { + _semaphore.wait(); + } + INLINE ~base_semaphore_lock(void) { _semaphore.post(); } + private: + semaphore& _semaphore; +}; + +typedef base_semaphore semaphore; +typedef base_semaphore_lock semaphore_lock; + +#endif /* __IPC_SEMAPHORE_H__ */ diff --git a/panda/src/ipc/ipc_solaris_traits.h b/panda/src/ipc/ipc_solaris_traits.h new file mode 100644 index 0000000000..ef674cff33 --- /dev/null +++ b/panda/src/ipc/ipc_solaris_traits.h @@ -0,0 +1,236 @@ +// Filename: ipc_solaris_traits.h +// Created by: cary (16Sep98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __IPC_SOLARIS_TRAITS_H__ +#define __IPC_SOLARIS_TRAITS_H__ + +#include + +#include +#include +#include + +#define THROW_FATAL(x, t) throw t##_fatal(x) +#define THROW_ERRORS(x, t) { int rc = (x); \ + if (rc != 0) THROW_FATAL(rc, t); } +#define THROW_ERRORS_G(x) THROW_ERRORS(x, ipc) +#define THROW_ERRORS_M(x) THROW_ERRORS(x, mutex) +#define THROW_ERRORS_C(x) THROW_ERRORS(x, condition) +#define THROW_ERRORS_S(x) THROW_ERRORS(x, semaphore) +#define THROW_ERRORS_T(x) THROW_ERRORS(x, thread) + +class EXPCL_PANDAEXPRESS ipc_traits { + public: + class mutex_class; + class condition_class; + class semaphore_class; + class thread_class; + class library_class; + class mutex_class { + private: + mutex_t _mutex; + public: + // interface to mutex + static const mutex_class* Null; + INLINE mutex_class(void) { + THROW_ERRORS_M(mutex_init(&_mutex, USYNC_THREAD, 0)); + } + INLINE ~mutex_class(void) { + THROW_ERRORS_M(mutex_destroy(&_mutex)); + } + INLINE void lock(void) { + THROW_ERRORS_M(mutex_lock(&_mutex)); + } + INLINE void unlock(void) { + THROW_ERRORS_M(mutex_unlock(&_mutex)); + } + }; + class condition_class { + private: + cond_t _condition; + mutex_class *_mutex; + public: + // interface to condition variables + static const condition_class* Null; + INLINE condition_class(mutex_class* m) : _mutex(m) { + THROW_ERRORS_C(cond_init(&_condition, USYNC_THREAD, 0)); + } + INLINE ~condition_class(void) { + THROW_ERRORS_C(cond_destroy(&_condition)); + } + INLINE void wait(void) { + THROW_ERRORS_C(cond_wait(&_condition, _mutex->get_mutex())); + } + INLINE int timedwait(const unsigned long secs, + const unsigned long nsecs) { + timespec rqts = { secs, nsecs }; + int rc = cond_timedwait(&_condition, _mutexm->get_mutex(), + &rqts); + if (rc == 0) + return 1; + if (rc == ETIME) + return 0; + throw condition_fatal(rc); + } + INLINE void signal(void) { + THROW_ERRORS_C(cond_signal(&_condition)); + } + INLINE void broadcast(void) { + THROW_ERRORS_C(cond_broadcast(&_condition)); + } + }; + class semaphore_class { + private: + sema_t _semaphore; + public: + // interface to semaphores + static const semaphore_class* Null; + INLINE semaphore_class(const unsigned int initial) { + THROW_ERRORS_S(sema_init(&_semaphore, initial, USYNC_THREAD + NULL)); + } + INLINE ~semaphore_class(void) { + THROW_ERRORS_S(sema_destroy(&_semaphore)); + } + INLINE void wait(void) { + THROW_ERRORS_S(sema_wait(&_semaphore)); + } + INLINE int trywait(void) { + // this is way not right, but I need access to a solaris box to + // see what it really should be. + THROW_ERRORS_S(sema_trywait(&_semaphore)); + } + INLINE void post(void) { + THROW_ERRORS_S(sema_post(&_semaphore)); + } + }; + class thread_class { + private: + thread_t _thread; + void* _b_ptr; // a handle to hang a pointer back to the high-level + // thread class on. Gross, but I couldn't think of + // a better way to do this reverse lookup. + void* (*_fn)(void*); + INLINE int priority_map(const int pri) { + switch (pri) { + case 0: + case 1: + case 2: + return pri; + } + throw thread_invalid(); + // shouldn't get here, but keeps the compiler happy + return 0; + } + static void* thread_wrapper(void*); + public: + // interface to threads + static const thread_class* Null; + INLINE thread_class(void* data) : _b_ptr(data) {} + INLINE ~thread_class(void) {} + INLINE void manual_init(void) { + _thread = thr_self(); + THROW_ERRORS_T(thr_setprio(_thread, priority_map(PRIORITY_NORMAL))); + } + INLINE void start_pre(void* (*fn)(void*), const bool det, + const int) { + _fn = fn; + long flags = 0; + if (det) + flags |= THR_DETACHED; + THROW_ERRORS_T(thr_create(0, 0, thread_wrapper, (void*)this, + flags, &_thread)); + } + INLINE void start_in(void) { + THROW_ERRORS_T(thr_setspecific(self_key, this)); + } + INLINE void start_post(const bool, const int pri) { + THROW_ERRORS_T(thr_setprio(_thread, priority_map(pri))); + } + INLINE void join(void** status) { + THROW_ERRORS_T(thr_join(_thread, (thraed_t*)NULL, status)); + } + INLINE void set_priority(const int pri) { + THROW_ERRORS_T(thr_setprio(_thread, priority_map(pri))); + } + INLINE void* back_ptr(void) { return _b_ptr; } + static INLINE void exit(void* return_value) { + thr_exit(return_value); + } + static INLINE thread_class* self(void) { + thread_class* me = thread_class::Null; + THROW_ERRORS_T(thr_getspecific(self_key, (void**)&me)); + return me; + } + static INLINE void yield(void) { + thr_yield(); + } + }; + class library_class { + public: + static library_class* const Null; + INLINE library_class(str&) { + throw lib_load_invalid(); + } + INLINE ~library_class(void) { + throw lib_load_invalid(); + } + INLINE void* get_symbol(str&) { + throw lib_load_invalid(); + } + }; + static INLINE mutex_class *make_mutex(void) { + return new mutex_class; + } + static INLINE condition_class *make_condition(mutex_class* m) { + return new condition_class(m); + } + static INLINE semaphore_class *make_semaphore(const unsigned int initial) { + return new semaphore_class(initial); + } + static INLINE thread_class *make_thread(void* data) { + return new thread_class(data); + } + static INLINE library_class *make_library(str&) { + throw lib_load_invalid(); + } + static INLINE void sleep(const unsigned long secs, + const unsigned long nsecs = 0) { + timespec rqts = { secs, nsecs }; + if (nanosleep(&rqts, (timespec*)NULL) != 0) + throw ipc_fatal(errno); + } + static INLINE void get_time(unsigned long& abs_secs, + unsigned long& abs_nsecs, + const unsigned long rel_secs = 0, + const unsigned long rel_nsecs = 0) { + timespec abs; + clock_gettime(CLOCK_REALTIME, &abs); + abs.tv_nsec += rel_nsecs; + abs.tv_sec += rel_secs + abs.tv_nsec / 1000000000; + abs.tv_nsec = abs.tv_nsec & 1000000000; + abs_secs = abs.tv_sec; + abs_nsecs = abs.tv_nsec; + } + static INLINE int set_atomic(int&, int) { + throw set_atomic_invalid(); + } + static INLINE int inc_atomic(int&) { + throw inc_atomic_invalid(); + } + static INLINE int dec_atomic(int&) { + throw dec_atomic_invalid(); + } + // This should probably be private, but it is annoying to make config a + // friend + static INLINE void __set_self_key(thread_key_t k) { + self_key = k; + } + private: + // other data specific to this implementation + static thread_key_t self_key; +}; + +#endif /* __IPC_SOLARIS_TRAITS_H__ */ diff --git a/panda/src/ipc/ipc_thread.h b/panda/src/ipc/ipc_thread.h new file mode 100644 index 0000000000..14d07a8f54 --- /dev/null +++ b/panda/src/ipc/ipc_thread.h @@ -0,0 +1,273 @@ +// Filename: ipc_thread.h +// Created by: cary (16Sep98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __IPC_THREAD_H__ +#define __IPC_THREAD_H__ + +#include +#include + +#include "ipc_traits.h" +#include "ipc_mutex.h" + +class EXPCL_PANDAEXPRESS base_thread { + public: + typedef ipc_traits traits; + typedef traits::thread_class thread_class; + typedef base_mutex mutex; + typedef base_mutex_lock mutex_lock; + + // expand this to allow real priorities and scheduling. determine the + // priority limits based on the scheduling. If scheduling is not + // allowed, silently ignore those requests. + enum priority_t { + PRIORITY_LOW, + PRIORITY_NORMAL, + PRIORITY_HIGH + }; + + enum state_t { + STATE_NEW, // thread exists, but hasn't started yet. + STATE_RUNNING, // thread is running + STATE_TERMINATED // thread is done, but storage hasn't been reclaimed + }; + + static base_thread* const Null; + // Constructors set up the thread object but the thread won't start until + // start() is called. The create(...) functions can be used to construct + // and start a thread in a single call. + // + // should be able to get rid of the voids here with clever templating. + INLINE base_thread(void (*fn)(void*), void* arg = (void*)0L, + const priority_t pri = PRIORITY_NORMAL) : + _thread(thread_class::Null), _fn_void(fn), _fn_ret(0L), _mutex(), + _state(STATE_NEW), _priority(pri), _thread_arg(arg), _detached(true) { + + + printf("Base_thread constructor 1 called.\n"); + common_constructor(); + } + INLINE base_thread(void* (*fn)(void*), void* arg = (void*)0L, + const priority_t pri = PRIORITY_NORMAL) : + _thread(thread_class::Null), _fn_void(0L), _fn_ret(fn), _mutex(), + _state(STATE_NEW), _priority(pri), _thread_arg(arg), _detached(false) { + + printf("Base_thread constructor 2 called.\n"); + common_constructor(); + } + // causes this thread to start executing + INLINE void start(void) { + mutex_lock l(_mutex); + if (_state != STATE_NEW) { + cerr << "base_thread::start throwing thread_invalid()" << endl; +#ifdef BROKEN_EXCEPTIONS + return; +#else + throw thread_invalid(); +#endif /* BROKEN_EXCEPTIONS */ + } + _thread = traits::make_thread(this); + _thread->start_pre(thread_wrapper, _detached, _priority); + _state = STATE_RUNNING; + _thread->start_post(_detached, _priority); + } + // causes the calling thread to wait for this thread's completion, + // putting the return value in the space passed by the parameter. Only + // undetached threads may be joined. Storage for the thread will be + // reclaimed when it exits. + // Again, clever templating should be able to eliminate this void + // nonsense. It's gross! + INLINE void join(void** status) { + _mutex.lock(); + if ((_state != STATE_RUNNING) && (_state != STATE_TERMINATED)) { + _mutex.unlock(); +#ifdef BROKEN_EXCEPTIONS + return; +#else + throw thread_invalid(); +#endif /* BROKEN_EXCEPTIONS */ + } + _mutex.unlock(); + if (this == self()) +#ifdef BROKEN_EXCEPTIONS + return; +#else + throw thread_invalid(); +#endif /* BROKEN_EXCEPTIONS */ + if (_detached) +#ifdef BROKEN_EXCEPTIONS + return; +#else + throw thread_invalid(); +#endif /* BROKEN_EXCEPTIONS */ + _thread->join(status); + delete this; + } + // set the priority of this thread + INLINE void set_priority(const priority_t pri) { + mutex_lock l(_mutex); + if (_state != STATE_RUNNING) +#ifdef BROKEN_EXCEPTIONS + return; +#else + throw thread_invalid(); +#endif /* BROKEN_EXCEPTIONS */ + _thread->set_priority(pri); + _priority = pri; + } + // return this thread's priority + INLINE priority_t get_priority(void) { + mutex_lock l(_mutex); + // in a more general priority system, we should check that the thread + // is running at the priority we think it is. + return _priority; + } + // return this thread's state + INLINE state_t get_state(void) { + mutex_lock l(_mutex); + return _state; + } + // return this thread's id within the current process + INLINE int get_id(void) { + return _id; // presumably this doesn't change + } + // causes the calling thread to exit, again the void action here is + // unhappy. + static INLINE void exit(void* return_value = (void*)0L) { + cout.flush(); + base_thread* me = self(); + if (me != (base_thread*)0L) { + me->_mutex.lock(); + // if (me->_state != STATE_RUNNING) non-fatal error + me->_state = STATE_TERMINATED; + me->_mutex.unlock(); + if (me->_detached) + delete me; + } + thread_class::exit(return_value); + } + // returns the caller's thread object. If the calling thread is not the + // main thread or created by us, thread_class::Null is returned + static INLINE base_thread* self(void) { + cout.flush(); + thread_class* tclass=thread_class::self(); + base_thread* ret=(base_thread*)0L; + if (tclass != thread_class::Null) + ret = (base_thread*)(tclass->back_ptr()); + return ret; + } + // calling task releasing control to the scheduler + static INLINE void yield(void) { + thread_class::yield(); + } + // sleep for the specified amount of time + static INLINE void sleep(const unsigned long secs, + const unsigned long nsecs = 0) { + traits::sleep(secs, nsecs); + } + // calculates an absolute time in seconds and nanoseconds, suitable for + // use in timed_waits on condition variables, which is the current time + // plus the given relative offset. + static INLINE void get_time(unsigned long& abs_secs, + unsigned long& abs_nsecs, + const unsigned long rel_secs = 0, + const unsigned long rel_nsecs = 0) { + traits::get_time(abs_secs, abs_nsecs, rel_secs, rel_nsecs); + } + // shortcut function to create and start a thread in one call + static INLINE base_thread* create(void (*fn)(void*), + void* arg = (void*)0L, + const priority_t pri=PRIORITY_NORMAL) { + base_thread* t = new base_thread(fn, arg, pri); + t->start(); + return t; + } + static INLINE base_thread* create(void* (*fn)(void*), + void* arg = (void*)0L, + const priority_t pri=PRIORITY_NORMAL) { + base_thread* t = new base_thread(fn, arg, pri); + t->start(); + return t; + } + // a handle to the implementation specific thread, this really should + // NEVER be called from user code. + INLINE thread_class* get_thread(void) { return _thread; } + protected: + // this constructor is used in a derived class. The thread will execute + // the run() or run_undetached() member functions depending on whether + // start() or start_undetached() is called, respectively. + INLINE base_thread(void* arg = (void*)0L, + const priority_t pri = PRIORITY_NORMAL) : + _thread(thread_class::Null), _fn_void(0L), _fn_ret(0L), _mutex(), + _state(STATE_NEW), _priority(pri), _thread_arg(arg), _detached(true) { + common_constructor(); + } + // can be used with the above constructor in a derived class to cause the + // thread to be undetached. In this case the thread executes the + // run_undetached member function. + INLINE void start_undetached(void) { + if ((_fn_void != NULL) || (_fn_ret != NULL)) +#ifdef BROKEN_EXCEPTIONS + return; +#else + throw thread_invalid(); +#endif /* BROKEN_EXCEPTIONS */ + _detached = false; + start(); + } + // destructor cannot be called by user (except via a derived class). + // Use exit() instead. This also means a thread object must be allocated + // with new, it cannot be statically or automatically allocated. The + // destructor of a class that inherits from thread<...> shouldn't be + // public either (otherwise the thread object can be destroyed while the + // underlying thread is still running (this is Bad(tm))). + virtual ~base_thread(void) { + delete _thread; + } + + private: + thread_class *_thread; // the implementation specific thread + void (*_fn_void)(void*); + void* (*_fn_ret)(void*); + mutex _mutex; // used to protect members which can change after + // construction. ie: _state and _priority + state_t _state; + priority_t _priority; + int _id; + void* _thread_arg; + bool _detached; + static mutex* _next_id_mutex; + static int _next_id; + + // can be overridden in a derived class. When constructed using the + // constructor thread(void*, priority_t), these functions are called by + // start() and start_undetached() respectively. + virtual void run(void*); + virtual void* run_undetached(void*); + // implements the common parts of the constructors + INLINE void common_constructor(void) { + if (_next_id_mutex == (mutex *)0L) { + // ok, we're the first in here. + _next_id_mutex = new mutex; + base_thread* t = new base_thread; + if (t->_state != STATE_NEW) { + // FATAL() << "thread initialization: problem creating initial thread object" << nend; + ::exit(1); + } + t->_state = STATE_RUNNING; + t->_thread = traits::make_thread(t); + t->_thread->manual_init(); + t->_thread->start_in(); + } + + mutex_lock l(*_next_id_mutex); + _id = _next_id++; + } + static void *thread_wrapper(void*); +}; + +typedef base_thread thread; + +#endif /* __IPC_THREAD_H__ */ diff --git a/panda/src/ipc/ipc_traits.cxx b/panda/src/ipc/ipc_traits.cxx new file mode 100644 index 0000000000..6f9e37d3a5 --- /dev/null +++ b/panda/src/ipc/ipc_traits.cxx @@ -0,0 +1,255 @@ +// Filename: ipc_traits.cxx +// Created by: frang (06Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "ipc_traits.h" + +Configure(ipc_trait); + +#if (__IPC_FLAVOR_DUJOUR_TOKEN__ == 0) + +// mach + +ipc_traits::mutex_class* const ipc_traits::mutex_class::Null = + (ipc_traits::mutex_class*)0L; +ipc_traits::condition_class* const ipc_traits::condition_class::Null = + (ipc_traits::condition_class*)0L; +ipc_traits::semaphore_class* const ipc_traits::semaphore_class::Null = + (ipc_traits::semaphore_class*)0L; +ipc_traits::thread_class* const ipc_traits::thread_class::Null = + (ipc_traits::thread_class*)0L; +ipc_traits::library_class* const ipc_traits::library_class::Null = + (ipc_traits::library_class*)0L; + +int ipc_traits::normal_priority; +int ipc_traits::highest_priority; + +ConfigureFn(ipc_trait) { + // find base and max priority. This is the inital thread, so the max + // priority of this thread also applies to any newly created thread. + kern_return_t error; + struct thread_sched_info info; + unsigned int info_count = THREAD_SCHED_INFO_COUNT; + + error = thread_info(thread_self(), THREAD_SCHED_INFO, (thread_info_t)&info, + &info_count); + if (error != KERN_SUCCESS) { + FATAL() << "ipc_traits(mach) initialization: error determining thread_info" + << nend; + ::exit(1); + } else { + ipc_traits::__set_normal_priority(info.base_priority); + ipc_traits::__set_highest_priority(info.max_priority); + } +} + +void* ipc_traits::thread_class::thread_wrapper(void* data) { + ipc_traits::thread_class* me = (ipc_traits::thread_class*)data; + return (*me->_fn)(me->_b_ptr); +} + +#elif (__IPC_FLAVOR_DUJOUR_TOKEN__ == 1) + +// NT + +ipc_traits::mutex_class* const ipc_traits::mutex_class::Null = + (ipc_traits::mutex_class*)0L; +ipc_traits::condition_class* const ipc_traits::condition_class::Null = + (ipc_traits::condition_class*)0L; +ipc_traits::semaphore_class* const ipc_traits::semaphore_class::Null = + (ipc_traits::semaphore_class*)0L; +ipc_traits::thread_class* const ipc_traits::thread_class::Null = + (ipc_traits::thread_class*)0L; +ipc_traits::library_class* const ipc_traits::library_class::Null = + (ipc_traits::library_class*)0L; + +DWORD ipc_traits::self_tls_index; +ipc_traits::condition_class::_internal_thread_dummy* + ipc_traits::condition_class::_internal_thread_helper::cache = + (ipc_traits::condition_class::_internal_thread_dummy*)0L; +ipc_traits::mutex_class* + ipc_traits::condition_class::_internal_thread_helper::cachelock = + (ipc_traits::mutex_class*)0L; + +ConfigureFn(ipc_trait) { + DWORD tmp = TlsAlloc(); + if (tmp == 0xffffffff) + throw ipc_fatal(GetLastError()); + ipc_traigs::__set_self_tls_index(tmp); +} + +#ifndef __BCPLUSPLUS__ +unsigned _stdcall ipc_traits::thread_class::thread_wrapper(void* data) { +#else /* __BCPLUSPLUS__ */ +void _USERENTRY ipc_traits::thread_class::thread_wrapper(void* data) { +#endif /* __BCPLUSPLUS__ */ + ipc_traits::thread_class* me = (ipc_traits::thread_class*)data; + me->_return_value = (*me->_fn)(me->_b_ptr); +#ifndef __BCPLUSPLUS__ + return 0; +#endif /* __BCPLUSPLUS__ */ +} + +#elif (__IPC_FLAVOR_DUJOUR_TOKEN__ == 2) + +// posix + +ipc_traits::mutex_class* const ipc_traits::mutex_class::Null = + (ipc_traits::mutex_class*)0L; +ipc_traits::condition_class* const ipc_traits::condition_class::Null = + (ipc_traits::condition_class*)0L; +ipc_traits::semaphore_class* const ipc_traits::semaphore_class::Null = + (ipc_traits::semaphore_class*)0L; +ipc_traits::thread_class* const ipc_traits::thread_class::Null = + (ipc_traits::thread_class*)0L; +ipc_traits::library_class* const ipc_traits::library_class::Null = + (ipc_traits::library_class*)0L; + +pthread_key_t ipc_traits::self_key; +#ifdef PthreadSupportThreadPriority +int ipc_traits::lowest_priority; +int ipc_traits::normal_priority; +int ipc_traits::highest_priority; +#endif /* PthreadSupportThreadPriority */ + +ConfigureFn(ipc_trait) { +#ifdef NeedPthreadInit + pthread_init(); +#endif /* NeedPthreadInit */ + + pthread_key_t ktmp; +#if (PthreadDraftVersion == 4) + THROW_ERRORS_G(pthread_keycreate(&ktmp, NULL)); +#else /* PthreadDraftVersion 4 */ + THROW_ERRORS_G(pthread_key_create(&ktmp, NULL)); +#endif /* PthreadDraftVersion 4 */ + ipc_traits::__set_self_key(ktmp); + +#ifdef PthreadSupportThreadPriority + int plow, pnorm, phigh; +#if defined(__osf1__) && defined(__alpha__) || defined(__VMS) + plow = PRI_OTHER_MIN; + phigh = PRI_OTHER_MAX; +#elif defined(__hpux__) + plow = PRI_OTHER_MIN; + phigh = PRI_OTHER_MAX; +#elif defined(__sunos__) && (__OSVERSION__ == 5) + // a bug in pthread_attr_setschedparam means lowest priority is 1 not 0 + plow = 1; + phigh = 3; +#else /* os testing */ + plow = sched_get_priority_min(SCHED_FIFO); + phigh = sched_get_priority_max(SCHED_FIFO); +#endif /* os testing */ + switch (phigh - plow) { + case 0: + case 1: + pnorm = plow; + break; + default: + pnorm = plow + 1; + break; + } + ipc_traits::__set_lowest_priority(plow); + ipc_traits::__set_normal_priority(pnorm); + ipc_traits::__set_highest_priority(phigh); +#endif /* PthreadSupportThreadPriority */ +} + +void* ipc_traits::thread_class::thread_wrapper(void* data) { + ipc_traits::thread_class* me = (ipc_traits::thread_class*)data; + return (*me->_fn)(me->_b_ptr); +} + +#elif (__IPC_FLAVOR_DUJOUR_TOKEN__ == 3) + +// solaris + +ipc_traits::mutex_class* const ipc_traits::mutex_class::Null = + (ipc_traits::mutex_class*)0L; +ipc_traits::condition_class* const ipc_traits::condition_class::Null = + (ipc_traits::condition_class*)0L; +ipc_traits::semaphore_class* const ipc_traits::semaphore_class::Null = + (ipc_traits::semaphore_class*)0L; +ipc_traits::thread_class* const ipc_traits::thread_class::Null = + (ipc_traits::thread_class*)0L; +ipc_traits::library_class* const ipc_traits::library_class::Null = + (ipc_traits::library_class*)0L; + +thread_key_t ipc_traits::self_key; + +ConfigureFn(ipc_trait) { + thread_key_t ktmp; + THROW_ERRORS_G(thr_keycreate(&ktmp, NULL)); + ipc_traits::__set_self_key(ktmp); +} + +void* ipc_traits::thread_class::thread_wrapper(void* data) { + ipc_traits::thread_class* me = (ipc_traits::thread_class*)data; + return (*me->_fn)(me->_b_ptr); +} + +#elif (__IPC_FLAVOR_DUJOUR_TOKEN__ == 4) + +// NSPR + +ipc_traits::mutex_class* const ipc_traits::mutex_class::Null = + (ipc_traits::mutex_class*)0L; +ipc_traits::condition_class* const ipc_traits::condition_class::Null = + (ipc_traits::condition_class*)0L; +ipc_traits::semaphore_class* const ipc_traits::semaphore_class::Null = + (ipc_traits::semaphore_class*)0L; +ipc_traits::thread_class* const ipc_traits::thread_class::Null = + (ipc_traits::thread_class*)0L; +ipc_traits::library_class* const ipc_traits::library_class::Null = + (ipc_traits::library_class*)0L; + +PRUintn ipc_traits::_data_index; + +ConfigureFn(ipc_trait) { + PRUintn tmp; + PR_NewThreadPrivateIndex(&tmp, (PRThreadPrivateDTOR)0L); + ipc_traits::__set_data_index(tmp); +} + +void ipc_traits::thread_class::thread_wrapper(void* data) { + ipc_traits::thread_class* me = (ipc_traits::thread_class*)data; + me->_return_value = (*me->_fn)(me->_b_ptr); +} + +#elif (__IPC_FLAVOR_DUJOUR_TOKEN__ == 5) + +// PS2 + +#include +#include + +ConfigureFn(ipc_trait) { + ipc_traits::thread_class::BuildRootThread(); +} + +vector ipc_traits::thread_class::thread_addr_vector; +ipc_traits::thread_class *ipc_traits::thread_class::m_root_thread; + +ipc_traits::mutex_class* const ipc_traits::mutex_class::Null = + (ipc_traits::mutex_class *) 0L; +ipc_traits::condition_class* const ipc_traits::condition_class::Null = + (ipc_traits::condition_class *) 0L; +ipc_traits::semaphore_class* const ipc_traits::semaphore_class::Null = + (ipc_traits::semaphore_class *) 0L; +ipc_traits::thread_class* const ipc_traits::thread_class::Null = + (ipc_traits::thread_class *) 0L; +ipc_traits::library_class* const ipc_traits::library_class::Null = + (ipc_traits::library_class *) 0L; + +void ipc_traits::thread_class::thread_wrapper(void *data) { + ipc_traits::thread_class *me = (ipc_traits::thread_class *) data; + (*me->_fn)(me->_b_ptr); +} + +#else /* out of flavor tokens */ +#error "invalid ipc flavor token" +#endif /* out of flavor tokens */ + + diff --git a/panda/src/ipc/ipc_traits.h b/panda/src/ipc/ipc_traits.h new file mode 100644 index 0000000000..29ed0bddb0 --- /dev/null +++ b/panda/src/ipc/ipc_traits.h @@ -0,0 +1,216 @@ +// Filename: ipc_traits.h +// Created by: cary (16Sep98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __IPC_TRAITS_H__ +#define __IPC_TRAITS_H__ + +#include +#include + +// decide which IPC implementation to use. This is so much fun, I just can't +// hardly stand it. + +// Flavor tokens: +// 0 - mach +// 1 - nt +// 2 - posix +// 3 - solaris +// 4 - NSPR +// 5 - PS2 + + +// I threw PS2 in first because sometimes other env vars are set, but if PENV is PS2, +// we're NEVER building anything else... CSN + +#if defined(PENV_PS2) + +#define __IPC_FLAVOR_DUJOUR__ PS2 +#define __IPC_FLAVOR_DUJOUR_TOKEN__ 5 + +#elif defined(HAVE_NSPR) + +#define __IPC_FLAVOR_DUJOUR__ nspr +#define __IPC_FLAVOR_DUJOUR_TOKEN__ 4 + +#elif defined(__arm__) && defined(__atmos__) + +#define __IPC_FLAVOR_DUJOUR__ posix +#define __IPC_FLAVOR_DUJOUR_TOKEN__ 2 + +#elif defined(__alpha__) && defined(__osf1__) + +#define __IPC_FLAVOR_DUJOUR__ posix +#define __IPC_FLAVOR_DUJOUR_TOKEN__ 2 + +#elif defined(__powerpc__) && defined(__aix__) + +#define __IPC_FLAVOR_DUJOUR__ posix +#define __IPC_FLAVOR_DUJOUR_TOKEN__ 2 + +#elif defined(__hpux__) + +#define __IPC_FLAVOR_DUJOUR__ posix +#define __IPC_FLAVOR_DUJOUR_TOKEN__ 2 + +#elif defined(PENV_WIN32) + +#define __IPC_FLAVOR_DUJOUR__ nt +#define __IPC_FLAVOR_DUJOUR_TOKEN__ 1 + +#elif defined(__sun__) + +#ifdef UsePthreads +#define __IPC_FLAVOR_DUJOUR__ posix +#define __IPC_FLAVOR_DUJOUR_TOKEN__ 2 +#else +#define __IPC_FLAVOR_DUJOUR__ solaris +#define __IPC_FLAVOR_DUJOUR_TOKEN__ 3 +#endif /* UsePthreads */ + +#elif defined(__linux__) + +#define __IPC_FLAVOR_DUJOUR__ posix +#define __IPC_FLAVOR_DUJOUR_TOKEN__ 2 + +#elif defined(__nextstep__) + +#define __IPC_FLAVOR_DUJOUR__ mach +#define __IPC_FLAVOR_DUJOUR_TOKEN__ 0 + +#elif defined(__VMS) + +#define __IPC_FLAVOR_DUJOUR__ posix +#define __IPC_FLAVOR_DUJOUR_TOKEN__ 2 + +#elif defined(__SINIX__) + +#define __IPC_FLAVOR_DUJOUR__ posix +#define __IPC_FLAVOR_DUJOUR_TOKEN__ 2 + +#elif defined(__sgi) + +#define __IPC_FLAVOR_DUJOUR__ posix +#define __IPC_FLAVOR_DUJOUR_TOKEN__ 2 + +#else + +#error "Unable to determine which IPC implementation to use" +#endif /* OS identification */ + +// this exception is a general fatal exception from the IPC code +class EXPCL_PANDAEXPRESS ipc_fatal { + public: + int error; + INLINE ipc_fatal(const int e = 0) : error(e) {} +}; + +// this exception is a general invalid value/function call exception for the +// IPC code +class EXPCL_PANDAEXPRESS ipc_invalid {}; + +// this exception is thrown in the event of a fatal error in the mutex code +class EXPCL_PANDAEXPRESS mutex_fatal : public ipc_fatal { + public: + INLINE mutex_fatal(const int e = 0) : ipc_fatal(e) {} +}; + +// this exception is thrown when a mutex is called in an invalid state, or the +// arguments to the call are invalid +class EXPCL_PANDAEXPRESS mutex_invalid : public ipc_invalid {}; + +// this exception is thrown in the event of a fatal error in the condition +// variable code +class EXPCL_PANDAEXPRESS condition_fatal : public ipc_fatal { + public: + INLINE condition_fatal(const int e = 0) : ipc_fatal(e) {} +}; + +// this exception is thrown when a condition variable is called in an +// invalid state, or the arguments to the call are invalid +class EXPCL_PANDAEXPRESS condition_invalid : public ipc_invalid {}; + +// this exception is thrown in the event of a fatal error in the semaphore code +class EXPCL_PANDAEXPRESS semaphore_fatal : public ipc_fatal { + public: + INLINE semaphore_fatal(const int e = 0) : ipc_fatal(e) {} +}; + +// this exception is thrown when a semaphore is called in an invalid state, or +// the arguments to the call are invalid +class EXPCL_PANDAEXPRESS semaphore_invalid : public ipc_invalid {}; + +// this exception is thrown in the event of a fatal error in the thread code +class EXPCL_PANDAEXPRESS thread_fatal : public ipc_fatal { + public: + INLINE thread_fatal(const int e = 0) : ipc_fatal(e) {} +}; + +// this exception is thrown when a thread is called in an invalid state, or +// the arguments to the call are invalid +class EXPCL_PANDAEXPRESS thread_invalid : public ipc_invalid {}; + +// this exception is thrown in the event of a fatal error in the atomic set +// code +class EXPCL_PANDAEXPRESS set_atomic_fatal : public ipc_fatal { +public: + INLINE set_atomic_fatal(const int e = 0) : ipc_fatal(e) {} +}; + +// this exception is thrown when an atomic set is called in an invalid state, +// or the arguments to the call are invalid +class EXPCL_PANDAEXPRESS set_atomic_invalid : public ipc_invalid {}; + +// this exception is thrown in the event of a fatal error in the atomic +// increment code +class EXPCL_PANDAEXPRESS inc_atomic_fatal : public ipc_fatal { +public: + INLINE inc_atomic_fatal(const int e = 0) : ipc_fatal(e) {} +}; + +// this exception is thrown when an atomic increment is called in an invalid +// state, or the arguments to the call are invalid +class EXPCL_PANDAEXPRESS inc_atomic_invalid : public ipc_invalid {}; + +// this exception is thrown in the event of a fatal error in the atomic +// decrement code +class EXPCL_PANDAEXPRESS dec_atomic_fatal : public ipc_fatal { +public: + INLINE dec_atomic_fatal(const int e = 0) : ipc_fatal(e) {} +}; + +// this exception is thrown when an atomic decrement is called in an invalid +// state, or the arguments to the call are invalid +class EXPCL_PANDAEXPRESS dec_atomic_invalid : public ipc_invalid {}; + +// this exception is thrown in the event of a fatal error in the library load +// code +class EXPCL_PANDAEXPRESS lib_load_fatal : public ipc_fatal { +public: + INLINE lib_load_fatal(const int e = 0) : ipc_fatal(e) {} +}; + +// this exception is thrown when a library load is called in an invalid state, +// or the arguments to the call are invalid +class EXPCL_PANDAEXPRESS lib_load_invalid : public ipc_invalid {}; + +#include + +#if (__IPC_FLAVOR_DUJOUR_TOKEN__ == 0) +#include "ipc_mach_traits.h" +#elif (__IPC_FLAVOR_DUJOUR_TOKEN__ == 1) +#include "ipc_nt_traits.h" +#elif (__IPC_FLAVOR_DUJOUR_TOKEN__ == 2) +#include "ipc_posix_traits.h" +#elif (__IPC_FLAVOR_DUJOUR_TOKEN__ == 3) +#include "ipc_solaris_traits.h" +#elif (__IPC_FLAVOR_DUJOUR_TOKEN__ == 4) +#include "ipc_nspr_traits.h" +#elif (__IPC_FLAVOR_DUJOUR_TOKEN__ == 5) +#include "ipc_ps2_traits.h" +#else +#error "invalid ipc flavor token" +#endif /* loading trait specializations */ + +#endif /* __IPC_TRAITS_H__ */ diff --git a/panda/src/ipc/loom.cxx b/panda/src/ipc/loom.cxx new file mode 100644 index 0000000000..8d52008d4d --- /dev/null +++ b/panda/src/ipc/loom.cxx @@ -0,0 +1,72 @@ +// Filename: loom.cxx +// Created by: cary (23Sep98) +// +//////////////////////////////////////////////////////////////////// + +#include "loom_internal.h" +#include + +void app_service::DoIt(void* data) +{ + Action ret; + app_service* me = (app_service*)data; + unsigned long s = 0, n = 0; + condition_variable* c = condition_variable::Null; + + me->_thread = thread::self(); + if (me->_init != NULL) + (*me->_init)(); + while ((ret = (*me->_service)(s, n, c)) != DONE) { + switch (ret) { + case RESERVICE: + break; // we're about to do this anyway + case YIELD: + thread::yield(); + break; + case SLEEP: + thread::sleep(s, n); + s = n = 0; + break; + case WAIT: + c->wait(); + c = condition_variable::Null; + break; + } + } + if (me->_cleanup != NULL) + (*me->_cleanup)(); + { + mutex_lock l(*main_thread_mutex); + me->_thread = thread::Null; + service_list::iterator i=find(task_list->begin(), task_list->end(), me); + if (i == task_list->end()) + throw thread_fatal(-1); + task_list->erase(i); + } + thread::exit(NULL); +} + +void RegisterAppService(vv_func init, av_func service, vv_func cleanup, + vv_func info) +{ + app_service* app = new app_service(init, service, cleanup, info); + if (task_list == (service_list *)0L) { + task_list = new service_list; + main_thread_mutex = new mutex; + } + mutex_lock l(*main_thread_mutex); + task_list->push_back(app); +} + +void SendMainThreadMessage(main_thread_message& m) +{ + main_thread_message *mess = new main_thread_message(m); + { + mutex_lock l(*message_mutex); + while (!main_thread_empty_flag) + main_thread_empty->wait(); + message_to_main_thread = mess; + main_thread_empty_flag = false; + main_thread_full->signal(); + } +} diff --git a/panda/src/ipc/loom.h b/panda/src/ipc/loom.h new file mode 100644 index 0000000000..4dbc63b387 --- /dev/null +++ b/panda/src/ipc/loom.h @@ -0,0 +1,61 @@ +// Filename: loom.h +// Created by: cary (23Sep98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __LOOM_H__ +#define __LOOM_H__ + +#include + +#include "ipc_mutex.h" +#include "ipc_condition.h" + +extern mutex main_thread_print_mutex; + +// interface to modules for being runable from app. + +enum Action { RESERVICE, DONE, YIELD, SLEEP, WAIT }; + +typedef void (*vv_func)(void); +typedef Action (*av_func)(unsigned long&, unsigned long&, condition_variable*&); + +// parameters in order are: +// initialization function - setup and data that the service function might +// need in order to run +// service function - perform the task of this thing. Either incrementally +// (thus returning RESERVICE), or all at once (handling +// yielding and waiting internally and returning DONE +// when all it's work is complete). +// cleanup function - when the service function returns DONE, if this is not +// NULL, this function will be called to cleanup whatever +// is left to be done by this task. +// info function - if not NULL and the main thread is asked to tell about +// what's running, this function will be called. It is +// expected that it will output something usefull about what +// the task is and what it's doing. +extern void RegisterAppService(vv_func, av_func, vv_func = (vv_func)0L, + vv_func = (vv_func)0L); + + +template +class main_thread_message_base { + public: + enum message_t { LOAD, RESCAN, INFO }; + INLINE main_thread_message_base(const message_t message, str s = "") : + _m(message), _lib(s) {} + INLINE main_thread_message_base(const main_thread_message_base& m) : + _m(m._m), _lib(m._lib) {} + INLINE ~main_thread_message_base(void) {} + INLINE message_t get_message(void) const { return _m; } + INLINE str get_lib(void) const { return _lib; } + private: + message_t _m; + str _lib; +}; + +typedef main_thread_message_base<> main_thread_message; + +extern void SendMainThreadMessage(main_thread_message&); + +#endif /* __LOOM_H__ */ diff --git a/panda/src/ipc/loom_internal.h b/panda/src/ipc/loom_internal.h new file mode 100644 index 0000000000..7faec75206 --- /dev/null +++ b/panda/src/ipc/loom_internal.h @@ -0,0 +1,75 @@ +// Filename: loom_internal.h +// Created by: cary (23Sep98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __LOOM_INTERNAL_H__ +#define __LOOM_INTERNAL_H__ + +#include + +#include "loom.h" +#include "ipc_mutex.h" +#include "ipc_condition.h" +#include "ipc_thread.h" +#include + +class app_service; + +typedef std::list service_list; + +extern service_list *task_list; +extern mutex *main_thread_mutex; + +class app_service { + private: + vv_func _init, _cleanup, _info; + av_func _service; + thread* _thread; + + INLINE app_service(void) {} + INLINE ~app_service(void) {} + static void DoIt(void*); + public: + INLINE app_service(vv_func init, av_func service, vv_func cleanup, + vv_func info) : _init(init), _service(service), + _cleanup(cleanup), _info(info), _thread(thread::Null) + {} + INLINE bool started() { + return (_thread != thread::Null); + } + INLINE void start(void) { + if (!this->started()) + thread::create(DoIt, (void*)this); + } + INLINE void info(void) { + // this probably should mutex lock the task_list while checking things + if (_info != (vv_func)0L) + (*_info)(); + if (_thread != thread::Null) { + cerr << "thread is currently running. Id = " << _thread->get_id() + << ". Current state = "; + switch (_thread->get_state()) { + case thread::STATE_NEW: + cerr << "New."; + break; + case thread::STATE_RUNNING: + cerr << "Running."; + break; + case thread::STATE_TERMINATED: + cerr << "Terminated"; + break; + } + cerr << endl; + } else + cerr << "thread not started or created yet." << endl; + } +}; + +extern main_thread_message* message_to_main_thread; +extern mutex* message_mutex; +extern condition_variable* main_thread_full; +extern condition_variable* main_thread_empty; +extern bool main_thread_empty_flag; + +#endif /* __LOOM_INTERNAL_H__ */ diff --git a/panda/src/ipc/loom_main.cxx b/panda/src/ipc/loom_main.cxx new file mode 100644 index 0000000000..b9ce093d4d --- /dev/null +++ b/panda/src/ipc/loom_main.cxx @@ -0,0 +1,84 @@ +// Filename: loom_main.cxx +// Created by: cary (23Sep98) +// +//////////////////////////////////////////////////////////////////// + +#include "loom_internal.h" +#include +#include +#include + +#include + +mutex main_thread_print_mutex; + +service_list *task_list = (service_list *)0L; +mutex *main_thread_mutex = mutex::Null; +main_thread_message* message_to_main_thread = (main_thread_message*)0L; +mutex *message_mutex; +condition_variable* main_thread_full = condition_variable::Null; +condition_variable* main_thread_empty = condition_variable::Null; +bool main_thread_empty_flag; + +int main(int argc, char** argv) +{ + for (int i=0; ilock(); + service_list::iterator j; + for (j=task_list->begin(); j!=task_list->end(); ++j) + (*j)->start(); + main_thread_mutex->unlock(); + + while (!task_list->empty()) { + unsigned long s, n; + mutex_lock l(*message_mutex); + thread::get_time(s, n, 2, 0); // 2 seconds from now + main_thread_full->timedwait(s, n); + if (!main_thread_empty_flag) { + switch (message_to_main_thread->get_message()) { + case main_thread_message::LOAD: + { + Filename filename = message_to_main_thread->get_lib(); + filename.set_type(Filename::F_dso); + load_dso(filename.to_os_specific()); + } + case main_thread_message::RESCAN: + for (j=task_list->begin(); j!=task_list->end(); ++j) + (*j)->start(); + break; + case main_thread_message::INFO: + for (j=task_list->begin(); j!=task_list->end(); ++j) + (*j)->info(); + break; + default: + // really should never be here + break; + } + delete message_to_main_thread; + message_to_main_thread = (main_thread_message*)0L; + main_thread_empty_flag = true; + main_thread_empty->signal(); + } + } + thread::sleep(0, 500000000); // give some time for cleanup (1/2 sec) + thread::exit(NULL); +} diff --git a/panda/src/ipc/loom_test1.cxx b/panda/src/ipc/loom_test1.cxx new file mode 100644 index 0000000000..ee0193fb3c --- /dev/null +++ b/panda/src/ipc/loom_test1.cxx @@ -0,0 +1,150 @@ +// Filename: loom_test1.cxx +// Created by: cary (30Sep98) +// +//////////////////////////////////////////////////////////////////// + +#include "loom.h" +#include + +static int test1_phase; +static int test1_phase2; +static mutex loom_test1_mutex; +static condition_variable loom_test1_cond(loom_test1_mutex); + +#define PRINT(x) { mutex_lock l(main_thread_print_mutex); x; } + +void loom_test1_init2(void) +{ + PRINT(cerr << "In loom_test1_init2" << endl); + test1_phase2 = 0; +} + +Action loom_test1_service2(unsigned long& s, unsigned long& n, + condition_variable*&) +{ + if (test1_phase2 == 0) { + test1_phase2 = 1; + s = 4; + PRINT(cerr << "loom_test1_service2: sleeping for 4 seconds" << endl); + return SLEEP; + } else if (test1_phase2 == 1) { + test1_phase2 = 2; + PRINT(cerr << "loom_test1_service2: signaling loom_test1_service to continue" << endl); + loom_test1_cond.signal(); + n = 500000000; + PRINT(cerr << "loom_test1_service2: sleeping for 1/2 second" << endl); + return SLEEP; + } else if (test1_phase2 == 2) { + test1_phase2 = 3; + PRINT(cerr << "loom_test1_service2: sending INFO message to main thread" << endl); + main_thread_message m(main_thread_message::INFO); + SendMainThreadMessage(m); + PRINT(cerr << "loom_test1_service2: returning RESERVICE" << endl); + return RESERVICE; + } else if (test1_phase2 == 3) { + if (test1_phase < 19) { + PRINT(cerr << "loom_test1_service2: returning RESERVICE" << endl); + return RESERVICE; + } else { + test1_phase2 = 4; + PRINT(cerr << "loom_test1_service2: sending INFO message to main thread" << endl); + main_thread_message m(main_thread_message::INFO); + SendMainThreadMessage(m); + PRINT(cerr << "loom_test1_service2: sleeping for 3/4 second" << endl); + n = 750000000; + return SLEEP; + } + } else { + PRINT(cerr << "loom_test1_service2: returning DONE" << endl); + return DONE; + } +} + +void loom_test1_cleanup2(void) +{ + PRINT(cerr << "In loom_test1_cleanup2" << endl); +} + +void loom_test1_init(void) +{ + PRINT(cerr << "In loom_test1_init" << endl); + test1_phase = 0; +} + +Action loom_test1_service(unsigned long& s, unsigned long& n, + condition_variable*& c) +{ + ++test1_phase; + switch (test1_phase) { + case 5: + PRINT(cerr << "loom_test1_service: returning YIELD to service manager" << endl); + return YIELD; + case 7: + s = 5; + PRINT(cerr << "loom_test1_service: returning SLEEP to service manager (s=5)" << endl); + return SLEEP; + case 9: + PRINT(cerr << "loom_test1_service: registering loom_test1_service2, etc, and returning YIELD" << endl); + RegisterAppService(loom_test1_init2, loom_test1_service2, loom_test1_cleanup2); + return YIELD; + case 11: + { + PRINT(cerr << "loom_test1_service: sending RESCAN message to main thread, returning YIELD" << endl); + main_thread_message m(main_thread_message::RESCAN); + SendMainThreadMessage(m); + return YIELD; + } + case 13: + PRINT(cerr << "loom_test1_service: returning WAIT to service manager" << endl); + loom_test1_mutex.lock(); + c = &loom_test1_cond; + return WAIT; + case 15: + n = 500000000; + PRINT(cerr << "loom_test1_service: returning SLEEP (n=500000000)" << endl); + return SLEEP; + case 17: + { + PRINT(cerr << "loom_test1_service: sending LOAD message to main thread (libloom_test2.so)" << endl); + main_thread_message m(main_thread_message::LOAD, "libloom_test2.so"); + SendMainThreadMessage(m); + s = 3; + PRINT(cerr << "loom_test1_service: returning SLEEP (s=3)" << endl); + return SLEEP; + } + case 19: + { + PRINT(cerr << "loom_test1_service: sending INFO message to main thread" << endl); + main_thread_message m(main_thread_message::INFO); + SendMainThreadMessage(m); + n = 500000000; + PRINT(cerr << "loom_test1_service: returning SLEEP (s=3)" << endl); + return SLEEP; + } + case 23: + PRINT(cerr << "loom_test1_service: returning DONE to service manager" << endl); + return DONE; + default: + PRINT(cerr << "loom_test1_service: returning RESERVICE to service manager" << endl); + return RESERVICE; + }; +} + +void loom_test1_cleanup(void) +{ + PRINT(cerr << "In loom_test1_cleanup" << endl); +} + +void loom_test1_info(void) +{ + PRINT(cerr << "Loom_test1_info function (phase = " << test1_phase << ")" << endl); +} + + +Configure(loom_test1); + +ConfigureFn(loom_test1) +{ + RegisterAppService(loom_test1_init, loom_test1_service, loom_test1_cleanup, + loom_test1_info); +} diff --git a/panda/src/ipc/loom_test2.cxx b/panda/src/ipc/loom_test2.cxx new file mode 100644 index 0000000000..88671dbf33 --- /dev/null +++ b/panda/src/ipc/loom_test2.cxx @@ -0,0 +1,41 @@ +// Filename: loom_test2.cxx +// Created by: cary (30Sep98) +// +//////////////////////////////////////////////////////////////////// + +#include "loom.h" +#include + +static int test2_phase; + +#define PRINT(x) { mutex_lock l(main_thread_print_mutex); x; } + +void loom_test2_init(void) +{ + PRINT(cerr << "In loom_test2_init" << endl); + test2_phase = 0; +} + +Action loom_test2_service(unsigned long& s, unsigned long&, + condition_variable*&) +{ + ++test2_phase; + switch (test2_phase) { + case 0: + s = 5; + PRINT(cerr << "loom_test2_service: sleeping for 5 seconds" << endl); + return SLEEP; + case 3: + PRINT(cerr << "loom_test2_service: returning DONE" << endl); + return DONE; + default: + PRINT(cerr << "loom_test2_service: returning RESERVICE" << endl); + return RESERVICE; + } +} + +Configure(loom_test2); +ConfigureFn(loom_test2) +{ + RegisterAppService(loom_test2_init, loom_test2_service); +} diff --git a/panda/src/ipc/test_diners.cxx b/panda/src/ipc/test_diners.cxx new file mode 100644 index 0000000000..701b10df81 --- /dev/null +++ b/panda/src/ipc/test_diners.cxx @@ -0,0 +1,139 @@ +// Filename: test_diners.cxx +// Created by: cary (16Sep98) +// +//////////////////////////////////////////////////////////////////// + +// A solution to the infamous dining philosophers, implemented using the +// threading abstraction. This program exercises thread creation and +// destruction, mutexes, and condition variables. + +#include +#include +#include "ipc_thread.h" +#include "ipc_condition.h" + +#ifdef PENV_WIN32 +static int last_rand = 0; +#endif /* __WIN32__ */ + +static mutex rand_mutex; + +static int random_l(void) +{ + mutex_lock l(rand_mutex); + int i = rand(); +#ifdef PENV_WIN32 + last_rand = i; +#endif /* __WIN32__ */ + return i; +} + +static mutex print_mutex; + +#define PRINTMSG(x) { mutex_lock l(print_mutex); x; } + +// n philosophers sharing n chopsticks. Philosophers are poor folk and can't +// afford luxuries like 2 chopsticks per person. +#define N_DINERS 5 + +static mutex chopsticks[N_DINERS]; + +// At most n philosophers are allowed into the room, others would have to +// wait at the door. This restriction demonstrates the use of condition +// variables. + +static mutex room_mutex; + +static condition_variable room_condition(room_mutex); +static int room_occupancy = 0; + +static class philosopher* phils[N_DINERS]; + +class philosopher : public thread { + private: + void run(void* arg) { + int id = *(int*)arg; + delete (int*)arg; +#ifdef PENV_WIN32 + rand_mutex.lock(); + srand(last_rand); + rand_mutex.unlock(); +#endif /* __WIN32__ */ + int l = id; + int r = l+1; + if (r == N_DINERS) + r = 0; + if (l & 1) { + int t = l; + l = r; + r = t; + } + PRINTMSG(cerr << "Philosopher #" << id << " has entered the room." + << endl); + int count = random_l() % 10 + 1; + while (--count) { + chopsticks[l].lock(); + chopsticks[r].lock(); + PRINTMSG(cerr << "Philosopher #" << id + << " is eating spaghetti now." << endl); + thread::sleep(random_l()%2, random_l()%1000000000); + chopsticks[l].unlock(); + chopsticks[r].unlock(); + PRINTMSG(cerr << "Philosopher #" << id + << " is pondering about life." << endl); + thread::sleep(random_l()%2, random_l()%1000000000); + } + room_mutex.lock(); + --room_occupancy; + phils[id] = (philosopher*)0L; + room_mutex.unlock(); + room_condition.signal(); + PRINTMSG(cerr << "Philosopher #" << id << " has left the room (" + << room_occupancy << " left)." << endl); + } + + // the destructor of a class that inherits from thread should never be + // public (otherwise the thread object can be destroyed while the + // underlying thread is still running). + ~philosopher(void) {} + inline void* make_arg(const int i) { return (void*)new int(i); } + public: + philosopher(const int id) : thread(make_arg(id)) { + start(); + } +}; + +int main(int, char**) +{ + int i; + room_mutex.lock(); + for (i=0; i +#include "ipc_file.h" +#include +#include +#include + +#define _ERROR_TEST + +int main(int argc, char* argv[]) +{ + //Test the writing capabilities + string test_file("test.out"); + string test_data("Jason Michael Clark"); + + Datagram test_out; test_out.add_string(test_data); + datagram_file stream(test_file); + + nout << "WRITE TEST" << endl; + nout << "Opening file for writing: " << test_file << endl; + if (!stream.open(file::FILE_WRITE)) nout << "Didn't open!" << endl; + nout << "Writing datagram with data: " << test_data << endl; + if (!stream.put_datagram(test_out)) nout << "Error in writing!" << endl; + nout << "Closing file: " << test_file << endl; + stream.close(); + + //Test the reading capabilites + Datagram test_in; + + nout << "READ TEST" << endl; + nout << "Opening file for reading: " << test_file << endl; + if (!stream.open(file::FILE_READ)) nout << "Didn't open!" << endl; + if (!stream.get_datagram(test_in)) nout << "Error reading!" << endl; + DatagramIterator scan(test_in); + nout << "Read datagram with data: " << scan.get_string() << endl; + nout << "Closing file: " << test_file << endl; + stream.close(); + +#ifdef _ERROR_TEST + //Test error handling + Datagram test_error; + + nout << "ERROR TEST" << endl; + nout << "Opening file for writing: " << test_file << endl; + stream.open(file::FILE_WRITE); + nout << "Trying to read datagram with data: " << endl; + stream.get_datagram(test_error); + nout << "Closing file: " << test_file << endl; + stream.close(); + nout << "Opening file for reading: " << test_file << endl; + stream.open(file::FILE_READ); + nout << "Trying to write datagram with data: " << test_data << endl; + test_error.add_string(test_data); + stream.put_datagram(test_error); + nout << "Closing file: " << test_file << endl; + stream.close(); +#endif + + return 1; +} + + + + diff --git a/panda/src/ipc/test_priority.cxx b/panda/src/ipc/test_priority.cxx new file mode 100644 index 0000000000..39d1566146 --- /dev/null +++ b/panda/src/ipc/test_priority.cxx @@ -0,0 +1,81 @@ +// Filename: test_priority.cxx +// Created by: cary (16Sep98) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include "ipc_thread.h" + +// test thread priorities + +#if defined(__arm__) && defined(__atmos__) +#define flush "" +#endif /* __arm__ */ + +static void func(void*); + +static int loop_iterations; + +static mutex print_mutex; +#define PRINTMSG(x) { mutex_lock l(print_mutex); x << flush; } + +int main(int argc, char** argv) +{ + unsigned long s1, s2, n1, n2; + char buf[20]; + + if ((argc != 2) || (argv[1][0] == '-')) { + cerr << "Usage: " << argv[0] << " loop_iterations" << endl; + exit(1); + } + loop_iterations = atoi(argv[1]); + thread::get_time(s1, n1); + for (int i=0; i n1) { + n2 -= n1; + s2 -= s1; + } else { + n2 = n2 + 1000000000 - n1; + s2 = s2 - 1 - s1; + } + sprintf(buf, "%d.%03d", s2, n2/1000000); + cout << argv[0] << ": doing " << loop_iterations + << " loop itterations (approx " << buf << " seconds per loop)" + << endl; + PRINTMSG(cout << "main: creating h1" << endl); + thread::create(func, (void*)"h1", thread::PRIORITY_HIGH); + PRINTMSG(cout << "main: creating m1" << endl); + thread::create(func, (void*)"m1", thread::PRIORITY_NORMAL); + PRINTMSG(cout << "main: creating l1" << endl); + thread::create(func, (void*)"l1", thread::PRIORITY_LOW); + PRINTMSG(cout << "main: creating h2" << endl); + thread::create(func, (void*)"h2", thread::PRIORITY_HIGH); + PRINTMSG(cout << "main: creating m2" << endl); + thread::create(func, (void*)"m2", thread::PRIORITY_NORMAL); + PRINTMSG(cout << "main: creating l2" << endl); + thread::self()->set_priority(thread::PRIORITY_LOW); + func((void*)"l2"); + + return 0; +} + +static void func(void* arg) +{ + char *name = (char *)arg; + + while (1) { + PRINTMSG(cout << name << ": entering 1st compute-bound loop" << endl); + int i; + for (i=0; i +#include +#include "ipc_thread.h" +#include "ipc_condition.h" +#include "ipc_mutex.h" + +// test condition variables and timed waits. makes 2 'producer' threads and +// 3 'consumer' threads + +static void producer(void*); +static void consumer(void*); + +mutex m; +condition_variable full(m); +condition_variable empty(m); + +bool empty_flag = true; +const char* message; + +static const char* msgs[] = { "wolf", "fox", "hyena", "dingo" }; + +int main(int, char**) +{ + cerr << "main: creating producer1" << endl; + thread::create(producer, (void*)"producer1"); + cerr << "main: creating producer2" << endl; + thread::create(producer, (void*)"producer2"); + cerr << "main: creating consumer1" << endl; + thread::create(consumer, (void*)"consumer1"); + cerr << "main: creating consumer2" << endl; + thread::create(consumer, (void*)"consumer2"); + cerr << "main: creating consumer3" << endl; + consumer((void*)"consumer3"); + return 0; +} + +static int random_l(void) +{ + static mutex rand_mutex; + mutex_lock l(rand_mutex); + int i = rand(); + return i; +} + +static void consumer(void* arg) +{ + char *name = (char *)arg; + unsigned long s, n; + + while (1) { + { + mutex_lock l(m); + thread::get_time(s, n, 0, 500000000); // 1/2 second from now + while (empty_flag) { + cerr << name << ": waiting for message" << endl; + if (!full.timedwait(s, n)) { + cerr << name << ": timed out, trying again" << endl; + thread::get_time(s, n, 0, 500000000); + } else if (empty_flag) + cerr << name << ": woken but message already consumed" << endl; + } + cerr << name << ": got message: '" << message << "'" << endl; + empty_flag = true; + empty.signal(); + } + thread::sleep(random_l() % 2, 1000000 * (random_l() % 1000)); + } +} + +static void producer(void* arg) +{ + char *name = (char *)arg; + + while (1) { + { + mutex_lock l(m); + while (!empty_flag) { + cerr << name << ": having to wait for consumer" << endl; + empty.wait(); + } + message = msgs[random_l() % 4]; + empty_flag = false; + full.signal(); + cerr << name << ": put message: '" << message << "'" << endl; + } + thread::sleep(random_l() % 2, 1000000 * (random_l() % 500) + 500); + } +} diff --git a/panda/src/ipc/test_thread.cxx b/panda/src/ipc/test_thread.cxx new file mode 100644 index 0000000000..c5e2b2ca45 --- /dev/null +++ b/panda/src/ipc/test_thread.cxx @@ -0,0 +1,90 @@ +// Filename: test_thread.cxx +// created by: charles (08jun00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include "ipc_thread.h" +#include "ipc_condition.h" + +static mutex print_mutex; + +static mutex m; +static condition_variable cv(m); + +class CTestThread : public thread +{ + private: + + void run(void *arg) + { + cout << "Hey main. This is " << (char *) arg << ". Wake the hell up." << endl; + cout.flush(); + + cv.signal(); + } + + public: + + CTestThread(char *name) : thread(name) + { + cout << name << " LIVES!." << endl; + cout.flush(); + + start(); + } + + ~CTestThread(void) {} +}; + +class CTestThread2 : public thread +{ + private: + + void run(void *arg) + { + cout << "Oh- so NOW you need me." << endl; + cout.flush(); + + cv.wait(); + cv.signal(); + + cout << "bastid." << endl; + cout.flush(); + } + + public: + + CTestThread2(char *name) : thread(name) + { + cout << name << " LIVES!." << endl; + cout.flush(); + + start(); + } + + ~CTestThread2(void) {} +}; + +int main(int, char **) +{ + CTestThread *test_thread = new CTestThread("test_thread"); + CTestThread2 *test_thread2 = new CTestThread2("test_thread2"); + + cout << "My name is main, and i'm going to go to sleep." << endl; + cout.flush(); + + cv.wait(); + + cout << "I hate you test_thread." << endl; + cout.flush(); + + cv.signal(); + cv.wait(); + + cout << "I hate you test_thread2." << endl; + cout.flush(); + + return 0; +} diff --git a/panda/src/ipc/test_threaddata.cxx b/panda/src/ipc/test_threaddata.cxx new file mode 100644 index 0000000000..74d3d0e375 --- /dev/null +++ b/panda/src/ipc/test_threaddata.cxx @@ -0,0 +1,44 @@ +// Filename: test_threaddata.cxx +// Created by: cary (16Sep98) +// +//////////////////////////////////////////////////////////////////// + +#include +#include "ipc_thread.h" + +// test a derived class from thread with thread specific data, as well as the +// joining mechanism. + +class thread_with_data : public thread { + private: + int _my_thread_id_plus_two; + void* run_undetached(void* ptr) { + int arg = *(int*)ptr; + delete (int*)ptr; + cerr << "Thread: run invoked with arg " << arg << endl; + cerr << "Thread: my id is " << get_id() << endl; + cerr << "Thread: my private data (id plus 2) is " + << _my_thread_id_plus_two << endl; + int* rv = new int(_my_thread_id_plus_two + 1); + cerr << "Thread: returning " << *rv << endl; + return (void*)rv; + } + ~thread_with_data(void) {} + static void* make_arg(const int i) { return (void*)new int(i); } + public: + thread_with_data(void) : thread(make_arg(5)) { + _my_thread_id_plus_two = get_id() + 2; + start_undetached(); + } +}; + +int main(int, char**) +{ + thread_with_data *t = new thread_with_data; + cerr << "main: joining" << endl; + int* rv; + t->join((void**)&rv); + cerr << "main: joined - got return value " << *rv << endl; + delete rv; + return 0; +} diff --git a/panda/src/lerp/Sources.pp b/panda/src/lerp/Sources.pp new file mode 100644 index 0000000000..c332f77dca --- /dev/null +++ b/panda/src/lerp/Sources.pp @@ -0,0 +1,18 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET lerp + #define LOCAL_LIBS \ + event linmath putil + + #define SOURCES \ + config_lerp.cxx config_lerp.h lerp.cxx lerp.h lerpblend.cxx \ + lerpblend.h lerpfunctor.cxx lerpfunctor.h + + #define INSTALL_HEADERS \ + lerp.h lerpblend.h lerpfunctor.h + + #define IGATESCAN all + +#end lib_target + diff --git a/panda/src/lerp/config_lerp.cxx b/panda/src/lerp/config_lerp.cxx new file mode 100644 index 0000000000..3414027aa7 --- /dev/null +++ b/panda/src/lerp/config_lerp.cxx @@ -0,0 +1,36 @@ +// Filename: config_lerp.cxx +// Created by: frang (30May00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_lerp.h" +#include "lerp.h" +#include "lerpfunctor.h" + +Configure(config_lerp); +NotifyCategoryDef(lerp, ""); + +ConfigureFn(config_lerp) { + Lerp::init_type(); + AutonomousLerp::init_type(); + + LerpFunctor::init_type(); + SimpleLerpFunctor::init_type(); + SimpleLerpFunctor::init_type(); + SimpleLerpFunctor::init_type(); + SimpleLerpFunctor::init_type(); + SimpleLerpFunctor::init_type(); + SimpleLerpFunctor::init_type(); + SimpleLerpFunctor::init_type(); + SimpleLerpFunctor::init_type(); + SimpleLerpFunctor::init_type(); + SimpleLerpFunctor::init_type(); + SimpleLerpFunctor::init_type(); + MultiLerpFunctor::init_type(); + + LerpBlendType::init_type(); + EaseInBlendType::init_type(); + EaseOutBlendType::init_type(); + EaseInOutBlendType::init_type(); + NoBlendType::init_type(); +} diff --git a/panda/src/lerp/config_lerp.h b/panda/src/lerp/config_lerp.h new file mode 100644 index 0000000000..b06e210f12 --- /dev/null +++ b/panda/src/lerp/config_lerp.h @@ -0,0 +1,15 @@ +// Filename: config_lerp.h +// Created by: frang (30May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __CONFIG_LERP_H__ +#define __CONFIG_LERP_H__ + +#include +#include +#include + +NotifyCategoryDecl(lerp, EXPCL_PANDA, EXPTP_PANDA); + +#endif /* __CONFIG_LERP_H__ */ diff --git a/panda/src/lerp/lerp.cxx b/panda/src/lerp/lerp.cxx new file mode 100644 index 0000000000..ea78d61a41 --- /dev/null +++ b/panda/src/lerp/lerp.cxx @@ -0,0 +1,182 @@ +// Filename: lerp.cxx +// Created by: frang (30May00) +// +//////////////////////////////////////////////////////////////////// + +#include "lerp.h" +#include +#include + +TypeHandle Lerp::_type_handle; +TypeHandle AutonomousLerp::_type_handle; + +static INLINE float scale_t(float t, float start, float end) { + float ret = t; + if (ret < start) + ret = start; + if (ret > end) + ret = end; + return ret / end; +} + +Lerp::Lerp(LerpFunctor* func, float endt, LerpBlendType* blend) + : _func(func), _startt(0.), _endt(endt), _blend(blend), _t(0.), + _delta(1.) {} + +Lerp::Lerp(LerpFunctor* func, float startt, float endt, + LerpBlendType* blend) : _func(func), _startt(startt), + _endt(endt), _blend(blend), + _t(startt), _delta(1.) {} + +Lerp::Lerp(const Lerp& c) : _blend(c._blend), _func(c._func), _event(c._event), + _startt(c._startt), _endt(c._endt), + _delta(c._delta), _t(c._t) {} + +Lerp::~Lerp(void) {} + +Lerp& Lerp::operator=(const Lerp& c) { + _blend = c._blend; + _func = c._func; + _event = c._event; + _startt = c._startt; + _endt = c._endt; + _delta = c._delta; + _t = c._t; + return *this; +} + +void Lerp::step(void) { + if (is_done()) + return; + _t += _delta; + float t = scale_t(_t, _startt, _endt); + t = (_blend==(LerpBlendType*)0L)?t:(*_blend)(t); + (*_func)(t); + if (is_done() && !_event.empty()) + throw_event(_event); +} + +void Lerp::set_step_size(float delta) { + _delta = delta; +} + +float Lerp::get_step_size(void) const { + return _delta; +} + +void Lerp::set_t(float t) { + _t = t; + float x = scale_t(_t, _startt, _endt); + x = (_blend==(LerpBlendType*)0L)?x:(*_blend)(x); + (*_func)(x); +} + +float Lerp::get_t(void) const { + return _t; +} + +bool Lerp::is_done(void) const { + return (_t >= _endt); +} + +LerpFunctor* Lerp::get_functor(void) const { + return _func; +} + +void Lerp::set_end_event(std::string& event) { + _event = event; +} + +std::string Lerp::get_end_event(void) const { + return _event; +} + +AutonomousLerp::AutonomousLerp(LerpFunctor* func, float endt, + LerpBlendType* blend, EventHandler* handler) + : _func(func), _startt(0.), _endt(endt), _blend(blend), _t(0.), + _handler(handler) {} + +AutonomousLerp::AutonomousLerp(LerpFunctor* func, float startt, float endt, + LerpBlendType* blend, EventHandler* handler) + : _func(func), _startt(startt), _endt(endt), _blend(blend), _t(startt), + _handler(handler) {} + +AutonomousLerp::AutonomousLerp(const AutonomousLerp& c) : _blend(c._blend), + _func(c._func), + _handler(c._handler), + _event(c._event), + _startt(c._startt), + _endt(c._endt), + _t(c._t) {} + +AutonomousLerp::~AutonomousLerp(void) {} + +AutonomousLerp& AutonomousLerp::operator=(const AutonomousLerp& c) { + _blend = c._blend; + _func = c._func; + _handler = c._handler; + _event = c._event; + _startt = c._startt; + _endt = c._endt; + _t = c._t; + return *this; +} + +void AutonomousLerp::start(void) { + _t = _startt; + _handler->add_hook("NewFrame", handle_event, this); +} + +void AutonomousLerp::stop(void) { + _handler->remove_hook("NewFrame", handle_event, this); +} + +void AutonomousLerp::resume(void) { + _handler->add_hook("NewFrame", handle_event, this); +} + +bool AutonomousLerp::is_done(void) const { + return (_t >= _endt); +} + +LerpFunctor* AutonomousLerp::get_functor(void) const { + return _func; +} + +void AutonomousLerp::set_t(float t) { + _t = t; + float x = scale_t(_t, _startt, _endt); + x = (_blend==(LerpBlendType*)0L)?x:(*_blend)(x); + (*_func)(x); +} + +float AutonomousLerp::get_t(void) const { + return _t; +} + +void AutonomousLerp::set_end_event(std::string& event) { + _event = event; +} + +std::string AutonomousLerp::get_end_event(void) const { + return _event; +} + +void AutonomousLerp::step(void) { + if (is_done()) { + stop(); + return; + } + float delta = ClockObject::get_global_clock()->get_dt(); + _t += delta; + float t = scale_t(_t, _startt, _endt); + t = (_blend==(LerpBlendType*)0L)?t:(*_blend)(t); + (*_func)(t); + if (is_done() && !_event.empty()) + throw_event(_event); +} + +void AutonomousLerp::handle_event(CPT(Event), void* data) { + AutonomousLerp* l = (AutonomousLerp*)data; + l->step(); +} diff --git a/panda/src/lerp/lerp.h b/panda/src/lerp/lerp.h new file mode 100644 index 0000000000..75dfeb6fa2 --- /dev/null +++ b/panda/src/lerp/lerp.h @@ -0,0 +1,111 @@ +// Filename: lerp.h +// Created by: frang (18Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __LERP_H__ +#define __LERP_H__ + +#include + +#include "lerpfunctor.h" +#include "lerpblend.h" + +#include +#include + +class EXPCL_PANDA Lerp : public TypedReferenceCount { +private: + PT(LerpBlendType) _blend; + PT(LerpFunctor) _func; + std::string _event; + float _startt; + float _endt; + float _delta; + float _t; +public: + Lerp(LerpFunctor* func, float endt, LerpBlendType* blend); + Lerp(LerpFunctor* func, float startt, float endt, LerpBlendType* blend); + Lerp(const Lerp&); + virtual ~Lerp(void); + Lerp& operator=(const Lerp&); + void step(void); + void set_step_size(float); + float get_step_size(void) const; + void set_t(float); + float get_t(void) const; + bool is_done(void) const; + LerpFunctor* get_functor(void) const; + void set_end_event(std::string&); + std::string get_end_event(void) const; +public: + // now for typehandle stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + TypedReferenceCount::init_type(); + register_type(_type_handle, "Lerp", TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +class EXPCL_PANDA AutonomousLerp : public TypedReferenceCount { +private: + PT(LerpBlendType) _blend; + PT(LerpFunctor) _func; + EventHandler* _handler; + std::string _event; + float _startt; + float _endt; + float _t; + + virtual void step(void); + static void handle_event(CPT(Event), void*); +public: + AutonomousLerp(LerpFunctor* func, float endt, LerpBlendType* blend, + EventHandler* handler); + AutonomousLerp(LerpFunctor* func, float startt, float endt, + LerpBlendType* blend, EventHandler* handler); + AutonomousLerp(const AutonomousLerp&); + virtual ~AutonomousLerp(void); + AutonomousLerp& operator=(const AutonomousLerp&); + void start(void); + void stop(void); + void resume(void); + bool is_done(void) const; + LerpFunctor* get_functor(void) const; + void set_t(float); + float get_t(void) const; + void set_end_event(std::string&); + std::string get_end_event(void) const; +public: + // now for typehandle stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + TypedReferenceCount::init_type(); + register_type(_type_handle, "AutonomousLerp", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +#endif /* __LERP_H__ */ diff --git a/panda/src/lerp/lerpblend.cxx b/panda/src/lerp/lerpblend.cxx new file mode 100644 index 0000000000..5a711fd9ae --- /dev/null +++ b/panda/src/lerp/lerpblend.cxx @@ -0,0 +1,84 @@ +// Filename: lerpblend.cxx +// Created by: frang (30May00) +// +//////////////////////////////////////////////////////////////////// + +#include "lerpblend.h" + +TypeHandle LerpBlendType::_type_handle; +TypeHandle EaseInBlendType::_type_handle; +TypeHandle EaseOutBlendType::_type_handle; +TypeHandle EaseInOutBlendType::_type_handle; +TypeHandle NoBlendType::_type_handle; + +LerpBlendType::LerpBlendType(const LerpBlendType&) {} + +LerpBlendType::~LerpBlendType(void) {} + +LerpBlendType& LerpBlendType::operator=(const LerpBlendType&) { + return *this; +} + +float LerpBlendType::operator()(float t) { + return t; +} + +EaseInBlendType::EaseInBlendType(const EaseInBlendType& c) : LerpBlendType(c) +{ +} + +EaseInBlendType::~EaseInBlendType(void) {} + +EaseInBlendType& EaseInBlendType::operator=(const EaseInBlendType& c) { + LerpBlendType::operator=(c); + return *this; +} + +float EaseInBlendType::operator()(float t) { + float x = t*t; + return ((3. * x) - (t * x)) * 0.5; +} + +EaseOutBlendType::EaseOutBlendType(const EaseOutBlendType& c) + : LerpBlendType(c) {} + +EaseOutBlendType::~EaseOutBlendType(void) {} + +EaseOutBlendType& EaseOutBlendType::operator=(const EaseOutBlendType& c) { + LerpBlendType::operator=(c); + return *this; +} + +float EaseOutBlendType::operator()(float t) { + return ((3. * t) - (t * t * t)) * 0.5; +} + +EaseInOutBlendType::EaseInOutBlendType(const EaseInOutBlendType& c) + : LerpBlendType(c) {} + +EaseInOutBlendType::~EaseInOutBlendType(void) {} + +EaseInOutBlendType& EaseInOutBlendType::operator=(const EaseInOutBlendType& c) +{ + LerpBlendType::operator=(c); + return *this; +} + +float EaseInOutBlendType::operator()(float t) { + float x = t*t; + return (3. * x) - (2. * t * x); +} + +NoBlendType::NoBlendType(const NoBlendType& c) : LerpBlendType(c) {} + +NoBlendType::~NoBlendType(void) {} + +NoBlendType& NoBlendType::operator=(const NoBlendType& c) { + LerpBlendType::operator=(c); + return *this; +} + +float NoBlendType::operator()(float t) { + return t; +} + diff --git a/panda/src/lerp/lerpblend.h b/panda/src/lerp/lerpblend.h new file mode 100644 index 0000000000..6251584987 --- /dev/null +++ b/panda/src/lerp/lerpblend.h @@ -0,0 +1,152 @@ +// Filename: lerpblend.h +// Created by: frang (30May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __LERPBLEND_H__ +#define __LERPBLEND_H__ + +#include +#include + +class LerpBlendType : public TypedReferenceCount { +public: + LerpBlendType(void) {} + LerpBlendType(const LerpBlendType&); + virtual ~LerpBlendType(void); + LerpBlendType& operator=(const LerpBlendType&); + virtual float operator()(float) = 0; +public: + // now for typehandle stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + TypedReferenceCount::init_type(); + register_type(_type_handle, "LerpBlendType", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +class EaseInBlendType : public LerpBlendType { +public: + EaseInBlendType(void) {} + EaseInBlendType(const EaseInBlendType&); + virtual ~EaseInBlendType(void); + EaseInBlendType& operator=(const EaseInBlendType&); + virtual float operator()(float); +public: + // now for typehandle stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + TypedReferenceCount::init_type(); + register_type(_type_handle, "EaseInBlendType", + LerpBlendType::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +class EaseOutBlendType : public LerpBlendType { +public: + EaseOutBlendType(void) {} + EaseOutBlendType(const EaseOutBlendType&); + virtual ~EaseOutBlendType(void); + EaseOutBlendType& operator=(const EaseOutBlendType&); + virtual float operator()(float); +public: + // now for typehandle stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + TypedReferenceCount::init_type(); + register_type(_type_handle, "EaseOutBlendType", + LerpBlendType::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +class EaseInOutBlendType : public LerpBlendType { +public: + EaseInOutBlendType(void) {} + EaseInOutBlendType(const EaseInOutBlendType&); + virtual ~EaseInOutBlendType(void); + EaseInOutBlendType& operator=(const EaseInOutBlendType&); + virtual float operator()(float); +public: + // now for typehandle stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + TypedReferenceCount::init_type(); + register_type(_type_handle, "EaseInOutBlendType", + LerpBlendType::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +class NoBlendType : public LerpBlendType { +public: + NoBlendType(void) {} + NoBlendType(const NoBlendType&); + virtual ~NoBlendType(void); + NoBlendType& operator=(const NoBlendType&); + virtual float operator()(float); +public: + // now for typehandle stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + TypedReferenceCount::init_type(); + register_type(_type_handle, "NoBlendType", + LerpBlendType::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +#endif /* __LERPBLEND_H__ */ diff --git a/panda/src/lerp/lerpchans.h b/panda/src/lerp/lerpchans.h new file mode 100644 index 0000000000..32c93744d8 --- /dev/null +++ b/panda/src/lerp/lerpchans.h @@ -0,0 +1,90 @@ +// Filename: lerpchans.h +// Created by: frang (11Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __LERPCHANS_H__ +#define __LERPCHANS_H__ + +// There are three fundamental types of lerp channel: tabular/keyframe, +// procedural, and composite. Tabular channels may have an interpolator +// associated with them, to determine values between samples. Proceedural +// channels compute each data point as needed. Composite channels can be +// either blending or concatenation objects. Blending objects take some +// number of channels as input, apply a blending function to them in order +// to yield what appears to be a single channel. Concatenation objects +// take some number of channels as input and string them end-to-end to yield +// what appears to be a single channel. + +class LerpChannelRange { +private: + float _low, _high; +public: + INLINE LerpChannelRange(float low, float high) : _low(low), _high(high) { + if (low > high) { + _low = high; + _high = low; + } + } + INLINE LerpChannelRange(const LerpChannelRange& c) : _low(c._low), + _high(c._high) {} + INLINE ~LerpChannelRange(void) {} + INLINE float GetLow(void) { return _low; } + INLINE float GetHigh(void) { return _high; } + INLINE void SetLow(float l) { + if (l > _high) { + _low = _high; + _high = l; + } else + _low = l; + } + INLINE void SetHigh(float h) { + if (h < _low) { + _high = _low; + _low = h; + } else + _high = h; + } + INLINE void SetRange(float l, float h) { + if (l > h) { + _low = h; + _high = l; + } else { + _low = l; + _high = h; + } + } + INLINE LerpChannelRange& operator=(const LerpChannelRange& c) { + _low = c._low; + _high = c._high; + return *this; + } +}; + +template +class LerpChannel { +public: + virtual GetValue(float p); +}; + +template +class TabularChannel : public LerpChannel { +}; + +template +class ProceduralChannel : public LerpChannel { +}; + +template +class CompositeChannel : public LerpChannel { +}; + +template +class BlendChannel : public CompositeChannel { +}; + +template +class ConcatenationChannel : public CompositeChannel { +}; + +#endif /* __LERPCHANS_H__ */ diff --git a/panda/src/lerp/lerpfunctor.cxx b/panda/src/lerp/lerpfunctor.cxx new file mode 100644 index 0000000000..5d2c4b7e44 --- /dev/null +++ b/panda/src/lerp/lerpfunctor.cxx @@ -0,0 +1,49 @@ +// Filename: lerpfunctor.cxx +// Created by: frang (26May00) +// +//////////////////////////////////////////////////////////////////// + +#include "lerpfunctor.h" + +TypeHandle LerpFunctor::_type_handle; +TypeHandle MultiLerpFunctor::_type_handle; + +LerpFunctor::LerpFunctor(const LerpFunctor&) +{ +} + +LerpFunctor::~LerpFunctor(void) +{ +} + +LerpFunctor& LerpFunctor::operator=(const LerpFunctor&) { + return *this; +} + +void LerpFunctor::operator()(float) { + // should not be here +} + +MultiLerpFunctor::MultiLerpFunctor(const MultiLerpFunctor& c) + : LerpFunctor(c), _funcs(c._funcs) {} + +MultiLerpFunctor::~MultiLerpFunctor(void) {} + +MultiLerpFunctor& MultiLerpFunctor::operator=(const MultiLerpFunctor& c) { + _funcs = c._funcs; + LerpFunctor::operator=(c); + return *this; +} + +void MultiLerpFunctor::operator()(float f) { + for (Functors::iterator i=_funcs.begin(); i!=_funcs.end(); ++i) + (*(*i))(f); +} + +void MultiLerpFunctor::add_functor(LerpFunctor* func) { + _funcs.insert(func); +} + +void MultiLerpFunctor::remove_functor(LerpFunctor* func) { + _funcs.erase(func); +} diff --git a/panda/src/lerp/lerpfunctor.h b/panda/src/lerp/lerpfunctor.h new file mode 100644 index 0000000000..07347bf0bd --- /dev/null +++ b/panda/src/lerp/lerpfunctor.h @@ -0,0 +1,179 @@ +// Filename: lerpfunctor.h +// Created by: frang (26May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __LERPFUNCTOR_H__ +#define __LERPFUNCTOR_H__ + +#include +#include + +class EXPCL_PANDA LerpFunctor : public TypedReferenceCount { +public: + LerpFunctor(void) {} + LerpFunctor(const LerpFunctor&); + virtual ~LerpFunctor(void); + LerpFunctor& operator=(const LerpFunctor&); + virtual void operator()(float) = 0; +public: + // now for typehandle stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + TypedReferenceCount::init_type(); + register_type(_type_handle, "LerpFunctor", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +template +class SimpleLerpFunctor : public LerpFunctor { +private: + value _start; + value _end; + value _diff_cache; + +protected: + value interpolate(float); + SimpleLerpFunctor(value start, value end) : LerpFunctor(), _start(start), + _end(end), _diff_cache(end-start) + {} + SimpleLerpFunctor(const SimpleLerpFunctor&); +public: + virtual ~SimpleLerpFunctor(void); + SimpleLerpFunctor& operator=(const SimpleLerpFunctor&); + virtual void operator()(float) = 0; +public: + // now for typehandle stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LerpFunctor::init_type(); + do_init_type(value); + ostringstream os; + os << "SimpleLerpFunctor<" << get_type_handle(value).get_name() << ">"; + register_type(_type_handle, os.str(), LerpFunctor::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +template +TypeHandle SimpleLerpFunctor::_type_handle; + +#include + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, SimpleLerpFunctor) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, SimpleLerpFunctor) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, SimpleLerpFunctor) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, SimpleLerpFunctor) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, SimpleLerpFunctor) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, SimpleLerpFunctor) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, SimpleLerpFunctor) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, SimpleLerpFunctor) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, SimpleLerpFunctor) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, SimpleLerpFunctor) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, SimpleLerpFunctor) + +typedef SimpleLerpFunctor IntLerpFunctor; +typedef SimpleLerpFunctor FloatLerpFunctor; +typedef SimpleLerpFunctor LPoint2fLerpFunctor; +typedef SimpleLerpFunctor LPoint3fLerpFunctor; +typedef SimpleLerpFunctor LPoint4fLerpFunctor; +typedef SimpleLerpFunctor LVecBase2fLerpFunctor; +typedef SimpleLerpFunctor LVecBase3fLerpFunctor; +typedef SimpleLerpFunctor LVecBase4fLerpFunctor; +typedef SimpleLerpFunctor LVector2fLerpFunctor; +typedef SimpleLerpFunctor LVector3fLerpFunctor; +typedef SimpleLerpFunctor LVector4fLerpFunctor; + +#include +#include + +class EXPCL_PANDA MultiLerpFunctor : public LerpFunctor { +private: + typedef set Functors; + Functors _funcs; +public: + MultiLerpFunctor(void) {} + MultiLerpFunctor(const MultiLerpFunctor&); + virtual ~MultiLerpFunctor(void); + MultiLerpFunctor& operator=(const MultiLerpFunctor&); + virtual void operator()(float); + void add_functor(LerpFunctor*); + void remove_functor(LerpFunctor*); +public: + // now for typehandle stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LerpFunctor::init_type(); + register_type(_type_handle, "MultiLerpFunctor", + LerpFunctor::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +// +// template implementation +// + +template +SimpleLerpFunctor::SimpleLerpFunctor(const SimpleLerpFunctor& c) + : _start(c._start), _end(c._end), _diff_cache(c._diff_cache), LerpFunctor(c) + {} + +template +SimpleLerpFunctor::~SimpleLerpFunctor(void) +{ +} + +template +SimpleLerpFunctor& +SimpleLerpFunctor::operator=(const SimpleLerpFunctor& c) { + _start = c._start; + _end = c._end; + _diff_cache = c._diff_cache; + LerpFunctor::operator=(c); + return *this; +} + +template +void SimpleLerpFunctor::operator()(float) { + // should not be here +} + +template +value SimpleLerpFunctor::interpolate(float t) { + return ((t * _diff_cache) + _start); +} + +#endif /* __LERPFUNCTOR_H__ */ diff --git a/panda/src/light/Sources.pp b/panda/src/light/Sources.pp new file mode 100644 index 0000000000..c184e731c1 --- /dev/null +++ b/panda/src/light/Sources.pp @@ -0,0 +1,27 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET light + #define LOCAL_LIBS \ + putil display graph sgraph gobj sgattrib pnmimage mathutil gsgbase \ + linmath + + #define SOURCES \ + ambientLight.cxx ambientLight.h config_light.cxx config_light.h \ + directionalLight.I directionalLight.cxx directionalLight.h \ + light.cxx light.h lightAttribute.I lightAttribute.cxx \ + lightAttribute.h lightTransition.I lightTransition.cxx \ + lightTransition.h pointLight.I pointLight.cxx pointLight.h \ + pt_Light.cxx pt_Light.h spotlight.I spotlight.cxx spotlight.h \ + vector_PT_Light.cxx vector_PT_Light.h + + #define INSTALL_HEADERS \ + ambientLight.h directionalLight.I directionalLight.h light.h \ + lightAttribute.I lightAttribute.h lightNameClass.h \ + lightTransition.I lightTransition.h pointLight.I pointLight.h \ + pt_Light.h spotlight.I spotlight.h vector_PT_Light.h + + #define IGATESCAN all + +#end lib_target + diff --git a/panda/src/light/ambientLight.cxx b/panda/src/light/ambientLight.cxx new file mode 100644 index 0000000000..6a81417a78 --- /dev/null +++ b/panda/src/light/ambientLight.cxx @@ -0,0 +1,50 @@ +// Filename: ambientLight.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "ambientLight.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle AmbientLight::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: AmbientLight::Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +AmbientLight::AmbientLight(const string& name) : NamedNode(name) +{ + set_color(Colorf(0.2, 0.2, 0.2, 1)); +} + +//////////////////////////////////////////////////////////////////// +// Function: AmbientLight::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void AmbientLight:: +output(ostream &out) const { + NamedNode::output(out); +} + + +//////////////////////////////////////////////////////////////////// +// Function: AmbientLight::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void AmbientLight:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << *this << ":\n"; + indent(out, indent_level + 2) << "color " << _color << "\n"; +} + diff --git a/panda/src/light/ambientLight.h b/panda/src/light/ambientLight.h new file mode 100644 index 0000000000..72471c6006 --- /dev/null +++ b/panda/src/light/ambientLight.h @@ -0,0 +1,72 @@ +// Filename: ambientLight.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef AMBIENTLIGHT_H +#define AMBIENTLIGHT_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include +#include +#include "light.h" + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : AmbientLight +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA AmbientLight : public Light, public NamedNode +{ + public: + + AmbientLight( const string& name = "" ); + ~AmbientLight( void ) { } + + virtual void apply( GraphicsStateGuardian* gsg ) { + gsg->apply_light( this ); + } + + virtual void output( ostream &out ) const; + virtual void write( ostream &out, int indent_level = 0 ) const; + + public: + + static TypeHandle get_class_type( void ) { + return _type_handle; + } + static void init_type( void ) { + Light::init_type(); + NamedNode::init_type(); + register_type( _type_handle, "AmbientLight", + Light::get_class_type(), + NamedNode::get_class_type() ); + } + virtual TypeHandle get_type( void ) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + virtual TypeHandle get_light_type( void ) const { + return get_class_type(); + } + + private: + + static TypeHandle _type_handle; +}; + +INLINE ostream &operator << (ostream &out, const AmbientLight &light) { + light.output(out); + return out; +} + +#endif diff --git a/panda/src/light/config_light.cxx b/panda/src/light/config_light.cxx new file mode 100644 index 0000000000..5766d7ddd3 --- /dev/null +++ b/panda/src/light/config_light.cxx @@ -0,0 +1,28 @@ +// Filename: config_light.cxx +// Created by: drose (19Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_light.h" +#include "ambientLight.h" +#include "directionalLight.h" +#include "light.h" +#include "lightTransition.h" +#include "lightAttribute.h" +#include "pointLight.h" +#include "spotlight.h" + +#include + +Configure(config_light); +NotifyCategoryDef(light, ""); + +ConfigureFn(config_light) { + AmbientLight::init_type(); + DirectionalLight::init_type(); + Light::init_type(); + LightTransition::init_type(); + LightAttribute::init_type(); + PointLight::init_type(); + Spotlight::init_type(); +} diff --git a/panda/src/light/config_light.h b/panda/src/light/config_light.h new file mode 100644 index 0000000000..4e88e4f787 --- /dev/null +++ b/panda/src/light/config_light.h @@ -0,0 +1,14 @@ +// Filename: config_light.h +// Created by: drose (19Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_LIGHT_H +#define CONFIG_LIGHT_H + +#include +#include + +NotifyCategoryDecl(light, EXPCL_PANDA, EXPTP_PANDA); + +#endif diff --git a/panda/src/light/directionalLight.I b/panda/src/light/directionalLight.I new file mode 100644 index 0000000000..18978e7efd --- /dev/null +++ b/panda/src/light/directionalLight.I @@ -0,0 +1,24 @@ +// Filename: directionalLight.I +// Created by: mike (04Feb99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: DirectionalLight::get_specular +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Colorf DirectionalLight::get_specular( void ) const +{ + return _specular; +} + +//////////////////////////////////////////////////////////////////// +// Function: DirectionalLight::set_specular +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DirectionalLight::set_specular( const Colorf& color ) +{ + _specular = color; +} diff --git a/panda/src/light/directionalLight.cxx b/panda/src/light/directionalLight.cxx new file mode 100644 index 0000000000..8705804a71 --- /dev/null +++ b/panda/src/light/directionalLight.cxx @@ -0,0 +1,52 @@ +// Filename: directionalLight.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include +#include "directionalLight.h" + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle DirectionalLight::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: DirectionalLight::Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +DirectionalLight::DirectionalLight(const string& name) : NamedNode(name) +{ + set_color(Colorf(1, 1, 1, 1)); + set_specular(Colorf(1, 1, 1, 1)); +} + +//////////////////////////////////////////////////////////////////// +// Function: DirectionalLight::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DirectionalLight:: +output(ostream &out) const { + NamedNode::output(out); +} + +//////////////////////////////////////////////////////////////////// +// Function: DirectionalLight::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DirectionalLight:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << *this << ":\n"; + indent(out, indent_level + 2) << "color " << _color << "\n"; + indent(out, indent_level + 2) << "specular " << _specular << "\n"; +} diff --git a/panda/src/light/directionalLight.h b/panda/src/light/directionalLight.h new file mode 100644 index 0000000000..7a0a4ee565 --- /dev/null +++ b/panda/src/light/directionalLight.h @@ -0,0 +1,81 @@ +// Filename: directionalLight.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef DIRECTIONALLIGHT_H +#define DIRECTIONALLIGHT_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include +#include +#include "light.h" + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : DirectionalLight +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DirectionalLight : public Light, public NamedNode +{ + public: + + DirectionalLight( const string& name = "" ); + ~DirectionalLight( void ) { } + + virtual void apply( GraphicsStateGuardian* gsg ) { + gsg->apply_light( this ); + } + + INLINE Colorf get_specular( void ) const; + INLINE void set_specular( const Colorf& color ); + + virtual void output( ostream &out ) const; + virtual void write( ostream &out, int indent_level = 0 ) const; + + protected: + + Colorf _specular; + + public: + + static TypeHandle get_class_type( void ) { + return _type_handle; + } + static void init_type( void ) { + Light::init_type(); + NamedNode::init_type(); + register_type( _type_handle, "DirectionalLight", + Light::get_class_type(), + NamedNode::get_class_type() ); + } + virtual TypeHandle get_type( void ) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + virtual TypeHandle get_light_type( void ) const { + return get_class_type(); + } + + private: + + static TypeHandle _type_handle; +}; + +INLINE ostream &operator << (ostream &out, const DirectionalLight &light) { + light.output(out); + return out; +} + +#include "directionalLight.I" + +#endif diff --git a/panda/src/light/light.cxx b/panda/src/light/light.cxx new file mode 100644 index 0000000000..bb587a0297 --- /dev/null +++ b/panda/src/light/light.cxx @@ -0,0 +1,14 @@ +// Filename: light.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "light.h" + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle Light::_type_handle; diff --git a/panda/src/light/light.h b/panda/src/light/light.h new file mode 100644 index 0000000000..859121e73c --- /dev/null +++ b/panda/src/light/light.h @@ -0,0 +1,69 @@ +// Filename: light.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef LIGHT_H +#define LIGHT_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : Light +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Light : virtual public ReferenceCount +{ + public: + + Light( void ) { _color.set(0.0, 0.0, 0.0, 1.0); } + ~Light( void ) { } + + virtual void apply( GraphicsStateGuardian* gsg ) = 0; + + INLINE Colorf get_color(void) const { return _color; } + INLINE void set_color(const Colorf& color) { _color = color; } + + virtual void output(ostream &out) const=0; + virtual void write(ostream &out, int indent_level = 0) const=0; + + protected: + + Colorf _color; + + public: + + static TypeHandle get_class_type( void ) { + return _type_handle; + } + static void init_type( void ) { + ReferenceCount::init_type(); + register_type( _type_handle, "Light", + ReferenceCount::get_class_type() ); + } + virtual TypeHandle get_light_type( void ) const { + return get_class_type(); + } + + private: + + static TypeHandle _type_handle; +}; + +INLINE ostream &operator << (ostream &out, const Light &light) { + light.output(out); + return out; +} + +#endif diff --git a/panda/src/light/lightAttribute.I b/panda/src/light/lightAttribute.I new file mode 100644 index 0000000000..49f30afd91 --- /dev/null +++ b/panda/src/light/lightAttribute.I @@ -0,0 +1,13 @@ +// Filename: lightAttribute.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: LightAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LightAttribute:: +LightAttribute() { +} diff --git a/panda/src/light/lightAttribute.cxx b/panda/src/light/lightAttribute.cxx new file mode 100644 index 0000000000..07b13740f4 --- /dev/null +++ b/panda/src/light/lightAttribute.cxx @@ -0,0 +1,79 @@ +// Filename: lightAttribute.cxx +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "lightAttribute.h" +#include "lightTransition.h" + +#include +#include + +TypeHandle LightAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LightAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle LightAttribute:: +get_handle() const { + return LightTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LightAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated LightAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *LightAttribute:: +make_copy() const { + return new LightAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: LightAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated LightAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *LightAttribute:: +make_initial() const { + return new LightAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: LightAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void LightAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_light(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: LightAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void LightAttribute:: +output_property(ostream &out, const PT_Light &prop) const { + out << *prop; +} + +//////////////////////////////////////////////////////////////////// +// Function: LightAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void LightAttribute:: +write_property(ostream &out, const PT_Light &prop, + int indent_level) const { + prop->write(out, indent_level); +} diff --git a/panda/src/light/lightAttribute.h b/panda/src/light/lightAttribute.h new file mode 100644 index 0000000000..07e4b4a4bd --- /dev/null +++ b/panda/src/light/lightAttribute.h @@ -0,0 +1,63 @@ +// Filename: lightAttribute.h +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LIGHTATTRIBUTE_H +#define LIGHTATTRIBUTE_H + +#include + +#include "light.h" +#include "pt_Light.h" +#include "vector_PT_Light.h" +#include "lightNameClass.h" + +#include +#include + +// We need to define this temporary macro so we can pass a parameter +// containing a comma through the macro. +#define MULTIATTRIBUTE_LIGHT MultiAttribute +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, MULTIATTRIBUTE_LIGHT); + +//////////////////////////////////////////////////////////////////// +// Class : LightAttribute +// Description : See LightTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA LightAttribute : public MultiAttribute { +public: + INLINE LightAttribute(); + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void output_property(ostream &out, const PT_Light &prop) const; + virtual void write_property(ostream &out, const PT_Light &prop, + int indent_level) const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MultiAttribute::init_type(); + register_type(_type_handle, "LightAttribute", + MultiAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "lightAttribute.I" + +#endif diff --git a/panda/src/light/lightNameClass.h b/panda/src/light/lightNameClass.h new file mode 100644 index 0000000000..a5a9c9d7f3 --- /dev/null +++ b/panda/src/light/lightNameClass.h @@ -0,0 +1,27 @@ +// Filename: lightNameClass.h +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LIGHTNAMECLASS_H +#define LIGHTNAMECLASS_H + +#include + +//////////////////////////////////////////////////////////////////// +// Class : LightNameClass +// Description : This is a stupid little class that's used by +// LightTransition to define the name of its +// PT(Light) class, so the MultiTransition it +// inherits from can initialize itself properly. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA LightNameClass { +public: + static string get_class_name() { + return "PT(Light)"; + } +}; + +#endif + + diff --git a/panda/src/light/lightTransition.I b/panda/src/light/lightTransition.I new file mode 100644 index 0000000000..41216c8b43 --- /dev/null +++ b/panda/src/light/lightTransition.I @@ -0,0 +1,26 @@ +// Filename: lightTransition.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: LightTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LightTransition:: +LightTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LightTransition::all_off +// Access: Public +// Description: This named constructor returns a LightTransition +// that's preconfigured to disable all lights below it. +//////////////////////////////////////////////////////////////////// +INLINE LightTransition LightTransition:: +all_off() { + LightTransition t; + t.set_default_dir(TD_off); + return t; +} diff --git a/panda/src/light/lightTransition.cxx b/panda/src/light/lightTransition.cxx new file mode 100644 index 0000000000..5dbab56c9d --- /dev/null +++ b/panda/src/light/lightTransition.cxx @@ -0,0 +1,65 @@ +// Filename: lightTransition.cxx +// Created by: mike (06Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "lightTransition.h" +#include "lightAttribute.h" + +#include + +TypeHandle LightTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LightTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated LightTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *LightTransition:: +make_copy() const { + return new LightTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: LightTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated LightAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *LightTransition:: +make_attrib() const { + return new LightAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: LightTransition::make_identity +// Access: Public, Virtual +// Description: Returns a newly allocated LightTransition in the +// initial state. +//////////////////////////////////////////////////////////////////// +NodeTransition *LightTransition:: +make_identity() const { + return new LightTransition; +} + +//////////////////////////////////////////////////////////////////// +// Function: LightTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void LightTransition:: +output_property(ostream &out, const PT_Light &prop) const { + out << *prop; +} + +//////////////////////////////////////////////////////////////////// +// Function: LightTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void LightTransition:: +write_property(ostream &out, const PT_Light &prop, + int indent_level) const { + prop->write(out, indent_level); +} diff --git a/panda/src/light/lightTransition.h b/panda/src/light/lightTransition.h new file mode 100644 index 0000000000..8eb379f4e0 --- /dev/null +++ b/panda/src/light/lightTransition.h @@ -0,0 +1,69 @@ +// Filename: lightTransition.h +// Created by: mike (19Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LIGHTTRANSITION_H +#define LIGHTTRANSITION_H + +#include + +#include "light.h" +#include "pt_Light.h" +#include "lightNameClass.h" + +#include +#include + +// We need to define this temporary macro so we can pass a parameter +// containing a comma through the macro. +#define MULTITRANSITION_LIGHT MultiTransition +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, MULTITRANSITION_LIGHT); + +//////////////////////////////////////////////////////////////////// +// Class : LightTransition +// Description : The LightTransition allows specifying the set of +// lights that affect the geometry in the scene graph. +// If the set of lights is empty (e.g. under a +// LightTransition::all_off() transition), then lighting +// is disabled; otherwise, lighting is enabled with the +// indicated lights in effect. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA LightTransition : public MultiTransition { +public: + INLINE LightTransition(); + INLINE static LightTransition all_off(); + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + virtual NodeTransition *make_identity() const; + +protected: + virtual void output_property(ostream &out, const PT_Light &prop) const; + virtual void write_property(ostream &out, const PT_Light &prop, + int indent_level) const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MultiTransition::init_type(); + register_type(_type_handle, "LightTransition", + MultiTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class LightAttribute; +}; + +#include "lightTransition.I" + +#endif + + diff --git a/panda/src/light/pointLight.I b/panda/src/light/pointLight.I new file mode 100644 index 0000000000..bc28b3bdad --- /dev/null +++ b/panda/src/light/pointLight.I @@ -0,0 +1,84 @@ +// Filename: pointLight.I +// Created by: mike (04Feb99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: PointLight::get_specular +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Colorf PointLight::get_specular( void ) const +{ + return _specular; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointLight::set_specular +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PointLight::set_specular( const Colorf& color ) +{ + _specular = color; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointLight::get_constant_attenuation +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float PointLight::get_constant_attenuation( void ) const +{ + return _constant_attenuation; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointLight::set_constant_attenuation +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PointLight::set_constant_attenuation( float att ) +{ + _constant_attenuation = att; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointLight::get_linear_attenuation +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float PointLight::get_linear_attenuation( void ) const +{ + return _linear_attenuation; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointLight::set_linear_attenuation +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PointLight::set_linear_attenuation( float att ) +{ + _linear_attenuation = att; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointLight::get_quadratic_attenuation +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float PointLight::get_quadratic_attenuation( void ) const +{ + return _quadratic_attenuation; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointLight::set_quadratic_attenuation +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PointLight::set_quadratic_attenuation( float att ) +{ + _quadratic_attenuation = att; +} diff --git a/panda/src/light/pointLight.cxx b/panda/src/light/pointLight.cxx new file mode 100644 index 0000000000..e687398541 --- /dev/null +++ b/panda/src/light/pointLight.cxx @@ -0,0 +1,54 @@ +// Filename: pointLight.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "pointLight.h" + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle PointLight::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PointLight::Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +PointLight::PointLight(const string& name) : NamedNode(name) +{ + set_color(Colorf(1, 1, 1, 1)); + set_specular(Colorf(1, 1, 1, 1)); + + set_constant_attenuation(1); + set_linear_attenuation(0); + set_quadratic_attenuation(0); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointLight::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void PointLight:: +output(ostream &out) const { + NamedNode::output(out); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointLight::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void PointLight:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << *this << ":\n"; + indent(out, indent_level + 2) << "color " << _color << "\n"; + indent(out, indent_level + 2) << "specular " << _specular << "\n"; + indent(out, indent_level + 2) + << "attenuation " << _constant_attenuation << ", " + << _linear_attenuation << ", " << _quadratic_attenuation << "\n"; +} diff --git a/panda/src/light/pointLight.h b/panda/src/light/pointLight.h new file mode 100644 index 0000000000..e91cbdb578 --- /dev/null +++ b/panda/src/light/pointLight.h @@ -0,0 +1,93 @@ +// Filename: pointLight.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef POINTLIGHT_H +#define POINTLIGHT_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include +#include +#include "light.h" + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : PointLight +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PointLight : public Light, public NamedNode +{ + public: + + PointLight( const string& name = "" ); + ~PointLight( void ) { } + + virtual void apply( GraphicsStateGuardian* gsg ) { + gsg->apply_light( this ); + } + + virtual void output( ostream &out ) const; + virtual void write( ostream &out, int indent_level = 0 ) const; + + INLINE Colorf get_specular( void ) const; + INLINE void set_specular( const Colorf& color ); + + INLINE float get_constant_attenuation( void ) const; + INLINE void set_constant_attenuation( float att ); + + INLINE float get_linear_attenuation( void ) const; + INLINE void set_linear_attenuation( float att ); + + INLINE float get_quadratic_attenuation( void ) const; + INLINE void set_quadratic_attenuation( float att ); + + protected: + + Colorf _specular; + float _constant_attenuation; + float _linear_attenuation; + float _quadratic_attenuation; + + public: + + static TypeHandle get_class_type( void ) { + return _type_handle; + } + static void init_type( void ) { + Light::init_type(); + NamedNode::init_type(); + register_type( _type_handle, "PointLight", + Light::get_class_type(), + NamedNode::get_class_type() ); + } + virtual TypeHandle get_type( void ) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + virtual TypeHandle get_light_type( void ) const { + return get_class_type(); + } + + private: + + static TypeHandle _type_handle; +}; + +INLINE ostream &operator << (ostream &out, const PointLight &light) { + light.output(out); + return out; +} + +#include "pointLight.I" + +#endif diff --git a/panda/src/light/pt_Light.cxx b/panda/src/light/pt_Light.cxx new file mode 100644 index 0000000000..b2413df895 --- /dev/null +++ b/panda/src/light/pt_Light.cxx @@ -0,0 +1,11 @@ +// Filename: pt_Light.cxx +// Created by: drose (16May00) +// +//////////////////////////////////////////////////////////////////// + +#include "pt_Light.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/light/pt_Light.h b/panda/src/light/pt_Light.h new file mode 100644 index 0000000000..da82dbb74a --- /dev/null +++ b/panda/src/light/pt_Light.h @@ -0,0 +1,34 @@ +// Filename: pt_Light.h +// Created by: drose (16May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PT_LIGHT_H +#define PT_LIGHT_H + +#include + +#include "light.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PT_Light +// Description : A PT(Light). This is defined here solely we can +// explicitly export the template class. It's not +// strictly necessary, but it doesn't hurt. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToBase) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerTo) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, ConstPointerTo) + +typedef PointerTo PT_Light; +typedef ConstPointerTo CPT_Light; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/light/spotlight.I b/panda/src/light/spotlight.I new file mode 100644 index 0000000000..93c507aa54 --- /dev/null +++ b/panda/src/light/spotlight.I @@ -0,0 +1,104 @@ +// Filename: spotlight.I +// Created by: mike (04Feb99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Spotlight::get_exponent +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float Spotlight::get_exponent( void ) const +{ + return _exponent; +} + +//////////////////////////////////////////////////////////////////// +// Function: Spotlight::set_exponent +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Spotlight::set_exponent( float exponent ) +{ + _exponent = exponent; +} + +//////////////////////////////////////////////////////////////////// +// Function: Spotlight::get_specular +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Colorf Spotlight::get_specular( void ) const +{ + return _specular; +} + +//////////////////////////////////////////////////////////////////// +// Function: Spotlight::set_specular +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Spotlight::set_specular( const Colorf& color ) +{ + _specular = color; +} + +//////////////////////////////////////////////////////////////////// +// Function: Spotlight::get_constant_attenuation +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float Spotlight::get_constant_attenuation( void ) const +{ + return _constant_attenuation; +} + +//////////////////////////////////////////////////////////////////// +// Function: Spotlight::set_constant_attenuation +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Spotlight::set_constant_attenuation( float att ) +{ + _constant_attenuation = att; +} + +//////////////////////////////////////////////////////////////////// +// Function: Spotlight::get_linear_attenuation +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float Spotlight::get_linear_attenuation( void ) const +{ + return _linear_attenuation; +} + +//////////////////////////////////////////////////////////////////// +// Function: Spotlight::set_linear_attenuation +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Spotlight::set_linear_attenuation( float att ) +{ + _linear_attenuation = att; +} + +//////////////////////////////////////////////////////////////////// +// Function: Spotlight::get_quadratic_attenuation +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float Spotlight::get_quadratic_attenuation( void ) const +{ + return _quadratic_attenuation; +} + +//////////////////////////////////////////////////////////////////// +// Function: Spotlight::set_quadratic_attenuation +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Spotlight::set_quadratic_attenuation( float att ) +{ + _quadratic_attenuation = att; +} diff --git a/panda/src/light/spotlight.cxx b/panda/src/light/spotlight.cxx new file mode 100644 index 0000000000..5e1d018ea9 --- /dev/null +++ b/panda/src/light/spotlight.cxx @@ -0,0 +1,262 @@ +// Filename: spotlight.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "spotlight.h" +#include "lightTransition.h" +#include "config_light.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle Spotlight::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: Spotlight::Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +Spotlight::Spotlight(const string& name) : ProjectionNode(name) +{ + set_exponent(1); + + set_color(Colorf(1, 1, 1, 1)); + set_specular(Colorf(1, 1, 1, 1)); + + set_constant_attenuation(1); + set_linear_attenuation(0); + set_quadratic_attenuation(0); +} + +//////////////////////////////////////////////////////////////////// +// Function: Spotlight::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void Spotlight:: +output(ostream &out) const { + NamedNode::output(out); +} + +//////////////////////////////////////////////////////////////////// +// Function: Spotlight::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void Spotlight:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << *this << ":\n"; + indent(out, indent_level + 2) << "color " << _color << "\n"; + indent(out, indent_level + 2) << "specular " << _specular << "\n"; + indent(out, indent_level + 2) + << "attenuation " << _constant_attenuation << ", " + << _linear_attenuation << ", " << _quadratic_attenuation << "\n"; + indent(out, indent_level + 2) << "exponent " << _exponent << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: Spotlight::get_cutoff_angle +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +float Spotlight::get_cutoff_angle(void) const +{ + const Projection* proj = get_projection(); + Frustumf frustum; + float cutoff = 0; + if (proj->get_type() == PerspectiveProjection::get_class_type()) { + frustum = ((PerspectiveProjection *)proj)->get_frustum(); + cutoff = rad_2_deg(atan(frustum._t / frustum._fnear)); + } + else + light_cat.error() + << "Spotlight::get_cutoff_angle() - spotlight has a non " + << "perspective projection!" << endl; + return cutoff; +} + +//////////////////////////////////////////////////////////////////// +// Function: Spotlight::make_image +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +bool Spotlight::make_image(Texture* texture, float radius) +{ + if (texture == NULL) { + light_cat.error() + << "Spotlight::make_image() - NULL texture" << endl; + return false; + } + PixelBuffer* pb = texture->_pbuffer; + int size = pb->get_xsize(); + if (size == 0) { + light_cat.error() + << "Spotlight::make_image() - pixel buffer has size == 0" << endl; + return false; + } + + uchar color[3]; + color[0] = (int)(_color[0] * 255.0f); + color[1] = (int)(_color[1] * 255.0f); + color[2] = (int)(_color[2] * 255.0f); + + float cutoff = get_cutoff_angle(); + float dist = 1 / (float)tan(cutoff); + int bufsize = size * size * 3; + int half_width = (size - 2) / 2; + float dXY = 1 / (float)half_width; + float Y = dXY + dXY; + float X, YY, dist_from_center, intensity; + uchar C[3]; + int tx, ty, tx2, ty2; + + for (int y = 0; y < half_width; y++, Y += dXY) { + X = dXY * y + dXY; + YY = Y * Y; + ty = y + half_width; + + for (int x = y; x < half_width; x++, X += dXY) { + dist_from_center = (float)sqrt(X * X + YY); + float D = dist_from_center; + if (D <= radius) + intensity = 1.0f; + else if (D < 1.0f) + intensity = pow(cos((D-radius) / + (1-radius) * (MathNumbers::pi/2.0f)), _exponent); + else + intensity = 0; + + C[0] = (uchar)(intensity * color[0]); + C[1] = (uchar)(intensity * color[1]); + C[2] = (uchar)(intensity * color[2]); + + tx = x + half_width; + + pb->set_uchar_rgb_texel(C, tx, ty, size); + pb->set_uchar_rgb_texel(C, tx, size - ty - 1, size); + pb->set_uchar_rgb_texel(C, size - tx - 1, ty, size); + pb->set_uchar_rgb_texel(C, size - tx - 1, size - ty - 1, size); + + tx2 = ty; ty2 = tx; + + pb->set_uchar_rgb_texel(C, tx2, ty2, size); + pb->set_uchar_rgb_texel(C, tx2, size - ty2 - 1, size); + pb->set_uchar_rgb_texel(C, size - tx2 - 1, ty2, size); + pb->set_uchar_rgb_texel(C, size - tx2 - 1, size - ty2 - 1, size); + } + } + texture->unprepare(); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Spotlight::make_geometry +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +NamedNode* Spotlight:: +make_geometry(float intensity, float length, int num_facets) +{ + Colorf diffuse = _color; + diffuse[3] = intensity; + Colorf black(0.0, 0.0, 0.0, intensity); + float radius = length * (float)tan(deg_2_rad(get_cutoff_angle())); + float ang_inc = 6.2831853 / (float)num_facets; + int num_verts = num_facets + 1; + int num_indices = num_facets + 2; + LVector3f offset(0.0, length, 0.0); + LPoint3f first_last_vert(radius, length, 0.0); + + PTA_Vertexf coords(num_verts); + PTA_ushort vindex(num_indices); + PTA_Colorf colors(2); + PTA_ushort cindex(num_indices); + PTA_int lengths(1); + + lengths[0] = num_indices; + + float ang = ang_inc; + LPoint3f origin(0.0, 0.0, 0.0); + LVector3f x_axis(1.0, 0.0, 0.0); + LVector3f z_axis(0.0, 0.0, 1.0); + LPoint3f dx, dz; + float t; + + coords[0] = origin; + coords[1] = first_last_vert; + vindex[0] = 0; + vindex[1] = 1; + vindex[num_indices-1] = 1; + + int i; + for (i = 2; i < num_indices-1; i++) { + t = (float)cos(ang) * radius; + dx = x_axis * t; + t = (float)sin(ang) * radius; + dz = z_axis * t; + coords[i] = dx + dz + offset; + ang += ang_inc; + vindex[i] = i; + } + + colors[0] = diffuse; + colors[1] = black; + cindex[0] = 0; + for (i = 1; i < num_indices; i++) + cindex[i] = 1; + + GeomTrifan* tfan = new GeomTrifan; + tfan->set_coords(coords, G_PER_VERTEX, vindex); + tfan->set_colors(colors, G_PER_VERTEX, cindex); + tfan->set_num_prims(1); + tfan->set_lengths(lengths); + + GeomNode* geomnode = new GeomNode("spotlight_frustum_geom"); + geomnode->add_geom(tfan); + + NamedNode* root = new NamedNode("spotlight_frustum"); + RenderRelation* root_arc = new RenderRelation(root, geomnode); + + // Disable lighting + LightTransition *light_trans = + new LightTransition(LightTransition::all_off()); + root_arc->set_transition(light_trans); + + // Disable texturing + TextureTransition *tex_trans = + new TextureTransition(TextureTransition::off()); + root_arc->set_transition(tex_trans); + + // Turn off writes to Z + DepthWriteTransition *depth_trans = + new DepthWriteTransition(DepthWriteTransition::off()); + root_arc->set_transition(depth_trans); + + // Enable transparency + TransparencyTransition *col_trans = + new TransparencyTransition(TransparencyProperty::M_alpha); + root_arc->set_transition(col_trans); + + // Disable culling + CullFaceTransition *cull_trans = + new CullFaceTransition(CullFaceProperty::M_cull_none); + root_arc->set_transition(cull_trans); + + return root; +} diff --git a/panda/src/light/spotlight.h b/panda/src/light/spotlight.h new file mode 100644 index 0000000000..053adf3350 --- /dev/null +++ b/panda/src/light/spotlight.h @@ -0,0 +1,105 @@ +// Filename: spotlight.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef SPOTLIGHT_H +#define SPOTLIGHT_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include +#include +#include +#include "light.h" +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : Spotlight +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Spotlight : public Light, public ProjectionNode +{ + public: + + Spotlight( const string& name = "" ); + ~Spotlight( void ) { } + + virtual void apply( GraphicsStateGuardian* gsg ) { + gsg->apply_light( this ); + } + + virtual void output( ostream &out ) const; + virtual void write( ostream &out, int indent_level = 0 ) const; + + INLINE float get_exponent( void ) const; + INLINE void set_exponent( float exponent ); + + float get_cutoff_angle( void ) const; + + INLINE Colorf get_specular( void ) const; + INLINE void set_specular( const Colorf& color ); + + INLINE float get_constant_attenuation( void ) const; + INLINE void set_constant_attenuation( float att ); + + INLINE float get_linear_attenuation( void ) const; + INLINE void set_linear_attenuation( float att ); + + INLINE float get_quadratic_attenuation( void ) const; + INLINE void set_quadratic_attenuation( float att ); + + bool make_image(Texture* texture, float radius = 0.7); + NamedNode* make_geometry(float intensity = 0.05, float length = 20.0, + int num_facets = 36); + + protected: + + float _exponent; + Colorf _specular; + float _constant_attenuation; + float _linear_attenuation; + float _quadratic_attenuation; + + public: + + static TypeHandle get_class_type( void ) { + return _type_handle; + } + static void init_type( void ) { + Light::init_type(); + ProjectionNode::init_type(); + register_type( _type_handle, "Spotlight", + Light::get_class_type(), + ProjectionNode::get_class_type() ); + } + virtual TypeHandle get_type( void ) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + virtual TypeHandle get_light_type( void ) const { + return get_class_type(); + } + + private: + + static TypeHandle _type_handle; +}; + +INLINE ostream &operator << (ostream &out, const Spotlight &light) { + light.output(out); + return out; +} + +#include "spotlight.I" + +#endif diff --git a/panda/src/light/vector_PT_Light.cxx b/panda/src/light/vector_PT_Light.cxx new file mode 100644 index 0000000000..563104752c --- /dev/null +++ b/panda/src/light/vector_PT_Light.cxx @@ -0,0 +1,11 @@ +// Filename: vector_PT_Light.cxx +// Created by: drose (16May00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_PT_Light.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/light/vector_PT_Light.h b/panda/src/light/vector_PT_Light.h new file mode 100644 index 0000000000..d87a6c89af --- /dev/null +++ b/panda/src/light/vector_PT_Light.h @@ -0,0 +1,32 @@ +// Filename: vector_PT_Light.h +// Created by: drose (16May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_PT_LIGHT_H +#define VECTOR_PT_LIGHT_H + +#include + +#include "pt_Light.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_PT_Light +// Description : A vector of PT(Light)'s. This class is defined once +// here, and exported to PANDA.DLL; other packages that +// want to use a vector of this type (whether they need +// to export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, std::vector) +typedef vector vector_PT_Light; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/linmath/Sources.pp b/panda/src/linmath/Sources.pp new file mode 100644 index 0000000000..1637aa20b2 --- /dev/null +++ b/panda/src/linmath/Sources.pp @@ -0,0 +1,47 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET linmath + #define LOCAL_LIBS \ + putil + + #define SOURCES \ + compose_matrix.I compose_matrix.cxx compose_matrix.h \ + config_linmath.cxx config_linmath.h coordinateSystem.cxx \ + coordinateSystem.h ioPtaDatagramLinMath.I ioPtaDatagramLinMath.cxx \ + ioPtaDatagramLinMath.h lmatrix.cxx lmatrix.h luse.I luse.N luse.cxx \ + luse.h mathNumbers.cxx mathNumbers.h pta_Colorf.cxx pta_Colorf.h \ + pta_Normalf.cxx pta_Normalf.h pta_TexCoordf.cxx pta_TexCoordf.h \ + pta_Vertexf.cxx pta_Vertexf.h vector_Colorf.cxx vector_Colorf.h \ + vector_LPoint2f.cxx vector_LPoint2f.h vector_Normalf.cxx \ + vector_Normalf.h vector_Vertexf.cxx vector_Vertexf.h + + #define INSTALL_HEADERS \ + cmath.I cmath.h compose_matrix.I compose_matrix.h config_linmath.h \ + coordinateSystem.h deg_2_rad.h ioPtaDatagramLinMath.I \ + ioPtaDatagramLinMath.h lmat_ops.I lmat_ops.h lmatrix.h lmatrix3.I \ + lmatrix3.h lmatrix4.I lmatrix4.h lorientation.I lorientation.h \ + lpoint2.I lpoint2.h lpoint3.I lpoint3.h lpoint4.I lpoint4.h \ + lquaternion.I lquaternion.h lrotation.I lrotation.h luse.I luse.h \ + lvec2_ops.I lvec2_ops.h lvec3_ops.I lvec3_ops.h lvec4_ops.I \ + lvec4_ops.h lvecBase2.I lvecBase2.h lvecBase3.I lvecBase3.h \ + lvecBase4.I lvecBase4.h lvector2.I lvector2.h lvector3.I lvector3.h \ + lvector4.I lvector4.h mathNumbers.h nearly_zero.h pta_Colorf.h \ + pta_Normalf.h pta_TexCoordf.h pta_Vertexf.h vector_Colorf.h \ + vector_LPoint2f.h vector_Normalf.h vector_TexCoordf.h \ + vector_Vertexf.h + + #define IGATESCAN all + +#end lib_target + +#begin test_bin_target + #define TARGET test_math + #define LOCAL_LIBS \ + linmath + + #define SOURCES \ + test_math.cxx + +#end test_bin_target + diff --git a/panda/src/linmath/cast_to_double.I b/panda/src/linmath/cast_to_double.I new file mode 100644 index 0000000000..352cfb196a --- /dev/null +++ b/panda/src/linmath/cast_to_double.I @@ -0,0 +1,49 @@ +// Filename: cast_to_double.I +// Created by: drose (24May00) +// +//////////////////////////////////////////////////////////////////// + +INLINE LVecBase2 cast_to_double(const LVecBase2 &source) { + return LCAST(double, source); +} + +INLINE LVecBase3 cast_to_double(const LVecBase3 &source) { + return LCAST(double, source); +} + +INLINE LVecBase4 cast_to_double(const LVecBase4 &source) { + return LCAST(double, source); +} + +INLINE LVector2 cast_to_double(const LVector2 &source) { + return LCAST(double, source); +} + +INLINE LVector3 cast_to_double(const LVector3 &source) { + return LCAST(double, source); +} + +INLINE LVector4 cast_to_double(const LVector4 &source) { + return LCAST(double, source); +} + +INLINE LPoint2 cast_to_double(const LPoint2 &source) { + return LCAST(double, source); +} + +INLINE LPoint3 cast_to_double(const LPoint3 &source) { + return LCAST(double, source); +} + +INLINE LPoint4 cast_to_double(const LPoint4 &source) { + return LCAST(double, source); +} + +INLINE LMatrix3 cast_to_double(const LMatrix3 &source) { + return LCAST(double, source); +} + +INLINE LMatrix4 cast_to_double(const LMatrix4 &source) { + return LCAST(double, source); +} + diff --git a/panda/src/linmath/cast_to_double.h b/panda/src/linmath/cast_to_double.h new file mode 100644 index 0000000000..fd22d87951 --- /dev/null +++ b/panda/src/linmath/cast_to_double.h @@ -0,0 +1,32 @@ +// Filename: cast_to_double.h +// Created by: drose (24May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CAST_TO_DOUBLE_H +#define CAST_TO_DOUBLE_H + +#include "luse.h" + +// The functions in this file are primarily for the benefit of a +// higher-level language that can't take advantage of the LCAST macro. +// These are a number of functions that convert our various math +// objects between floats and doubles. + +INLINE LVecBase2 cast_to_double(const LVecBase2 &source); +INLINE LVecBase3 cast_to_double(const LVecBase3 &source); +INLINE LVecBase4 cast_to_double(const LVecBase4 &source); +INLINE LVector2 cast_to_double(const LVector2 &source); +INLINE LVector3 cast_to_double(const LVector3 &source); +INLINE LVector4 cast_to_double(const LVector4 &source); +INLINE LPoint2 cast_to_double(const LPoint2 &source); +INLINE LPoint3 cast_to_double(const LPoint3 &source); +INLINE LPoint4 cast_to_double(const LPoint4 &source); +INLINE LMatrix3 cast_to_double(const LMatrix3 &source); +INLINE LMatrix4 cast_to_double(const LMatrix4 &source); + +#include "cast_to_double.I" + +#endif + + diff --git a/panda/src/linmath/cast_to_float.I b/panda/src/linmath/cast_to_float.I new file mode 100644 index 0000000000..980c3dde28 --- /dev/null +++ b/panda/src/linmath/cast_to_float.I @@ -0,0 +1,49 @@ +// Filename: cast_to_float.I +// Created by: drose (24May00) +// +//////////////////////////////////////////////////////////////////// + +INLINE LVecBase2 cast_to_float(const LVecBase2 &source) { + return LCAST(float, source); +} + +INLINE LVecBase3 cast_to_float(const LVecBase3 &source) { + return LCAST(float, source); +} + +INLINE LVecBase4 cast_to_float(const LVecBase4 &source) { + return LCAST(float, source); +} + +INLINE LVector2 cast_to_float(const LVector2 &source) { + return LCAST(float, source); +} + +INLINE LVector3 cast_to_float(const LVector3 &source) { + return LCAST(float, source); +} + +INLINE LVector4 cast_to_float(const LVector4 &source) { + return LCAST(float, source); +} + +INLINE LPoint2 cast_to_float(const LPoint2 &source) { + return LCAST(float, source); +} + +INLINE LPoint3 cast_to_float(const LPoint3 &source) { + return LCAST(float, source); +} + +INLINE LPoint4 cast_to_float(const LPoint4 &source) { + return LCAST(float, source); +} + +INLINE LMatrix3 cast_to_float(const LMatrix3 &source) { + return LCAST(float, source); +} + +INLINE LMatrix4 cast_to_float(const LMatrix4 &source) { + return LCAST(float, source); +} + diff --git a/panda/src/linmath/cast_to_float.h b/panda/src/linmath/cast_to_float.h new file mode 100644 index 0000000000..c8a6cbf5c5 --- /dev/null +++ b/panda/src/linmath/cast_to_float.h @@ -0,0 +1,32 @@ +// Filename: cast_to_float.h +// Created by: drose (24May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CAST_TO_FLOAT_H +#define CAST_TO_FLOAT_H + +#include "luse.h" + +// The functions in this file are primarily for the benefit of a +// higher-level language that can't take advantage of the LCAST macro. +// These are a number of functions that convert our various math +// objects between floats and doubles. + +INLINE LVecBase2 cast_to_float(const LVecBase2 &source); +INLINE LVecBase3 cast_to_float(const LVecBase3 &source); +INLINE LVecBase4 cast_to_float(const LVecBase4 &source); +INLINE LVector2 cast_to_float(const LVector2 &source); +INLINE LVector3 cast_to_float(const LVector3 &source); +INLINE LVector4 cast_to_float(const LVector4 &source); +INLINE LPoint2 cast_to_float(const LPoint2 &source); +INLINE LPoint3 cast_to_float(const LPoint3 &source); +INLINE LPoint4 cast_to_float(const LPoint4 &source); +INLINE LMatrix3 cast_to_float(const LMatrix3 &source); +INLINE LMatrix4 cast_to_float(const LMatrix4 &source); + +#include "cast_to_float.I" + +#endif + + diff --git a/panda/src/linmath/cmath.I b/panda/src/linmath/cmath.I new file mode 100644 index 0000000000..1f4c679943 --- /dev/null +++ b/panda/src/linmath/cmath.I @@ -0,0 +1,52 @@ +// Filename: cmath.I +// Created by: drose (19May00) +// +//////////////////////////////////////////////////////////////////// + +//Windows has isnan in a different place and with a different name +//than everyone else. Sheesh +#ifdef _WIN32 +#include +#endif + +INLINE float csqrt(float v) { + return sqrtf(v); +} + +INLINE float csin(float v) { + return sinf(v); +} + +INLINE float ccos(float v) { + return cosf(v); +} + +INLINE float cabs(float v) { + return fabs(v); +} + +INLINE double csqrt(double v) { + return sqrt(v); +} + +INLINE double csin(double v) { + return sin(v); +} + +INLINE double ccos(double v) { + return cos(v); +} + +INLINE double cabs(double v) { + return fabs(v); +} + +INLINE bool cnan(double v) { +#ifndef _WIN32 + return (isnan(v) != 0); +#else + return (_isnan(v) != 0); +#endif +} + + diff --git a/panda/src/linmath/cmath.h b/panda/src/linmath/cmath.h new file mode 100644 index 0000000000..4172f8c223 --- /dev/null +++ b/panda/src/linmath/cmath.h @@ -0,0 +1,34 @@ +// Filename: cmath.h +// Created by: drose (19May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CMATH_H +#define CMATH_H + +#include + +// This file declares a number of C++-style overloading wrappers +// around the standard math library functions, so we can use +// overloading to differentiate on type instead of having to know +// explicitly whether we need to call, for instance, sqrtf() or +// sqrt(). + +INLINE float csqrt(float v); +INLINE float csin(float v); +INLINE float ccos(float v); +INLINE float cabs(float v); + +INLINE double csqrt(double v); +INLINE double csin(double v); +INLINE double ccos(double v); +INLINE double cabs(double v); + +// Returns true if the number is nan, false if it's a genuine number +// or infinity. +INLINE bool cnan(double v); + +#include "cmath.I" + +#endif + diff --git a/panda/src/linmath/compose_matrix.I b/panda/src/linmath/compose_matrix.I new file mode 100644 index 0000000000..b0c6c1f5e5 --- /dev/null +++ b/panda/src/linmath/compose_matrix.I @@ -0,0 +1,77 @@ +// Filename: compose_matrix.I +// Created by: drose (21Feb99) +// +//////////////////////////////////////////////////////////////////// + +INLINE void +compose_matrix(LMatrix4f &mat, + const float components[9], + CoordinateSystem cs) { + LVector3f scale(components[0], + components[1], + components[2]); + LVector3f hpr(components[3], + components[4], + components[5]); + LVector3f translate(components[6], + components[7], + components[8]); + compose_matrix(mat, scale, hpr, translate, cs); +} + +INLINE bool +decompose_matrix(const LMatrix4f &mat, + float components[9], + CoordinateSystem cs) { + LVector3f scale, hpr, translate; + if (!decompose_matrix(mat, scale, hpr, translate, cs)) { + return false; + } + components[0] = scale[0]; + components[1] = scale[1]; + components[2] = scale[2]; + components[3] = hpr[0]; + components[4] = hpr[1]; + components[5] = hpr[2]; + components[6] = translate[0]; + components[7] = translate[1]; + components[8] = translate[2]; + return true; +} + +INLINE void +compose_matrix(LMatrix4d &mat, + const double components[9], + CoordinateSystem cs) { + LVector3d scale(components[0], + components[1], + components[2]); + LVector3d hpr(components[3], + components[4], + components[5]); + LVector3d translate(components[6], + components[7], + components[8]); + compose_matrix(mat, scale, hpr, translate, cs); +} + +INLINE bool +decompose_matrix(const LMatrix4d &mat, + double components[9], + CoordinateSystem cs) { + LVector3d scale, hpr, translate; + if (!decompose_matrix(mat, scale, hpr, translate, cs)) { + return false; + } + + components[0] = scale[0]; + components[1] = scale[1]; + components[2] = scale[2]; + components[3] = hpr[0]; + components[4] = hpr[1]; + components[5] = hpr[2]; + components[6] = translate[0]; + components[7] = translate[1]; + components[8] = translate[2]; + return true; +} diff --git a/panda/src/linmath/compose_matrix.cxx b/panda/src/linmath/compose_matrix.cxx new file mode 100644 index 0000000000..ae62bfa4af --- /dev/null +++ b/panda/src/linmath/compose_matrix.cxx @@ -0,0 +1,398 @@ +// Filename: compose_matrix.cxx +// Created by: drose (27Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "compose_matrix.h" +#include "deg_2_rad.h" +#include "config_linmath.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: compose_matrix +// Description: Computes the 3x3 matrix from scale and rotation. +//////////////////////////////////////////////////////////////////// +template +INLINE void +_compose_matrix(LMatrix3 &mat, + const LVecBase3 &scale, + const LVecBase3 &hpr, + CoordinateSystem cs) { + mat = + LMatrix3::scale_mat(scale) * + LMatrix3::rotate_mat(hpr[1], LVector3::right(cs), cs) * + LMatrix3::rotate_mat(hpr[0], LVector3::up(cs), cs) * + LMatrix3::rotate_mat(hpr[2], LVector3::back(cs), cs); +} + +//////////////////////////////////////////////////////////////////// +// Function: compose_matrix +// Description: Computes the 4x4 matrix according to scale, rotation, +// and translation. +//////////////////////////////////////////////////////////////////// +template +INLINE void +_compose_matrix(LMatrix4 &mat, + const LVecBase3 &scale, + const LVecBase3 &hpr, + const LVecBase3 &translate, + CoordinateSystem cs) { + LMatrix3 upper3; + _compose_matrix(upper3, scale, hpr, cs); + mat = LMatrix4(upper3, translate); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: unwind_yup_rotation +// Description: Extracts the rotation about the x, y, and z axes from +// the given hpr & scale matrix. Adjusts the matrix +// to eliminate the rotation. +// +// This function assumes the matrix is stored in a +// right-handed Y-up coordinate system. +//////////////////////////////////////////////////////////////////// +template +static void +unwind_yup_rotation(LMatrix3 &mat, LVecBase3 &hpr) { + typedef LMatrix3 Matrix; + + // Extract the axes from the matrix. + LVector3 x, y, z; + x = mat.get_row(0); + y = mat.get_row(1); + z = mat.get_row(2); + + // Project X onto the XY plane. + LVector2 xy(x[0], x[1]); + xy = normalize(xy); + + // Compute the rotation about the +Z (back) axis. This is roll. + NumType roll = rad_2_deg(atan2(xy[1], xy[0])); + + // Unwind the roll from the axes, and continue. + Matrix rot_z; + rot_z = Matrix::rotate_mat(-roll, LVector3(0.0, 0.0, 1.0), + CS_yup_right); + + x = x * rot_z; + y = y * rot_z; + z = z * rot_z; + + // Project the rotated X into the XZ plane. + LVector2 xz(x[0], x[2]); + xz = normalize(xz); + + // Compute the rotation about the +Y (up) axis. This is yaw, or + // "heading". + NumType heading = rad_2_deg(-atan2(xz[1], xz[0])); + + // Unwind the heading, and continue. + Matrix rot_y; + rot_y = Matrix::rotate_mat(-heading, LVector3(0.0, 1.0, 0.0), + CS_yup_right); + + x = x * rot_y; + y = y * rot_y; + z = z * rot_y; + + // Project the rotated Z into the YZ plane. + LVector2 yz(z[1], z[2]); + yz = normalize(yz); + + // Compute the rotation about the +X (right) axis. This is pitch. + NumType pitch = rad_2_deg(-atan2(yz[0], yz[1])); + + // Unwind the pitch. + Matrix rot_x; + rot_x = Matrix::rotate_mat(-pitch, LVector3(1.0, 0.0, 0.0), + CS_yup_right); + + x = x * rot_x; + y = y * rot_x; + z = z * rot_x; + + // Reset the matrix to reflect the unwinding. + mat.set_row(0, x); + mat.set_row(1, y); + mat.set_row(2, z); + + // Return the three rotation components. + hpr[0] = heading; + hpr[1] = pitch; + hpr[2] = roll; +} + +//////////////////////////////////////////////////////////////////// +// Function: unwind_zup_rotation +// Description: Extracts the rotation about the x, y, and z axes from +// the given hpr & scale matrix. Adjusts the matrix +// to eliminate the rotation. +// +// This function assumes the matrix is stored in a +// right-handed Z-up coordinate system. +//////////////////////////////////////////////////////////////////// +template +static void +unwind_zup_rotation(LMatrix3 &mat, LVecBase3 &hpr) { + typedef LMatrix3 Matrix; + + // Extract the axes from the matrix. + LVector3 x, y, z; + x = mat.get_row(0); + y = mat.get_row(1); + z = mat.get_row(2); + + + // Project X into the XZ plane. + LVector2 xz(x[0], x[2]); + xz = normalize(xz); + + // Compute the rotation about the -Y (back) axis. This is roll. + NumType roll = rad_2_deg(atan2(xz[1], xz[0])); + + if (y[1] < 0.0) { + if (roll < 0.0) { + roll += 180.0; + } else { + roll -= 180.0; + } + } + + // Unwind the roll from the axes, and continue. + Matrix rot_y; + rot_y = Matrix::rotate_mat(roll, LVector3(0.0, 1.0, 0.0), + CS_zup_right); + + x = x * rot_y; + y = y * rot_y; + z = z * rot_y; + + + // Project the rotated X into the XY plane. + LVector2 xy(x[0], x[1]); + xy = normalize(xy); + + // Compute the rotation about the +Z (up) axis. This is yaw, or + // "heading". + NumType heading = rad_2_deg(atan2(xy[1], xy[0])); + + // Unwind the heading, and continue. + Matrix rot_z; + rot_z = Matrix::rotate_mat(-heading, LVector3(0.0, 0.0, 1.0), + CS_zup_right); + + x = x * rot_z; + y = y * rot_z; + z = z * rot_z; + + + // Project the rotated Y into the YZ plane. + LVector2 yz(y[1], y[2]); + yz = normalize(yz); + + // Compute the rotation about the +X (right) axis. This is pitch. + NumType pitch = rad_2_deg(atan2(yz[1], yz[0])); + + // Unwind the pitch. + Matrix rot_x; + rot_x = Matrix::rotate_mat(-pitch, LVector3(1.0, 0.0, 0.0), + CS_zup_right); + + x = x * rot_x; + y = y * rot_x; + z = z * rot_x; + + + // Reset the matrix to reflect the unwinding. + mat.set_row(0, x); + mat.set_row(1, y); + mat.set_row(2, z); + + // Return the three rotation components. + hpr[0] = heading; + hpr[1] = pitch; + hpr[2] = roll; +} + +//////////////////////////////////////////////////////////////////// +// Function: decompose_matrix +// Description: Extracts out the components of a 3x3 rotation matrix. +// Returns true if the scale and hpr completely describe +// the matrix, or false if there is also a shear +// component or if the matrix is not affine. +//////////////////////////////////////////////////////////////////// +template +static bool +_decompose_matrix(const LMatrix3 &mat, + LVecBase3 &scale, + LVecBase3 &hpr, + CoordinateSystem cs) { + if (cs == CS_default) { + cs = default_coordinate_system; + } + + // Extract the rotation and scale, according to the coordinate + // system of choice. + bool shear; + + switch (cs) { + case CS_zup_right: + { + LMatrix3 rm(mat); + unwind_zup_rotation(rm, hpr); + scale[0] = rm(0, 0); + scale[1] = rm(1, 1); + scale[2] = rm(2, 2); + shear = + (fabs(rm(0, 1)) + fabs(rm(0, 2)) + + fabs(rm(1, 0)) + fabs(rm(1, 2)) + + fabs(rm(2, 0)) + fabs(rm(2, 1))) >= 0.0001; + } + break; + + case CS_yup_right: + { + LMatrix3 rm(mat); + unwind_yup_rotation(rm, hpr); + scale[0] = rm(0, 0); + scale[1] = rm(1, 1); + scale[2] = rm(2, 2); + shear = + (fabs(rm(0, 1)) + fabs(rm(0, 2)) + + fabs(rm(1, 0)) + fabs(rm(1, 2)) + + fabs(rm(2, 0)) + fabs(rm(2, 1))) >= 0.0001; + } + break; + + case CS_zup_left: + { + LMatrix3 lm(mat(0, 0), mat(0, 1), -mat(0, 2), + mat(1, 0), mat(1, 1), -mat(1, 2), + -mat(2, 0), -mat(2, 1), mat(2, 2)); + unwind_zup_rotation(lm, hpr); + scale[0] = -lm(0, 0); + scale[1] = -lm(1, 1); + scale[2] = lm(2, 2); + shear = + (fabs(lm(0, 1)) + fabs(lm(0, 2)) + + fabs(lm(1, 0)) + fabs(lm(1, 2)) + + fabs(lm(2, 0)) + fabs(lm(2, 1))) >= 0.0001; + } + break; + + case CS_yup_left: + { + LMatrix3 lm(mat(0, 0), mat(0, 1), -mat(0, 2), + mat(1, 0), mat(1, 1), -mat(1, 2), + -mat(2, 0), -mat(2, 1), mat(2, 2)); + unwind_yup_rotation(lm, hpr); + scale[0] = -lm(0, 0); + scale[1] = -lm(1, 1); + scale[2] = lm(2, 2); + shear = + (fabs(lm(0, 1)) + fabs(lm(0, 2)) + + fabs(lm(1, 0)) + fabs(lm(1, 2)) + + fabs(lm(2, 0)) + fabs(lm(2, 1))) >= 0.0001; + } + break; + + default: + linmath_cat.error() + << "Unexpected coordinate system!\n"; + return false; + } + + return !shear; +} + +//////////////////////////////////////////////////////////////////// +// Function: decompose_matrix +// Description: Extracts out the components of an affine matrix. +// Returns true if the scale, hpr, translate +// completely describe the matrix, or false if there is +// also a shear component or if the matrix is not +// affine. +//////////////////////////////////////////////////////////////////// +template +INLINE bool +_decompose_matrix(const LMatrix4 &mat, + LVecBase3 &scale, + LVecBase3 &hpr, + LVecBase3 &translate, + CoordinateSystem cs) { + // Get the translation first. + translate = mat.get_row3(3); + return _decompose_matrix(mat.get_upper_3(), scale, hpr, cs); +} + +void +compose_matrix(LMatrix3f &mat, + const LVecBase3f &scale, + const LVecBase3f &hpr, + CoordinateSystem cs) { + _compose_matrix(mat, scale, hpr, cs); +} + +bool +decompose_matrix(const LMatrix3f &mat, + LVecBase3f &scale, + LVecBase3f &hpr, + CoordinateSystem cs) { + return _decompose_matrix(mat, scale, hpr, cs); +} + +void +compose_matrix(LMatrix3d &mat, + const LVecBase3d &scale, + const LVecBase3d &hpr, + CoordinateSystem cs) { + _compose_matrix(mat, scale, hpr, cs); +} + +bool +decompose_matrix(const LMatrix3d &mat, + LVecBase3d &scale, + LVecBase3d &hpr, + CoordinateSystem cs) { + return _decompose_matrix(mat, scale, hpr, cs); +} + +void +compose_matrix(LMatrix4f &mat, + const LVecBase3f &scale, + const LVecBase3f &hpr, + const LVecBase3f &translate, + CoordinateSystem cs) { + _compose_matrix(mat, scale, hpr, translate, cs); +} + +bool +decompose_matrix(const LMatrix4f &mat, + LVecBase3f &scale, + LVecBase3f &hpr, + LVecBase3f &translate, + CoordinateSystem cs) { + return _decompose_matrix(mat, scale, hpr, translate, cs); +} + +void +compose_matrix(LMatrix4d &mat, + const LVecBase3d &scale, + const LVecBase3d &hpr, + const LVecBase3d &translate, + CoordinateSystem cs) { + _compose_matrix(mat, scale, hpr, translate, cs); +} + +bool +decompose_matrix(const LMatrix4d &mat, + LVecBase3d &scale, + LVecBase3d &hpr, + LVecBase3d &translate, + CoordinateSystem cs) { + return _decompose_matrix(mat, scale, hpr, translate, cs); +} + diff --git a/panda/src/linmath/compose_matrix.h b/panda/src/linmath/compose_matrix.h new file mode 100644 index 0000000000..62d5722591 --- /dev/null +++ b/panda/src/linmath/compose_matrix.h @@ -0,0 +1,95 @@ +// Filename: compose_matrix.h +// Created by: drose (27Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COMPOSE_MATRIX_H +#define COMPOSE_MATRIX_H + +//////////////////////////////////////////////////////////////////// +// +// compose_matrix(), decompose_matrix() +// +// These two functions build and/or extract an affine matrix into +// its constituent parts: scale, hpr, and translate. +// +// The functions here come in two flavors, for doubles and floats. +// They're actually implemented as template functions so could be +// instantiated on any numeric type, but I only bothered to +// instantiate and expose these two. It saves on some cross-library +// pre-linking stuff to have all the templates be hidden. +// +// There are also two additional flavors for 3x3 matrices. These are +// treated as the upper 3x3 part of a general 4x4 matrix, and so can +// only represent rotations and scales. +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "lmatrix.h" +#include "luse.h" + + +EXPCL_PANDA void +compose_matrix(LMatrix3f &mat, + const LVecBase3f &scale, + const LVecBase3f &hpr, + CoordinateSystem cs = CS_default); +EXPCL_PANDA bool +decompose_matrix(const LMatrix3f &mat, + LVecBase3f &scale, + LVecBase3f &hpr, + CoordinateSystem cs = CS_default); + +EXPCL_PANDA void +compose_matrix(LMatrix3d &mat, + const LVecBase3d &scale, + const LVecBase3d &hpr, + CoordinateSystem cs = CS_default); +EXPCL_PANDA bool +decompose_matrix(const LMatrix3d &mat, + LVecBase3d &scale, + LVecBase3d &hpr, + CoordinateSystem cs = CS_default); + +EXPCL_PANDA void +compose_matrix(LMatrix4f &mat, + const LVecBase3f &scale, + const LVecBase3f &hpr, + const LVecBase3f &translate, + CoordinateSystem cs = CS_default); +INLINE void compose_matrix(LMatrix4f &mat, const float components[9], + CoordinateSystem cs = CS_default); + +EXPCL_PANDA bool +decompose_matrix(const LMatrix4f &mat, + LVecBase3f &scale, + LVecBase3f &hpr, + LVecBase3f &translate, + CoordinateSystem cs = CS_default); +INLINE bool decompose_matrix(const LMatrix4f &mat, float components[9], + CoordinateSystem CS = CS_default); + +EXPCL_PANDA void +compose_matrix(LMatrix4d &mat, + const LVecBase3d &scale, + const LVecBase3d &hpr, + const LVecBase3d &translate, + CoordinateSystem cs = CS_default); +INLINE void compose_matrix(LMatrix4d &mat, const double components[9], + CoordinateSystem cs = CS_default); + +bool EXPCL_PANDA +decompose_matrix(const LMatrix4d &mat, + LVecBase3d &scale, + LVecBase3d &hpr, + LVecBase3d &translate, + CoordinateSystem cs = CS_default); +INLINE bool decompose_matrix(const LMatrix4d &mat, double components[9], + CoordinateSystem cs = CS_default); + +#include "compose_matrix.I" + +#endif + diff --git a/panda/src/linmath/config_linmath.cxx b/panda/src/linmath/config_linmath.cxx new file mode 100644 index 0000000000..fe57f8026d --- /dev/null +++ b/panda/src/linmath/config_linmath.cxx @@ -0,0 +1,57 @@ +// Filename: config_linmath.cxx +// Created by: drose (23Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_linmath.h" +#include "luse.h" +#include "coordinateSystem.h" + +#include + +Configure(config_linmath); +NotifyCategoryDef(linmath, ""); + +ConfigureFn(config_linmath) { + LVecBase2f::init_type(); + LVecBase3f::init_type(); + LVecBase4f::init_type(); + LVector2f::init_type(); + LVector3f::init_type(); + LVector4f::init_type(); + LPoint2f::init_type(); + LPoint3f::init_type(); + LPoint4f::init_type(); + LMatrix3f::init_type(); + LMatrix4f::init_type(); + + LVecBase2d::init_type(); + LVecBase3d::init_type(); + LVecBase4d::init_type(); + LVector2d::init_type(); + LVector3d::init_type(); + LVector4d::init_type(); + LPoint2d::init_type(); + LPoint3d::init_type(); + LPoint4d::init_type(); + LMatrix3d::init_type(); + LMatrix4d::init_type(); + + LQuaternionf::init_type(); + LRotationf::init_type(); + LOrientationf::init_type(); + + LQuaterniond::init_type(); + LRotationd::init_type(); + LOrientationd::init_type(); + + string csstr = config_linmath.GetString("coordinate-system", "default"); + CoordinateSystem cs = parse_coordinate_system_string(csstr); + + if (cs == CS_invalid) { + linmath_cat.error() + << "Unexpected coordinate-system string: " << csstr << "\n"; + cs = CS_default; + } + default_coordinate_system = (cs == CS_default) ? CS_zup_right : cs; +} diff --git a/panda/src/linmath/config_linmath.h b/panda/src/linmath/config_linmath.h new file mode 100644 index 0000000000..c850e7bdcf --- /dev/null +++ b/panda/src/linmath/config_linmath.h @@ -0,0 +1,14 @@ +// Filename: config_linmath.h +// Created by: drose (23Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_LINMATH_H +#define CONFIG_LINMATH_H + +#include +#include + +NotifyCategoryDecl(linmath, EXPCL_PANDA, EXPTP_PANDA); + +#endif diff --git a/panda/src/linmath/coordinateSystem.cxx b/panda/src/linmath/coordinateSystem.cxx new file mode 100644 index 0000000000..d238b73681 --- /dev/null +++ b/panda/src/linmath/coordinateSystem.cxx @@ -0,0 +1,94 @@ +// Filename: coordinateSystem.cxx +// Created by: drose (24Sep99) +// +//////////////////////////////////////////////////////////////////// + +#include "coordinateSystem.h" +#include "config_linmath.h" + +#include +#include + +#include +#include + +CoordinateSystem default_coordinate_system; + + +CoordinateSystem +parse_coordinate_system_string(const string &str) { + // First, make sure the string is lowercase before we compare it, so + // we'll be case-insensitive. + string lstr = str; + for (string::iterator si = lstr.begin(); + si != lstr.end(); + ++si) { + (*si) = tolower(*si); + } + + if (lstr == "default") { + return CS_default; + + } else if (lstr == "z-up" || lstr == "z-up-right") { + return CS_zup_right; + + } else if (lstr == "y-up" || lstr == "y-up-right") { + return CS_yup_right; + + } else if (lstr == "z-up-left") { + return CS_zup_left; + + } else if (lstr == "y-up-left") { + return CS_yup_left; + } + + return CS_invalid; +} + +bool +is_right_handed(CoordinateSystem cs) { + if (cs == CS_default) { + cs = default_coordinate_system; + } + switch (cs) { + case CS_zup_right: + case CS_yup_right: + return true; + + case CS_zup_left: + case CS_yup_left: + return false; + } + linmath_cat.error() + << "Invalid coordinate system value: " << (int)cs << "\n"; + nassertr(false, false); + return false; +} + +ostream & +operator << (ostream &out, CoordinateSystem cs) { + switch (cs) { + case CS_default: + return out << "default"; + + case CS_zup_right: + return out << "zup_right"; + + case CS_yup_right: + return out << "yup_right"; + + case CS_zup_left: + return out << "zup_left"; + + case CS_yup_left: + return out << "yup_left"; + + case CS_invalid: + return out << "invalid"; + } + + linmath_cat.error() + << "Invalid coordinate_system value: " << (int)cs << "\n"; + nassertr(false, out); + return out; +} diff --git a/panda/src/linmath/coordinateSystem.h b/panda/src/linmath/coordinateSystem.h new file mode 100644 index 0000000000..ccb189f5bf --- /dev/null +++ b/panda/src/linmath/coordinateSystem.h @@ -0,0 +1,42 @@ +// Filename: coordinateSystem.h +// Created by: drose (24Sep99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COORDINATESYSTEM_H +#define COORDINATESYSTEM_H + +#include + +#include + +#include + +enum CoordinateSystem { + // The CS_default entry does not refer to a particular coordinate + // system, but rather to the value stored in + // default_coordinate_system, which in turn is loaded from the + // Configrc variable "coordinate-system". + CS_default, + + CS_zup_right, + CS_yup_right, + CS_zup_left, + CS_yup_left, + + // CS_invalid is not a coordinate system at all. It can be used in + // user-input processing code to indicate a contradictory coordinate + // system request. + CS_invalid, +}; + +extern CoordinateSystem EXPCL_PANDA default_coordinate_system; + +CoordinateSystem EXPCL_PANDA parse_coordinate_system_string(const string &str); +bool EXPCL_PANDA is_right_handed(CoordinateSystem cs = CS_default); + +ostream EXPCL_PANDA &operator << (ostream &out, CoordinateSystem cs); + + +#endif + diff --git a/panda/src/linmath/deg_2_rad.h b/panda/src/linmath/deg_2_rad.h new file mode 100644 index 0000000000..e6ce6e1fa8 --- /dev/null +++ b/panda/src/linmath/deg_2_rad.h @@ -0,0 +1,17 @@ +// Filename: deg_2_rad.h +// Created by: drose (29Sep99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DEG_2_RAD_H +#define DEG_2_RAD_H + +#include + +#include "mathNumbers.h" + +INLINE double deg_2_rad( double f ) { return f * MathNumbers::pi / 180.0; } +INLINE double rad_2_deg( double f ) { return f * 180.0 / MathNumbers::pi; } + +#endif + diff --git a/panda/src/linmath/ioPtaDatagramLinMath.I b/panda/src/linmath/ioPtaDatagramLinMath.I new file mode 100644 index 0000000000..c09b719e5f --- /dev/null +++ b/panda/src/linmath/ioPtaDatagramLinMath.I @@ -0,0 +1,45 @@ +// Filename: ioPtaDatagramLinMath.I +// Created by: jason (26Jun00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: IoPtaDatagamLinMath::write_datagram +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +template +void IoPtaDatagramLinMath:: +write_datagram(Datagram &dest, CPTA(LinMathElement) array) +{ + dest.add_uint32(array.size()); + for(int i = 0; i < array.size(); i++) + { + array[i].write_datagram(dest); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: IoPtaDatagamLinMath::read_datagram +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +template +PTA(LinMathElement) IoPtaDatagramLinMath:: +read_datagram(DatagramIterator &source) +{ + PTA(LinMathElement) array; + LinMathElement temp; + + int size = source.get_uint32(); + for(int i = 0; i < size; i++) + { + temp.read_datagram(source); + array.push_back(temp); + } + + return array; +} + + diff --git a/panda/src/linmath/ioPtaDatagramLinMath.cxx b/panda/src/linmath/ioPtaDatagramLinMath.cxx new file mode 100644 index 0000000000..d14e9caf82 --- /dev/null +++ b/panda/src/linmath/ioPtaDatagramLinMath.cxx @@ -0,0 +1,11 @@ +// Filename: ioPtaDatagramLinMath.cxx +// Created by: drose (29Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "ioPtaDatagramLinMath.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/linmath/ioPtaDatagramLinMath.h b/panda/src/linmath/ioPtaDatagramLinMath.h new file mode 100644 index 0000000000..7076e53464 --- /dev/null +++ b/panda/src/linmath/ioPtaDatagramLinMath.h @@ -0,0 +1,62 @@ +// Filename: ioPtaDatagramLinMath.h +// Created by: jason (26Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef _IO_PTA_DATAGRAM_LINMATH +#define _IO_PTA_DATAGRAM_LINMATH + +#include + +#include "luse.h" +#include "pta_Vertexf.h" +#include "pta_Colorf.h" +#include "pta_Normalf.h" +#include "pta_TexCoordf.h" + +#include + +class Datagram; +class DatagramIterator; + +/////////////////////////////////////////////////////////////////// +// Class : IoPtaDatagramLinMath +// Description : This class is used to read and write a PTA_something +// (where something is some kind of LinMath object like +// LMatrix4f or LVector3f) from a Datagram, in support +// of Bam. It's not intended to be constructed; it's +// just a convenient place to scope these static methods +// which should be called directly. +//////////////////////////////////////////////////////////////////// +template +class IoPtaDatagramLinMath { +public: + static void write_datagram(Datagram &dest, CPTA(LinMathElement) array); + static PTA(LinMathElement) read_datagram(DatagramIterator &source); +}; + +#include "ioPtaDatagramLinMath.I" + +// Now export all of the likely template classes for Windows' benefit. +// This must be done in this file, and not in the individual pta_* +// files, because it's important that this export command be the first +// appearance of a particular template instantiation. + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, IoPtaDatagramLinMath) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, IoPtaDatagramLinMath) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, IoPtaDatagramLinMath) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, IoPtaDatagramLinMath) + + +typedef IoPtaDatagramLinMath IPD_Colorf; +typedef IoPtaDatagramLinMath IPD_Normalf; +typedef IoPtaDatagramLinMath IPD_TexCoordf; +typedef IoPtaDatagramLinMath IPD_Vertexf; + + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/linmath/lmat_ops.I b/panda/src/linmath/lmat_ops.I new file mode 100644 index 0000000000..802ebec4c2 --- /dev/null +++ b/panda/src/linmath/lmat_ops.I @@ -0,0 +1,66 @@ +// Filename: lmat_ops.I +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3 times LMatrix3 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 +operator * (const LVecBase3 &v, const LMatrix3 &m) { + return m.xform(v); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2 times LMatrix3 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2 +operator * (const LVector2 &v, const LMatrix3 &m) { + return m.xform_vec(v); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2 times LMatrix3 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint2 +operator * (const LPoint2 &v, const LMatrix3 &m) { + return m.xform_point(v); +} + + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4 times LMatrix4 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 +operator * (const LVecBase4 &v, const LMatrix4 &m) { + return m.xform(v); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3 times LMatrix4 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 +operator * (const LVector3 &v, const LMatrix4 &m) { + return m.xform_vec(v); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3 times LMatrix4 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3 +operator * (const LPoint3 &v, const LMatrix4 &m) { + return m.xform_point(v); +} + diff --git a/panda/src/linmath/lmat_ops.h b/panda/src/linmath/lmat_ops.h new file mode 100644 index 0000000000..0750c0c28a --- /dev/null +++ b/panda/src/linmath/lmat_ops.h @@ -0,0 +1,81 @@ +// Filename: lmat_ops.h +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LMAT_OPS_H +#define LMAT_OPS_H + +#include "lvecBase3.h" +#include "lpoint3.h" +#include "lvector3.h" +#include "lvecBase4.h" +#include "lpoint4.h" +#include "lvector4.h" +#include "lmatrix3.h" +#include "lmatrix4.h" + + +// vector times matrix3 +template +INLINE LVecBase3 +operator * (const LVecBase3 &v, const LMatrix3 &m); + +template +INLINE LVector2 +operator * (const LVector2 &v, const LMatrix3 &m); + +template +INLINE LPoint2 +operator * (const LPoint2 &v, const LMatrix3 &m); + + +// vector times matrix4 +template +INLINE LVecBase4 +operator * (const LVecBase4 &v, const LMatrix4 &m); + +template +INLINE LVector3 +operator * (const LVector3 &v, const LMatrix4 &m); + +template +INLINE LPoint3 +operator * (const LPoint3 &v, const LMatrix4 &m); + +#ifdef CPPPARSER +// Strictly for the benefit of interrogate, we'll define explicit +// 'instantiations' of the above template functions on types float and +// double. + +INLINE LVecBase3 +operator * (const LVecBase3 &v, const LMatrix3 &m); +INLINE LVector2 +operator * (const LVector2 &v, const LMatrix3 &m); +INLINE LPoint2 +operator * (const LPoint2 &v, const LMatrix3 &m); +INLINE LVecBase4 +operator * (const LVecBase4 &v, const LMatrix4 &m); +INLINE LVector3 +operator * (const LVector3 &v, const LMatrix4 &m); +INLINE LPoint3 +operator * (const LPoint3 &v, const LMatrix4 &m); + +INLINE LVecBase3 +operator * (const LVecBase3 &v, const LMatrix3 &m); +INLINE LVector2 +operator * (const LVector2 &v, const LMatrix3 &m); +INLINE LPoint2 +operator * (const LPoint2 &v, const LMatrix3 &m); +INLINE LVecBase4 +operator * (const LVecBase4 &v, const LMatrix4 &m); +INLINE LVector3 +operator * (const LVector3 &v, const LMatrix4 &m); +INLINE LPoint3 +operator * (const LPoint3 &v, const LMatrix4 &m); + +#endif // CPPPARSER + +#include "lmat_ops.I" + +#endif diff --git a/panda/src/linmath/lmatrix.cxx b/panda/src/linmath/lmatrix.cxx new file mode 100644 index 0000000000..fd0042df3c --- /dev/null +++ b/panda/src/linmath/lmatrix.cxx @@ -0,0 +1,13 @@ +// Filename: lmatrix.cxx +// Created by: drose (22Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include +#include "lmatrix.h" + +// This tells GCC to explicitly instantiate the templates defined in +// lmatrix.h and leave them here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/linmath/lmatrix.h b/panda/src/linmath/lmatrix.h new file mode 100644 index 0000000000..d56cbf2a5a --- /dev/null +++ b/panda/src/linmath/lmatrix.h @@ -0,0 +1,25 @@ +// Filename: lmatrix.h +// Created by: drose (15Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LMATRIX_H +#define LMATRIX_H + +#include + +#include "lmatrix3.h" +#include "lmatrix4.h" + +typedef LMatrix3 LMatrix3f; +typedef LMatrix4 LMatrix4f; + +typedef LMatrix3 LMatrix3d; +typedef LMatrix4 LMatrix4d; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/linmath/lmatrix3.I b/panda/src/linmath/lmatrix3.I new file mode 100644 index 0000000000..4ce2baa0be --- /dev/null +++ b/panda/src/linmath/lmatrix3.I @@ -0,0 +1,1109 @@ +// Filename: lmatrix3.I +// Created by: drose (29Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "deg_2_rad.h" +#include "nearly_zero.h" + +#include +#include + +#include + +template +TypeHandle LMatrix3::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix3:: +LMatrix3() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix3:: +LMatrix3(const LMatrix3 ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +LMatrix3 &LMatrix3:: +operator = (const LMatrix3 ©) { + set(copy(0, 0), copy(0, 1), copy(0, 2), + copy(1, 0), copy(1, 1), copy(1, 2), + copy(2, 0), copy(2, 1), copy(2, 2)); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::Fill Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix3 &LMatrix3:: +operator = (NumType fill_value) { + fill(fill_value); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix3:: +LMatrix3(NumType e00, NumType e01, NumType e02, + NumType e10, NumType e11, NumType e12, + NumType e20, NumType e21, NumType e22) { + set(e00, e01, e02, + e10, e11, e12, + e20, e21, e22); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::fill +// Access: Public +// Description: Sets each element of the matrix to the indicated +// fill_value. This is of questionable value, but is +// sometimes useful when initializing to zero. +//////////////////////////////////////////////////////////////////// +template +void LMatrix3:: +fill(NumType fill_value) { + set(fill_value, fill_value, fill_value, + fill_value, fill_value, fill_value, + fill_value, fill_value, fill_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::set +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix3:: +set(NumType e00, NumType e01, NumType e02, + NumType e10, NumType e11, NumType e12, + NumType e20, NumType e21, NumType e22) { + (*this)(0, 0) = e00; + (*this)(0, 1) = e01; + (*this)(0, 2) = e02; + (*this)(1, 0) = e10; + (*this)(1, 1) = e11; + (*this)(1, 2) = e12; + (*this)(2, 0) = e20; + (*this)(2, 1) = e21; + (*this)(2, 2) = e22; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::set_row +// Access: Public +// Description: Replaces the indicated row of the matrix from a +// three-component vector. +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix3:: +set_row(int row, const LVecBase3 &v) { + (*this)(row, 0) = v[0]; + (*this)(row, 1) = v[1]; + (*this)(row, 2) = v[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::set_column +// Access: Public +// Description: Replaces the indicated column of the matrix from a +// three-component vector. +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix3:: +set_col(int col, const LVecBase3 &v) { + (*this)(0, col) = v[0]; + (*this)(1, col) = v[1]; + (*this)(2, col) = v[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::set_row +// Access: Public +// Description: Replaces the indicated row of the matrix from a +// two-component vector, ignoring the last column. +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix3:: +set_row(int row, const LVecBase2 &v) { + (*this)(row, 0) = v[0]; + (*this)(row, 1) = v[1]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::set_column +// Access: Public +// Description: Replaces the indicated column of the matrix from a +// two-component vector, ignoring the last row. +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix3:: +set_col(int col, const LVecBase2 &v) { + (*this)(0, col) = v[0]; + (*this)(1, col) = v[1]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::get_row +// Access: Public +// Description: Returns the indicated row of the matrix as a +// three-component vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LMatrix3:: +get_row(int row) const { + return LVecBase3((*this)(row, 0), (*this)(row, 1), (*this)(row, 2)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::get_col +// Access: Public +// Description: Returns the indicated column of the matrix as a +// three-component vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LMatrix3:: +get_col(int col) const { + return LVecBase3((*this)(0, col), (*this)(1, col), (*this)(2, col)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::get_row2 +// Access: Public +// Description: Returns the indicated row of the matrix as a +// two-component vector, ignoring the last column. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 LMatrix3:: +get_row2(int row) const { + return LVecBase2((*this)(row, 0), (*this)(row, 1)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::get_col2 +// Access: Public +// Description: Returns the indicated column of the matrix as a +// two-component vector, ignoring the last row. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 LMatrix3:: +get_col2(int col) const { + return LVecBase2((*this)(0, col), (*this)(1, col)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::Indexing operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType &LMatrix3:: +operator () (int row, int col) { + nassertr(row >= 0 && row < 3, _data[0]); + nassertr(col >= 0 && col < 3, _data[0]); + return _data[row * 3 + col]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::Indexing operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LMatrix3:: +operator () (int row, int col) const { + nassertr(row >= 0 && row < 3, 0.0); + nassertr(col >= 0 && col < 3, 0.0); + return _data[row * 3 + col]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::is_nan +// Access: Public +// Description: Returns true if any component of the matrix is +// not-a-number, false otherwise. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LMatrix3:: +is_nan() const { + return + cnan(_data[0]) || cnan(_data[1]) || cnan(_data[2]) || + cnan(_data[3]) || cnan(_data[4]) || cnan(_data[5]) || + cnan(_data[6]) || cnan(_data[7]) || cnan(_data[8]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::get_cell +// Access: Public +// Description: Returns a particular element of the matrix. +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LMatrix3:: +get_cell(int row, int col) const { + nassertr(row >= 0 && row < 3, 0.0); + nassertr(col >= 0 && col < 3, 0.0); + return _data[row * 3 + col]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::set_cell +// Access: Public +// Description: Changes a particular element of the matrix. +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix3:: +set_cell(int row, int col, NumType value) { + nassertv(row >= 0 && row < 3); + nassertv(col >= 0 && col < 3); + _data[row * 3 + col] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::get_data +// Access: Public +// Description: Returns the address of the first of the nine data +// elements in the matrix. The remaining elements +// occupy the next eight positions in row-major order. +//////////////////////////////////////////////////////////////////// +template +INLINE const NumType *LMatrix3:: +get_data() const { + return _data; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::get_num_components +// Access: Public +// Description: Returns the number of elements in the matrix, nine. +//////////////////////////////////////////////////////////////////// +template +INLINE int LMatrix3:: +get_num_components() const { + return 9; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::begin +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix3::iterator LMatrix3:: +begin() { + return _data; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::end +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix3::iterator LMatrix3:: +end() { + return begin() + get_num_components(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::begin +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix3::const_iterator LMatrix3:: +begin() const { + return _data; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::end +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix3::const_iterator LMatrix3:: +end() const { + return begin() + get_num_components(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::Equality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +bool LMatrix3:: +operator == (const LMatrix3 &other) const { + return ((*this)(0, 0) == other(0, 0) && + (*this)(0, 1) == other(0, 1) && + (*this)(0, 2) == other(0, 2) && + (*this)(1, 0) == other(1, 0) && + (*this)(1, 1) == other(1, 1) && + (*this)(1, 2) == other(1, 2) && + (*this)(2, 0) == other(2, 0) && + (*this)(2, 1) == other(2, 1) && + (*this)(2, 2) == other(2, 2)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool LMatrix3:: +operator != (const LMatrix3 &other) const { + return !operator == (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::compare_to +// Access: Public +// Description: This flavor of compare_to uses a default threshold +// value based on the numeric type. +//////////////////////////////////////////////////////////////////// +template +INLINE int LMatrix3:: +compare_to(const LMatrix3 &other) const { + return compare_to(other, NEARLY_ZERO(NumType)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::compare_to +// Access: Public +// Description: Sorts matrices lexicographically, componentwise. +// Returns a number less than 0 if this matrix sorts +// before the other one, greater than zero if it sorts +// after, 0 if they are equivalent (within the indicated +// tolerance). +//////////////////////////////////////////////////////////////////// +template +int LMatrix3:: +compare_to(const LMatrix3 &other, NumType threshold) const { + for (int i = 0; i < 9; i++) { + if (!IS_THRESHOLD_EQUAL(_data[i], other._data[i], threshold)) { + return (_data[i] < other._data[i]) ? -1 : 1; + } + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::xform +// Access: Public +// Description: 3-component vector or point times matrix. This is a +// fully general operation. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LMatrix3:: +xform(const LVecBase3 &v) const { + return LVecBase3(v.dot(get_col(0)), + v.dot(get_col(1)), + v.dot(get_col(2))); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::xform_point +// Access: Public +// Description: The matrix transforms a 2-component point (including +// translation component) and returns the result. This +// assumes the matrix is an affine transform. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 LMatrix3:: +xform_point(const LVecBase2 &v) const { + return LVecBase2(v.dot(get_col2(0)) + (*this)(2, 0), + v.dot(get_col2(1)) + (*this)(2, 1)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::xform_vec +// Access: Public +// Description: The matrix transforms a 2-component vector (without +// translation component) and returns the result. This +// assumes the matrix is an affine transform. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 LMatrix3:: +xform_vec(const LVecBase2 &v) const { + return LVecBase2(v.dot(get_col2(0)), + v.dot(get_col2(1))); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::mult_cel +// Access: Private +// Description: Returns one cell of the result of a matrix-matrix +// multiplication operation. +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LMatrix3:: +mult_cel(const LMatrix3 &other, int row, int col) const { + return get_row(row).dot(other.get_col(col)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::matrix * matrix +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +LMatrix3 LMatrix3:: +operator * (const LMatrix3 &other) const { + LMatrix3 t; + + t(0, 0) = mult_cel(other, 0, 0); + t(0, 1) = mult_cel(other, 0, 1); + t(0, 2) = mult_cel(other, 0, 2); + + t(1, 0) = mult_cel(other, 1, 0); + t(1, 1) = mult_cel(other, 1, 1); + t(1, 2) = mult_cel(other, 1, 2); + + t(2, 0) = mult_cel(other, 2, 0); + t(2, 1) = mult_cel(other, 2, 1); + t(2, 2) = mult_cel(other, 2, 2); + + return t; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::matrix * scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +LMatrix3 LMatrix3:: +operator * (NumType scalar) const { + LMatrix3 t; + + t(0, 0) = (*this)(0, 0) * scalar; + t(0, 1) = (*this)(0, 1) * scalar; + t(0, 2) = (*this)(0, 2) * scalar; + + t(1, 0) = (*this)(1, 0) * scalar; + t(1, 1) = (*this)(1, 1) * scalar; + t(1, 2) = (*this)(1, 2) * scalar; + + t(2, 0) = (*this)(2, 0) * scalar; + t(2, 1) = (*this)(2, 1) * scalar; + t(2, 2) = (*this)(2, 2) * scalar; + + return t; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::matrix / scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +LMatrix3 LMatrix3:: +operator / (NumType scalar) const { + LMatrix3 t; + + t(0, 0) = (*this)(0, 0) / scalar; + t(0, 1) = (*this)(0, 1) / scalar; + t(0, 2) = (*this)(0, 2) / scalar; + + t(1, 0) = (*this)(1, 0) / scalar; + t(1, 1) = (*this)(1, 1) / scalar; + t(1, 2) = (*this)(1, 2) / scalar; + + t(2, 0) = (*this)(2, 0) / scalar; + t(2, 1) = (*this)(2, 1) / scalar; + t(2, 2) = (*this)(2, 2) / scalar; + + return t; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::matrix += matrix +// Access: Public +// Description: Performs a memberwise addition between two matrices. +//////////////////////////////////////////////////////////////////// +template +LMatrix3 &LMatrix3:: +operator += (const LMatrix3 &other) { + (*this)(0, 0) += other(0, 0); + (*this)(0, 1) += other(0, 1); + (*this)(0, 2) += other(0, 2); + + (*this)(1, 0) += other(1, 0); + (*this)(1, 1) += other(1, 1); + (*this)(1, 2) += other(1, 2); + + (*this)(2, 0) += other(2, 0); + (*this)(2, 1) += other(2, 1); + (*this)(2, 2) += other(2, 2); + + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::matrix -= matrix +// Access: Public +// Description: Performs a memberwise subtraction between two matrices. +//////////////////////////////////////////////////////////////////// +template +LMatrix3 &LMatrix3:: +operator -= (const LMatrix3 &other) { + (*this)(0, 0) -= other(0, 0); + (*this)(0, 1) -= other(0, 1); + (*this)(0, 2) -= other(0, 2); + + (*this)(1, 0) -= other(1, 0); + (*this)(1, 1) -= other(1, 1); + (*this)(1, 2) -= other(1, 2); + + (*this)(2, 0) -= other(2, 0); + (*this)(2, 1) -= other(2, 1); + (*this)(2, 2) -= other(2, 2); + + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::matrix *= matrix +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix3 &LMatrix3:: +operator *= (const LMatrix3 &other) { + (*this) = (*this) * other; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::matrix *= scalar +// Access: Public +// Description: Performs a memberwise scale. +//////////////////////////////////////////////////////////////////// +template +LMatrix3 &LMatrix3:: +operator *= (NumType scalar) { + (*this)(0, 0) *= scalar; + (*this)(0, 1) *= scalar; + (*this)(0, 2) *= scalar; + + (*this)(1, 0) *= scalar; + (*this)(1, 1) *= scalar; + (*this)(1, 2) *= scalar; + + (*this)(2, 0) *= scalar; + (*this)(2, 1) *= scalar; + (*this)(2, 2) *= scalar; + + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::matrix /= scalar +// Access: Public +// Description: Performs a memberwise scale. +//////////////////////////////////////////////////////////////////// +template +LMatrix3 &LMatrix3:: +operator /= (NumType scalar) { + (*this)(0, 0) /= scalar; + (*this)(0, 1) /= scalar; + (*this)(0, 2) /= scalar; + + (*this)(1, 0) /= scalar; + (*this)(1, 1) /= scalar; + (*this)(1, 2) /= scalar; + + (*this)(2, 0) /= scalar; + (*this)(2, 1) /= scalar; + (*this)(2, 2) /= scalar; + + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::transpose_from +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void LMatrix3:: +transpose_from(const LMatrix3 &other) { + (*this)(0, 0) = other(0, 0); + (*this)(0, 1) = other(1, 0); + (*this)(0, 2) = other(2, 0); + + (*this)(1, 0) = other(0, 1); + (*this)(1, 1) = other(1, 1); + (*this)(1, 2) = other(2, 1); + + (*this)(2, 0) = other(0, 2); + (*this)(2, 1) = other(1, 2); + (*this)(2, 2) = other(2, 2); +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::transpose_in_place +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix3:: +transpose_in_place() { + LMatrix3 temp = (*this); + transpose_from(temp); +} + +// Matrix inversion code from Numerical Recipes in C. + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::det2 +// Access: Private, Static +// Description: Returns the determinant of a 2x2 matrix. +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LMatrix3:: +det2(NumType e00, NumType e01, NumType e10, NumType e11) const { + return (e00 * e11 - e10 * e01); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::determinant +// Access: Public +// Description: Returns the determinant of the matrix. +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LMatrix3:: +determinant() const { + return + (*this)(0,0) * det2((*this)(1,1),(*this)(1,2),(*this)(2,1),(*this)(2,2)) + -(*this)(0,1) * det2((*this)(1,0),(*this)(1,2),(*this)(2,0),(*this)(2,2)) + +(*this)(0,2) * det2((*this)(1,0),(*this)(1,1),(*this)(2,0),(*this)(2,1)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::invert_from +// Access: Public +// Description: Computes the inverse of the other matrix, and stores +// the result in this matrix. This is a fully general +// operation and makes no assumptions about the type of +// transform represented by the matrix. +// +// The other matrix must be a different object than this +// matrix. However, if you need to invert a matrix in +// place, see invert_in_place. +// +// The return value is true if the matrix was +// successfully inverted, false if the was a +// singularity. +//////////////////////////////////////////////////////////////////// +template +bool LMatrix3:: +invert_from(const LMatrix3 &other) { + NumType d = other.determinant(); + + if (IS_NEARLY_ZERO(d)) { + nout << "Tried to invert singular LMatrix3.\n"; + (*this) = ident_mat(); + return false; + } + + d = 1.0 / d; + (*this)(0,0) = d * det2(other(1,1), other(1,2), other(2,1), other(2,2)); + (*this)(1,0) = -d * det2(other(1,0), other(1,2), other(2,0), other(2,2)); + (*this)(2,0) = d * det2(other(1,0), other(1,1), other(2,0), other(2,1)); + + (*this)(0,1) = -d * det2(other(0,1), other(0,2), other(2,1), other(2,2)); + (*this)(1,1) = d * det2(other(0,0), other(0,2), other(2,0), other(2,2)); + (*this)(2,1) = -d * det2(other(0,0), other(0,1), other(2,0), other(2,1)); + + (*this)(0,2) = d * det2(other(0,1), other(0,2), other(1,1), other(1,2)); + (*this)(1,2) = -d * det2(other(0,0), other(0,2), other(1,0), other(1,2)); + (*this)(2,2) = d * det2(other(0,0), other(0,1), other(1,0), other(1,1)); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::invert_in_place +// Access: Public +// Description: Inverts the current matrix. Returns true if the +// inverse is successful, false if the matrix was +// singular. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LMatrix3:: +invert_in_place() { + LMatrix3 temp = (*this); + return invert_from(temp); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::ident_mat +// Access: Public, Static +// Description: Returns an identity matrix. +//////////////////////////////////////////////////////////////////// +template +const LMatrix3 &LMatrix3:: +ident_mat() { + static LMatrix3 mat(1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + return mat; +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::translate_mat +// Access: Public, Static +// Description: Returns a matrix that applies the indicated +// translation. +//////////////////////////////////////////////////////////////////// +template +LMatrix3 LMatrix3:: +translate_mat(const LVecBase2 &trans) { + return LMatrix3(1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + trans[0], trans[1], 1.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::translate_mat +// Access: Public, Static +// Description: Returns a matrix that applies the indicated +// translation. +//////////////////////////////////////////////////////////////////// +template +LMatrix3 LMatrix3:: +translate_mat(NumType tx, NumType ty) { + return LMatrix3(1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + tx, ty, 1.0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::rotate_mat +// Access: Public, Static +// Description: Returns a matrix that rotates by the given angle in +// degrees counterclockwise. +//////////////////////////////////////////////////////////////////// +template +LMatrix3 LMatrix3:: +rotate_mat(NumType angle) { + LMatrix3 mat; + + double s = sin(deg_2_rad(angle)); + double c = cos(deg_2_rad(angle)); + + return LMatrix3( c, s, 0.0, + -s, c, 0.0, + 0.0, 0.0, 1.0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::scale_mat +// Access: Public, Static +// Description: Returns a matrix that applies the indicated +// scale in each of the two axes. +//////////////////////////////////////////////////////////////////// +template +LMatrix3 LMatrix3:: +scale_mat(const LVecBase2 &scale) { + return LMatrix3(scale[0], 0.0, 0.0, + 0.0, scale[1], 0.0, + 0.0, 0.0, 1.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::scale_mat +// Access: Public, Static +// Description: Returns a matrix that applies the indicated +// scale in each of the two axes. +//////////////////////////////////////////////////////////////////// +template +LMatrix3 LMatrix3:: +scale_mat(NumType sx, NumType sy) { + return LMatrix3(sx, 0.0, 0.0, + 0.0, sy, 0.0, + 0.0, 0.0, 1.0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::rotate_mat +// Access: Public, Static +// Description: Returns a matrix that rotates by the given angle in +// degrees counterclockwise about the indicated vector. +//////////////////////////////////////////////////////////////////// +template +LMatrix3 LMatrix3:: +rotate_mat(NumType angle, LVecBase3 axis, + CoordinateSystem cs) { + if (cs == CS_default) { + cs = default_coordinate_system; + } + LMatrix3 mat; + + if (!is_right_handed(cs)) { + // In a left-handed coordinate system, counterclockwise is the + // other direction. + angle = -angle; + } + + // Normalize the axis. + NumType length = axis.dot(axis); + nassertr(length != 0.0, ident_mat()); + axis /= length; + + double s = sin(deg_2_rad(angle)); + double c = cos(deg_2_rad(angle)); + double t = 1.0 - c; + + mat(0, 0) = t * axis[0] * axis[0] + c; + mat(0, 1) = t * axis[0] * axis[1] + s * axis[2]; + mat(0, 2) = t * axis[0] * axis[2] - s * axis[1]; + + mat(1, 0) = t * axis[1] * axis[0] - s * axis[2]; + mat(1, 1) = t * axis[1] * axis[1] + c; + mat(1, 2) = t * axis[1] * axis[2] + s * axis[0]; + + mat(2, 0) = t * axis[2] * axis[0] + s * axis[1]; + mat(2, 1) = t * axis[2] * axis[1] - s * axis[0]; + mat(2, 2) = t * axis[2] * axis[2] + c; + + return mat; +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::scale_mat +// Access: Public, Static +// Description: Returns a matrix that applies the indicated +// scale in each of the three axes. +//////////////////////////////////////////////////////////////////// +template +LMatrix3 LMatrix3:: +scale_mat(const LVecBase3 &scale) { + return LMatrix3(scale[0], 0.0, 0.0, + 0.0, scale[1], 0.0, + 0.0, 0.0, scale[2]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::scale_mat +// Access: Public, Static +// Description: Returns a matrix that applies the indicated +// scale in each of the three axes. +//////////////////////////////////////////////////////////////////// +template +LMatrix3 LMatrix3:: +scale_mat(NumType sx, NumType sy, NumType sz) { + return LMatrix3(sx, 0.0, 0.0, + 0.0, sy, 0.0, + 0.0, 0.0, sz); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::almost_equal +// Access: Public +// Description: Returns true if two matrices are memberwise equal +// within a specified tolerance. +//////////////////////////////////////////////////////////////////// +template +bool LMatrix3:: +almost_equal(const LMatrix3 &other, NumType threshold) const { + return (IS_THRESHOLD_EQUAL((*this)(0, 0), other(0, 0), threshold) && + IS_THRESHOLD_EQUAL((*this)(0, 1), other(0, 1), threshold) && + IS_THRESHOLD_EQUAL((*this)(0, 2), other(0, 2), threshold) && + IS_THRESHOLD_EQUAL((*this)(1, 0), other(1, 0), threshold) && + IS_THRESHOLD_EQUAL((*this)(1, 1), other(1, 1), threshold) && + IS_THRESHOLD_EQUAL((*this)(1, 2), other(1, 2), threshold) && + IS_THRESHOLD_EQUAL((*this)(2, 0), other(2, 0), threshold) && + IS_THRESHOLD_EQUAL((*this)(2, 1), other(2, 1), threshold) && + IS_THRESHOLD_EQUAL((*this)(2, 2), other(2, 2), threshold)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::almost_equal +// Access: Public +// Description: Returns true if two matrices are memberwise equal +// within a default tolerance based on the numeric type. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LMatrix3:: +almost_equal(const LMatrix3 &other) const { + return almost_equal(other, NEARLY_ZERO(NumType)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix3:: +output(ostream &out) const { + out << "[ " + << MAYBE_ZERO((*this)(0, 0)) << " " + << MAYBE_ZERO((*this)(0, 1)) << " " + << MAYBE_ZERO((*this)(0, 2)) + << " ] [ " + << MAYBE_ZERO((*this)(1, 0)) << " " + << MAYBE_ZERO((*this)(1, 1)) << " " + << MAYBE_ZERO((*this)(1, 2)) + << " ] [ " + << MAYBE_ZERO((*this)(2, 0)) << " " + << MAYBE_ZERO((*this)(2, 1)) << " " + << MAYBE_ZERO((*this)(2, 2)) + << " ]"; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix3:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) + << MAYBE_ZERO((*this)(0, 0)) << " " + << MAYBE_ZERO((*this)(0, 1)) << " " + << MAYBE_ZERO((*this)(0, 2)) + << "\n"; + indent(out, indent_level) + << MAYBE_ZERO((*this)(1, 0)) << " " + << MAYBE_ZERO((*this)(1, 1)) << " " + << MAYBE_ZERO((*this)(1, 2)) + << "\n"; + indent(out, indent_level) + << MAYBE_ZERO((*this)(2, 0)) << " " + << MAYBE_ZERO((*this)(2, 1)) << " " + << MAYBE_ZERO((*this)(2, 2)) + << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +template +void LMatrix3:: +init_type() { + if (_type_handle == TypeHandle::none()) { + // Format a string to describe the type. + do_init_type(NumType); + string name = + "LMatrix3<" + get_type_handle(NumType).get_name() + ">"; + register_type(_type_handle, name); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::write_datagram +// Description: Writes the matrix to the datagram +//////////////////////////////////////////////////////////////////// +template +void LMatrix3:: +write_datagram(Datagram &destination) const +{ + for(int i = 0; i < 3; i++) + { + for(int j = 0; j < 3; j++) + { + destination.add_float64(get_cell(i,j)); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::read_datagram +// Description: Reads itself out of the datagram +//////////////////////////////////////////////////////////////////// +template +void LMatrix3:: +read_datagram(DatagramIterator &scan) +{ + for(int i = 0; i < 3; i++) + { + for(int j = 0; j < 3; j++) + { + set_cell(i, j, scan.get_float64()); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::transpose +// Description: Transposes the given matrix and returns it. +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix3 +transpose(const LMatrix3 &a) { + LMatrix3 result; + result.transpose_from(a); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::invert +// Description: Inverts the given matrix and returns it. +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix3 +invert(const LMatrix3 &a) { + LMatrix3 result; + bool nonsingular = result.invert_from(a); + nassertr(nonsingular, LMatrix3::ident_mat()); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: lcast_to +// Description: Converts a matrix from one numeric representation to +// another one. This is usually invoked using the macro +// LCAST. +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix3 +lcast_to(NumType2 *, const LMatrix3 &source) { + return LMatrix3 + (source(0, 0), source(0, 1), source(0, 2), + source(1, 0), source(1, 1), source(1, 2), + source(2, 0), source(2, 1), source(2, 2)); +} diff --git a/panda/src/linmath/lmatrix3.h b/panda/src/linmath/lmatrix3.h new file mode 100644 index 0000000000..076d4e19f0 --- /dev/null +++ b/panda/src/linmath/lmatrix3.h @@ -0,0 +1,193 @@ +// Filename: lmatrix3.h +// Created by: drose (29Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LMATRIX3_H +#define LMATRIX3_H + +#include + +#include "coordinateSystem.h" +#include "lvecBase3.h" +#include "lvecBase2.h" + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : LMatrix3 +// Description : This is a 3-by-3 transform matrix. It typically will +// represent either a rotation-and-scale (no +// translation) matrix in 3-d, or a full affine matrix +// (rotation, scale, translation) in 2-d, e.g. for a +// texture matrix. +//////////////////////////////////////////////////////////////////// +template +class LMatrix3 { +public: + typedef const NumType *iterator; + typedef const NumType *const_iterator; + + INLINE LMatrix3(); + INLINE LMatrix3(const LMatrix3 &other); + LMatrix3 &operator = (const LMatrix3 &other); + INLINE LMatrix3 &operator = (NumType fill_value); + INLINE LMatrix3(NumType e00, NumType e01, NumType e02, + NumType e10, NumType e11, NumType e12, + NumType e20, NumType e21, NumType e22); + + void fill(NumType fill_value); + INLINE void set(NumType e00, NumType e01, NumType e02, + NumType e10, NumType e11, NumType e12, + NumType e20, NumType e21, NumType e22); + + INLINE void set_row(int row, const LVecBase3 &v); + INLINE void set_col(int col, const LVecBase3 &v); + + INLINE void set_row(int row, const LVecBase2 &v); + INLINE void set_col(int col, const LVecBase2 &v); + + INLINE LVecBase3 get_row(int row) const; + INLINE LVecBase3 get_col(int col) const; + + INLINE LVecBase2 get_row2(int row) const; + INLINE LVecBase2 get_col2(int col) const; + + INLINE NumType &operator () (int row, int col); + INLINE NumType operator () (int row, int col) const; + + INLINE bool is_nan() const; + + INLINE NumType get_cell(int row, int col) const; + INLINE void set_cell(int row, int col, NumType value); + + INLINE const NumType *get_data() const; + INLINE int get_num_components() const; + + INLINE iterator begin(); + INLINE iterator end(); + + INLINE const_iterator begin() const; + INLINE const_iterator end() const; + + + bool operator == (const LMatrix3 &other) const; + INLINE bool operator != (const LMatrix3 &other) const; + + INLINE int compare_to(const LMatrix3 &other) const; + int compare_to(const LMatrix3 &other, NumType threshold) const; + + INLINE LVecBase3 + xform(const LVecBase3 &v) const; + + INLINE LVecBase2 + xform_point(const LVecBase2 &v) const; + + INLINE LVecBase2 + xform_vec(const LVecBase2 &v) const; + + LMatrix3 operator * (const LMatrix3 &other) const; + LMatrix3 operator * (NumType scalar) const; + LMatrix3 operator / (NumType scalar) const; + + LMatrix3 &operator += (const LMatrix3 &other); + LMatrix3 &operator -= (const LMatrix3 &other); + + INLINE LMatrix3 &operator *= (const LMatrix3 &other); + + LMatrix3 &operator *= (NumType scalar); + LMatrix3 &operator /= (NumType scalar); + + INLINE NumType determinant() const; + + void transpose_from(const LMatrix3 &other); + INLINE void transpose_in_place(); + + bool invert_from(const LMatrix3 &other); + INLINE bool invert_in_place(); + + static const LMatrix3 &ident_mat(); + + // A 3x3 matrix is likely to be used for one of two purposes. In + // 2-d coordinate space (e.g. texture or surface coordinates), it + // can contain a full affine transform, with scale, rotate, + // translate. In 3-d coordinate space, it can contain only scale + // and/or rotate; e.g., the upper 3x3 rectangle of a full 4x4 + // matrix. + + // The following named constructors return 3x3 matrices suitable for + // affine transforms in 2-d coordinate space. + + static LMatrix3 translate_mat(const LVecBase2 &trans); + static LMatrix3 translate_mat(NumType tx, NumType ty); + static LMatrix3 rotate_mat(NumType angle); + static LMatrix3 scale_mat(const LVecBase2 &scale); + static LMatrix3 scale_mat(NumType sx, NumType sy); + + // The following named constructors return 3x3 matrices suitable for + // scale/rotate transforms in 3-d coordinate space. + static LMatrix3 rotate_mat(NumType angle, + LVecBase3 axis, + CoordinateSystem cs = CS_default); + static LMatrix3 scale_mat(const LVecBase3 &scale); + static LMatrix3 scale_mat(NumType sx, NumType sy, NumType sz); + + // We don't have a scale_mat() that takes a single uniform scale + // parameter, because it would be ambiguous whether we mean a 2-d or + // a 3-d scale. + + + bool almost_equal(const LMatrix3 &other, + NumType threshold) const; + + INLINE bool almost_equal(const LMatrix3 &other) const; + + INLINE void output(ostream &out) const; + INLINE void write(ostream &out, int indent_level = 0) const; + +private: + INLINE NumType mult_cel(const LMatrix3 &other, int x, int y) const; + INLINE NumType det2(NumType e00, NumType e01, NumType e10, NumType e11) const; + + NumType _data[3 * 3]; + + //Functionality for reading and writing from/to a binary source +public: + void write_datagram(Datagram& destination) const; + void read_datagram(DatagramIterator& scan); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +template +INLINE ostream &operator << (ostream &out, const LMatrix3 &mat) { + mat.output(out); + return out; +} + +template +INLINE LMatrix3 transpose(const LMatrix3 &a); + +template +INLINE LMatrix3 invert(const LMatrix3 &a); + +// Cast to a different numeric type +template +INLINE LMatrix3 +lcast_to(NumType2 *type, const LMatrix3 &source); + +#include "lmatrix3.I" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LMatrix3) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LMatrix3) + +#endif diff --git a/panda/src/linmath/lmatrix4.I b/panda/src/linmath/lmatrix4.I new file mode 100644 index 0000000000..566012267b --- /dev/null +++ b/panda/src/linmath/lmatrix4.I @@ -0,0 +1,1506 @@ +// Filename: lmatrix.I +// Created by: drose (15Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "deg_2_rad.h" +#include "nearly_zero.h" +#include "config_linmath.h" + +#include + +#include + +template +TypeHandle LMatrix4::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix4:: +LMatrix4() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix4:: +LMatrix4(const LMatrix4 ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +LMatrix4 &LMatrix4:: +operator = (const LMatrix4 ©) { + set(copy(0, 0), copy(0, 1), copy(0, 2), copy(0, 3), + copy(1, 0), copy(1, 1), copy(1, 2), copy(1, 3), + copy(2, 0), copy(2, 1), copy(2, 2), copy(2, 3), + copy(3, 0), copy(3, 1), copy(3, 2), copy(3, 3)); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::Fill Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix4 &LMatrix4:: +operator = (NumType fill_value) { + fill(fill_value); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix4:: +LMatrix4(NumType e00, NumType e01, NumType e02, NumType e03, + NumType e10, NumType e11, NumType e12, NumType e13, + NumType e20, NumType e21, NumType e22, NumType e23, + NumType e30, NumType e31, NumType e32, NumType e33) { + set(e00, e01, e02, e03, + e10, e11, e12, e13, + e20, e21, e22, e23, + e30, e31, e32, e33); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::Constructor, upper 3x3 +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +LMatrix4:: +LMatrix4(const LMatrix3 &upper3) { + set(upper3(0, 0), upper3(0, 1), upper3(0, 2), 0.0, + upper3(1, 0), upper3(1, 1), upper3(1, 2), 0.0, + upper3(2, 0), upper3(2, 1), upper3(2, 2), 0.0, + 0.0, 0.0, 0.0, 1.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::Constructor, upper 3x3 plus translation +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +LMatrix4:: +LMatrix4(const LMatrix3 &upper3, + const LVecBase3 &trans) { + set(upper3(0, 0), upper3(0, 1), upper3(0, 2), 0.0, + upper3(1, 0), upper3(1, 1), upper3(1, 2), 0.0, + upper3(2, 0), upper3(2, 1), upper3(2, 2), 0.0, + trans[0], trans[1], trans[2], 1.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::fill +// Access: Public +// Description: Sets each element of the matrix to the indicated +// fill_value. This is of questionable value, but is +// sometimes useful when initializing to zero. +//////////////////////////////////////////////////////////////////// +template +void LMatrix4:: +fill(NumType fill_value) { + set(fill_value, fill_value, fill_value, fill_value, + fill_value, fill_value, fill_value, fill_value, + fill_value, fill_value, fill_value, fill_value, + fill_value, fill_value, fill_value, fill_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::set +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix4:: +set(NumType e00, NumType e01, NumType e02, NumType e03, + NumType e10, NumType e11, NumType e12, NumType e13, + NumType e20, NumType e21, NumType e22, NumType e23, + NumType e30, NumType e31, NumType e32, NumType e33) { + (*this)(0, 0) = e00; + (*this)(0, 1) = e01; + (*this)(0, 2) = e02; + (*this)(0, 3) = e03; + + (*this)(1, 0) = e10; + (*this)(1, 1) = e11; + (*this)(1, 2) = e12; + (*this)(1, 3) = e13; + + (*this)(2, 0) = e20; + (*this)(2, 1) = e21; + (*this)(2, 2) = e22; + (*this)(2, 3) = e23; + + (*this)(3, 0) = e30; + (*this)(3, 1) = e31; + (*this)(3, 2) = e32; + (*this)(3, 3) = e33; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::set_upper_3 +// Access: Public +// Description: Sets the upper 3x3 submatrix. +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix4:: +set_upper_3(const LMatrix3 &upper3) { + (*this)(0, 0) = upper3(0, 0); + (*this)(0, 1) = upper3(0, 1); + (*this)(0, 2) = upper3(0, 2); + + (*this)(1, 0) = upper3(1, 0); + (*this)(1, 1) = upper3(1, 1); + (*this)(1, 2) = upper3(1, 2); + + (*this)(2, 0) = upper3(2, 0); + (*this)(2, 1) = upper3(2, 1); + (*this)(2, 2) = upper3(2, 2); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::get_upper_3 +// Access: Public +// Description: Retrieves the upper 3x3 submatrix. +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix3 LMatrix4:: +get_upper_3() const { + return LMatrix3 + ((*this)(0, 0), (*this)(0, 1), (*this)(0, 2), + (*this)(1, 0), (*this)(1, 1), (*this)(1, 2), + (*this)(2, 0), (*this)(2, 1), (*this)(2, 2)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::set_row +// Access: Public +// Description: Replaces the indicated row of the matrix. +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix4:: +set_row(int row, const LVecBase4 &v) { + (*this)(row, 0) = v[0]; + (*this)(row, 1) = v[1]; + (*this)(row, 2) = v[2]; + (*this)(row, 3) = v[3]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::set_col +// Access: Public +// Description: Replaces the indicated column of the matrix. +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix4:: +set_col(int col, const LVecBase4 &v) { + (*this)(0, col) = v[0]; + (*this)(1, col) = v[1]; + (*this)(2, col) = v[2]; + (*this)(3, col) = v[3]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::set_row +// Access: Public +// Description: Replaces the indicated row of the matrix with the +// indicated 3-component vector, ignoring the last +// column. +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix4:: +set_row(int row, const LVecBase3 &v) { + (*this)(row, 0) = v[0]; + (*this)(row, 1) = v[1]; + (*this)(row, 2) = v[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::set_col +// Access: Public +// Description: Replaces the indicated column of the matrix with the +// indicated 3-component vector, ignoring the last +// row. +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix4:: +set_col(int col, const LVecBase3 &v) { + (*this)(0, col) = v[0]; + (*this)(1, col) = v[1]; + (*this)(2, col) = v[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::get_row +// Access: Public +// Description: Retrieves the indicated row of the matrix as a +// 4-component vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 LMatrix4:: +get_row(int row) const { + return LVecBase4((*this)(row, 0), + (*this)(row, 1), + (*this)(row, 2), + (*this)(row, 3)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::get_col +// Access: Public +// Description: Retrieves the indicated column of the matrix as a +// 4-component vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 LMatrix4:: +get_col(int col) const { + return LVecBase4((*this)(0, col), + (*this)(1, col), + (*this)(2, col), + (*this)(3, col)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::get_row3 +// Access: Public +// Description: Retrieves the row column of the matrix as a +// 3-component vector, ignoring the last column. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LMatrix4:: +get_row3(int row) const { + return LVecBase3((*this)(row, 0), + (*this)(row, 1), + (*this)(row, 2)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::get_col3 +// Access: Public +// Description: Retrieves the indicated column of the matrix as a +// 3-component vector, ignoring the last row. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LMatrix4:: +get_col3(int col) const { + return LVecBase3((*this)(0, col), + (*this)(1, col), + (*this)(2, col)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::Indexing operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType &LMatrix4:: +operator () (int row, int col) { + nassertr(row >= 0 && row < 4, _data[0]); + nassertr(col >= 0 && col < 4, _data[0]); + return _data[row * 4 + col]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::Indexing operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LMatrix4:: +operator () (int row, int col) const { + nassertr(row >= 0 && row < 4, 0.0); + nassertr(col >= 0 && col < 4, 0.0); + return _data[row * 4 + col]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::is_nan +// Access: Public +// Description: Returns true if any component of the matrix is +// not-a-number, false otherwise. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LMatrix4:: +is_nan() const { + return + cnan(_data[0]) || cnan(_data[1]) || cnan(_data[2]) || cnan(_data[3]) || + cnan(_data[4]) || cnan(_data[5]) || cnan(_data[6]) || cnan(_data[7]) || + cnan(_data[8]) || cnan(_data[9]) || cnan(_data[10]) || cnan(_data[11]) || + cnan(_data[12]) || cnan(_data[13]) || cnan(_data[14]) || cnan(_data[15]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::get_cell +// Access: Public +// Description: Returns a particular element of the matrix. +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LMatrix4:: +get_cell(int row, int col) const { + nassertr(row >= 0 && row < 4, 0.0); + nassertr(col >= 0 && col < 4, 0.0); + return _data[row * 4 + col]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::set_cell +// Access: Public +// Description: Changes a particular element of the matrix. +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix4:: +set_cell(int row, int col, NumType value) { + nassertv(row >= 0 && row < 4); + nassertv(col >= 0 && col < 4); + _data[row * 4 + col] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::get_data +// Access: Public +// Description: Returns the address of the first of the nine data +// elements in the matrix. The remaining elements +// occupy the next eight positions in row-major order. +//////////////////////////////////////////////////////////////////// +template +INLINE const NumType *LMatrix4:: +get_data() const { + return _data; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::get_num_components +// Access: Public +// Description: Returns the number of elements in the matrix, 16. +//////////////////////////////////////////////////////////////////// +template +INLINE int LMatrix4:: +get_num_components() const { + return 16; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::begin +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix4::iterator LMatrix4:: +begin() { + return _data; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::end +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix4::iterator LMatrix4:: +end() { + return begin() + 16; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::begin +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix4::const_iterator LMatrix4:: +begin() const { + return _data; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::end +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix4::const_iterator LMatrix4:: +end() const { + return begin() + 16; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::Equality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +bool LMatrix4:: +operator == (const LMatrix4 &other) const { + return ((*this)(0, 0) == other(0, 0) && + (*this)(0, 1) == other(0, 1) && + (*this)(0, 2) == other(0, 2) && + (*this)(0, 3) == other(0, 3) && + (*this)(1, 0) == other(1, 0) && + (*this)(1, 1) == other(1, 1) && + (*this)(1, 2) == other(1, 2) && + (*this)(1, 3) == other(1, 3) && + (*this)(2, 0) == other(2, 0) && + (*this)(2, 1) == other(2, 1) && + (*this)(2, 2) == other(2, 2) && + (*this)(2, 3) == other(2, 3) && + (*this)(3, 0) == other(3, 0) && + (*this)(3, 1) == other(3, 1) && + (*this)(3, 2) == other(3, 2) && + (*this)(3, 3) == other(3, 3)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool LMatrix4:: +operator != (const LMatrix4 &other) const { + return !operator == (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::compare_to +// Access: Public +// Description: This flavor of compare_to uses a default threshold +// value based on the numeric type. +//////////////////////////////////////////////////////////////////// +template +INLINE int LMatrix4:: +compare_to(const LMatrix4 &other) const { + return compare_to(other, NEARLY_ZERO(NumType)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::compare_to +// Access: Public +// Description: Sorts matrices lexicographically, componentwise. +// Returns a number less than 0 if this matrix sorts +// before the other one, greater than zero if it sorts +// after, 0 if they are equivalent (within the indicated +// tolerance). +//////////////////////////////////////////////////////////////////// +template +int LMatrix4:: +compare_to(const LMatrix4 &other, NumType threshold) const { + for (int i = 0; i < 16; i++) { + if (!IS_THRESHOLD_EQUAL(_data[i], other._data[i], threshold)) { + return (_data[i] < other._data[i]) ? -1 : 1; + } + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::xform +// Access: Public +// Description: 4-component vector or point times matrix. This is a +// fully general operation. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 LMatrix4:: +xform(const LVecBase4 &v) const { + return LVecBase4(v.dot(get_col(0)), + v.dot(get_col(1)), + v.dot(get_col(2)), + v.dot(get_col(3))); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::xform_point +// Access: Public +// Description: The matrix transforms a 3-component point (including +// translation component) and returns the result. This +// assumes the matrix is an affine transform. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LMatrix4:: +xform_point(const LVecBase3 &v) const { + return LVecBase3(v.dot(get_col3(0)) + (*this)(3, 0), + v.dot(get_col3(1)) + (*this)(3, 1), + v.dot(get_col3(2)) + (*this)(3, 2)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::xform_vec +// Access: Public +// Description: The matrix transforms a 3-component vector (without +// translation component) and returns the result. This +// assumes the matrix is an affine transform. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LMatrix4:: +xform_vec(const LVecBase3 &v) const { + return LVecBase3(v.dot(get_col3(0)), + v.dot(get_col3(1)), + v.dot(get_col3(2))); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::mult_cel +// Access: Private +// Description: Returns one cell of the result of a matrix-matrix +// multiplication operation. +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LMatrix4:: +mult_cel(const LMatrix4 &other, int row, int col) const { + return get_row(row).dot(other.get_col(col)); +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::matrix * matrix +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +LMatrix4 LMatrix4:: +operator * (const LMatrix4 &other) const { + LMatrix4 t; + + t(0, 0) = mult_cel(other, 0, 0); + t(0, 1) = mult_cel(other, 0, 1); + t(0, 2) = mult_cel(other, 0, 2); + t(0, 3) = mult_cel(other, 0, 3); + + t(1, 0) = mult_cel(other, 1, 0); + t(1, 1) = mult_cel(other, 1, 1); + t(1, 2) = mult_cel(other, 1, 2); + t(1, 3) = mult_cel(other, 1, 3); + + t(2, 0) = mult_cel(other, 2, 0); + t(2, 1) = mult_cel(other, 2, 1); + t(2, 2) = mult_cel(other, 2, 2); + t(2, 3) = mult_cel(other, 2, 3); + + t(3, 0) = mult_cel(other, 3, 0); + t(3, 1) = mult_cel(other, 3, 1); + t(3, 2) = mult_cel(other, 3, 2); + t(3, 3) = mult_cel(other, 3, 3); + + return t; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::matrix * scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +LMatrix4 LMatrix4:: +operator * (NumType scalar) const { + LMatrix4 t; + + t(0, 0) = (*this)(0, 0) * scalar; + t(0, 1) = (*this)(0, 1) * scalar; + t(0, 2) = (*this)(0, 2) * scalar; + t(0, 3) = (*this)(0, 3) * scalar; + + t(1, 0) = (*this)(1, 0) * scalar; + t(1, 1) = (*this)(1, 1) * scalar; + t(1, 2) = (*this)(1, 2) * scalar; + t(1, 3) = (*this)(1, 3) * scalar; + + t(2, 0) = (*this)(2, 0) * scalar; + t(2, 1) = (*this)(2, 1) * scalar; + t(2, 2) = (*this)(2, 2) * scalar; + t(2, 3) = (*this)(2, 3) * scalar; + + t(3, 0) = (*this)(3, 0) * scalar; + t(3, 1) = (*this)(3, 1) * scalar; + t(3, 2) = (*this)(3, 2) * scalar; + t(3, 3) = (*this)(3, 3) * scalar; + + return t; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::matrix / scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +LMatrix4 LMatrix4:: +operator / (NumType scalar) const { + LMatrix4 t; + + t(0, 0) = (*this)(0, 0) / scalar; + t(0, 1) = (*this)(0, 1) / scalar; + t(0, 2) = (*this)(0, 2) / scalar; + t(0, 3) = (*this)(0, 3) / scalar; + + t(1, 0) = (*this)(1, 0) / scalar; + t(1, 1) = (*this)(1, 1) / scalar; + t(1, 2) = (*this)(1, 2) / scalar; + t(1, 3) = (*this)(1, 3) / scalar; + + t(2, 0) = (*this)(2, 0) / scalar; + t(2, 1) = (*this)(2, 1) / scalar; + t(2, 2) = (*this)(2, 2) / scalar; + t(2, 3) = (*this)(2, 3) / scalar; + + t(3, 0) = (*this)(3, 0) / scalar; + t(3, 1) = (*this)(3, 1) / scalar; + t(3, 2) = (*this)(3, 2) / scalar; + t(3, 3) = (*this)(3, 3) / scalar; + + return t; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::matrix += matrix +// Access: Public +// Description: Performs a memberwise addition between two matrices. +//////////////////////////////////////////////////////////////////// +template +LMatrix4 &LMatrix4:: +operator += (const LMatrix4 &other) { + (*this)(0, 0) += other(0, 0); + (*this)(0, 1) += other(0, 1); + (*this)(0, 2) += other(0, 2); + (*this)(0, 3) += other(0, 3); + + (*this)(1, 0) += other(1, 0); + (*this)(1, 1) += other(1, 1); + (*this)(1, 2) += other(1, 2); + (*this)(1, 3) += other(1, 3); + + (*this)(2, 0) += other(2, 0); + (*this)(2, 1) += other(2, 1); + (*this)(2, 2) += other(2, 2); + (*this)(2, 3) += other(2, 3); + + (*this)(3, 0) += other(3, 0); + (*this)(3, 1) += other(3, 1); + (*this)(3, 2) += other(3, 2); + (*this)(3, 3) += other(3, 3); + + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::matrix -= matrix +// Access: Public +// Description: Performs a memberwise addition between two matrices. +//////////////////////////////////////////////////////////////////// +template +LMatrix4 &LMatrix4:: +operator -= (const LMatrix4 &other) { + (*this)(0, 0) -= other(0, 0); + (*this)(0, 1) -= other(0, 1); + (*this)(0, 2) -= other(0, 2); + (*this)(0, 3) -= other(0, 3); + + (*this)(1, 0) -= other(1, 0); + (*this)(1, 1) -= other(1, 1); + (*this)(1, 2) -= other(1, 2); + (*this)(1, 3) -= other(1, 3); + + (*this)(2, 0) -= other(2, 0); + (*this)(2, 1) -= other(2, 1); + (*this)(2, 2) -= other(2, 2); + (*this)(2, 3) -= other(2, 3); + + (*this)(3, 0) -= other(3, 0); + (*this)(3, 1) -= other(3, 1); + (*this)(3, 2) -= other(3, 2); + (*this)(3, 3) -= other(3, 3); + + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::matrix *= matrix +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix4 &LMatrix4:: +operator *= (const LMatrix4 &other) { + (*this) = (*this) * other; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::matrix *= scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +LMatrix4 &LMatrix4:: +operator *= (NumType scalar) { + (*this)(0, 0) *= scalar; + (*this)(0, 1) *= scalar; + (*this)(0, 2) *= scalar; + (*this)(0, 3) *= scalar; + + (*this)(1, 0) *= scalar; + (*this)(1, 1) *= scalar; + (*this)(1, 2) *= scalar; + (*this)(1, 3) *= scalar; + + (*this)(2, 0) *= scalar; + (*this)(2, 1) *= scalar; + (*this)(2, 2) *= scalar; + (*this)(2, 3) *= scalar; + + (*this)(3, 0) *= scalar; + (*this)(3, 1) *= scalar; + (*this)(3, 2) *= scalar; + (*this)(3, 3) *= scalar; + + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::matrix /= scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +LMatrix4 &LMatrix4:: +operator /= (NumType scalar) { + (*this)(0, 0) /= scalar; + (*this)(0, 1) /= scalar; + (*this)(0, 2) /= scalar; + (*this)(0, 3) /= scalar; + + (*this)(1, 0) /= scalar; + (*this)(1, 1) /= scalar; + (*this)(1, 2) /= scalar; + (*this)(1, 3) /= scalar; + + (*this)(2, 0) /= scalar; + (*this)(2, 1) /= scalar; + (*this)(2, 2) /= scalar; + (*this)(2, 3) /= scalar; + + (*this)(3, 0) /= scalar; + (*this)(3, 1) /= scalar; + (*this)(3, 2) /= scalar; + (*this)(3, 3) /= scalar; + + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::transpose_from +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void LMatrix4:: +transpose_from(const LMatrix4 &other) { + (*this)(0, 0) = other(0, 0); + (*this)(0, 1) = other(1, 0); + (*this)(0, 2) = other(2, 0); + (*this)(0, 3) = other(3, 0); + + (*this)(1, 0) = other(0, 1); + (*this)(1, 1) = other(1, 1); + (*this)(1, 2) = other(2, 1); + (*this)(1, 3) = other(3, 1); + + (*this)(2, 0) = other(0, 2); + (*this)(2, 1) = other(1, 2); + (*this)(2, 2) = other(2, 2); + (*this)(2, 3) = other(3, 2); + + (*this)(3, 0) = other(0, 3); + (*this)(3, 1) = other(1, 3); + (*this)(3, 2) = other(2, 3); + (*this)(3, 3) = other(3, 3); +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::transpose_in_place +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix4:: +transpose_in_place() { + LMatrix4 temp = (*this); + transpose_from(temp); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::invert_from +// Access: Public +// Description: Computes the inverse of the other matrix, and stores +// the result in this matrix. This is a fully general +// operation and makes no assumptions about the type of +// transform represented by the matrix. +// +// The other matrix must be a different object than this +// matrix. However, if you need to invert a matrix in +// place, see invert_in_place. +// +// The return value is true if the matrix was +// successfully inverted, false if the was a +// singularity. +//////////////////////////////////////////////////////////////////// +template +bool LMatrix4:: +invert_from(const LMatrix4 &other) { + if (IS_NEARLY_EQUAL(other(3, 0), 0.0) && + IS_NEARLY_EQUAL(other(3, 1), 0.0) && + IS_NEARLY_EQUAL(other(3, 2), 0.0) && + IS_NEARLY_EQUAL(other(3, 3), 1.0)) { + return invert_affine_from(other); + } + + (*this) = other; + + int index[4]; + + if (!decompose_mat(index)) { + nout << "Tried to invert singular LMatrix4.\n"; + return false; + } + + LMatrix4 inv = LMatrix4::ident_mat(); + int row; + + for (row = 0; row < 4; row++) { + back_sub_mat(index, inv, row); + } + + transpose_from(inv); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::invert_affine_from +// Access: Public +// Description: Performs an invert of the indicated matrix, storing +// the result in this matrix. The calculation is only +// correct of the other matrix represents an affine +// transform. +// +// The other matrix must be a different object than this +// matrix. However, if you need to invert a matrix in +// place, see invert_in_place. +// +// The return value is true if the matrix was +// successfully inverted, false if the was a +// singularity. +//////////////////////////////////////////////////////////////////// +template +bool LMatrix4:: +invert_affine_from(const LMatrix4 &other) { + LMatrix3 rot; + rot.invert_from(other.get_upper_3()); + + set_upper_3(rot); + set_col(3, LVecBase4(0.0, 0.0, 0.0, 1.0)); + + // compute -C*inv(A) + for (int i = 0; i < 3; i++) { + (*this)(3, i) = 0.0; + for (int j = 0; j < 3; j++) { + (*this)(3, i) -= other(3, j) * (*this)(j, i); + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::invert_in_place +// Access: Public +// Description: Inverts the current matrix. Returns true if the +// inverse is successful, false if the matrix was +// singular. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LMatrix4:: +invert_in_place() { + LMatrix4 temp = (*this); + return invert_from(temp); +} + + + + + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::ident_mat +// Access: Public, Static +// Description: Returns an identity matrix. +//////////////////////////////////////////////////////////////////// +template +const LMatrix4 &LMatrix4:: +ident_mat() { + static LMatrix4 mat(1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0); + return mat; +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::translate_mat +// Access: Public, Static +// Description: Returns a matrix that applies the indicated +// translation. +//////////////////////////////////////////////////////////////////// +template +LMatrix4 LMatrix4:: +translate_mat(const LVecBase3 &trans) { + return LMatrix4(1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + trans[0], trans[1], trans[2], 1.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::translate_mat +// Access: Public, Static +// Description: Returns a matrix that applies the indicated +// translation. +//////////////////////////////////////////////////////////////////// +template +LMatrix4 LMatrix4:: +translate_mat(NumType tx, NumType ty, NumType tz) { + return LMatrix4(1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + tx, ty, tz, 1.0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::rotate_mat +// Access: Public, Static +// Description: Returns a matrix that rotates by the given angle in +// degrees counterclockwise about the indicated vector. +//////////////////////////////////////////////////////////////////// +template +LMatrix4 LMatrix4:: +rotate_mat(NumType angle, LVecBase3 axis, + CoordinateSystem cs) { + if (cs == CS_default) { + cs = default_coordinate_system; + } + LMatrix4 mat; + + if (!is_right_handed(cs)) { + // In a left-handed coordinate system, counterclockwise is the + // other direction. + angle = -angle; + } + + // Normalize the axis. + NumType length = axis.dot(axis); + nassertr(length != 0.0, ident_mat()); + axis /= length; + + double s = sin(deg_2_rad(angle)); + double c = cos(deg_2_rad(angle)); + double t = 1.0 - c; + + mat(0, 0) = t * axis[0] * axis[0] + c; + mat(0, 1) = t * axis[0] * axis[1] + s * axis[2]; + mat(0, 2) = t * axis[0] * axis[2] - s * axis[1]; + mat(0, 3) = 0.0; + + mat(1, 0) = t * axis[1] * axis[0] - s * axis[2]; + mat(1, 1) = t * axis[1] * axis[1] + c; + mat(1, 2) = t * axis[1] * axis[2] + s * axis[0]; + mat(1, 3) = 0.0; + + mat(2, 0) = t * axis[2] * axis[0] + s * axis[1]; + mat(2, 1) = t * axis[2] * axis[1] - s * axis[0]; + mat(2, 2) = t * axis[2] * axis[2] + c; + mat(2, 3) = 0.0; + + mat(3, 0) = 0.0; + mat(3, 1) = 0.0; + mat(3, 2) = 0.0; + mat(3, 3) = 1.0; + + return mat; +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::scale_mat +// Access: Public, Static +// Description: Returns a matrix that applies the indicated +// scale in each of the three axes. +//////////////////////////////////////////////////////////////////// +template +LMatrix4 LMatrix4:: +scale_mat(const LVecBase3 &scale) { + return LMatrix4(scale[0], 0.0, 0.0, 0.0, + 0.0, scale[1], 0.0, 0.0, + 0.0, 0.0, scale[2], 0.0, + 0.0, 0.0, 0.0, 1.0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::scale_mat +// Access: Public, Static +// Description: Returns a matrix that applies the indicated +// scale in each of the three axes. +//////////////////////////////////////////////////////////////////// +template +LMatrix4 LMatrix4:: +scale_mat(NumType sx, NumType sy, NumType sz) { + return LMatrix4(sx, 0.0, 0.0, 0.0, + 0.0, sy, 0.0, 0.0, + 0.0, 0.0, sz, 0.0, + 0.0, 0.0, 0.0, 1.0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::scale_mat +// Access: Public, Static +// Description: Returns a matrix that applies the indicated +// uniform scale. +//////////////////////////////////////////////////////////////////// +template +LMatrix4 LMatrix4:: +scale_mat(NumType scale) { + return LMatrix4(scale, 0.0, 0.0, 0.0, + 0.0, scale, 0.0, 0.0, + 0.0, 0.0, scale, 0.0, + 0.0, 0.0, 0.0, 1.0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::y_to_z_up_mat +// Access: Public, Static +// Description: Returns a matrix that transforms from the Y-up +// coordinate system to the Z-up coordinate system. +//////////////////////////////////////////////////////////////////// +template +const LMatrix4 &LMatrix4:: +y_to_z_up_mat() { + static LMatrix4 mat(1.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0,-1.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 1.0); + return mat; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::z_to_y_up_mat +// Access: Public, Static +// Description: Returns a matrix that transforms from the Y-up +// coordinate system to the Z-up coordinate system. +//////////////////////////////////////////////////////////////////// +template +const LMatrix4 &LMatrix4:: +z_to_y_up_mat() { + static LMatrix4 mat(1.0, 0.0, 0.0, 0.0, + 0.0, 0.0,-1.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 1.0); + return mat; +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix::convert_mat +// Access: Public, Static +// Description: Returns a matrix that transforms from the indicated +// coordinate system to the indicated coordinate system. +//////////////////////////////////////////////////////////////////// +template +LMatrix4 LMatrix4:: +convert_mat(CoordinateSystem from, CoordinateSystem to) { + if (from == CS_default) { + from = default_coordinate_system; + } + if (to == CS_default) { + to = default_coordinate_system; + } + switch (from) { + case CS_zup_left: + switch (to) { + case CS_zup_left: return ident_mat(); + case CS_yup_left: return z_to_y_up_mat(); + case CS_zup_right: return scale_mat(1.0, -1.0, 1.0); + case CS_yup_right: return scale_mat(1.0, -1.0, 1.0) * z_to_y_up_mat(); + } + break; + + case CS_yup_left: + switch (to) { + case CS_zup_left: return y_to_z_up_mat(); + case CS_yup_left: return ident_mat(); + case CS_zup_right: return scale_mat(1.0, 1.0, -1.0) * y_to_z_up_mat(); + case CS_yup_right: return scale_mat(1.0, 1.0, -1.0); + } + break; + + case CS_zup_right: + switch (to) { + case CS_zup_left: return scale_mat(1.0, -1.0, 1.0); + case CS_yup_left: return scale_mat(1.0, -1.0, 1.0) * z_to_y_up_mat(); + case CS_zup_right: return ident_mat(); + case CS_yup_right: return z_to_y_up_mat(); + } + break; + + case CS_yup_right: + switch (to) { + case CS_zup_left: return scale_mat(1.0, 1.0, -1.0) * y_to_z_up_mat(); + case CS_yup_left: return scale_mat(1.0, 1.0, -1.0); + case CS_zup_right: return y_to_z_up_mat(); + case CS_yup_right: return ident_mat(); + } + break; + } + + linmath_cat.error() + << "Invalid coordinate system value!\n"; + return ident_mat(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::almost_equal +// Access: Public +// Description: Returns true if two matrices are memberwise equal +// within a specified tolerance. +//////////////////////////////////////////////////////////////////// +template +bool LMatrix4:: +almost_equal(const LMatrix4 &other, NumType threshold) const { + return (IS_THRESHOLD_EQUAL((*this)(0, 0), other(0, 0), threshold) && + IS_THRESHOLD_EQUAL((*this)(0, 1), other(0, 1), threshold) && + IS_THRESHOLD_EQUAL((*this)(0, 2), other(0, 2), threshold) && + IS_THRESHOLD_EQUAL((*this)(0, 3), other(0, 3), threshold) && + IS_THRESHOLD_EQUAL((*this)(1, 0), other(1, 0), threshold) && + IS_THRESHOLD_EQUAL((*this)(1, 1), other(1, 1), threshold) && + IS_THRESHOLD_EQUAL((*this)(1, 2), other(1, 2), threshold) && + IS_THRESHOLD_EQUAL((*this)(1, 3), other(1, 3), threshold) && + IS_THRESHOLD_EQUAL((*this)(2, 0), other(2, 0), threshold) && + IS_THRESHOLD_EQUAL((*this)(2, 1), other(2, 1), threshold) && + IS_THRESHOLD_EQUAL((*this)(2, 2), other(2, 2), threshold) && + IS_THRESHOLD_EQUAL((*this)(2, 3), other(2, 3), threshold) && + IS_THRESHOLD_EQUAL((*this)(3, 0), other(3, 0), threshold) && + IS_THRESHOLD_EQUAL((*this)(3, 1), other(3, 1), threshold) && + IS_THRESHOLD_EQUAL((*this)(3, 2), other(3, 2), threshold) && + IS_THRESHOLD_EQUAL((*this)(3, 3), other(3, 3), threshold)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::almost_equal +// Access: Public +// Description: Returns true if two matrices are memberwise equal +// within a default tolerance based on the numeric type. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LMatrix4:: +almost_equal(const LMatrix4 &other) const { + return almost_equal(other, NEARLY_ZERO(NumType)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix4:: +output(ostream &out) const { + out << "[ " + << MAYBE_ZERO((*this)(0, 0)) << " " + << MAYBE_ZERO((*this)(0, 1)) << " " + << MAYBE_ZERO((*this)(0, 2)) << " " + << MAYBE_ZERO((*this)(0, 3)) + << " ] [ " + << MAYBE_ZERO((*this)(1, 0)) << " " + << MAYBE_ZERO((*this)(1, 1)) << " " + << MAYBE_ZERO((*this)(1, 2)) << " " + << MAYBE_ZERO((*this)(1, 3)) + << " ] [ " + << MAYBE_ZERO((*this)(2, 0)) << " " + << MAYBE_ZERO((*this)(2, 1)) << " " + << MAYBE_ZERO((*this)(2, 2)) << " " + << MAYBE_ZERO((*this)(2, 3)) + << " ] [ " + << MAYBE_ZERO((*this)(3, 0)) << " " + << MAYBE_ZERO((*this)(3, 1)) << " " + << MAYBE_ZERO((*this)(3, 2)) << " " + << MAYBE_ZERO((*this)(3, 3)) + << " ]"; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LMatrix4:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) + << MAYBE_ZERO((*this)(0, 0)) << " " + << MAYBE_ZERO((*this)(0, 1)) << " " + << MAYBE_ZERO((*this)(0, 2)) << " " + << MAYBE_ZERO((*this)(0, 3)) + << "\n"; + indent(out, indent_level) + << MAYBE_ZERO((*this)(1, 0)) << " " + << MAYBE_ZERO((*this)(1, 1)) << " " + << MAYBE_ZERO((*this)(1, 2)) << " " + << MAYBE_ZERO((*this)(1, 3)) + << "\n"; + indent(out, indent_level) + << MAYBE_ZERO((*this)(2, 0)) << " " + << MAYBE_ZERO((*this)(2, 1)) << " " + << MAYBE_ZERO((*this)(2, 2)) << " " + << MAYBE_ZERO((*this)(2, 3)) + << "\n"; + indent(out, indent_level) + << MAYBE_ZERO((*this)(3, 0)) << " " + << MAYBE_ZERO((*this)(3, 1)) << " " + << MAYBE_ZERO((*this)(3, 2)) << " " + << MAYBE_ZERO((*this)(3, 3)) + << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::decompose_mat +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +template +bool LMatrix4:: +decompose_mat(int index[4]) { + int i, j, k; + NumType vv[4]; + for (i = 0; i < 4; i++) { + NumType big = 0.0; + for (j = 0; j < 4; j++) { + NumType temp = fabs((*this)(i,j)); + if (temp > big) { + big = temp; + } + } + + if (IS_NEARLY_ZERO(big)) { + return false; + } + vv[i] = 1.0 / big; + } + + for (j = 0; j < 4; j++) { + for (i = 0; i < j; i++) { + NumType sum = (*this)(i,j); + for (k = 0; k < i; k++) { + sum -= (*this)(i,k) * (*this)(k,j); + } + (*this)(i,j) = sum; + } + + NumType big = 0.0; + int imax = -1; + for (i = j; i < 4; i++) { + NumType sum = (*this)(i,j); + for (k = 0; k < j; k++) { + sum -= (*this)(i,k) * (*this)(k,j); + } + (*this)(i,j) = sum; + + NumType dum = vv[i] * fabs(sum); + if (dum >= big) { + big = dum; + imax = i; + } + } + nassertr(imax >= 0, false); + if (j != imax) { + for (k = 0; k < 4; k++) { + NumType dum = (*this)(imax,k); + (*this)(imax,k) = (*this)(j,k); + (*this)(j,k) = dum; + } + vv[imax] = vv[j]; + } + index[j] = imax; + + if ((*this)(j,j) == 0.0) { + (*this)(j,j) = NEARLY_ZERO(NumType); + } + + if (j != 4 - 1) { + NumType dum = 1.0 / (*this)(j,j); + for (i = j + 1; i < 4; i++) { + (*this)(i,j) *= dum; + } + } + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::back_sub_mat +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +template +bool LMatrix4:: +back_sub_mat(int index[4], LMatrix4 &inv, int row) const { + int ii = -1; + int i, j; + for (i = 0; i < 4; i++) { + int ip = index[i]; + NumType sum = inv(row, ip); + inv(row, ip) = inv(row, i); + if (ii >= 0) { + for (j = ii; j <= i - 1; j++) { + sum -= (*this)(i,j) * inv(row, j); + } + } else if (sum) { + ii = i; + } + + inv(row, i) = sum; + } + + for (i = 4 - 1; i >= 0; i--) { + NumType sum = inv(row, i); + for (j = i + 1; j < 4; j++) { + sum -= (*this)(i,j) * inv(row, j); + } + inv(row, i) = sum / (*this)(i,i); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +template +void LMatrix4:: +init_type() { + if (_type_handle == TypeHandle::none()) { + // Format a string to describe the type. + do_init_type(NumType); + string name = + "LMatrix4<" + get_type_handle(NumType).get_name() + ">"; + register_type(_type_handle, name); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::write_datagram +// Description: Writes the matrix to the datagram +//////////////////////////////////////////////////////////////////// +template +void LMatrix4:: +write_datagram(Datagram &destination) const +{ + for(int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + destination.add_float64(get_cell(i,j)); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::read_datagram +// Description: Reads itself out of the datagram +//////////////////////////////////////////////////////////////////// +template +void LMatrix4:: +read_datagram(DatagramIterator &scan) +{ + for(int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + set_cell(i, j, scan.get_float64()); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::transpose +// Description: Transposes the given matrix and returns it. +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix4 +transpose(const LMatrix4 &a) { + LMatrix4 result; + result.transpose_from(a); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::invert +// Description: Inverts the given matrix and returns it. +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix4 +invert(const LMatrix4 &a) { + LMatrix4 result; + bool nonsingular = result.invert_from(a); + nassertr(nonsingular, LMatrix4::ident_mat()); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: lcast_to +// Description: Converts a matrix from one numeric representation to +// another one. This is usually invoked using the macro +// LCAST. +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix4 +lcast_to(NumType2 *, const LMatrix4 &source) { + return LMatrix4 + (source(0, 0), source(0, 1), source(0, 2), source(0, 3), + source(1, 0), source(1, 1), source(1, 2), source(1, 3), + source(2, 0), source(2, 1), source(2, 2), source(2, 3), + source(3, 0), source(3, 1), source(3, 2), source(3, 3)); +} diff --git a/panda/src/linmath/lmatrix4.h b/panda/src/linmath/lmatrix4.h new file mode 100644 index 0000000000..b8c849e905 --- /dev/null +++ b/panda/src/linmath/lmatrix4.h @@ -0,0 +1,188 @@ +// Filename: lmatrix4.h +// Created by: drose (29Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LMATRIX4_H +#define LMATRIX4_H + +#include + +#include "coordinateSystem.h" +#include "lvecBase4.h" +#include "lvecBase3.h" +#include "lmatrix3.h" + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : LMatrix4 +// Description : This is a 4-by-4 transform matrix. +//////////////////////////////////////////////////////////////////// +template +class LMatrix4 { +public: + typedef const NumType *iterator; + typedef const NumType *const_iterator; + + INLINE LMatrix4(); + INLINE LMatrix4(const LMatrix4 &other); + LMatrix4 &operator = (const LMatrix4 &other); + INLINE LMatrix4 &operator = (NumType fill_value); + INLINE LMatrix4(NumType e00, NumType e01, NumType e02, NumType e03, + NumType e10, NumType e11, NumType e12, NumType e13, + NumType e20, NumType e21, NumType e22, NumType e23, + NumType e30, NumType e31, NumType e32, NumType e33); + + // Construct a 4x4 matrix given a 3x3 rotation matrix and an optional + // translation component. + LMatrix4(const LMatrix3 &upper3); + LMatrix4(const LMatrix3 &upper3, + const LVecBase3 &trans); + + void fill(NumType fill_value); + INLINE void set(NumType e00, NumType e01, NumType e02, NumType e03, + NumType e10, NumType e11, NumType e12, NumType e13, + NumType e20, NumType e21, NumType e22, NumType e23, + NumType e30, NumType e31, NumType e32, NumType e33); + + // Get and set the upper 3x3 rotation matrix. + INLINE void set_upper_3(const LMatrix3 &upper3); + INLINE LMatrix3 get_upper_3() const; + + INLINE void set_row(int row, const LVecBase4 &v); + INLINE void set_col(int col, const LVecBase4 &v); + + INLINE void set_row(int row, const LVecBase3 &v); + INLINE void set_col(int col, const LVecBase3 &v); + + INLINE LVecBase4 get_row(int row) const; + INLINE LVecBase4 get_col(int col) const; + + INLINE LVecBase3 get_row3(int row) const; + INLINE LVecBase3 get_col3(int col) const; + + INLINE NumType &operator () (int row, int col); + INLINE NumType operator () (int row, int col) const; + + INLINE bool is_nan() const; + + INLINE NumType get_cell(int row, int col) const; + INLINE void set_cell(int row, int col, NumType value); + + INLINE const NumType *get_data() const; + INLINE int get_num_components() const; + + INLINE iterator begin(); + INLINE iterator end(); + + INLINE const_iterator begin() const; + INLINE const_iterator end() const; + + + bool operator == (const LMatrix4 &other) const; + INLINE bool operator != (const LMatrix4 &other) const; + + INLINE int compare_to(const LMatrix4 &other) const; + int compare_to(const LMatrix4 &other, NumType threshold) const; + + INLINE LVecBase4 + xform(const LVecBase4 &v) const; + + INLINE LVecBase3 + xform_point(const LVecBase3 &v) const; + + INLINE LVecBase3 + xform_vec(const LVecBase3 &v) const; + + LMatrix4 operator * (const LMatrix4 &other) const; + LMatrix4 operator * (NumType scalar) const; + LMatrix4 operator / (NumType scalar) const; + + LMatrix4 &operator += (const LMatrix4 &other); + LMatrix4 &operator -= (const LMatrix4 &other); + + INLINE LMatrix4 &operator *= (const LMatrix4 &other); + + LMatrix4 &operator *= (NumType scalar); + LMatrix4 &operator /= (NumType scalar); + + void transpose_from(const LMatrix4 &other); + INLINE void transpose_in_place(); + + bool invert_from(const LMatrix4 &other); + bool invert_affine_from(const LMatrix4 &other); + INLINE bool invert_in_place(); + + static const LMatrix4 &ident_mat(); + static LMatrix4 translate_mat(const LVecBase3 &trans); + static LMatrix4 translate_mat(NumType tx, NumType ty, NumType tz); + static LMatrix4 rotate_mat(NumType angle, + LVecBase3 axis, + CoordinateSystem cs = CS_default); + static LMatrix4 scale_mat(const LVecBase3 &scale); + static LMatrix4 scale_mat(NumType sx, NumType sy, NumType sz); + static LMatrix4 scale_mat(NumType scale); + + static const LMatrix4 &y_to_z_up_mat(); + static const LMatrix4 &z_to_y_up_mat(); + + static LMatrix4 convert_mat(CoordinateSystem from, + CoordinateSystem to); + + + bool almost_equal(const LMatrix4 &other, + NumType threshold) const; + INLINE bool almost_equal(const LMatrix4 &other) const; + + INLINE void output(ostream &out) const; + INLINE void write(ostream &out, int indent_level = 0) const; + +private: + INLINE NumType mult_cel(const LMatrix4 &other, int x, int y) const; + bool decompose_mat(int index[4]); + bool back_sub_mat(int index[4], LMatrix4 &inv, int row) const; + + NumType _data[4 * 4]; + + //Functionality for reading and writing from/to a binary source +public: + void write_datagram(Datagram& destination) const; + void read_datagram(DatagramIterator& scan); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +template +INLINE ostream &operator << (ostream &out, const LMatrix4 &mat) { + mat.output(out); + return out; +} + +template +INLINE LMatrix4 transpose(const LMatrix4 &a); + +template +INLINE LMatrix4 invert(const LMatrix4 &a); + +// Cast to a different numeric type +template +INLINE LMatrix4 +lcast_to(NumType2 *type, const LMatrix4 &source); + + +#include "lmatrix4.I" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LMatrix4) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LMatrix4) + +#endif diff --git a/panda/src/linmath/lorientation.I b/panda/src/linmath/lorientation.I new file mode 100644 index 0000000000..d4454a6228 --- /dev/null +++ b/panda/src/linmath/lorientation.I @@ -0,0 +1,134 @@ +// Filename: lorientation.I +// Created by: frang, charles (23Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "lorientation.h" +#include + +template +TypeHandle LOrientation::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LOrientation::Default Constructor +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LOrientation:: +LOrientation() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LOrientation::Copy Constructor +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LOrientation:: +LOrientation(const LQuaternionBase& c) : + LQuaternionBase(c) { +} + +//////////////////////////////////////////////////////////////////// +// Function: LOrientation::Constructor +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LOrientation:: +LOrientation(NumType r, NumType i, NumType j, NumType k) : + LQuaternionBase(r, i, j, k) { +} + +//////////////////////////////////////////////////////////////////// +// Function: LOrientation::Constructor +// Access: public +// Description: vector + twist +//////////////////////////////////////////////////////////////////// +template +INLINE LOrientation:: +LOrientation(const LVector3 &point_at, float twist) { + float radians = twist * ((float) MathNumbers::pi / 180.0f); + float theta_over_2 = radians / 2.0f; + float sin_to2 = sinf(theta_over_2); + + set_r(cosf(theta_over_2)); + set_i(point_at[0] * sin_to2); + set_j(point_at[1] * sin_to2); + set_k(point_at[2] * sin_to2); +} + +//////////////////////////////////////////////////////////////////// +// Function: LOrientation::Constructor +// Access: public +// Description: matrix3 +//////////////////////////////////////////////////////////////////// +template +INLINE LOrientation:: +LOrientation(const LMatrix3 &m) { + set(m); +} + +//////////////////////////////////////////////////////////////////// +// Function: LOrientation::Constructor +// Access: public +// Description: matrix4 +//////////////////////////////////////////////////////////////////// +template +INLINE LOrientation:: +LOrientation(const LMatrix4 &m) { + set(m); +} + +//////////////////////////////////////////////////////////////////// +// Function: LOrientation::Destructor +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +LOrientation:: +~LOrientation() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LOrientation::operator * +// Access: public +// Description: Orientation * rotation = Orientation +// Applies an rotation to an orientation. +//////////////////////////////////////////////////////////////////// +template +INLINE LOrientation LOrientation:: +operator *(const LQuaternionBase& other) const { + return multiply(other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LOrientation::operator * +// Access: public +// Description: Orientation * Orientation +// This is a meaningless operation, and will always +// simply return the rhs. +//////////////////////////////////////////////////////////////////// +template +INLINE LOrientation LOrientation:: +operator *(const LOrientation& other) const { + return other; +} + +//////////////////////////////////////////////////////////////////// +// Function: LOrientation::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +template +void LOrientation:: +init_type() { + if (_type_handle == TypeHandle::none()) { + // Format a string to describe the type. + do_init_type(NumType); + string name = + "LOrientation<" + get_type_handle(NumType).get_name() + ">"; + register_type(_type_handle, name); + } +} diff --git a/panda/src/linmath/lorientation.h b/panda/src/linmath/lorientation.h new file mode 100644 index 0000000000..20a77490b0 --- /dev/null +++ b/panda/src/linmath/lorientation.h @@ -0,0 +1,47 @@ +// Filename: lorientation.h +// Created by: frang, charles (23Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __LORIENTATION_H__ +#define __LORIENTATION_H__ + +#include +#include "lquaternion.h" + +//////////////////////////////////////////////////////////////////////// +// Class : LOrientation +// Description : This is a unit quaternion representing an orientation. +//////////////////////////////////////////////////////////////////////// +template +class LOrientation : public LQuaternionBase { +public: + INLINE LOrientation(); + INLINE LOrientation(const LQuaternionBase&); + INLINE LOrientation(NumType, NumType, NumType, NumType); + INLINE LOrientation(const LVector3 &, float); + INLINE LOrientation(const LMatrix3 &); + INLINE LOrientation(const LMatrix4 &); + virtual ~LOrientation(); + + INLINE LOrientation + operator *(const LQuaternionBase& other) const; + + INLINE LOrientation + operator *(const LOrientation& other) const; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); +private: + static TypeHandle _type_handle; +}; + +#include "lorientation.I" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LOrientation) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LOrientation) + +#endif /* __LORIENTATION_H__ */ diff --git a/panda/src/linmath/lpoint2.I b/panda/src/linmath/lpoint2.I new file mode 100644 index 0000000000..695472aea5 --- /dev/null +++ b/panda/src/linmath/lpoint2.I @@ -0,0 +1,225 @@ +// Filename: lpoint2.I +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +template +TypeHandle LPoint2::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint2:: +LPoint2() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint2:: +LPoint2(const LVecBase2 ©) : LVecBase2(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint2 &LPoint2:: +operator = (const LVecBase2 ©) { + LVecBase2::operator = (copy); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::Copy Fill Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint2 &LPoint2:: +operator = (NumType fill_value) { + LVecBase2::operator = (fill_value); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint2:: +LPoint2(NumType fill_value) : + LVecBase2(fill_value) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint2:: +LPoint2(NumType x, NumType y) : + LVecBase2(x, y) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::zero Named Constructor +// Access: Public +// Description: Returns a zero point. +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint2 LPoint2:: +zero() { + return LPoint2(0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::unit_x Named Constructor +// Access: Public +// Description: Returns a unit X point. +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint2 LPoint2:: +unit_x() { + return LPoint2(1.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::unit_y Named Constructor +// Access: Public +// Description: Returns a unit Y point. +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint2 LPoint2:: +unit_y() { + return LPoint2(0.0, 1.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::unary - +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint2 LPoint2:: +operator - () const { + return LVecBase2::operator - (); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::point + vecbase +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 LPoint2:: +operator + (const LVecBase2 &other) const { + return LVecBase2::operator + (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::point + vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint2 LPoint2:: +operator + (const LVector2 &other) const { + return LVecBase2::operator + (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::point - vecbase +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 LPoint2:: +operator - (const LVecBase2 &other) const { + return LVecBase2::operator - (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::point - point +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2 LPoint2:: +operator - (const LPoint2 &other) const { + return LVecBase2::operator - (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::point - vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint2 LPoint2:: +operator - (const LVector2 &other) const { + return LVecBase2::operator - (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::operator * scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint2 LPoint2:: +operator * (NumType scalar) const { + return LPoint2(LVecBase2::operator * (scalar)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::operator / scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint2 LPoint2:: +operator / (NumType scalar) const { + return LPoint2(LVecBase2::operator / (scalar)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint2::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +template +void LPoint2:: +init_type() { + if (_type_handle == TypeHandle::none()) { + LVecBase2::init_type(); + string name = + "LPoint2<" + get_type_handle(NumType).get_name() + ">"; + register_type(_type_handle, name, + LVecBase2::get_class_type()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: lcast_to +// Description: Converts a vector from one numeric representation to +// another one. This is usually invoked using the macro +// LCAST. +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint2 +lcast_to(NumType2 *, const LPoint2 &source) { + return LPoint2(source[0], source[1]); +} diff --git a/panda/src/linmath/lpoint2.h b/panda/src/linmath/lpoint2.h new file mode 100644 index 0000000000..190745fbfa --- /dev/null +++ b/panda/src/linmath/lpoint2.h @@ -0,0 +1,69 @@ +// Filename: lpoint2.h +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LPOINT2_H +#define LPOINT2_H + +#include + +#include "lvecBase2.h" +#include "lvector2.h" + +//////////////////////////////////////////////////////////////////// +// Class : LPoint2 +// Description : This is a two-component point in space. +//////////////////////////////////////////////////////////////////// +template +class LPoint2 : public LVecBase2 { +public: + INLINE LPoint2(); + INLINE LPoint2(const LVecBase2 ©); + INLINE LPoint2 &operator = (const LVecBase2 ©); + INLINE LPoint2 &operator = (NumType fill_value); + INLINE LPoint2(NumType fill_value); + INLINE LPoint2(NumType x, NumType y); + + INLINE static LPoint2 zero(); + INLINE static LPoint2 unit_x(); + INLINE static LPoint2 unit_y(); + + INLINE LPoint2 operator - () const; + + INLINE LVecBase2 + operator + (const LVecBase2 &other) const; + INLINE LPoint2 + operator + (const LVector2 &other) const; + + INLINE LVecBase2 + operator - (const LVecBase2 &other) const; + INLINE LVector2 + operator - (const LPoint2 &other) const; + INLINE LPoint2 + operator - (const LVector2 &other) const; + + INLINE LPoint2 operator * (NumType scalar) const; + INLINE LPoint2 operator / (NumType scalar) const; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +// Cast to a different numeric type +template +INLINE LPoint2 +lcast_to(NumType2 *type, const LPoint2 &source); + +#include "lpoint2.I" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LPoint2) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LPoint2) + +#endif diff --git a/panda/src/linmath/lpoint3.I b/panda/src/linmath/lpoint3.I new file mode 100644 index 0000000000..14bd5d1326 --- /dev/null +++ b/panda/src/linmath/lpoint3.I @@ -0,0 +1,278 @@ +// Filename: lpoint3.I +// Created by: drose (25Sep99) +// +//////////////////////////////////////////////////////////////////// + +#include "lvector3.h" + +template +TypeHandle LPoint3::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3:: +LPoint3() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3:: +LPoint3(const LVecBase3 ©) : LVecBase3(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3 &LPoint3:: +operator = (const LVecBase3 ©) { + LVecBase3::operator = (copy); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::Copy Fill Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3 &LPoint3:: +operator = (NumType fill_value) { + LVecBase3::operator = (fill_value); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3:: +LPoint3(NumType fill_value) : + LVecBase3(fill_value) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3:: +LPoint3(NumType x, NumType y, NumType z) : + LVecBase3(x, y, z) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::zero Named Constructor +// Access: Public +// Description: Returns a zero point. +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3 LPoint3:: +zero() { + return LPoint3(0.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::unit_x Named Constructor +// Access: Public +// Description: Returns a unit X point. +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3 LPoint3:: +unit_x() { + return LPoint3(1.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::unit_y Named Constructor +// Access: Public +// Description: Returns a unit Y point. +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3 LPoint3:: +unit_y() { + return LPoint3(0.0, 1.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::unit_z Named Constructor +// Access: Public +// Description: Returns a unit Z point. +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3 LPoint3:: +unit_z() { + return LPoint3(0.0, 0.0, 1.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::unary - +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3 LPoint3:: +operator - () const { + return LVecBase3::operator - (); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::point + vecbase +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LPoint3:: +operator + (const LVecBase3 &other) const { + return LVecBase3::operator + (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::point + vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3 LPoint3:: +operator + (const LVector3 &other) const { + return LVecBase3::operator + (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::point - vecbase +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LPoint3:: +operator - (const LVecBase3 &other) const { + return LVecBase3::operator - (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::point - point +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 LPoint3:: +operator - (const LPoint3 &other) const { + return LVecBase3::operator - (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::point - vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3 LPoint3:: +operator - (const LVector3 &other) const { + return LVecBase3::operator - (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::cross +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3 LPoint3:: +cross(const LVecBase3 &other) const { + return LVecBase3::cross(other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::operator * scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3 LPoint3:: +operator * (NumType scalar) const { + return LPoint3(LVecBase3::operator * (scalar)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::operator / scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3 LPoint3:: +operator / (NumType scalar) const { + return LPoint3(LVecBase3::operator / (scalar)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::origin +// Access: Public, Static +// Description: Returns the origin of the indicated coordinate +// system. This is always 0, 0, 0 with all of our +// existing coordinate systems; it's hard to imagine it +// ever being different. +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3 LPoint3:: +origin(CoordinateSystem) { + return LPoint3(0.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::rfu +// Access: Public, Static +// Description: Returns a point described by right, forward, up +// displacements from the origin, wherever that maps to +// in the given coordinate system. +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3 LPoint3:: +rfu(NumType right_v, NumType fwd_v, NumType up_v, + CoordinateSystem cs) { + return origin(cs) + + LVector3::rfu(right_v, fwd_v, up_v, cs); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint3::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +template +void LPoint3:: +init_type() { + if (_type_handle == TypeHandle::none()) { + LVecBase3::init_type(); + string name = + "LPoint3<" + get_type_handle(NumType).get_name() + ">"; + register_type(_type_handle, name, + LVecBase3::get_class_type()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: lcast_to +// Description: Converts a vector from one numeric representation to +// another one. This is usually invoked using the macro +// LCAST. +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3 +lcast_to(NumType2 *, const LPoint3 &source) { + return LPoint3(source[0], source[1], source[2]); +} diff --git a/panda/src/linmath/lpoint3.h b/panda/src/linmath/lpoint3.h new file mode 100644 index 0000000000..6f3d20829d --- /dev/null +++ b/panda/src/linmath/lpoint3.h @@ -0,0 +1,85 @@ +// Filename: lpoint3.h +// Created by: drose (25Sep99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LPOINT3_H +#define LPOINT3_H + +#include + +#include "coordinateSystem.h" +#include "lvecBase3.h" +#include "lvector3.h" + +//////////////////////////////////////////////////////////////////// +// Class : LPoint3 +// Description : This is a three-component point in space (as opposed +// to a three-component vector, which represents a +// direction and a distance). Some of the methods are +// slightly different between LPoint3 and LVector3; in +// particular, subtraction of two points yields a +// vector, while addition of a vector and a point yields +// a point. +//////////////////////////////////////////////////////////////////// +template +class LPoint3 : public LVecBase3 { +public: + INLINE LPoint3(); + INLINE LPoint3(const LVecBase3 ©); + INLINE LPoint3 &operator = (const LVecBase3 ©); + INLINE LPoint3 &operator = (NumType fill_value); + INLINE LPoint3(NumType fill_value); + INLINE LPoint3(NumType x, NumType y, NumType z); + + INLINE static LPoint3 zero(); + INLINE static LPoint3 unit_x(); + INLINE static LPoint3 unit_y(); + INLINE static LPoint3 unit_z(); + + INLINE LPoint3 operator - () const; + + INLINE LVecBase3 + operator + (const LVecBase3 &other) const; + INLINE LPoint3 + operator + (const LVector3 &other) const; + + INLINE LVecBase3 + operator - (const LVecBase3 &other) const; + INLINE LVector3 + operator - (const LPoint3 &other) const; + INLINE LPoint3 + operator - (const LVector3 &other) const; + + INLINE LPoint3 cross(const LVecBase3 &other) const; + INLINE LPoint3 operator * (NumType scalar) const; + INLINE LPoint3 operator / (NumType scalar) const; + + // Some special named constructors for LPoint3. + + INLINE static LPoint3 origin(CoordinateSystem cs = CS_default); + INLINE static LPoint3 rfu(NumType right, + NumType fwd, + NumType up, + CoordinateSystem cs = CS_default); +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +// Cast to a different numeric type +template +INLINE LPoint3 +lcast_to(NumType2 *type, const LPoint3 &source); + +#include "lpoint3.I" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LPoint3) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LPoint3) + +#endif diff --git a/panda/src/linmath/lpoint4.I b/panda/src/linmath/lpoint4.I new file mode 100644 index 0000000000..c1dd818c71 --- /dev/null +++ b/panda/src/linmath/lpoint4.I @@ -0,0 +1,247 @@ +// Filename: lpoint4.I +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +template +TypeHandle LPoint4::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4:: +LPoint4() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4:: +LPoint4(const LVecBase4 ©) : LVecBase4(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4 &LPoint4:: +operator = (const LVecBase4 ©) { + LVecBase4::operator = (copy); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::Copy Fill Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4 &LPoint4:: +operator = (NumType fill_value) { + LVecBase4::operator = (fill_value); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4:: +LPoint4(NumType fill_value) : + LVecBase4(fill_value) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4:: +LPoint4(NumType x, NumType y, NumType z, NumType w) : + LVecBase4(x, y, z, w) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::zero Named Constructor +// Access: Public +// Description: Returns a zero point. +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4 LPoint4:: +zero() { + return LPoint4(0.0, 0.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::unit_x Named Constructor +// Access: Public +// Description: Returns a unit X point. +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4 LPoint4:: +unit_x() { + return LPoint4(1.0, 0.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::unit_y Named Constructor +// Access: Public +// Description: Returns a unit Y point. +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4 LPoint4:: +unit_y() { + return LPoint4(0.0, 1.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::unit_z Named Constructor +// Access: Public +// Description: Returns a unit Z point. +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4 LPoint4:: +unit_z() { + return LPoint4(0.0, 0.0, 1.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::unit_w Named Constructor +// Access: Public +// Description: Returns a unit W point. +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4 LPoint4:: +unit_w() { + return LPoint4(0.0, 0.0, 0.0, 1.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::unary - +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4 LPoint4:: +operator - () const { + return LVecBase4::operator - (); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::point + vecbase +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 LPoint4:: +operator + (const LVecBase4 &other) const { + return LVecBase4::operator + (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::point + vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4 LPoint4:: +operator + (const LVector4 &other) const { + return LVecBase4::operator + (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::point - vecbase +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 LPoint4:: +operator - (const LVecBase4 &other) const { + return LVecBase4::operator - (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::point - point +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4 LPoint4:: +operator - (const LPoint4 &other) const { + return LVecBase4::operator - (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::point - vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4 LPoint4:: +operator - (const LVector4 &other) const { + return LVecBase4::operator - (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::operator * scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4 LPoint4:: +operator * (NumType scalar) const { + return LPoint4(LVecBase4::operator * (scalar)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::operator / scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4 LPoint4:: +operator / (NumType scalar) const { + return LPoint4(LVecBase4::operator / (scalar)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LPoint4::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +template +void LPoint4:: +init_type() { + if (_type_handle == TypeHandle::none()) { + LVecBase4::init_type(); + string name = + "LPoint4<" + get_type_handle(NumType).get_name() + ">"; + register_type(_type_handle, name, + LVecBase4::get_class_type()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: lcast_to +// Description: Converts a vector from one numeric representation to +// another one. This is usually invoked using the macro +// LCAST. +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4 +lcast_to(NumType2 *, const LPoint4 &source) { + return LPoint4(source[0], source[1], source[2], source[3]); +} diff --git a/panda/src/linmath/lpoint4.h b/panda/src/linmath/lpoint4.h new file mode 100644 index 0000000000..5d299e3c75 --- /dev/null +++ b/panda/src/linmath/lpoint4.h @@ -0,0 +1,71 @@ +// Filename: lpoint4.h +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LPOINT4_H +#define LPOINT4_H + +#include + +#include "lvecBase4.h" +#include "lvector4.h" + +//////////////////////////////////////////////////////////////////// +// Class : LPoint4 +// Description : This is a four-component point in space. +//////////////////////////////////////////////////////////////////// +template +class LPoint4 : public LVecBase4 { +public: + INLINE LPoint4(); + INLINE LPoint4(const LVecBase4 ©); + INLINE LPoint4 &operator = (const LVecBase4 ©); + INLINE LPoint4 &operator = (NumType fill_value); + INLINE LPoint4(NumType fill_value); + INLINE LPoint4(NumType x, NumType y, NumType z, NumType w); + + INLINE static LPoint4 zero(); + INLINE static LPoint4 unit_x(); + INLINE static LPoint4 unit_y(); + INLINE static LPoint4 unit_z(); + INLINE static LPoint4 unit_w(); + + INLINE LPoint4 operator - () const; + + INLINE LVecBase4 + operator + (const LVecBase4 &other) const; + INLINE LPoint4 + operator + (const LVector4 &other) const; + + INLINE LVecBase4 + operator - (const LVecBase4 &other) const; + INLINE LVector4 + operator - (const LPoint4 &other) const; + INLINE LPoint4 + operator - (const LVector4 &other) const; + + INLINE LPoint4 operator * (NumType scalar) const; + INLINE LPoint4 operator / (NumType scalar) const; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +// Cast to a different numeric type +template +INLINE LPoint4 +lcast_to(NumType2 *type, const LPoint4 &source); + +#include "lpoint4.I" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LPoint4) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LPoint4) + +#endif diff --git a/panda/src/linmath/lquaternion.I b/panda/src/linmath/lquaternion.I new file mode 100644 index 0000000000..c083f3eb2b --- /dev/null +++ b/panda/src/linmath/lquaternion.I @@ -0,0 +1,550 @@ +// Filename: lquaternion.I +// Created by: frang (06Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "lquaternion.h" +#include "nearly_zero.h" +#include + +template +TypeHandle LQuaternionBase::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::Default Constructor +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LQuaternionBase:: +LQuaternionBase(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::Copy Constructor +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LQuaternionBase:: +LQuaternionBase(const LQuaternionBase& c) : + _r(c._r), _i(c._i), _j(c._j), _k(c._k) { +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::Constructor +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LQuaternionBase:: +LQuaternionBase(NumType r, NumType i, NumType j, NumType k) { + set(r, i, j, k); +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::Destructor +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +LQuaternionBase:: +~LQuaternionBase() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::pure_imaginary_quat +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +LQuaternionBase LQuaternionBase:: +pure_imaginary(const LVector3 &v) { + return LQuaternionBase(0, v[0], v[1], v[2]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::ident_quat +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +const LQuaternionBase &LQuaternionBase:: +ident_quat(void) { + static LQuaternionBase q(1, 0, 0, 0); + return q; +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::set +// Access: public +// Description: assignment +//////////////////////////////////////////////////////////////////// +template +INLINE void LQuaternionBase:: +set(NumType r, NumType i, NumType j, NumType k) { + _r = r; + _i = i; + _j = j; + _k = k; +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::Assignment Operator +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LQuaternionBase& LQuaternionBase:: +operator =(const LQuaternionBase& c) { + _r = c._r; + _i = c._i; + _j = c._j; + _k = c._k; + + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::Equality Operator +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool LQuaternionBase:: +operator ==(const LQuaternionBase& c) const { + return (_r == c._r && + _i == c._i && + _j == c._j && + _k == c._k); +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::Inequality Operator +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool LQuaternionBase:: +operator !=(const LQuaternionBase& c) const { + return !operator==(c); +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::multiply +// Access: protected +// Description: actual multiply call (non virtual) +//////////////////////////////////////////////////////////////////// +template +INLINE LQuaternionBase LQuaternionBase:: +multiply(const LQuaternionBase& rhs) const { + NumType r = (_r * rhs._r) - (_i * rhs._i) - (_j * rhs._j) - (_k * rhs._k); + NumType i = (_i * rhs._r) + (_r * rhs._i) - (_k * rhs._j) + (_j * rhs._k); + NumType j = (_j * rhs._r) + (_k * rhs._i) + (_r * rhs._j) - (_i * rhs._k); + NumType k = (_k * rhs._r) - (_j * rhs._i) + (_i * rhs._j) + (_r * rhs._k); + + return LQuaternionBase(r, i , j, k); +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::Multiply Operator +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LQuaternionBase LQuaternionBase:: +operator *(const LQuaternionBase& c) { + return multiply(c); +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::Multiply Assignment Operator +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LQuaternionBase& LQuaternionBase:: +operator *=(const LQuaternionBase& c) { + (*this) = operator*(c); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::Multiply Operator +// Access: public +// Description: Quat * Matrix = matrix +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix3 LQuaternionBase:: +operator *(const LMatrix3 &m) { + LMatrix3 result; + extract_to_matrix(result); + return result * m; +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::Multiply Operator +// Access: public +// Description: Quat * Matrix = matrix +//////////////////////////////////////////////////////////////////// +template +INLINE LMatrix4 LQuaternionBase:: +operator *(const LMatrix4 &m) { + LMatrix3 m_upper_3 = m.get_upper_3(); + LMatrix3 this_quat; + extract_to_matrix(this_quat); + + LMatrix4 result; + result.set_upper_3(this_quat * m_upper_3); + result.set_row(3, m.get_row(3)); + result.set_col(3, m.get_col(3)); + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::almost_equal +// Access: public +// Description: Returns true if two quaternions are memberwise equal +// within a specified tolerance. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LQuaternionBase:: +almost_equal(const LQuaternionBase& c, NumType threshold) const { + return (IS_THRESHOLD_EQUAL(_r, c._r, threshold) && + IS_THRESHOLD_EQUAL(_i, c._i, threshold) && + IS_THRESHOLD_EQUAL(_j, c._j, threshold) && + IS_THRESHOLD_EQUAL(_k, c._k, threshold)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::almost_equal +// Access: public +// Description: Returns true if two quaternions are memberwise equal +// within a default tolerance based on the numeric type. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LQuaternionBase:: +almost_equal(const LQuaternionBase& c) const { + return almost_equal(c, NEARLY_ZERO(NumType)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::output +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LQuaternionBase:: +output(ostream& os) const { + os << MAYBE_ZERO(_r) << " + " + << MAYBE_ZERO(_i) << "i + " + << MAYBE_ZERO(_j) << "j + " + << MAYBE_ZERO(_k) << "k"; +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::get_r +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LQuaternionBase:: +get_r(void) const { + return _r; +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::get_i +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LQuaternionBase:: +get_i(void) const { + return _i; +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::get_j +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LQuaternionBase:: +get_j(void) const { + return _j; +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::get_k +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LQuaternionBase:: +get_k(void) const { + return _k; +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::set_r +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LQuaternionBase:: +set_r(NumType r) { + _r = r; +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::set_i +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LQuaternionBase:: +set_i(NumType i) { + _i = i; +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::set_j +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LQuaternionBase:: +set_j(NumType j) { + _j = j; +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::set_k +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LQuaternionBase:: +set_k(NumType k) { + _k = k; +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::normalize +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LQuaternionBase:: +normalize(void) { + NumType l = csqrt((_r*_r)+(_i*_i)+(_j*_j)+(_k*_k)); + + if (IS_NEARLY_ZERO(l)) { + _r = 0.; + _i = 0.; + _j = 0.; + _k = 0.; + return; + } + + l = 1. / l; + _r *= l; + _i *= l; + _j *= l; + _k *= l; +} + +//////////////////////////////////////////////////////////////////// +// Function: set +// Access: public +// Description: Do-While Jones. +//////////////////////////////////////////////////////////////////// +template +INLINE void LQuaternionBase:: +set(const LMatrix3 &m) { + NumType m00 = m.get_cell(0, 0); + NumType m11 = m.get_cell(1, 1); + NumType m22 = m.get_cell(2, 2); + + if (m00 >= 0.0 && ((m11 + m22) >= 0.0)) { + _r = 1.0 + m00 + m11 + m22; + _i = m.get_cell(2, 1) - m.get_cell(1, 2); + _j = m.get_cell(0, 2) - m.get_cell(2, 0); + _k = m.get_cell(1, 0) - m.get_cell(0, 1); + } + else if (m00 >= 0.0 && ((m11 + m22) == 0.0)) { + _r = m.get_cell(2, 1) - m.get_cell(1, 2); + _i = 1.0 + m00 - m11 - m22; + _j = m.get_cell(1, 0) + m.get_cell(0, 1); + _k = m.get_cell(0, 2) + m.get_cell(2, 0); + } + else if (m00 == 0.0 && ((m11 - m22) >= 0.0)) { + _r = m.get_cell(0, 2) - m.get_cell(2, 0); + _i = m.get_cell(1, 0) + m.get_cell(0, 1); + _j = 1.0 - m00 + m11 - m22; + _k = m.get_cell(2, 1) + m.get_cell(1, 2); + } + else { + _r = m.get_cell(1, 0) - m.get_cell(0, 1); + _i = m.get_cell(0, 2) + m.get_cell(2, 0); + _j = m.get_cell(2, 1) + m.get_cell(1, 2); + _k = 1.0 - m00 - m11 + m22; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: set +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LQuaternionBase:: +set(const LMatrix4 &m) { + set(m.get_upper_3()); +} + +//////////////////////////////////////////////////////////////////// +// Function: extract (LMatrix3) +// Access: public +// Description: Do-While Jones paper from cary. +//////////////////////////////////////////////////////////////////// +template +INLINE void LQuaternionBase:: +extract_to_matrix(LMatrix3 &m) const { + NumType tx, ty, tz, tq, tk, tk_denom; + + tx = _i * _i; + ty = _j * _j; + tz = _k * _k; + tq = ty + tz; + + tk_denom = tq + tx + (_r * _r); + if (tk_denom == 0.0) + tk = 0.0; + else + tk = 2.0 / tk_denom; + + m.set_cell(0, 0, 1.0 - (tk * tq)); + m.set_cell(1, 1, 1.0 - (tk * (tx + tz))); + m.set_cell(2, 2, 1.0 - (tk * (tx + ty))); + tx = tk * _i; + ty = tk * _j; + tq = (tk * _k) * _r; + tk = tx * _j; + m.set_cell(0, 1, tk - tq); + m.set_cell(1, 0, tk + tq); + tq = ty * _r; + tk = tx * _k; + m.set_cell(0, 2, tk + tq); + m.set_cell(2, 0, tk - tq); + tq = tx * _r; + tk = ty * _k; + m.set_cell(1, 2, tk - tq); + m.set_cell(2, 1, tk + tq); +} + +//////////////////////////////////////////////////////////////////// +// Function: extract (LMatrix4) +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LQuaternionBase:: +extract_to_matrix(LMatrix4 &m) const { + NumType tx, ty, tz, tq, tk, tk_denom; + + tx = _i * _i; + ty = _j * _j; + tz = _k * _k; + tq = ty + tz; + + tk_denom = tq + tx + (_r * _r); + if (tk_denom == 0.0) + tk = 0.0; + else + tk = 2.0 / tk_denom; + + m.set_cell(0, 0, 1.0 - (tk * tq)); + m.set_cell(1, 1, 1.0 - (tk * (tx + tz))); + m.set_cell(2, 2, 1.0 - (tk * (tx + ty))); + tx = tk * _i; + ty = tk * _j; + tq = (tk * _k) * _r; + tk = tx * _j; + m.set_cell(0, 1, tk - tq); + m.set_cell(1, 0, tk + tq); + tq = ty * _r; + tk = tx * _k; + m.set_cell(0, 2, tk + tq); + m.set_cell(2, 0, tk - tq); + tq = tx * _r; + tk = ty * _k; + m.set_cell(1, 2, tk - tq); + m.set_cell(2, 1, tk + tq); +} + +//////////////////////////////////////////////////////////////////// +// Function: operator *(Matrix3, Quat) +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +LMatrix3 operator *(const LMatrix3 &m, + const LQuaternionBase &q) { + LMatrix3 q_matrix; + q.extract_to_matrix(q_matrix); + + return m * q_matrix; +} + +//////////////////////////////////////////////////////////////////// +// Function: operator *(Matrix4, Quat) +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +LMatrix4 operator *(const LMatrix4 &m, + const LQuaternionBase &q) { + LMatrix4 q_matrix; + q.extract_to_matrix(q_matrix); + + // preserve the homogeneous coords and the translate + LVector4 m_row3 = m.get_row(3); + LVector4 m_col3 = m.get_col(3); + + q_matrix = m * q_matrix; + q_matrix.set_row(3, m_row3); + q_matrix.set_col(3, m_col3); + + return q_matrix; +} + +//////////////////////////////////////////////////////////////////// +// Function: LQuaternionBase::init_type +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +void LQuaternionBase:: +init_type(void) { + if (_type_handle == TypeHandle::none()) { + do_init_type(NumType); + string name = "LQuaternionBase<" + get_type_handle(NumType).get_name() + + ">"; + register_type(_type_handle, name); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: lcast_to +// Description: Converts a quaternion from one numeric representation +// to another one. This is usually invoked using the +// macro LCAST. +//////////////////////////////////////////////////////////////////// +template +INLINE LQuaternionBase +lcast_to(NumType2 *, const LQuaternionBase& c) { + return LQuaternionBase(c.get_r(), c.get_i(), c.get_j(), c.get_k()); +} diff --git a/panda/src/linmath/lquaternion.h b/panda/src/linmath/lquaternion.h new file mode 100644 index 0000000000..635b92d6ec --- /dev/null +++ b/panda/src/linmath/lquaternion.h @@ -0,0 +1,113 @@ +// Filename: lquaternion.h +// Created by: frang (06Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __LQUATERNION_H__ +#define __LQUATERNION_H__ + +#include "lmatrix.h" + +//////////////////////////////////////////////////////////////////// +// Class : LQuaternionBase +// Description : This is the base quaternion class +//////////////////////////////////////////////////////////////////// +template +class LQuaternionBase { +protected: + INLINE LQuaternionBase + multiply(const LQuaternionBase&) const; + +public: + INLINE LQuaternionBase(void); + INLINE LQuaternionBase(const LQuaternionBase &); + INLINE LQuaternionBase(NumType, NumType, NumType, NumType); + virtual ~LQuaternionBase(void); + + static LQuaternionBase pure_imaginary(const LVector3 &); + + INLINE LQuaternionBase& operator =(const LQuaternionBase &); + INLINE bool operator ==(const LQuaternionBase &) const; + INLINE bool operator !=(const LQuaternionBase &) const; + + INLINE LQuaternionBase operator *(const LQuaternionBase &); + INLINE LQuaternionBase& operator *=(const LQuaternionBase &); + + INLINE LMatrix3 operator *(const LMatrix3 &); + INLINE LMatrix4 operator *(const LMatrix4 &); + + INLINE bool almost_equal(const LQuaternionBase &, NumType) const; + INLINE bool almost_equal(const LQuaternionBase &) const; + + INLINE void output(ostream&) const; + + INLINE void set(NumType, NumType, NumType, NumType); + + INLINE void set(const LMatrix3 &); + INLINE void set(const LMatrix4 &); + + INLINE void extract_to_matrix(LMatrix3 &) const; + INLINE void extract_to_matrix(LMatrix4 &) const; + + INLINE NumType get_r(void) const; + INLINE NumType get_i(void) const; + INLINE NumType get_j(void) const; + INLINE NumType get_k(void) const; + + INLINE void set_r(NumType r); + INLINE void set_i(NumType i); + INLINE void set_j(NumType j); + INLINE void set_k(NumType k); + + INLINE void normalize(void); + + static const LQuaternionBase &ident_quat(void); + +private: + NumType _r, _i, _j, _k; +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void); +private: + static TypeHandle _type_handle; +}; + +template +INLINE ostream& operator<<(ostream& os, const LQuaternionBase& q) { + q.output(os); + return os; +} + +// matrix times quat +template +INLINE LMatrix3 +operator * (const LMatrix3 &m, const LQuaternionBase &q); + +template +INLINE LMatrix4 +operator * (const LMatrix4 &m, const LQuaternionBase &q); + +// pacify interrogate. +#ifdef CPPPARSER +INLINE LMatrix3 +operator * (const LMatrix3 &m, const LQuaternionBase &q); +INLINE LMatrix4 +operator * (const LMatrix4 &m, const LQuaternionBase &q); +INLINE LMatrix3 +operator * (const LMatrix3 &m, const LQuaternionBase &q); +INLINE LMatrix4 +operator * (const LMatrix4 &m, const LQuaternionBase &q); +#endif + +// Cast to a different numeric type +template +INLINE LQuaternionBase lcast_to(NumType2*, const LQuaternionBase&); + +#include "lquaternion.I" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LQuaternionBase) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LQuaternionBase) + +#endif /* __LQUATERNION_H__ */ diff --git a/panda/src/linmath/lrotation.I b/panda/src/linmath/lrotation.I new file mode 100644 index 0000000000..c7bfccc5da --- /dev/null +++ b/panda/src/linmath/lrotation.I @@ -0,0 +1,152 @@ +// Filename: lrotation.I +// Created by: frang, charles (23Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "lrotation.h" +#include +#include + +template +TypeHandle LRotation::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LRotation::Default Constructor +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LRotation:: +LRotation() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LRotation::Copy Constructor +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LRotation:: +LRotation(const LQuaternionBase& c) : + LQuaternionBase(c) { +} + +//////////////////////////////////////////////////////////////////// +// Function: LRotation::Constructor +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LRotation:: +LRotation(NumType r, NumType i, NumType j, NumType k) : + LQuaternionBase(r, i, j, k) { +} + +//////////////////////////////////////////////////////////////////// +// Function: LRotation::Constructor +// Access: public +// Description: lmatrix3 +//////////////////////////////////////////////////////////////////// +template +INLINE LRotation:: +LRotation(const LMatrix3 &m) { + set(m); +} + +//////////////////////////////////////////////////////////////////// +// Function: LRotation::Constructor +// Access: public +// Description: lmatrix4 +//////////////////////////////////////////////////////////////////// +template +INLINE LRotation:: +LRotation(const LMatrix4 &m) { + set(m); +} + +//////////////////////////////////////////////////////////////////// +// Function: LRotation::Constructor +// Access: public +// Description: axis + angle (in degrees) +//////////////////////////////////////////////////////////////////// +template +INLINE LRotation:: +LRotation(const LVector3 &axis, float angle) { + float radians = angle * ((float) MathNumbers::pi / 180.0f); + float theta_over_2 = radians / 2.0f; + float sin_to2 = sinf(theta_over_2); + + set_r(cosf(theta_over_2)); + set_i(axis[0] * sin_to2); + set_j(axis[1] * sin_to2); + set_k(axis[2] * sin_to2); +} + +//////////////////////////////////////////////////////////////////// +// Function: LRotation::Constructor +// Access: public +// Description: hpr (Real-time Rendering, p.49) +//////////////////////////////////////////////////////////////////// +template +INLINE LRotation:: +LRotation(float h, float p, float r) { + LQuaternionBase quat_h, quat_p, quat_r; + + quat_h.set(cosf(h), 0, sinf(h), 0); + quat_p.set(cosf(p), sinf(p), 0, 0); + quat_r.set(cosf(r), 0, 0, sinf(r)); + + (*this) = quat_h * quat_p * quat_r; +} + +//////////////////////////////////////////////////////////////////// +// Function: LRotation::Destructor +// Access: public +// Description: +//////////////////////////////////////////////////////////////////// +template +LRotation:: +~LRotation() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LRotation::operator * +// Access: public +// Description: Rotation * Rotation = Rotation +//////////////////////////////////////////////////////////////////// +template +INLINE LRotation LRotation:: +operator*(const LRotation& other) const { + return multiply(other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LRotation::operator * +// Access: public +// Description: Rotation * Orientation = Orientation +// This is another meaningless operation, attempting +// to apply an orientation to a rotation. It simply +// returns the rhs. +//////////////////////////////////////////////////////////////////// +template +INLINE LQuaternionBase LRotation:: +operator*(const LQuaternionBase& other) const { + return other; +} + +//////////////////////////////////////////////////////////////////// +// Function: LRotation::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +template +void LRotation:: +init_type() { + if (_type_handle == TypeHandle::none()) { + // Format a string to describe the type. + do_init_type(NumType); + string name = + "LRotation<" + get_type_handle(NumType).get_name() + ">"; + register_type(_type_handle, name); + } +} diff --git a/panda/src/linmath/lrotation.h b/panda/src/linmath/lrotation.h new file mode 100644 index 0000000000..def0fb3df6 --- /dev/null +++ b/panda/src/linmath/lrotation.h @@ -0,0 +1,48 @@ +// Filename: lrotation.h +// Created by: frang, charles (07Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __LROTATION_H__ +#define __LROTATION_H__ + +#include +#include "lquaternion.h" + +//////////////////////////////////////////////////////////////////////// +// Class : LRotation +// Description : This is a unit quaternion representing a rotation. +//////////////////////////////////////////////////////////////////////// +template +class LRotation : public LQuaternionBase { +public: + INLINE LRotation(); + INLINE LRotation(const LQuaternionBase&); + INLINE LRotation(NumType, NumType, NumType, NumType); + INLINE LRotation(const LVector3 &, float); + INLINE LRotation(const LMatrix3 &); + INLINE LRotation(const LMatrix4 &); + INLINE LRotation(float, float, float); + virtual ~LRotation(); + + INLINE LRotation + operator*(const LRotation& other) const; + + INLINE LQuaternionBase + operator*(const LQuaternionBase& other) const; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); +private: + static TypeHandle _type_handle; +}; + +#include "lrotation.I" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LRotation) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LRotation) + +#endif /* __LROTATION_H__ */ diff --git a/panda/src/linmath/luse.I b/panda/src/linmath/luse.I new file mode 100644 index 0000000000..13fd2a7957 --- /dev/null +++ b/panda/src/linmath/luse.I @@ -0,0 +1,4 @@ +// Filename: luse.I +// Created by: drose (13Jan99) +// +//////////////////////////////////////////////////////////////////// diff --git a/panda/src/linmath/luse.N b/panda/src/linmath/luse.N new file mode 100644 index 0000000000..5d55dd814e --- /dev/null +++ b/panda/src/linmath/luse.N @@ -0,0 +1,75 @@ +forcetype LVecBase2 +forcetype LVecBase3 +forcetype LVecBase4 + +forcetype LPoint2 +forcetype LPoint3 +forcetype LPoint4 +forcetype LVector2 +forcetype LVector3 +forcetype LVector4 + +forcetype LMatrix3 +forcetype LMatrix4 + +forcetype LPoint2f +forcetype LPoint3f +forcetype LPoint4f + +forcetype LVector2f +forcetype LVector3f +forcetype LVector4f + +forcetype Vertexf +forcetype Normalf +forcetype TexCoordf +forcetype Colorf +forcetype RGBColorf + +forcetype LMatrix3f +forcetype LMatrix4f + +forcetype LVecBase2 +forcetype LVecBase3 +forcetype LVecBase4 + +forcetype LPoint2 +forcetype LPoint3 +forcetype LPoint4 +forcetype LVector2 +forcetype LVector3 +forcetype LVector4 + +forcetype LMatrix3 +forcetype LMatrix4 + +forcetype LPoint2d +forcetype LPoint3d +forcetype LPoint4d + +forcetype LVector2d +forcetype LVector3d +forcetype LVector4d + +forcetype Vertexd +forcetype Normald +forcetype TexCoordd +forcetype Colord +forcetype RGBColord + +forcetype LMatrix3d +forcetype LMatrix4d + +forcetype LQuaternionBase +forcetype LQuaternionBase +forcetype LRotation +forcetype LRotation +forcetype LOrientation +forcetype LOrientation + +forcetype LQuaternionf +forcetype LRotationf +forcetype LOrientationf +forcetype LQuaterniond +forcetype LRotationd +forcetype LOrientationd diff --git a/panda/src/linmath/luse.cxx b/panda/src/linmath/luse.cxx new file mode 100644 index 0000000000..c33fcdf794 --- /dev/null +++ b/panda/src/linmath/luse.cxx @@ -0,0 +1,14 @@ +// Filename: luse.cxx +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "luse.h" + +// This tells GCC to explicitly instantiate the templates defined in +// luse.h and leave them here. +#ifdef __GNUC__ +#pragma implementation +#endif + + diff --git a/panda/src/linmath/luse.h b/panda/src/linmath/luse.h new file mode 100644 index 0000000000..a9b34ed1bc --- /dev/null +++ b/panda/src/linmath/luse.h @@ -0,0 +1,142 @@ +// Filename: luse.h +// Created by: drose (13Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LUSE_H +#define LUSE_H + +//////////////////////////////////////////////////////////////////// +// +// This file defines a number of vector-based classes that are +// designed for specific uses. These all inherit from LVecBase, which +// is the base class of all linear algebra vectors. +// +// LPoint +// +// This should be used to represent a specific point in space. It +// inherits most properties from LVecBase. +// +// LVector +// +// This should be used to represent a vector, or a distance between +// two points in space. +// +// The distinction between LPoint and LVector is worth emphasizing. +// They differ in some subtle typing behavior (vector - vector = +// vector, point + vector = point, point - point = vector) and also in +// the way they are transformed when multiplied by a matrix (a point +// gets the translation component of the matrix, while the vector does +// not). Also, vector has length() and normalize() functions defined +// for it, while point does not. +// +// LPoint and LVector should be used whenever the concept of "point" +// or "vector" applies. If neither applies--for instance, if you are +// storing a plane equation in a vector or some such nonsense--use the +// base class, LVecBase. +// +// This file also defines the following: +// +// Vertex, Vertexd, Vertexf +// Normal, Normald, Normalf +// TexCoord, TexCoordd, TexCoordf +// Color, Colord, Colorf +// RGBColor, RGBColord, RGBColorf +// +// These classes are derivations of LPoint or LVector, as appropriate, +// and are intended to store a specific kind of rendering attribute. +// (Color is a four-component color; RGBColor is three-component.) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "lvec2_ops.h" +#include "lvec3_ops.h" +#include "lvec4_ops.h" +#include "lmat_ops.h" +#include "lmatrix.h" +#include "lquaternion.h" +#include "lrotation.h" +#include "lorientation.h" + +// This macro defines the cast-to-another-numeric-type operator for +// all of the things defined in this package. It works by virtue of +// there being an appropriate lcast_to() template function defined for +// each class. + +#define LCAST(numeric_type, object) lcast_to((numeric_type *)0, object) + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + + +// Now we define some handy typedefs for these classes. + +typedef LVecBase2 LVecBase2f; +typedef LVecBase3 LVecBase3f; +typedef LVecBase4 LVecBase4f; + +typedef LVector2 LVector2f; +typedef LVector3 LVector3f; +typedef LVector4 LVector4f; +typedef LPoint2 LPoint2f; +typedef LPoint3 LPoint3f; +typedef LPoint4 LPoint4f; + +typedef LPoint3f Vertexf; +typedef LVector3f Normalf; +typedef LPoint2f TexCoordf; +typedef LVecBase4f Colorf; +typedef LVecBase3f RGBColorf; + +typedef LVecBase2 LVecBase2d; +typedef LVecBase3 LVecBase3d; +typedef LVecBase4 LVecBase4d; + +typedef LVector2 LVector2d; +typedef LVector3 LVector3d; +typedef LVector4 LVector4d; +typedef LPoint2 LPoint2d; +typedef LPoint3 LPoint3d; +typedef LPoint4 LPoint4d; + +typedef LPoint3d Vertexd; +typedef LVector3d Normald; +typedef LPoint2d TexCoordd; +typedef LVecBase4d Colord; +typedef LVecBase3d RGBColord; + +typedef LQuaternionBase LQuaternionf; +typedef LRotation LRotationf; +typedef LOrientation LOrientationf; + +typedef LQuaternionBase LQuaterniond; +typedef LRotation LRotationd; +typedef LOrientation LOrientationd; + +// Now define explicit instantiations of the output operator functions +// for interrogate's benefit. These functions don't actually exist +// anywhere, but interrogate can't know how to instantiate template +// functions, so we pretend these are real functions. +#ifdef CPPPARSER +INLINE ostream &operator << (ostream &out, const LVecBase2f &vec); +INLINE ostream &operator << (ostream &out, const LVecBase3f &vec); +INLINE ostream &operator << (ostream &out, const LVecBase4f &vec); +INLINE ostream &operator << (ostream &out, const LMatrix3f &mat); +INLINE ostream &operator << (ostream &out, const LMatrix4f &mat); +INLINE ostream &operator << (ostream &out, const LQuaternionf &q); + +INLINE ostream &operator << (ostream &out, const LVecBase2d &vec); +INLINE ostream &operator << (ostream &out, const LVecBase3d &vec); +INLINE ostream &operator << (ostream &out, const LVecBase4d &vec); +INLINE ostream &operator << (ostream &out, const LMatrix3d &mat); +INLINE ostream &operator << (ostream &out, const LMatrix4d &mat); +INLINE ostream &operator << (ostream &out, const LQuaterniond &q); +#endif + + +#endif + diff --git a/panda/src/linmath/lvec2_ops.I b/panda/src/linmath/lvec2_ops.I new file mode 100644 index 0000000000..d63fd182c0 --- /dev/null +++ b/panda/src/linmath/lvec2_ops.I @@ -0,0 +1,91 @@ +// Filename: lvec2_ops.I +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "nearly_zero.h" + +//////////////////////////////////////////////////////////////////// +// Function: scalar * LVecBase2 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 +operator * (NumType2 scalar, const LVecBase2 &a) { + return a * scalar; +} + +//////////////////////////////////////////////////////////////////// +// Function: scalar * LPoint2 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint2 +operator * (NumType2 scalar, const LPoint2 &a) { + return a * scalar; +} + +//////////////////////////////////////////////////////////////////// +// Function: scalar * LVector2 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2 +operator * (NumType2 scalar, const LVector2 &a) { + return a * scalar; +} + +//////////////////////////////////////////////////////////////////// +// Function: dot product of LVecBase2 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType +dot(const LVecBase2 &a, const LVecBase2 &b) { + return a.dot(b); +} + +//////////////////////////////////////////////////////////////////// +// Function: cross product of LVecBase2 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 +cross(const LVecBase2 &a, const LVecBase2 &b) { + return a.cross(b); +} + +//////////////////////////////////////////////////////////////////// +// Function: cross product of LVector2 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2 +cross(const LVector2 &a, const LVector2 &b) { + return LVector2(a.cross(b)); +} + +//////////////////////////////////////////////////////////////////// +// Function: length of a vector +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType +length(const LVector2 &a) { + return a.length(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: normalize +// Description: Returns a normalized vector from the given vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2 +normalize(const LVector2 &v) { + LVector2 v1 = v; + v1.normalize(); + return v1; +} diff --git a/panda/src/linmath/lvec2_ops.h b/panda/src/linmath/lvec2_ops.h new file mode 100644 index 0000000000..07ada43cd4 --- /dev/null +++ b/panda/src/linmath/lvec2_ops.h @@ -0,0 +1,54 @@ +// Filename: lvec2_ops.h +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LVEC2_OPS_H +#define LVEC2_OPS_H + +#include "lvecBase2.h" +#include "lpoint2.h" +#include "lvector2.h" + +// When possible, operators have been defined within the classes. +// This file defines operator functions outside of classes where +// necessary. It also defines some convenient out-of-class wrappers +// around in-class functions (like dot, length, normalize). + + +// scalar * vec (vec * scalar is defined in class) +template +INLINE LVecBase2 +operator * (NumType2 scalar, const LVecBase2 &a); + +template +INLINE LPoint2 +operator * (NumType2 scalar, const LPoint2 &a); + +template +INLINE LVector2 +operator * (NumType2 scalar, const LVector2 &a); + + +// dot product +template +INLINE NumType +dot(const LVecBase2 &a, const LVecBase2 &b); + + +// Length of a vector. +template +INLINE NumType +length(const LVector2 &a); + + +// A normalized vector. +template +INLINE LVector2 +normalize(const LVector2 &v); + + + +#include "lvec2_ops.I" + +#endif diff --git a/panda/src/linmath/lvec3_ops.I b/panda/src/linmath/lvec3_ops.I new file mode 100644 index 0000000000..1e0d1ef116 --- /dev/null +++ b/panda/src/linmath/lvec3_ops.I @@ -0,0 +1,92 @@ +// Filename: lvec3_ops.I +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "nearly_zero.h" + +//////////////////////////////////////////////////////////////////// +// Function: scalar * LVecBase3 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 +operator * (NumType2 scalar, const LVecBase3 &a) { + return a * scalar; +} + +//////////////////////////////////////////////////////////////////// +// Function: scalar * LPoint3 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint3 +operator * (NumType2 scalar, const LPoint3 &a) { + return a * scalar; +} + +//////////////////////////////////////////////////////////////////// +// Function: scalar * LVector3 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 +operator * (NumType2 scalar, const LVector3 &a) { + return a * scalar; +} + +//////////////////////////////////////////////////////////////////// +// Function: dot product of LVecBase3 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType +dot(const LVecBase3 &a, const LVecBase3 &b) { + return a.dot(b); +} + +//////////////////////////////////////////////////////////////////// +// Function: cross product of LVecBase3 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 +cross(const LVecBase3 &a, const LVecBase3 &b) { + return a.cross(b); +} + +//////////////////////////////////////////////////////////////////// +// Function: cross product of LVector3 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 +cross(const LVector3 &a, const LVector3 &b) { + return LVector3(a.cross(b)); +} + + +//////////////////////////////////////////////////////////////////// +// Function: length of a vector +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType +length(const LVector3 &a) { + return a.length(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: normalize +// Description: Returns a normalized vector from the given vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 +normalize(const LVector3 &v) { + LVector3 v1 = v; + v1.normalize(); + return v1; +} diff --git a/panda/src/linmath/lvec3_ops.h b/panda/src/linmath/lvec3_ops.h new file mode 100644 index 0000000000..3330e380d7 --- /dev/null +++ b/panda/src/linmath/lvec3_ops.h @@ -0,0 +1,64 @@ +// Filename: lvec3_ops.h +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LVEC3_OPS_H +#define LVEC3_OPS_H + +#include "lvecBase3.h" +#include "lpoint3.h" +#include "lvector3.h" + +// When possible, operators have been defined within the classes. +// This file defines operator functions outside of classes where +// necessary. It also defines some convenient out-of-class wrappers +// around in-class functions (like dot, length, normalize). + + +// scalar * vec (vec * scalar is defined in class) +template +INLINE LVecBase3 +operator * (NumType2 scalar, const LVecBase3 &a); + +template +INLINE LPoint3 +operator * (NumType2 scalar, const LPoint3 &a); + +template +INLINE LVector3 +operator * (NumType2 scalar, const LVector3 &a); + + +// dot product +template +INLINE NumType +dot(const LVecBase3 &a, const LVecBase3 &b); + + +// cross product +template +INLINE LVecBase3 +cross(const LVecBase3 &a, const LVecBase3 &b); + +template +INLINE LVector3 +cross(const LVector3 &a, const LVector3 &b); + + +// Length of a vector. +template +INLINE NumType +length(const LVector3 &a); + + +// A normalized vector. +template +INLINE LVector3 +normalize(const LVector3 &v); + + + +#include "lvec3_ops.I" + +#endif diff --git a/panda/src/linmath/lvec4_ops.I b/panda/src/linmath/lvec4_ops.I new file mode 100644 index 0000000000..9ffc7ee191 --- /dev/null +++ b/panda/src/linmath/lvec4_ops.I @@ -0,0 +1,71 @@ +// Filename: lvec4_ops.I +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "nearly_zero.h" + +//////////////////////////////////////////////////////////////////// +// Function: scalar * LVecBase4 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 +operator * (NumType2 scalar, const LVecBase4 &a) { + return a * scalar; +} + +//////////////////////////////////////////////////////////////////// +// Function: scalar * LPoint4 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LPoint4 +operator * (NumType2 scalar, const LPoint4 &a) { + return a * scalar; +} + +//////////////////////////////////////////////////////////////////// +// Function: scalar * LVector4 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4 +operator * (NumType2 scalar, const LVector4 &a) { + return a * scalar; +} + +//////////////////////////////////////////////////////////////////// +// Function: dot product of LVecBase4 +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType +dot(const LVecBase4 &a, const LVecBase4 &b) { + return a.dot(b); +} + +//////////////////////////////////////////////////////////////////// +// Function: length of a vector +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType +length(const LVector4 &a) { + return a.length(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: normalize +// Description: Returns a normalized vector from the given vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4 +normalize(const LVector4 &v) { + LVector4 v1 = v; + v1.normalize(); + return v1; +} diff --git a/panda/src/linmath/lvec4_ops.h b/panda/src/linmath/lvec4_ops.h new file mode 100644 index 0000000000..cd46d676c8 --- /dev/null +++ b/panda/src/linmath/lvec4_ops.h @@ -0,0 +1,54 @@ +// Filename: lvec4_ops.h +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LVEC4_OPS_H +#define LVEC4_OPS_H + +#include "lvecBase4.h" +#include "lpoint4.h" +#include "lvector4.h" + +// When possible, operators have been defined within the classes. +// This file defines operator functions outside of classes where +// necessary. It also defines some convenient out-of-class wrappers +// around in-class functions (like dot, length, normalize). + + +// scalar * vec (vec * scalar is defined in class) +template +INLINE LVecBase4 +operator * (NumType2 scalar, const LVecBase4 &a); + +template +INLINE LPoint4 +operator * (NumType2 scalar, const LPoint4 &a); + +template +INLINE LVector4 +operator * (NumType2 scalar, const LVector4 &a); + + +// dot product +template +INLINE NumType +dot(const LVecBase4 &a, const LVecBase4 &b); + + +// Length of a vector. +template +INLINE NumType +length(const LVector4 &a); + + +// A normalized vector. +template +INLINE LVector4 +normalize(const LVector4 &v); + + + +#include "lvec4_ops.I" + +#endif diff --git a/panda/src/linmath/lvecBase2.I b/panda/src/linmath/lvecBase2.I new file mode 100644 index 0000000000..11b4a777ba --- /dev/null +++ b/panda/src/linmath/lvecBase2.I @@ -0,0 +1,604 @@ +// Filename: lvecBase2.I +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nearly_zero.h" + +#include +#include +#include + +template +TypeHandle LVecBase2::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2:: +LVecBase2() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2:: +LVecBase2(const LVecBase2 ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 &LVecBase2:: +operator = (const LVecBase2 ©) { + set(copy[0], copy[1]); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::Fill Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 &LVecBase2:: +operator = (NumType fill_value) { + fill(fill_value); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2:: +LVecBase2(NumType fill_value) { + fill(fill_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2:: +LVecBase2(NumType x, NumType y) { + set(x, y); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::zero Named Constructor +// Access: Public +// Description: Returns a zero-length vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 LVecBase2:: +zero() { + return LVecBase2(0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::unit_x Named Constructor +// Access: Public +// Description: Returns a unit X vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 LVecBase2:: +unit_x() { + return LVecBase2(1.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::unit_y Named Constructor +// Access: Public +// Description: Returns a unit Y vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 LVecBase2:: +unit_y() { + return LVecBase2(0.0, 1.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2:: +~LVecBase2() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::Indexing Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase2:: +operator [](int i) const { + nassertr(i >= 0 && i < 2, 0); + return _data[i]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::Indexing Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType &LVecBase2:: +operator [](int i) { + nassertr(i >= 0 && i < 2, _data[0]); + return _data[i]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::is_nan +// Access: Public +// Description: Returns true if any component of the vector is +// not-a-number, false otherwise. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase2:: +is_nan() const { + return cnan(_data[0]) || cnan(_data[1]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::get_cell +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase2:: +get_cell(int i) const { + nassertr(i >= 0 && i < 2, 0); + return _data[i]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::get_x +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase2:: +get_x() const { + return _data[0]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::get_y +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase2:: +get_y() const { + return _data[1]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::set_cell +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase2:: +set_cell(int i, NumType value) { + nassertv(i >= 0 && i < 2); + _data[i] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::set_x +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase2:: +set_x(NumType value) { + _data[0] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::set_y +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase2:: +set_y(NumType value) { + _data[1] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::get_data +// Access: Public +// Description: Returns the address of the first of the two data +// elements in the vector. The next element +// occupies the next position consecutively in memory. +//////////////////////////////////////////////////////////////////// +template +INLINE const NumType *LVecBase2:: +get_data() const { + return _data; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::get_num_components +// Access: Public +// Description: Returns the number of elements in the vector, two. +//////////////////////////////////////////////////////////////////// +template +INLINE int LVecBase2:: +get_num_components() const { + return 2; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::begin +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2::iterator LVecBase2:: +begin() { + return _data; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::end +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2::iterator LVecBase2:: +end() { + return begin() + get_num_components(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::begin +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2::const_iterator LVecBase2:: +begin() const { + return _data; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::end +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2::const_iterator LVecBase2:: +end() const { + return begin() + get_num_components(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::fill +// Access: Public +// Description: Sets each element of the vector to the indicated +// fill_value. This is particularly useful for +// initializing to zero. +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase2:: +fill(NumType fill_value) { + _data[0] = fill_value; + _data[1] = fill_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::set +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase2:: +set(NumType x, NumType y) { + _data[0] = x; + _data[1] = y; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::dot +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase2:: +dot(const LVecBase2 &other) const { + return _data[0] * other[0] + _data[1] * other[1]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::operator < +// Access: Public +// Description: This performs a lexicographical comparison. It's of +// questionable mathematical meaning, but sometimes has +// a practical purpose for sorting unique vectors, +// especially in an STL container. Also see +// compare_to(). +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase2:: +operator < (const LVecBase2 &other) const { + return (compare_to(other) < 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::operator == +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase2:: +operator == (const LVecBase2 &other) const { + return (_data[0] == other[0] && + _data[1] == other[1]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::operator != +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase2:: +operator != (const LVecBase2 &other) const { + return !operator == (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::compare_to +// Access: Public +// Description: This flavor of compare_to uses a default threshold +// value based on the numeric type. +//////////////////////////////////////////////////////////////////// +template +INLINE int LVecBase2:: +compare_to(const LVecBase2 &other) const { + return compare_to(other, NEARLY_ZERO(NumType)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::compare_to +// Access: Public +// Description: Sorts vectors lexicographically, componentwise. +// Returns a number less than 0 if this vector sorts +// before the other one, greater than zero if it sorts +// after, 0 if they are equivalent (within the indicated +// tolerance). +//////////////////////////////////////////////////////////////////// +template +INLINE int LVecBase2:: +compare_to(const LVecBase2 &other, NumType threshold) const { + if (!IS_THRESHOLD_EQUAL(_data[0], other[0], threshold)) { + return (_data[0] < other[0]) ? -1 : 1; + } + if (!IS_THRESHOLD_EQUAL(_data[1], other[1], threshold)) { + return (_data[1] < other[1]) ? -1 : 1; + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::unary - +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 LVecBase2:: +operator - () const { + return LVecBase2(-_data[0], -_data[1]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::vector + vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 LVecBase2:: +operator + (const LVecBase2 &other) const { + return LVecBase2(_data[0] + other[0], + _data[1] + other[1]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::vector - vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 LVecBase2:: +operator - (const LVecBase2 &other) const { + return LVecBase2(_data[0] - other[0], + _data[1] - other[1]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::vector * scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 LVecBase2:: +operator * (NumType scalar) const { + return LVecBase2(_data[0] * scalar, + _data[1] * scalar); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::vector / scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 LVecBase2:: +operator / (NumType scalar) const { + return LVecBase2(_data[0] / scalar, + _data[1] / scalar); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::operator += +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase2:: +operator += (const LVecBase2 &other) { + _data[0] += other[0]; + _data[1] += other[1]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::operator -= +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase2:: +operator -= (const LVecBase2 &other) { + _data[0] -= other[0]; + _data[1] -= other[1]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::operator *= +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase2:: +operator *= (NumType scalar) { + _data[0] *= scalar; + _data[1] *= scalar; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::operator /= +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase2:: +operator /= (NumType scalar) { + _data[0] /= scalar; + _data[1] /= scalar; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::almost_equal +// Access: Public +// Description: Returns true if two vectors are memberwise equal +// within a specified tolerance. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase2:: +almost_equal(const LVecBase2 &other, NumType threshold) const { + return (IS_THRESHOLD_EQUAL(_data[0], other[0], threshold) && + IS_THRESHOLD_EQUAL(_data[1], other[1], threshold)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::almost_equal +// Access: Public +// Description: Returns true if two vectors are memberwise equal +// within a default tolerance based on the numeric type. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase2:: +almost_equal(const LVecBase2 &other) const { + return almost_equal(other, NEARLY_ZERO(NumType)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase2:: +output(ostream &out) const { + out << MAYBE_ZERO(_data[0]) << " " + << MAYBE_ZERO(_data[1]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +template +void LVecBase2:: +init_type() { + if (_type_handle == TypeHandle::none()) { + // Format a string to describe the type. + do_init_type(NumType); + string name = + "LVecBase2<" + get_type_handle(NumType).get_name() + ">"; + register_type(_type_handle, name); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::write_datagram +// Access: Public +// Description: Function to write itself into a datagram +//////////////////////////////////////////////////////////////////// +template +void LVecBase2:: +write_datagram(Datagram &destination) const { + destination.add_float64(_data[0]); + destination.add_float64(_data[1]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::read_datagram +// Access: Public +// Description: Function to read itself from a datagramIterator +//////////////////////////////////////////////////////////////////// +template +void LVecBase2:: +read_datagram(DatagramIterator &source) { + _data[0] = source.get_float64(); + _data[1] = source.get_float64(); +} + +//////////////////////////////////////////////////////////////////// +// Function: lcast_to +// Description: Converts a vector from one numeric representation to +// another one. This is usually invoked using the macro +// LCAST. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 +lcast_to(NumType2 *, const LVecBase2 &source) { + return LVecBase2(source[0], source[1]); +} diff --git a/panda/src/linmath/lvecBase2.h b/panda/src/linmath/lvecBase2.h new file mode 100644 index 0000000000..0a1d63ed7f --- /dev/null +++ b/panda/src/linmath/lvecBase2.h @@ -0,0 +1,133 @@ +// Filename: lvecBase2.h +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LVECBASE2_H +#define LVECBASE2_H + +#include + +#include "cmath.h" + +#include + +class Datagram; +class DatagramIterator; + +//////////////////////////////////////////////////////////////////// +// Class : LVecBase2 +// Description : This is the base class for all two-component +// vectors and points. +//////////////////////////////////////////////////////////////////// +template +class LVecBase2 { +public: + typedef const NumType *iterator; + typedef const NumType *const_iterator; + + INLINE LVecBase2(); + INLINE LVecBase2(const LVecBase2 ©); + INLINE LVecBase2 &operator = (const LVecBase2 ©); + INLINE LVecBase2 &operator = (NumType fill_value); + INLINE LVecBase2(NumType fill_value); + INLINE LVecBase2(NumType x, NumType y); + + INLINE static LVecBase2 zero(); + INLINE static LVecBase2 unit_x(); + INLINE static LVecBase2 unit_y(); + + INLINE ~LVecBase2(); + + INLINE NumType operator [](int i) const; + INLINE NumType &operator [](int i); + + INLINE bool is_nan() const; + + INLINE NumType get_cell(int i) const; + INLINE NumType get_x() const; + INLINE NumType get_y() const; + INLINE void set_cell(int i, NumType value); + INLINE void set_x(NumType value); + INLINE void set_y(NumType value); + + INLINE const NumType *get_data() const; + INLINE int get_num_components() const; + + INLINE iterator begin(); + INLINE iterator end(); + + INLINE const_iterator begin() const; + INLINE const_iterator end() const; + + INLINE void fill(NumType fill_value); + INLINE void set(NumType x, NumType y); + + INLINE NumType dot(const LVecBase2 &other) const; + + INLINE bool operator < (const LVecBase2 &other) const; + INLINE bool operator == (const LVecBase2 &other) const; + INLINE bool operator != (const LVecBase2 &other) const; + + INLINE int compare_to(const LVecBase2 &other) const; + INLINE int compare_to(const LVecBase2 &other, + NumType threshold) const; + + INLINE LVecBase2 + operator - () const; + + INLINE LVecBase2 + operator + (const LVecBase2 &other) const; + INLINE LVecBase2 + operator - (const LVecBase2 &other) const; + + INLINE LVecBase2 operator * (NumType scalar) const; + INLINE LVecBase2 operator / (NumType scalar) const; + + INLINE void operator += (const LVecBase2 &other); + INLINE void operator -= (const LVecBase2 &other); + + INLINE void operator *= (NumType scalar); + INLINE void operator /= (NumType scalar); + + INLINE bool almost_equal(const LVecBase2 &other, + NumType threshold) const; + INLINE bool almost_equal(const LVecBase2 &other) const; + + INLINE void output(ostream &out) const; + +private: + NumType _data[2]; + +public: + INLINE void write_datagram(Datagram &destination) const; + INLINE void read_datagram(DatagramIterator &source); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +template +INLINE ostream &operator << (ostream &out, const LVecBase2 &vec) { + vec.output(out); + return out; +} + + +// Cast to a different numeric type +template +INLINE LVecBase2 +lcast_to(NumType2 *type, const LVecBase2 &source); + +#include "lvecBase2.I" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LVecBase2) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LVecBase2) + +#endif diff --git a/panda/src/linmath/lvecBase3.I b/panda/src/linmath/lvecBase3.I new file mode 100644 index 0000000000..5c7b2d1ddb --- /dev/null +++ b/panda/src/linmath/lvecBase3.I @@ -0,0 +1,680 @@ +// Filename: lvecBase3.I +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nearly_zero.h" + +#include +#include +#include + +template +TypeHandle LVecBase3::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3:: +LVecBase3() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3:: +LVecBase3(const LVecBase3 ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 &LVecBase3:: +operator = (const LVecBase3 ©) { + set(copy[0], copy[1], copy[2]); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::Fill Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 &LVecBase3:: +operator = (NumType fill_value) { + fill(fill_value); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3:: +LVecBase3(NumType fill_value) { + fill(fill_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3:: +LVecBase3(NumType x, NumType y, NumType z) { + set(x, y, z); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::zero Named Constructor +// Access: Public +// Description: Returns a zero-length vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LVecBase3:: +zero() { + return LVecBase3(0.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::unit_x Named Constructor +// Access: Public +// Description: Returns a unit X vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LVecBase3:: +unit_x() { + return LVecBase3(1.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::unit_y Named Constructor +// Access: Public +// Description: Returns a unit Y vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LVecBase3:: +unit_y() { + return LVecBase3(0.0, 1.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::unit_z Named Constructor +// Access: Public +// Description: Returns a unit Z vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LVecBase3:: +unit_z() { + return LVecBase3(0.0, 0.0, 1.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3:: +~LVecBase3() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::Indexing Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase3:: +operator [](int i) const { + nassertr(i >= 0 && i < 3, 0); + return _data[i]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::Indexing Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType &LVecBase3:: +operator [](int i) { + nassertr(i >= 0 && i < 3, _data[0]); + return _data[i]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::is_nan +// Access: Public +// Description: Returns true if any component of the vector is +// not-a-number, false otherwise. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase3:: +is_nan() const { + return cnan(_data[0]) || cnan(_data[1]) || cnan(_data[2]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::get_cell +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase3:: +get_cell(int i) const { + nassertr(i >= 0 && i < 3, 0); + return _data[i]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::get_x +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase3:: +get_x() const { + return _data[0]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::get_y +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase3:: +get_y() const { + return _data[1]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::get_z +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase3:: +get_z() const { + return _data[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::set_cell +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase3:: +set_cell(int i, NumType value) { + nassertv(i >= 0 && i < 3); + _data[i] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::set_x +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase3:: +set_x(NumType value) { + _data[0] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::set_y +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase3:: +set_y(NumType value) { + _data[1] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::set_z +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase3:: +set_z(NumType value) { + _data[2] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::get_data +// Access: Public +// Description: Returns the address of the first of the three data +// elements in the vector. The remaining elements +// occupy the next positions consecutively in memory. +//////////////////////////////////////////////////////////////////// +template +INLINE const NumType *LVecBase3:: +get_data() const { + return _data; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::get_num_components +// Access: Public +// Description: Returns the number of elements in the vector, three. +//////////////////////////////////////////////////////////////////// +template +INLINE int LVecBase3:: +get_num_components() const { + return 3; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::begin +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3::iterator LVecBase3:: +begin() { + return _data; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::end +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3::iterator LVecBase3:: +end() { + return begin() + get_num_components(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::begin +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3::const_iterator LVecBase3:: +begin() const { + return _data; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::end +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3::const_iterator LVecBase3:: +end() const { + return begin() + get_num_components(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::fill +// Access: Public +// Description: Sets each element of the vector to the indicated +// fill_value. This is particularly useful for +// initializing to zero. +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase3:: +fill(NumType fill_value) { + _data[0] = fill_value; + _data[1] = fill_value; + _data[2] = fill_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::set +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase3:: +set(NumType x, NumType y, NumType z) { + _data[0] = x; + _data[1] = y; + _data[2] = z; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::dot +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase3:: +dot(const LVecBase3 &other) const { + return _data[0] * other[0] + _data[1] * other[1] + _data[2] * other[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::cross +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LVecBase3:: +cross(const LVecBase3 &other) const { + return LVecBase3(_data[1] * other[2] - other[1] * _data[2], + other[0] * _data[2] - _data[0] * other[2], + _data[0] * other[1] - other[0] * _data[1]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::operator < +// Access: Public +// Description: This performs a lexicographical comparison. It's of +// questionable mathematical meaning, but sometimes has +// a practical purpose for sorting unique vectors, +// especially in an STL container. Also see +// compare_to(). +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase3:: +operator < (const LVecBase3 &other) const { + return (compare_to(other) < 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::operator == +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase3:: +operator == (const LVecBase3 &other) const { + return (_data[0] == other[0] && + _data[1] == other[1] && + _data[2] == other[2]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::operator != +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase3:: +operator != (const LVecBase3 &other) const { + return !operator == (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::compare_to +// Access: Public +// Description: This flavor of compare_to uses a default threshold +// value based on the numeric type. +//////////////////////////////////////////////////////////////////// +template +INLINE int LVecBase3:: +compare_to(const LVecBase3 &other) const { + return compare_to(other, NEARLY_ZERO(NumType)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::compare_to +// Access: Public +// Description: Sorts vectors lexicographically, componentwise. +// Returns a number less than 0 if this vector sorts +// before the other one, greater than zero if it sorts +// after, 0 if they are equivalent (within the indicated +// tolerance). +//////////////////////////////////////////////////////////////////// +template +INLINE int LVecBase3:: +compare_to(const LVecBase3 &other, NumType threshold) const { + if (!IS_THRESHOLD_EQUAL(_data[0], other[0], threshold)) { + return (_data[0] < other[0]) ? -1 : 1; + } + if (!IS_THRESHOLD_EQUAL(_data[1], other[1], threshold)) { + return (_data[1] < other[1]) ? -1 : 1; + } + if (!IS_THRESHOLD_EQUAL(_data[2], other[2], threshold)) { + return (_data[2] < other[2]) ? -1 : 1; + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::unary - +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LVecBase3:: +operator - () const { + return LVecBase3(-_data[0], -_data[1], -_data[2]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::vector + vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LVecBase3:: +operator + (const LVecBase3 &other) const { + return LVecBase3(_data[0] + other[0], + _data[1] + other[1], + _data[2] + other[2]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::vector - vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LVecBase3:: +operator - (const LVecBase3 &other) const { + return LVecBase3(_data[0] - other[0], + _data[1] - other[1], + _data[2] - other[2]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::vector * scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LVecBase3:: +operator * (NumType scalar) const { + return LVecBase3(_data[0] * scalar, + _data[1] * scalar, + _data[2] * scalar); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::vector / scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LVecBase3:: +operator / (NumType scalar) const { + return LVecBase3(_data[0] / scalar, + _data[1] / scalar, + _data[2] / scalar); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::operator += +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase3:: +operator += (const LVecBase3 &other) { + _data[0] += other[0]; + _data[1] += other[1]; + _data[2] += other[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::operator -= +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase3:: +operator -= (const LVecBase3 &other) { + _data[0] -= other[0]; + _data[1] -= other[1]; + _data[2] -= other[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::operator *= +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase3:: +operator *= (NumType scalar) { + _data[0] *= scalar; + _data[1] *= scalar; + _data[2] *= scalar; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::operator /= +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase3:: +operator /= (NumType scalar) { + _data[0] /= scalar; + _data[1] /= scalar; + _data[2] /= scalar; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::cross product (with assigment) +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase3:: +cross_into(const LVecBase3 &other) { + (*this) = cross(other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::almost_equal +// Access: Public +// Description: Returns true if two vectors are memberwise equal +// within a specified tolerance. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase3:: +almost_equal(const LVecBase3 &other, NumType threshold) const { + return (IS_THRESHOLD_EQUAL(_data[0], other[0], threshold) && + IS_THRESHOLD_EQUAL(_data[1], other[1], threshold) && + IS_THRESHOLD_EQUAL(_data[2], other[2], threshold)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::almost_equal +// Access: Public +// Description: Returns true if two vectors are memberwise equal +// within a default tolerance based on the numeric type. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase3:: +almost_equal(const LVecBase3 &other) const { + return almost_equal(other, NEARLY_ZERO(NumType)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase3:: +output(ostream &out) const { + out << MAYBE_ZERO(_data[0]) << " " + << MAYBE_ZERO(_data[1]) << " " + << MAYBE_ZERO(_data[2]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +template +void LVecBase3:: +init_type() { + if (_type_handle == TypeHandle::none()) { + // Format a string to describe the type. + do_init_type(NumType); + string name = + "LVecBase3<" + get_type_handle(NumType).get_name() + ">"; + register_type(_type_handle, name); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::write_datagram +// Access: Public +// Description: Function to write itself into a datagram +//////////////////////////////////////////////////////////////////// +template +void LVecBase3:: +write_datagram(Datagram &destination) const { + destination.add_float64(_data[0]); + destination.add_float64(_data[1]); + destination.add_float64(_data[2]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::read_datagram +// Access: Public +// Description: Function to read itself from a datagramIterator +//////////////////////////////////////////////////////////////////// +template +void LVecBase3:: +read_datagram(DatagramIterator &source) { + _data[0] = source.get_float64(); + _data[1] = source.get_float64(); + _data[2] = source.get_float64(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: lcast_to +// Description: Converts a vector from one numeric representation to +// another one. This is usually invoked using the macro +// LCAST. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 +lcast_to(NumType2 *, const LVecBase3 &source) { + return LVecBase3(source[0], source[1], source[2]); +} diff --git a/panda/src/linmath/lvecBase3.h b/panda/src/linmath/lvecBase3.h new file mode 100644 index 0000000000..91e3e6c032 --- /dev/null +++ b/panda/src/linmath/lvecBase3.h @@ -0,0 +1,139 @@ +// Filename: lvecBase3.h +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LVECBASE3_H +#define LVECBASE3_H + +#include + +#include "cmath.h" + +#include + +class Datagram; +class DatagramIterator; + +//////////////////////////////////////////////////////////////////// +// Class : LVecBase3 +// Description : This is the base class for all three-component +// vectors and points. +//////////////////////////////////////////////////////////////////// +template +class LVecBase3 { +public: + typedef const NumType *iterator; + typedef const NumType *const_iterator; + + INLINE LVecBase3(); + INLINE LVecBase3(const LVecBase3 ©); + INLINE LVecBase3 &operator = (const LVecBase3 ©); + INLINE LVecBase3 &operator = (NumType fill_value); + INLINE LVecBase3(NumType fill_value); + INLINE LVecBase3(NumType x, NumType y, NumType z); + + INLINE static LVecBase3 zero(); + INLINE static LVecBase3 unit_x(); + INLINE static LVecBase3 unit_y(); + INLINE static LVecBase3 unit_z(); + + INLINE ~LVecBase3(); + + INLINE NumType operator [](int i) const; + INLINE NumType &operator [](int i); + + INLINE bool is_nan() const; + + INLINE NumType get_cell(int i) const; + INLINE NumType get_x() const; + INLINE NumType get_y() const; + INLINE NumType get_z() const; + INLINE void set_cell(int i, NumType value); + INLINE void set_x(NumType value); + INLINE void set_y(NumType value); + INLINE void set_z(NumType value); + + INLINE const NumType *get_data() const; + INLINE int get_num_components() const; + + INLINE iterator begin(); + INLINE iterator end(); + + INLINE const_iterator begin() const; + INLINE const_iterator end() const; + + INLINE void fill(NumType fill_value); + INLINE void set(NumType x, NumType y, NumType z); + + INLINE NumType dot(const LVecBase3 &other) const; + INLINE LVecBase3 cross(const LVecBase3 &other) const; + + INLINE bool operator < (const LVecBase3 &other) const; + INLINE bool operator == (const LVecBase3 &other) const; + INLINE bool operator != (const LVecBase3 &other) const; + + INLINE int compare_to(const LVecBase3 &other) const; + INLINE int compare_to(const LVecBase3 &other, + NumType threshold) const; + + INLINE LVecBase3 + operator - () const; + + INLINE LVecBase3 + operator + (const LVecBase3 &other) const; + INLINE LVecBase3 + operator - (const LVecBase3 &other) const; + + INLINE LVecBase3 operator * (NumType scalar) const; + INLINE LVecBase3 operator / (NumType scalar) const; + + INLINE void operator += (const LVecBase3 &other); + INLINE void operator -= (const LVecBase3 &other); + + INLINE void operator *= (NumType scalar); + INLINE void operator /= (NumType scalar); + + INLINE void cross_into(const LVecBase3 &other); + + INLINE bool almost_equal(const LVecBase3 &other, + NumType threshold) const; + INLINE bool almost_equal(const LVecBase3 &other) const; + + INLINE void output(ostream &out) const; + +private: + NumType _data[3]; + +public: + INLINE void write_datagram(Datagram &destination) const; + INLINE void read_datagram(DatagramIterator &source); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +template +INLINE ostream &operator << (ostream &out, const LVecBase3 &vec) { + vec.output(out); + return out; +} + + +// Cast to a different numeric type +template +INLINE LVecBase3 +lcast_to(NumType2 *type, const LVecBase3 &source); + +#include "lvecBase3.I" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LVecBase3) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LVecBase3) + +#endif diff --git a/panda/src/linmath/lvecBase4.I b/panda/src/linmath/lvecBase4.I new file mode 100644 index 0000000000..eae2792345 --- /dev/null +++ b/panda/src/linmath/lvecBase4.I @@ -0,0 +1,710 @@ +// Filename: lvecBase4.I +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nearly_zero.h" + +#include +#include +#include + +template +TypeHandle LVecBase4::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4:: +LVecBase4() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4:: +LVecBase4(const LVecBase4 ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 &LVecBase4:: +operator = (const LVecBase4 ©) { + set(copy[0], copy[1], copy[2], copy[3]); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::Fill Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 &LVecBase4:: +operator = (NumType fill_value) { + fill(fill_value); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4:: +LVecBase4(NumType fill_value) { + fill(fill_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4:: +LVecBase4(NumType x, NumType y, NumType z, NumType w) { + set(x, y, z, w); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4:: +~LVecBase4() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::zero Named Constructor +// Access: Public +// Description: Returns a zero-length vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 LVecBase4:: +zero() { + return LVecBase4(0.0, 0.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::unit_x Named Constructor +// Access: Public +// Description: Returns a unit X vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 LVecBase4:: +unit_x() { + return LVecBase4(1.0, 0.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::unit_y Named Constructor +// Access: Public +// Description: Returns a unit Y vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 LVecBase4:: +unit_y() { + return LVecBase4(0.0, 1.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::unit_z Named Constructor +// Access: Public +// Description: Returns a unit Z vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 LVecBase4:: +unit_z() { + return LVecBase4(0.0, 0.0, 1.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::unit_w Named Constructor +// Access: Public +// Description: Returns a unit W vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 LVecBase4:: +unit_w() { + return LVecBase4(0.0, 0.0, 0.0, 1.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::Indexing Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase4:: +operator [](int i) const { + nassertr(i >= 0 && i < 4, 0); + return _data[i]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::Indexing Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType &LVecBase4:: +operator [](int i) { + nassertr(i >= 0 && i < 4, _data[0]); + return _data[i]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::is_nan +// Access: Public +// Description: Returns true if any component of the vector is +// not-a-number, false otherwise. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase4:: +is_nan() const { + return cnan(_data[0]) || cnan(_data[1]) || cnan(_data[2]) || cnan(_data[3]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::get_cell +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase4:: +get_cell(int i) const { + nassertr(i >= 0 && i < 4, 0); + return _data[i]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::get_x +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase4:: +get_x() const { + return _data[0]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::get_y +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase4:: +get_y() const { + return _data[1]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::get_z +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase4:: +get_z() const { + return _data[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::get_w +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase4:: +get_w() const { + return _data[3]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::set_cell +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase4:: +set_cell(int i, NumType value) { + nassertv(i >= 0 && i < 4); + _data[i] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::set_x +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase4:: +set_x(NumType value) { + _data[0] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::set_y +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase4:: +set_y(NumType value) { + _data[1] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::set_z +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase4:: +set_z(NumType value) { + _data[2] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::set_w +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase4:: +set_w(NumType value) { + _data[3] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::get_data +// Access: Public +// Description: Returns the address of the first of the four data +// elements in the vector. The remaining elements +// occupy the next positions consecutively in memory. +//////////////////////////////////////////////////////////////////// +template +INLINE const NumType *LVecBase4:: +get_data() const { + return _data; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::get_num_components +// Access: Public +// Description: Returns the number of elements in the vector, four. +//////////////////////////////////////////////////////////////////// +template +INLINE int LVecBase4:: +get_num_components() const { + return 4; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::begin +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4::iterator LVecBase4:: +begin() { + return _data; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::end +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4::iterator LVecBase4:: +end() { + return begin() + get_num_components(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::begin +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4::const_iterator LVecBase4:: +begin() const { + return _data; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::end +// Access: Public +// Description: Returns an iterator that may be used to traverse the +// elements of the matrix, STL-style. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4::const_iterator LVecBase4:: +end() const { + return begin() + get_num_components(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::fill +// Access: Public +// Description: Sets each element of the vector to the indicated +// fill_value. This is particularly useful for +// initializing to zero. +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase4:: +fill(NumType fill_value) { + _data[0] = fill_value; + _data[1] = fill_value; + _data[2] = fill_value; + _data[3] = fill_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::set +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase4:: +set(NumType x, NumType y, NumType z, NumType w) { + _data[0] = x; + _data[1] = y; + _data[2] = z; + _data[3] = w; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::dot +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVecBase4:: +dot(const LVecBase4 &other) const { + return + _data[0] * other[0] + _data[1] * other[1] + + _data[2] * other[2] + _data[3] * other[3]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::operator < +// Access: Public +// Description: This performs a lexicographical comparison. It's of +// questionable mathematical meaning, but sometimes has +// a practical purpose for sorting unique vectors, +// especially in an STL container. Also see +// compare_to(). +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase4:: +operator < (const LVecBase4 &other) const { + return (compare_to(other) < 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::operator == +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase4:: +operator == (const LVecBase4 &other) const { + return (_data[0] == other[0] && + _data[1] == other[1] && + _data[2] == other[2] && + _data[3] == other[3]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::operator != +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase4:: +operator != (const LVecBase4 &other) const { + return !operator == (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::compare_to +// Access: Public +// Description: This flavor of compare_to uses a default threshold +// value based on the numeric type. +//////////////////////////////////////////////////////////////////// +template +INLINE int LVecBase4:: +compare_to(const LVecBase4 &other) const { + return compare_to(other, NEARLY_ZERO(NumType)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::compare_to +// Access: Public +// Description: Sorts vectors lexicographically, componentwise. +// Returns a number less than 0 if this vector sorts +// before the other one, greater than zero if it sorts +// after, 0 if they are equivalent (within the indicated +// tolerance). +//////////////////////////////////////////////////////////////////// +template +INLINE int LVecBase4:: +compare_to(const LVecBase4 &other, NumType threshold) const { + if (!IS_THRESHOLD_EQUAL(_data[0], other[0], threshold)) { + return (_data[0] < other[0]) ? -1 : 1; + } + if (!IS_THRESHOLD_EQUAL(_data[1], other[1], threshold)) { + return (_data[1] < other[1]) ? -1 : 1; + } + if (!IS_THRESHOLD_EQUAL(_data[2], other[2], threshold)) { + return (_data[2] < other[2]) ? -1 : 1; + } + if (!IS_THRESHOLD_EQUAL(_data[3], other[3], threshold)) { + return (_data[3] < other[3]) ? -1 : 1; + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::unary - +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 LVecBase4:: +operator - () const { + return LVecBase4(-_data[0], -_data[1], -_data[2], -_data[3]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::vector + vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 LVecBase4:: +operator + (const LVecBase4 &other) const { + return LVecBase4(_data[0] + other[0], + _data[1] + other[1], + _data[2] + other[2], + _data[3] + other[3]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::vector - vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 LVecBase4:: +operator - (const LVecBase4 &other) const { + return LVecBase4(_data[0] - other[0], + _data[1] - other[1], + _data[2] - other[2], + _data[3] - other[3]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::vector * scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 LVecBase4:: +operator * (NumType scalar) const { + return LVecBase4(_data[0] * scalar, + _data[1] * scalar, + _data[2] * scalar, + _data[3] * scalar); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::vector / scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 LVecBase4:: +operator / (NumType scalar) const { + return LVecBase4(_data[0] / scalar, + _data[1] / scalar, + _data[2] / scalar, + _data[3] / scalar); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::operator += +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase4:: +operator += (const LVecBase4 &other) { + _data[0] += other[0]; + _data[1] += other[1]; + _data[2] += other[2]; + _data[3] += other[3]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::operator -= +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase4:: +operator -= (const LVecBase4 &other) { + _data[0] -= other[0]; + _data[1] -= other[1]; + _data[2] -= other[2]; + _data[3] -= other[3]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::operator *= +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase4:: +operator *= (NumType scalar) { + _data[0] *= scalar; + _data[1] *= scalar; + _data[2] *= scalar; + _data[3] *= scalar; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::operator /= +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase4:: +operator /= (NumType scalar) { + _data[0] /= scalar; + _data[1] /= scalar; + _data[2] /= scalar; + _data[3] /= scalar; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::almost_equal +// Access: Public +// Description: Returns true if two vectors are memberwise equal +// within a specified tolerance. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase4:: +almost_equal(const LVecBase4 &other, NumType threshold) const { + return (IS_THRESHOLD_EQUAL(_data[0], other[0], threshold) && + IS_THRESHOLD_EQUAL(_data[1], other[1], threshold) && + IS_THRESHOLD_EQUAL(_data[2], other[2], threshold) && + IS_THRESHOLD_EQUAL(_data[3], other[3], threshold)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::almost_equal +// Access: Public +// Description: Returns true if two vectors are memberwise equal +// within a default tolerance based on the numeric type. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVecBase4:: +almost_equal(const LVecBase4 &other) const { + return almost_equal(other, NEARLY_ZERO(NumType)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void LVecBase4:: +output(ostream &out) const { + out << MAYBE_ZERO(_data[0]) << " " + << MAYBE_ZERO(_data[1]) << " " + << MAYBE_ZERO(_data[2]) << " " + << MAYBE_ZERO(_data[3]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +template +void LVecBase4:: +init_type() { + if (_type_handle == TypeHandle::none()) { + // Format a string to describe the type. + do_init_type(NumType); + string name = + "LVecBase4<" + get_type_handle(NumType).get_name() + ">"; + register_type(_type_handle, name); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::write_datagram +// Access: Public +// Description: Function to write itself into a datagram +//////////////////////////////////////////////////////////////////// +template +void LVecBase4:: +write_datagram(Datagram &destination) const { + destination.add_float64(_data[0]); + destination.add_float64(_data[1]); + destination.add_float64(_data[2]); + destination.add_float64(_data[3]); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::read_datagram +// Access: Public +// Description: Function to read itself from a datagramIterator +//////////////////////////////////////////////////////////////////// +template +void LVecBase4:: +read_datagram(DatagramIterator &source) { + _data[0] = source.get_float64(); + _data[1] = source.get_float64(); + _data[2] = source.get_float64(); + _data[3] = source.get_float64(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: lcast_to +// Description: Converts a vector from one numeric representation to +// another one. This is usually invoked using the macro +// LCAST. +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 +lcast_to(NumType2 *, const LVecBase4 &source) { + return LVecBase4(source[0], source[1], source[2], source[3]); +} diff --git a/panda/src/linmath/lvecBase4.h b/panda/src/linmath/lvecBase4.h new file mode 100644 index 0000000000..7a8e5dd0c0 --- /dev/null +++ b/panda/src/linmath/lvecBase4.h @@ -0,0 +1,139 @@ +// Filename: lvecBase4.h +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LVECBASE4_H +#define LVECBASE4_H + +#include + +#include "cmath.h" + +#include + +class Datagram; +class DatagramIterator; + +//////////////////////////////////////////////////////////////////// +// Class : LVecBase4 +// Description : This is the base class for all three-component +// vectors and points. +//////////////////////////////////////////////////////////////////// +template +class LVecBase4 { +public: + typedef const NumType *iterator; + typedef const NumType *const_iterator; + + INLINE LVecBase4(); + INLINE LVecBase4(const LVecBase4 ©); + INLINE LVecBase4 &operator = (const LVecBase4 ©); + INLINE LVecBase4 &operator = (NumType fill_value); + INLINE LVecBase4(NumType fill_value); + INLINE LVecBase4(NumType x, NumType y, NumType z, NumType w); + + INLINE static LVecBase4 zero(); + INLINE static LVecBase4 unit_x(); + INLINE static LVecBase4 unit_y(); + INLINE static LVecBase4 unit_z(); + INLINE static LVecBase4 unit_w(); + + INLINE ~LVecBase4(); + + INLINE NumType operator [](int i) const; + INLINE NumType &operator [](int i); + + INLINE bool is_nan() const; + + INLINE NumType get_cell(int i) const; + INLINE NumType get_x() const; + INLINE NumType get_y() const; + INLINE NumType get_z() const; + INLINE NumType get_w() const; + INLINE void set_cell(int i, NumType value); + INLINE void set_x(NumType value); + INLINE void set_y(NumType value); + INLINE void set_z(NumType value); + INLINE void set_w(NumType value); + + INLINE const NumType *get_data() const; + INLINE int get_num_components() const; + + INLINE iterator begin(); + INLINE iterator end(); + + INLINE const_iterator begin() const; + INLINE const_iterator end() const; + + INLINE void fill(NumType fill_value); + INLINE void set(NumType x, NumType y, NumType z, NumType w); + + INLINE NumType dot(const LVecBase4 &other) const; + + INLINE bool operator < (const LVecBase4 &other) const; + INLINE bool operator == (const LVecBase4 &other) const; + INLINE bool operator != (const LVecBase4 &other) const; + + INLINE int compare_to(const LVecBase4 &other) const; + INLINE int compare_to(const LVecBase4 &other, + NumType threshold) const; + + INLINE LVecBase4 + operator - () const; + + INLINE LVecBase4 + operator + (const LVecBase4 &other) const; + INLINE LVecBase4 + operator - (const LVecBase4 &other) const; + + INLINE LVecBase4 operator * (NumType scalar) const; + INLINE LVecBase4 operator / (NumType scalar) const; + + INLINE void operator += (const LVecBase4 &other); + INLINE void operator -= (const LVecBase4 &other); + + INLINE void operator *= (NumType scalar); + INLINE void operator /= (NumType scalar); + + INLINE bool almost_equal(const LVecBase4 &other, + NumType threshold) const; + INLINE bool almost_equal(const LVecBase4 &other) const; + + INLINE void output(ostream &out) const; + +private: + NumType _data[4]; + +public: + INLINE void write_datagram(Datagram &destination) const; + INLINE void read_datagram(DatagramIterator &source); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +template +INLINE ostream &operator << (ostream &out, const LVecBase4 &vec) { + vec.output(out); + return out; +} + + +// Cast to a different numeric type +template +INLINE LVecBase4 +lcast_to(NumType2 *type, const LVecBase4 &source); + +#include "lvecBase4.I" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LVecBase4) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LVecBase4) + +#endif diff --git a/panda/src/linmath/lvector2.I b/panda/src/linmath/lvector2.I new file mode 100644 index 0000000000..d71eecece7 --- /dev/null +++ b/panda/src/linmath/lvector2.I @@ -0,0 +1,262 @@ +// Filename: lvector2.I +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "cmath.h" + +template +TypeHandle LVector2::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2:: +LVector2() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2:: +LVector2(const LVecBase2 ©) : LVecBase2(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2 &LVector2:: +operator = (const LVecBase2 ©) { + LVecBase2::operator = (copy); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::Copy Fill Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2 &LVector2:: +operator = (NumType fill_value) { + LVecBase2::operator = (fill_value); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2:: +LVector2(NumType fill_value) : + LVecBase2(fill_value) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2:: +LVector2(NumType x, NumType y) : + LVecBase2(x, y) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::zero Named Constructor +// Access: Public +// Description: Returns a zero-length vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2 LVector2:: +zero() { + return LVector2(0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::unit_x Named Constructor +// Access: Public +// Description: Returns a unit X vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2 LVector2:: +unit_x() { + return LVector2(1.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::unit_y Named Constructor +// Access: Public +// Description: Returns a unit Y vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2 LVector2:: +unit_y() { + return LVector2(0.0, 1.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::unary - +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2 LVector2:: +operator - () const { + return LVecBase2::operator - (); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::vector + vecbase +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 LVector2:: +operator + (const LVecBase2 &other) const { + return LVecBase2::operator + (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::vector + vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2 LVector2:: +operator + (const LVector2 &other) const { + return LVecBase2::operator + (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::vector - vecbase +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase2 LVector2:: +operator - (const LVecBase2 &other) const { + return LVecBase2::operator - (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::vector - vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2 LVector2:: +operator - (const LVector2 &other) const { + return LVecBase2::operator - (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::length +// Access: Public +// Description: Returns the length of the vector, by the Pythagorean +// theorem. +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVector2:: +length() const { + return csqrt((*this).dot(*this)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::length_squared +// Access: Public +// Description: Returns the square of the vector's length, cheap and +// easy. +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVector2:: +length_squared() const { + return (*this).dot(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::normalize +// Access: Public +// Description: Normalizes the vector in place. Returns true if the +// vector was normalized, false if it was a zero-length +// vector. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVector2:: +normalize() { + NumType l2 = length_squared(); + if (l2 == 0.0) { + set(0.0, 0.0); + return false; + + } else if (!IS_THRESHOLD_EQUAL(l2, 1.0, NEARLY_ZERO(NumType) * NEARLY_ZERO(NumType))) { + (*this) /= csqrt(l2); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::operator * scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2 LVector2:: +operator * (NumType scalar) const { + return LVector2(LVecBase2::operator * (scalar)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::operator / scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2 LVector2:: +operator / (NumType scalar) const { + return LVector2(LVecBase2::operator / (scalar)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector2::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +template +void LVector2:: +init_type() { + if (_type_handle == TypeHandle::none()) { + LVecBase2::init_type(); + string name = + "LVector2<" + get_type_handle(NumType).get_name() + ">"; + register_type(_type_handle, name, + LVecBase2::get_class_type()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: lcast_to +// Description: Converts a vector from one numeric representation to +// another one. This is usually invoked using the macro +// LCAST. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector2 +lcast_to(NumType2 *, const LVector2 &source) { + return LVector2(source[0], source[1]); +} diff --git a/panda/src/linmath/lvector2.h b/panda/src/linmath/lvector2.h new file mode 100644 index 0000000000..09045a4957 --- /dev/null +++ b/panda/src/linmath/lvector2.h @@ -0,0 +1,69 @@ +// Filename: lvector2.h +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LVECTOR2_H +#define LVECTOR2_H + +#include + +#include "lvecBase2.h" + +//////////////////////////////////////////////////////////////////// +// Class : LVector2 +// Description : This is a two-component vector offset. +//////////////////////////////////////////////////////////////////// +template +class LVector2 : public LVecBase2 { +public: + INLINE LVector2(); + INLINE LVector2(const LVecBase2 ©); + INLINE LVector2 &operator = (const LVecBase2 ©); + INLINE LVector2 &operator = (NumType fill_value); + INLINE LVector2(NumType fill_value); + INLINE LVector2(NumType x, NumType y); + + INLINE static LVector2 zero(); + INLINE static LVector2 unit_x(); + INLINE static LVector2 unit_y(); + + INLINE LVector2 operator - () const; + + INLINE LVecBase2 + operator + (const LVecBase2 &other) const; + INLINE LVector2 + operator + (const LVector2 &other) const; + + INLINE LVecBase2 + operator - (const LVecBase2 &other) const; + INLINE LVector2 + operator - (const LVector2 &other) const; + + INLINE NumType length() const; + INLINE NumType length_squared() const; + INLINE bool normalize(); + INLINE LVector2 operator * (NumType scalar) const; + INLINE LVector2 operator / (NumType scalar) const; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +// Cast to a different numeric type +template +INLINE LVector2 +lcast_to(NumType2 *type, const LVector2 &source); + +#include "lvector2.I" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LVector2) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LVector2) + +#endif diff --git a/panda/src/linmath/lvector3.I b/panda/src/linmath/lvector3.I new file mode 100644 index 0000000000..59feac91cf --- /dev/null +++ b/panda/src/linmath/lvector3.I @@ -0,0 +1,403 @@ +// Filename: lvector3.I +// Created by: drose (24Sep99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_linmath.h" +#include "cmath.h" + +template +TypeHandle LVector3::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3:: +LVector3() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3:: +LVector3(const LVecBase3 ©) : LVecBase3(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 &LVector3:: +operator = (const LVecBase3 ©) { + LVecBase3::operator = (copy); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::Copy Fill Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 &LVector3:: +operator = (NumType fill_value) { + LVecBase3::operator = (fill_value); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3:: +LVector3(NumType fill_value) : + LVecBase3(fill_value) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3:: +LVector3(NumType x, NumType y, NumType z) : + LVecBase3(x, y, z) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::zero Named Constructor +// Access: Public +// Description: Returns a zero-length vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 LVector3:: +zero() { + return LVector3(0.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::unit_x Named Constructor +// Access: Public +// Description: Returns a unit X vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 LVector3:: +unit_x() { + return LVector3(1.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::unit_y Named Constructor +// Access: Public +// Description: Returns a unit Y vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 LVector3:: +unit_y() { + return LVector3(0.0, 1.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::unit_z Named Constructor +// Access: Public +// Description: Returns a unit Z vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 LVector3:: +unit_z() { + return LVector3(0.0, 0.0, 1.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::unary - +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 LVector3:: +operator - () const { + return LVecBase3::operator - (); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::vector + vecbase +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LVector3:: +operator + (const LVecBase3 &other) const { + return LVecBase3::operator + (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::vector + vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 LVector3:: +operator + (const LVector3 &other) const { + return LVecBase3::operator + (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::vector - vecbase +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase3 LVector3:: +operator - (const LVecBase3 &other) const { + return LVecBase3::operator - (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::vector - vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 LVector3:: +operator - (const LVector3 &other) const { + return LVecBase3::operator - (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::length +// Access: Public +// Description: Returns the length of the vector, by the Pythagorean +// theorem. +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVector3:: +length() const { + return csqrt((*this).dot(*this)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::length_squared +// Access: Public +// Description: Returns the square of the vector's length, cheap and +// easy. +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVector3:: +length_squared() const { + return (*this).dot(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::normalize +// Access: Public +// Description: Normalizes the vector in place. Returns true if the +// vector was normalized, false if it was a zero-length +// vector. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVector3:: +normalize() { + NumType l2 = length_squared(); + if (l2 == 0.0) { + set(0.0, 0.0, 0.0); + return false; + + } else if (!IS_THRESHOLD_EQUAL(l2, 1.0, NEARLY_ZERO(NumType) * NEARLY_ZERO(NumType))) { + (*this) /= csqrt(l2); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::cross +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 LVector3:: +cross(const LVecBase3 &other) const { + return LVecBase3::cross(other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::operator * scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 LVector3:: +operator * (NumType scalar) const { + return LVector3(LVecBase3::operator * (scalar)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::operator / scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 LVector3:: +operator / (NumType scalar) const { + return LVector3(LVecBase3::operator / (scalar)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::up +// Access: Public, Static +// Description: Returns the up vector for the given coordinate +// system. +//////////////////////////////////////////////////////////////////// +template +LVector3 LVector3:: +up(CoordinateSystem cs) { + if (cs == CS_default) { + cs = default_coordinate_system; + } + switch (cs) { + case CS_zup_right: + case CS_zup_left: + return LVector3(0.0, 0.0, 1.0); + + case CS_yup_right: + case CS_yup_left: + return LVector3(0.0, 1.0, 0.0); + } + linmath_cat.error() + << "Invalid coordinate system!\n"; + return LVector3(0.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::right +// Access: Public, Static +// Description: Returns the right vector for the given coordinate +// system. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 LVector3:: +right(CoordinateSystem) { + return LVector3(1.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::forward +// Access: Public, Static +// Description: Returns the forward vector for the given coordinate +// system. +//////////////////////////////////////////////////////////////////// +template +LVector3 LVector3:: +forward(CoordinateSystem cs) { + if (cs == CS_default) { + cs = default_coordinate_system; + } + switch (cs) { + case CS_zup_right: + return LVector3(0.0, 1.0, 0.0); + + case CS_zup_left: + return LVector3(0.0, -1.0, 0.0); + + case CS_yup_right: + return LVector3(0.0, 0.0, -1.0); + + case CS_yup_left: + return LVector3(0.0, 0.0, 1.0); + } + linmath_cat.error() + << "Invalid coordinate system!\n"; + return LVector3(0.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::down +// Access: Public, Static +// Description: Returns the down vector for the given coordinate +// system. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 LVector3:: +down(CoordinateSystem cs) { + return -up(cs); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::left +// Access: Public, Static +// Description: Returns the left vector for the given coordinate +// system. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 LVector3:: +left(CoordinateSystem cs) { + return -right(cs); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::back +// Access: Public, Static +// Description: Returns the back vector for the given coordinate +// system. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 LVector3:: +back(CoordinateSystem cs) { + return -forward(cs); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::rfu +// Access: Public, Static +// Description: Returns a vector that is described by its right, +// forward, and up components, in whatever way the +// coordinate system represents that vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 LVector3:: +rfu(NumType right_v, NumType fwd_v, NumType up_v, + CoordinateSystem cs) { + return fwd_v * forward(cs) + up_v * up(cs) + right_v * right(cs); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector3::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +template +void LVector3:: +init_type() { + if (_type_handle == TypeHandle::none()) { + LVecBase3::init_type(); + string name = + "LVector3<" + get_type_handle(NumType).get_name() + ">"; + register_type(_type_handle, name, + LVecBase3::get_class_type()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: lcast_to +// Description: Converts a vector from one numeric representation to +// another one. This is usually invoked using the macro +// LCAST. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 +lcast_to(NumType2 *, const LVector3 &source) { + return LVector3(source[0], source[1], source[2]); +} diff --git a/panda/src/linmath/lvector3.h b/panda/src/linmath/lvector3.h new file mode 100644 index 0000000000..a0d0182b22 --- /dev/null +++ b/panda/src/linmath/lvector3.h @@ -0,0 +1,93 @@ +// Filename: lvector3.h +// Created by: drose (24Sep99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LVECTOR3_H +#define LVECTOR3_H + +#include + +#include "coordinateSystem.h" +#include "lvecBase3.h" + +//////////////////////////////////////////////////////////////////// +// Class : LVector3 +// Description : This is a three-component vector distance (as opposed +// to a three-component point, which represents a +// particular point in space). Some of the methods are +// slightly different between LPoint3 and LVector3; in +// particular, subtraction of two points yields a +// vector, while addition of a vector and a point yields +// a point. +//////////////////////////////////////////////////////////////////// +template +class LVector3 : public LVecBase3 { +public: + INLINE LVector3(); + INLINE LVector3(const LVecBase3 ©); + INLINE LVector3 &operator = (const LVecBase3 ©); + INLINE LVector3 &operator = (NumType fill_value); + INLINE LVector3(NumType fill_value); + INLINE LVector3(NumType x, NumType y, NumType z); + + INLINE static LVector3 zero(); + INLINE static LVector3 unit_x(); + INLINE static LVector3 unit_y(); + INLINE static LVector3 unit_z(); + + INLINE LVector3 operator - () const; + + INLINE LVecBase3 + operator + (const LVecBase3 &other) const; + INLINE LVector3 + operator + (const LVector3 &other) const; + + INLINE LVecBase3 + operator - (const LVecBase3 &other) const; + INLINE LVector3 + operator - (const LVector3 &other) const; + + INLINE NumType length() const; + INLINE NumType length_squared() const; + INLINE bool normalize(); + INLINE LVector3 cross(const LVecBase3 &other) const; + INLINE LVector3 operator * (NumType scalar) const; + INLINE LVector3 operator / (NumType scalar) const; + + // Some special named constructors for LVector3. + + static LVector3 up(CoordinateSystem cs = CS_default); + INLINE static LVector3 right(CoordinateSystem cs = CS_default); + static LVector3 forward(CoordinateSystem cs = CS_default); + + INLINE static LVector3 down(CoordinateSystem cs = CS_default); + INLINE static LVector3 left(CoordinateSystem cs = CS_default); + INLINE static LVector3 back(CoordinateSystem cs = CS_default); + + INLINE static LVector3 rfu(NumType right, + NumType fwd, + NumType up, + CoordinateSystem cs = CS_default); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +// Cast to a different numeric type +template +INLINE LVector3 +lcast_to(NumType2 *type, const LVector3 &source); + +#include "lvector3.I" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LVector3) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LVector3) + +#endif diff --git a/panda/src/linmath/lvector4.I b/panda/src/linmath/lvector4.I new file mode 100644 index 0000000000..fab2837c93 --- /dev/null +++ b/panda/src/linmath/lvector4.I @@ -0,0 +1,285 @@ +// Filename: lvector4.I +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "cmath.h" + + +template +TypeHandle LVector4::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4:: +LVector4() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4:: +LVector4(const LVecBase4 ©) : LVecBase4(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4 &LVector4:: +operator = (const LVecBase4 ©) { + LVecBase4::operator = (copy); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::Copy Fill Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4 &LVector4:: +operator = (NumType fill_value) { + LVecBase4::operator = (fill_value); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4:: +LVector4(NumType fill_value) : + LVecBase4(fill_value) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4:: +LVector4(NumType x, NumType y, NumType z, NumType w) : + LVecBase4(x, y, z, w) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::zero Named Constructor +// Access: Public +// Description: Returns a zero-length vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4 LVector4:: +zero() { + return LVector4(0.0, 0.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::unit_x Named Constructor +// Access: Public +// Description: Returns a unit X vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4 LVector4:: +unit_x() { + return LVector4(1.0, 0.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::unit_y Named Constructor +// Access: Public +// Description: Returns a unit Y vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4 LVector4:: +unit_y() { + return LVector4(0.0, 1.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::unit_z Named Constructor +// Access: Public +// Description: Returns a unit Z vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4 LVector4:: +unit_z() { + return LVector4(0.0, 0.0, 1.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::unit_w Named Constructor +// Access: Public +// Description: Returns a unit W vector. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4 LVector4:: +unit_w() { + return LVector4(0.0, 0.0, 0.0, 1.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::unary - +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4 LVector4:: +operator - () const { + return LVecBase4::operator - (); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::vector + vecbase +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 LVector4:: +operator + (const LVecBase4 &other) const { + return LVecBase4::operator + (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::vector + vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4 LVector4:: +operator + (const LVector4 &other) const { + return LVecBase4::operator + (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::vector - vecbase +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVecBase4 LVector4:: +operator - (const LVecBase4 &other) const { + return LVecBase4::operator - (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::vector - vector +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4 LVector4:: +operator - (const LVector4 &other) const { + return LVecBase4::operator - (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::length +// Access: Public +// Description: Returns the length of the vector, by the Pythagorean +// theorem. +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVector4:: +length() const { + return csqrt((*this).dot(*this)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::length_squared +// Access: Public +// Description: Returns the square of the vector's length, cheap and +// easy. +//////////////////////////////////////////////////////////////////// +template +INLINE NumType LVector4:: +length_squared() const { + return (*this).dot(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::normalize +// Access: Public +// Description: Normalizes the vector in place. Returns true if the +// vector was normalized, false if it was a zero-length +// vector. +//////////////////////////////////////////////////////////////////// +template +INLINE bool LVector4:: +normalize() { + NumType l2 = length_squared(); + if (l2 == 0.0) { + set(0.0, 0.0, 0.0, 0.0); + return false; + + } else if (!IS_THRESHOLD_EQUAL(l2, 1.0, NEARLY_ZERO(NumType) * NEARLY_ZERO(NumType))) { + (*this) /= csqrt(l2); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::operator * scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4 LVector4:: +operator * (NumType scalar) const { + return LVector4(LVecBase4::operator * (scalar)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::operator / scalar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4 LVector4:: +operator / (NumType scalar) const { + return LVector4(LVecBase4::operator / (scalar)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LVector4::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +template +void LVector4:: +init_type() { + if (_type_handle == TypeHandle::none()) { + LVecBase4::init_type(); + string name = + "LVector4<" + get_type_handle(NumType).get_name() + ">"; + register_type(_type_handle, name, + LVecBase4::get_class_type()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: lcast_to +// Description: Converts a vector from one numeric representation to +// another one. This is usually invoked using the macro +// LCAST. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector4 +lcast_to(NumType2 *, const LVector4 &source) { + return LVector4(source[0], source[1], source[2], source[3]); +} diff --git a/panda/src/linmath/lvector4.h b/panda/src/linmath/lvector4.h new file mode 100644 index 0000000000..13e01d6ae7 --- /dev/null +++ b/panda/src/linmath/lvector4.h @@ -0,0 +1,70 @@ +// Filename: lvector4.h +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LVECTOR4_H +#define LVECTOR4_H + +#include + +#include "lvecBase4.h" + +//////////////////////////////////////////////////////////////////// +// Class : LVector4 +// Description : This is a four-component vector distance. +//////////////////////////////////////////////////////////////////// +template +class LVector4 : public LVecBase4 { +public: + INLINE LVector4(); + INLINE LVector4(const LVecBase4 ©); + INLINE LVector4 &operator = (const LVecBase4 ©); + INLINE LVector4 &operator = (NumType fill_value); + INLINE LVector4(NumType fill_value); + INLINE LVector4(NumType x, NumType y, NumType z, NumType w); + + INLINE static LVector4 zero(); + INLINE static LVector4 unit_x(); + INLINE static LVector4 unit_y(); + INLINE static LVector4 unit_z(); + INLINE static LVector4 unit_w(); + + INLINE LVector4 operator - () const; + + INLINE LVecBase4 + operator + (const LVecBase4 &other) const; + INLINE LVector4 + operator + (const LVector4 &other) const; + + INLINE LVecBase4 + operator - (const LVecBase4 &other) const; + INLINE LVector4 + operator - (const LVector4 &other) const; + + INLINE NumType length() const; + INLINE NumType length_squared() const; + INLINE bool normalize(); + INLINE LVector4 operator * (NumType scalar) const; + INLINE LVector4 operator / (NumType scalar) const; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +template +INLINE LVector4 +lcast_to(NumType2 *type, const LVector4 &source); + +#include "lvector4.I" + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LVector4) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, LVector4) + +#endif diff --git a/panda/src/linmath/mathNumbers.cxx b/panda/src/linmath/mathNumbers.cxx new file mode 100644 index 0000000000..f018d450b8 --- /dev/null +++ b/panda/src/linmath/mathNumbers.cxx @@ -0,0 +1,9 @@ +// Filename: mathNumbers.cxx +// Created by: mike (24Sep99) +// +//////////////////////////////////////////////////////////////////// + +#include "mathNumbers.h" +#include + +const double MathNumbers::pi = 4.0 * atan(1); diff --git a/panda/src/linmath/mathNumbers.h b/panda/src/linmath/mathNumbers.h new file mode 100644 index 0000000000..ead5515629 --- /dev/null +++ b/panda/src/linmath/mathNumbers.h @@ -0,0 +1,16 @@ +// Filename: mathNumbers.h +// Created by: mike (23Jan99) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef MATHNUMBERS_H +#define MATHNUMBERS_H + +#include + +class EXPCL_PANDA MathNumbers { +public: + static const double pi; +}; + +#endif diff --git a/panda/src/linmath/nearly_zero.h b/panda/src/linmath/nearly_zero.h new file mode 100644 index 0000000000..31de0a048a --- /dev/null +++ b/panda/src/linmath/nearly_zero.h @@ -0,0 +1,58 @@ +// Filename: nearly_zero.h +// Created by: drose (08Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NEARLY_ZERO_H +#define NEARLY_ZERO_H + + +// The following two functions are defined just to make the +// NEARLY_ZERO() macro work. They each return a suitable nearly-zero +// value for their corresponding numeric type. +INLINE double +get_nearly_zero_value(double) { + return 1.0e-12; +} + +INLINE float +get_nearly_zero_value(float) { + return 1.0e-6f; +} + + +// IS_THRESHOLD_ZERO(value, threshold) returns true if the value is +// within threshold of zero. +#define IS_THRESHOLD_ZERO(value, threshold) \ + ((value) < (threshold) && (value) > -(threshold)) + +// IS_THRESHOLD_EQUAL(value1, value2, threshold) returns true if the +// two values are within threshold of each other. +#define IS_THRESHOLD_EQUAL(value1, value2, threshold) \ + (IS_THRESHOLD_ZERO((value1) - (value2), threshold)) + + +// NEARLY_ZERO(float) returns a number that is considered to be so +// close to zero as not to matter for a float. NEARLY_ZERO(double) +// returns a similar, smaller number for a double. +#define NEARLY_ZERO(NumType) (get_nearly_zero_value((NumType)0)) + +// IS_NEARLY_ZERO(value) returns true if the value is very close to +// zero. +#define IS_NEARLY_ZERO(value) \ + (IS_THRESHOLD_ZERO(value, get_nearly_zero_value(value))) + +// IS_NEARLY_EQUAL(value1, value2) returns true if the two values are +// very close to each other. +#define IS_NEARLY_EQUAL(value1, value2) \ + IS_NEARLY_ZERO((value1) - (value2)) + + +// MAYBE_ZERO(value) returns 0 if the value is nearly zero, and the +// value itself otherwise. +#define MAYBE_ZERO(value) \ + (IS_NEARLY_ZERO(value) ? 0.0 : value) + + +#endif + diff --git a/panda/src/linmath/pta_Colorf.cxx b/panda/src/linmath/pta_Colorf.cxx new file mode 100644 index 0000000000..fe648db736 --- /dev/null +++ b/panda/src/linmath/pta_Colorf.cxx @@ -0,0 +1,11 @@ +// Filename: pta_Colorf.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "pta_Colorf.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/linmath/pta_Colorf.h b/panda/src/linmath/pta_Colorf.h new file mode 100644 index 0000000000..e7f4f0a28b --- /dev/null +++ b/panda/src/linmath/pta_Colorf.h @@ -0,0 +1,37 @@ +// Filename: pta_Colorf.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PTA_COLORF_H +#define PTA_COLORF_H + +#include + +#include "vector_Colorf.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PTA_Colorf +// Description : A pta of Colorfs. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a pta of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the pta again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, RefCountObj); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToBase >); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToArray) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, ConstPointerToArray) + +typedef PointerToArray PTA_Colorf; +typedef ConstPointerToArray CPTA_Colorf; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/linmath/pta_Normalf.cxx b/panda/src/linmath/pta_Normalf.cxx new file mode 100644 index 0000000000..596e3595a8 --- /dev/null +++ b/panda/src/linmath/pta_Normalf.cxx @@ -0,0 +1,11 @@ +// Filename: pta_Normalf.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "pta_Normalf.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/linmath/pta_Normalf.h b/panda/src/linmath/pta_Normalf.h new file mode 100644 index 0000000000..12df8d4eae --- /dev/null +++ b/panda/src/linmath/pta_Normalf.h @@ -0,0 +1,37 @@ +// Filename: pta_Normalf.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PTA_NORMALF_H +#define PTA_NORMALF_H + +#include + +#include "vector_Normalf.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PTA_Normalf +// Description : A pta of Normalfs. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a pta of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the pta again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, RefCountObj); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToBase >); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToArray) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, ConstPointerToArray) + +typedef PointerToArray PTA_Normalf; +typedef ConstPointerToArray CPTA_Normalf; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/linmath/pta_TexCoordf.cxx b/panda/src/linmath/pta_TexCoordf.cxx new file mode 100644 index 0000000000..149e36b800 --- /dev/null +++ b/panda/src/linmath/pta_TexCoordf.cxx @@ -0,0 +1,11 @@ +// Filename: pta_TexCoordf.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "pta_TexCoordf.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/linmath/pta_TexCoordf.h b/panda/src/linmath/pta_TexCoordf.h new file mode 100644 index 0000000000..ae321bac5b --- /dev/null +++ b/panda/src/linmath/pta_TexCoordf.h @@ -0,0 +1,37 @@ +// Filename: pta_TexCoordf.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PTA_TEXCOORDF_H +#define PTA_TEXCOORDF_H + +#include + +#include "vector_TexCoordf.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PTA_TexCoordf +// Description : A pta of TexCoordfs. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a pta of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the pta again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, RefCountObj); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToBase >); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToArray) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, ConstPointerToArray) + +typedef PointerToArray PTA_TexCoordf; +typedef ConstPointerToArray CPTA_TexCoordf; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/linmath/pta_Vertexf.cxx b/panda/src/linmath/pta_Vertexf.cxx new file mode 100644 index 0000000000..1c145bc5b0 --- /dev/null +++ b/panda/src/linmath/pta_Vertexf.cxx @@ -0,0 +1,11 @@ +// Filename: pta_Vertexf.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "pta_Vertexf.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/linmath/pta_Vertexf.h b/panda/src/linmath/pta_Vertexf.h new file mode 100644 index 0000000000..7f3eed9c9b --- /dev/null +++ b/panda/src/linmath/pta_Vertexf.h @@ -0,0 +1,37 @@ +// Filename: pta_Vertexf.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PTA_VERTEXF_H +#define PTA_VERTEXF_H + +#include + +#include "vector_Vertexf.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PTA_Vertexf +// Description : A pta of Vertexfs. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a pta of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the pta again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, RefCountObj); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToBase >); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToArray); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, ConstPointerToArray); + +typedef PointerToArray PTA_Vertexf; +typedef ConstPointerToArray CPTA_Vertexf; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/linmath/test_math.cxx b/panda/src/linmath/test_math.cxx new file mode 100644 index 0000000000..52ad6d6234 --- /dev/null +++ b/panda/src/linmath/test_math.cxx @@ -0,0 +1,135 @@ +// Filename: test_math.cxx +// Created by: drose (14Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "luse.h" +#include "lmatrix.h" +#include "compose_matrix.h" + +#include +#include + +int main(int argc, char *argv[]) { + LOrientationf orientation; // = LQuaternionf::ident_quat(); + orientation.set(LMatrix4f::rotate_mat(-45.0f, LVector3f(0, 0, 1))); + LRotationf rotation(LMatrix4f::rotate_mat(45.0f, LVector3f(0, 0, 1))); + + nout << "Orientation: " << orientation << endl; + nout << "Rotation: " << rotation << endl; + + LQuaternionf composition = orientation * rotation; + + nout << "Composition (o * r): " << composition << endl; + composition.normalize(); + nout << "Composition (after normalize): " << composition << endl; + + LPoint3f p(1, 0, 0); + LMatrix4f m = LMatrix4f::ident_mat(); + + composition.extract_to_matrix(m); + cout << "Rotation => Matrix: " << m << endl; + cout << "Point: " << p << endl; + cout << "Matrix * Point: " << m * p << endl; + + /* + LMatrix4d x = LMatrix4d::ident_mat(); + LMatrix4d y = LMatrix4d::rotate_mat(90.0, LVector3d::up()); + LMatrix4d a = LMatrix4d::translate_mat(10.0, 10.0, 0.0); + + nout << "x is " << x << "\ny is " << y << "\n" + << "x * y is " << x * y << "\n" + << "y * x is " << y * x << "\n" + << "invert(x) is " << invert(x) << "\n" + << "invert(y) is " << invert(y) << "\n" + << "y * a is " << y * a << "\n" + << "invert(y * a) is " << invert(y * a) << "\n" + << "invert(invert(y * a)) is " << invert(invert(y * a)) << "\n" + << "(y * a) * invert(y * a) is " << (y * a) * invert(y * a) << "\n" + << "a * y is " << a * y << "\n" + << "invert(a * y) is " << invert(a * y) << "\n" + << "invert(invert(a * y)) is " << invert(invert(a * y)) << "\n"; + + nout << "a is " << a << "\n" + << "a * y is " << a * y << "\n" + << "y * a is " << y * a << "\n"; + + LVector3d r = LVector3d::right(); + nout << "r is " << r << "\n" + << "r * x is " << r * x << "\n" + << "r * y is " << r * y << "\n" + << "r * invert(y) is " << r * invert(y) << "\n" + << "r * a is " << r * a << "\n"; + + LPoint3d p(0.0, 1.0, 1.0); + nout << "p is " << p << "\n" + << "p * x is " << p * x << "\n" + << "p * y is " << p * y << "\n" + << "p * invert(y) is " << p * invert(y) << "\n" + << "p * a is " << p * a << "\n"; + + LVecBase4d q(0.0, 1.0, 1.0, 1.0); + nout << "q is " << q << "\n" + << "q * x is " << q * x << "\n" + << "q * y is " << q * y << "\n" + << "q * invert(y) is " << q * invert(y) << "\n" + << "q * a is " << q * a << "\n"; + + Normald v1(0,0,1), v2(1,1,1); + Vertexd p1(1,0,1), p2(1,2,3); + Colorf c1(1,1,1,1), c2(0,0,0,0); + + p2 = p2 - v1; + + nout << "v1 = " << v1 + << "\nv2 = " << v2 + << "\np1 = " << p1 + << "\np2 = " << p2 + << "\nc1 = " << c1 + << "\n(c1 == c2) = " << (c1 == c2) + << "\n"; + + { + LVecBase3f hpr(0.0, 0.0, 0.0); + LVecBase3f scale(1.0, 1.0, 1.0); + + if (argc > 3) { + hpr.set(atof(argv[1]), atof(argv[2]), atof(argv[3])); + } + if (argc > 6) { + scale.set(atof(argv[4]), atof(argv[5]), atof(argv[6])); + } + + cerr << "< hpr = " << hpr << " scale = " << scale << "\n"; + LMatrix3f mat; + compose_matrix(mat, scale, hpr); + + if (decompose_matrix(mat, scale, hpr)) { + nout << "> hpr = " << hpr << " scale = " << scale << "\n"; + } else { + nout << "Cannot decompose\n"; + } + } + */ + + /* + for (int p = -90; p < 90; p += 10) { + for (int x = -10; x < 10; x += 5) { + LVecBase3f hpr(0, p, 0); + LVecBase3f xyz(x, x, x); + LVecBase3f scale(1, 1, 1); + nout << "\n< hpr = " << hpr << " xyz = " << xyz << "\n"; + LMatrix4f mat; + compose_matrix(mat, scale, hpr, xyz); + if (decompose_matrix(mat, scale, hpr, xyz)) { + nout << "> hpr = " << hpr << " xyz = " << xyz << "\n"; + } else { + nout << "Cannot decompose\n"; + } + } + } + */ + + return(0); +} + diff --git a/panda/src/linmath/vector_Colorf.cxx b/panda/src/linmath/vector_Colorf.cxx new file mode 100644 index 0000000000..b5eb7311c5 --- /dev/null +++ b/panda/src/linmath/vector_Colorf.cxx @@ -0,0 +1,11 @@ +// Filename: vector_Colorf.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_Colorf.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/linmath/vector_Colorf.h b/panda/src/linmath/vector_Colorf.h new file mode 100644 index 0000000000..753483c87b --- /dev/null +++ b/panda/src/linmath/vector_Colorf.h @@ -0,0 +1,32 @@ +// Filename: vector_Colorf.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_COLORF_H +#define VECTOR_COLORF_H + +#include + +#include "luse.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_Colorf +// Description : A vector of Colorfs. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a vector of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, std::vector) +typedef vector vector_Colorf; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/linmath/vector_LPoint2f.cxx b/panda/src/linmath/vector_LPoint2f.cxx new file mode 100644 index 0000000000..cbf77ba3c6 --- /dev/null +++ b/panda/src/linmath/vector_LPoint2f.cxx @@ -0,0 +1,11 @@ +// Filename: vector_LPoint2f.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_LPoint2f.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/linmath/vector_LPoint2f.h b/panda/src/linmath/vector_LPoint2f.h new file mode 100644 index 0000000000..2be164f9c9 --- /dev/null +++ b/panda/src/linmath/vector_LPoint2f.h @@ -0,0 +1,32 @@ +// Filename: vector_LPoint2f.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_LPOINT2F_H +#define VECTOR_LPOINT2F_H + +#include + +#include "luse.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_LPoint2f +// Description : A vector of LPoint2fs. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a vector of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, std::vector) +typedef vector vector_LPoint2f; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/linmath/vector_Normalf.cxx b/panda/src/linmath/vector_Normalf.cxx new file mode 100644 index 0000000000..1e9805076e --- /dev/null +++ b/panda/src/linmath/vector_Normalf.cxx @@ -0,0 +1,11 @@ +// Filename: vector_Normalf.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_Normalf.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/linmath/vector_Normalf.h b/panda/src/linmath/vector_Normalf.h new file mode 100644 index 0000000000..22c1577615 --- /dev/null +++ b/panda/src/linmath/vector_Normalf.h @@ -0,0 +1,32 @@ +// Filename: vector_Normalf.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_NORMALF_H +#define VECTOR_NORMALF_H + +#include + +#include "luse.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_Normalf +// Description : A vector of Normalfs. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a vector of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, std::vector) +typedef vector vector_Normalf; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/linmath/vector_TexCoordf.h b/panda/src/linmath/vector_TexCoordf.h new file mode 100644 index 0000000000..f3153b1d7e --- /dev/null +++ b/panda/src/linmath/vector_TexCoordf.h @@ -0,0 +1,20 @@ +// Filename: vector_TexCoordf.h +// Created by: drose (11May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_TEXCOORDF_H +#define VECTOR_TEXCOORDF_H + +#include + +#include "vector_LPoint2f.h" + +//////////////////////////////////////////////////////////////////// +// Class : vector_TexCoordf +// Description : This is just another name for vector_LPoint2f. +//////////////////////////////////////////////////////////////////// + +typedef vector_LPoint2f vector_TexCoordf; + +#endif diff --git a/panda/src/linmath/vector_Vertexf.cxx b/panda/src/linmath/vector_Vertexf.cxx new file mode 100644 index 0000000000..40d732c49f --- /dev/null +++ b/panda/src/linmath/vector_Vertexf.cxx @@ -0,0 +1,11 @@ +// Filename: vector_Vertexf.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_Vertexf.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/linmath/vector_Vertexf.h b/panda/src/linmath/vector_Vertexf.h new file mode 100644 index 0000000000..a043f6c30e --- /dev/null +++ b/panda/src/linmath/vector_Vertexf.h @@ -0,0 +1,32 @@ +// Filename: vector_Vertexf.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_VERTEXF_H +#define VECTOR_VERTEXF_H + +#include + +#include "luse.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_Vertexf +// Description : A vector of Vertexfs. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a vector of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, std::vector) +typedef vector vector_Vertexf; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/loader/Sources.pp b/panda/src/loader/Sources.pp new file mode 100644 index 0000000000..4644d13cae --- /dev/null +++ b/panda/src/loader/Sources.pp @@ -0,0 +1,23 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET loader + #define LOCAL_LIBS \ + event graph ipc putil express downloader + + #define SOURCES \ + bamFile.I bamFile.cxx bamFile.h config_loader.cxx config_loader.h \ + loader.I loader.cxx loader.h loaderFileType.cxx loaderFileType.h \ + loaderFileTypeBam.cxx loaderFileTypeBam.h \ + loaderFileTypeRegistry.cxx loaderFileTypeRegistry.h modelPool.I \ + modelPool.cxx modelPool.h + + #define INSTALL_HEADERS \ + bamFile.I bamFile.h loader.I loader.h loaderFileType.h \ + loaderFileTypeBam.h loaderFileTypeRegistry.h modelPool.I \ + modelPool.h + + #define IGATESCAN all + +#end lib_target + diff --git a/panda/src/loader/bamFile.I b/panda/src/loader/bamFile.I new file mode 100644 index 0000000000..7c1b0cecc2 --- /dev/null +++ b/panda/src/loader/bamFile.I @@ -0,0 +1,31 @@ +// Filename: bamFile.I +// Created by: drose (02Jul00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: BamFile::is_valid_read +// Access: Public +// Description: Returns true if the Bam file is open and ready for +// reading with no errors so far detected, or false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool BamFile:: +is_valid_read() const { + return (_reader != (BamReader *)NULL); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BamFile::is_valid_write +// Access: Public +// Description: Returns true if the Bam file is open and ready for +// writing with no errors so far detected, or false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool BamFile:: +is_valid_write() const { + return (_writer != (BamWriter *)NULL); +} + diff --git a/panda/src/loader/bamFile.cxx b/panda/src/loader/bamFile.cxx new file mode 100644 index 0000000000..6787c9e77c --- /dev/null +++ b/panda/src/loader/bamFile.cxx @@ -0,0 +1,247 @@ +// Filename: bamFile.cxx +// Created by: drose (02Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "bamFile.h" +#include "config_loader.h" + +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: BamFile::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +BamFile:: +BamFile() { + _reader = NULL; + _writer = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamFile::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +BamFile:: +~BamFile() { + close(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BamFile::open_read +// Access: Public +// Description: Attempts to open the indicated filename for reading. +// The model_path and bam_path are automatically +// searched for the file. Returns true if successful, +// false on error. +//////////////////////////////////////////////////////////////////// +bool BamFile:: +open_read(const Filename &filename, bool report_errors) { + close(); + + Filename bam_filename(filename); + + bam_filename.resolve_filename(get_bam_path()); + bam_filename.resolve_filename(get_model_path()); + + if (!bam_filename.exists()) { + if (report_errors) { + loader_cat.error() << "Could not find " << bam_filename << "\n"; + } + return false; + } + + loader_cat.info() << "Reading " << bam_filename << "\n"; + + _dfile.setFile(bam_filename.to_os_specific()); + if (!_dfile.open(file::FILE_READ, _bam_header)) { + return false; + } + + _reader = new BamReader(&_dfile); + if (!_reader->init()) { + close(); + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamFile::read_object +// Access: Public +// Description: Reads and returns the next object from the Bam file, +// or NULL if the end of the file has been reached, or +// if there is an error condition. +// +// Note that there is presently no way to differentiate +// a normal end-of-file from an error. +// +// The pointers returned by this method will not be +// valid for use until resolve() is subsequently called. +//////////////////////////////////////////////////////////////////// +TypedWriteable *BamFile:: +read_object() { + if (_reader == (BamReader *)NULL) { + return NULL; + } + + return _reader->read_object(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BamFile::resolve +// Access: Public +// Description: This must be called after one or more objects have +// been read via calls to read_object() in order to +// resolve all internal pointer references in the +// objects read and make all the pointers valid. It +// returns true if successful, false if there is some +// error. +//////////////////////////////////////////////////////////////////// +bool BamFile:: +resolve() { + if (_reader == (BamReader *)NULL) { + return false; + } + + return _reader->resolve(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BamFile::open_write +// Access: Public +// Description: Attempts to open the indicated file for writing. If +// another file by the same name already exists, it will +// be silently truncated. Returns true if successful, +// false otherwise. +//////////////////////////////////////////////////////////////////// +bool BamFile:: +open_write(const Filename &filename, bool) { + close(); + + loader_cat.info() << "Writing " << filename << "\n"; + + _dfile.setFile(filename.to_os_specific()); + if (!_dfile.open(file::FILE_WRITE, _bam_header)) { + return false; + } + + _writer = new BamWriter(&_dfile); + + if (!_writer->init()) { + close(); + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamFile::write_object +// Access: Public +// Description: Writes the indicated object to the Bam file. Returns +// true if successful, false on error. +//////////////////////////////////////////////////////////////////// +bool BamFile:: +write_object(const TypedWriteable *object) { + if (_writer == (BamWriter *)NULL) { + return false; + } + + // We'll go ahead and cast the const TypedWriteable pointer to a + // non-const for issuing the actual write. This allows + // write_datagram to be defined in such a way that it can modify + // itself in some minor way if it must; we assume that no class will + // abuse this privilege and modify its contents significantly during + // write_datagram(). + if (!_writer->write_object((TypedWriteable *)object)) { + close(); + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamFile::close +// Access: Public +// Description: Closes the input or output stream. +//////////////////////////////////////////////////////////////////// +void BamFile:: +close() { + if (_reader != (BamReader *)NULL) { + resolve(); + delete _reader; + _reader = NULL; + } + if (_writer != (BamWriter *)NULL) { + delete _writer; + _writer = NULL; + } + _dfile.close(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: BamFile::get_file_major_ver +// Access: Public +// Description: Returns the major version number of the file +// currently being read, or the system current major +// version number if no file is currently open for +// reading. +//////////////////////////////////////////////////////////////////// +int BamFile:: +get_file_major_ver() { + if (_reader == (BamReader *)NULL) { + return _bam_major_ver; + } + return _reader->get_file_major_ver(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BamFile::get_file_minor_ver +// Access: Public +// Description: Returns the minor version number of the file +// currently being read, or the system current minor +// version number if no file is currently open for +// reading. +//////////////////////////////////////////////////////////////////// +int BamFile:: +get_file_minor_ver() { + if (_reader == (BamReader *)NULL) { + return _bam_minor_ver; + } + return _reader->get_file_minor_ver(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BamFile::get_current_major_ver +// Access: Public +// Description: Returns the system current major version number. +// This is the version number that will be assigned to +// any generated Bam files. +//////////////////////////////////////////////////////////////////// +int BamFile:: +get_current_major_ver() { + return _bam_major_ver; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamFile::get_current_minor_ver +// Access: Public +// Description: Returns the system current minor version number. +// This is the version number that will be assigned to +// any generated Bam files. +//////////////////////////////////////////////////////////////////// +int BamFile:: +get_current_minor_ver() { + return _bam_minor_ver; +} diff --git a/panda/src/loader/bamFile.h b/panda/src/loader/bamFile.h new file mode 100644 index 0000000000..b6b1356d17 --- /dev/null +++ b/panda/src/loader/bamFile.h @@ -0,0 +1,53 @@ +// Filename: bamFile.h +// Created by: drose (02Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BAMFILE_H +#define BAMFILE_H + +#include + +#include + +class BamReader; +class BamWriter; +class TypedWriteable; +class Filename; + +//////////////////////////////////////////////////////////////////// +// Class : BamFile +// Description : The principle public interface to reading and writing +// Bam disk files. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA BamFile { +public: + BamFile(); + ~BamFile(); + + bool open_read(const Filename &filename, bool report_errors = true); + TypedWriteable *read_object(); + bool resolve(); + + bool open_write(const Filename &filename, bool report_errors = true); + bool write_object(const TypedWriteable *object); + + void close(); + INLINE bool is_valid_read() const; + INLINE bool is_valid_write() const; + + int get_file_major_ver(); + int get_file_minor_ver(); + + int get_current_major_ver(); + int get_current_minor_ver(); + +private: + datagram_file _dfile; + BamReader *_reader; + BamWriter *_writer; +}; + +#include "bamFile.I" + +#endif diff --git a/panda/src/loader/config_loader.cxx b/panda/src/loader/config_loader.cxx new file mode 100644 index 0000000000..7af72ba8a4 --- /dev/null +++ b/panda/src/loader/config_loader.cxx @@ -0,0 +1,42 @@ +// Filename: config_loader.cxx +// Created by: drose (19Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_loader.h" +#include "loaderFileType.h" +#include "loaderFileTypeBam.h" +#include "loaderFileTypeRegistry.h" + +#include +#include + +Configure(config_loader); +NotifyCategoryDef(loader, ""); + +// Set this true to support actual asynchronous loads via the +// request_load()/fetch_load() interface to Loader. Set it false to +// map these to blocking, synchronous loads instead. Currently, the +// rest of Panda isn't quite ready for asynchronous loads, so leave +// this false for now. +const bool asynchronous_loads = config_loader.GetBool("asynchronous-loads", false); + +Config::ConfigTable::Symbol *load_file_type = (Config::ConfigTable::Symbol *)NULL; + +ConfigureFn(config_loader) { + load_file_type = new Config::ConfigTable::Symbol; + config_loader.GetAll("load-file-type", *load_file_type); + + LoaderFileType::init_type(); + LoaderFileTypeBam::init_type(); + + LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_ptr(); + + reg->register_type(new LoaderFileTypeBam); +} + +const DSearchPath & +get_bam_path() { + static DSearchPath *bam_path = NULL; + return get_config_path("bam-path", bam_path); +} diff --git a/panda/src/loader/config_loader.h b/panda/src/loader/config_loader.h new file mode 100644 index 0000000000..9e16a4aa40 --- /dev/null +++ b/panda/src/loader/config_loader.h @@ -0,0 +1,23 @@ +// Filename: config_loader.h +// Created by: drose (19Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_LOADER_H +#define CONFIG_LOADER_H + +#include + +#include +#include + +class DSearchPath; + +NotifyCategoryDecl(loader, EXPCL_PANDA, EXPTP_PANDA); + +extern const bool asynchronous_loads; +const DSearchPath &get_bam_path(); + +extern Config::ConfigTable::Symbol *load_file_type; + +#endif diff --git a/panda/src/loader/loader.I b/panda/src/loader/loader.I new file mode 100644 index 0000000000..ed58162c69 --- /dev/null +++ b/panda/src/loader/loader.I @@ -0,0 +1,17 @@ +// Filename: loader.I +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Loader::load_sync +// Access: Public +// Description: Loads the file immediately, waiting for it to complete. +//////////////////////////////////////////////////////////////////// +INLINE PT_Node Loader:: +load_sync(const Filename &filename) const { + if (!_file_types_loaded) { + load_file_types(); + } + return load_file(filename); +} diff --git a/panda/src/loader/loader.cxx b/panda/src/loader/loader.cxx new file mode 100644 index 0000000000..4e6bb23835 --- /dev/null +++ b/panda/src/loader/loader.cxx @@ -0,0 +1,465 @@ +// Filename: loader.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "loader.h" +#include "loaderFileType.h" +#include "loaderFileTypeRegistry.h" +#include "config_loader.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +bool Loader::_file_types_loaded = false; + +//////////////////////////////////////////////////////////////////// +// Struct : LoaderToken +// Description : Holds a request for the loader (load or delete), as +// well as the return information after the request has +// completed. +//////////////////////////////////////////////////////////////////// +class LoaderToken : public ReferenceCount { +public: + INLINE LoaderToken(uint id, Filename path, const string &event_name, + PT_Node node=NULL) : _id(id), _node(node) { + _path = path; + _event_name = event_name; + } + uint _id; + Filename _path; + string _event_name; + PT_Node _node; +}; + +//////////////////////////////////////////////////////////////////// +// Function: Loader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Loader:: +Loader() : AsyncUtility() { + _token_board = new LoaderTokenBoard; +} + +//////////////////////////////////////////////////////////////////// +// Function: Loader::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Loader:: +~Loader() { + destroy_thread(); + delete _token_board; +} + +//////////////////////////////////////////////////////////////////// +// Function: Loader::resolve_filename +// Access: Public +// Description: Looks for the given filename somewhere on the various +// model paths. (The filename extension is used to +// determine which model paths are searched.) If the +// filename is found, updates the Filename to indicate +// the full path; otherwise, leaves the Filename alone. +// +// It is not necessary to call this before loading a +// model; this is just a useful thing to have in case +// you want to look for a file without loading it +// immediately. +//////////////////////////////////////////////////////////////////// +void Loader:: +resolve_filename(Filename &filename) const { + string extension = filename.get_extension(); + + if (extension.empty()) { + resolve_unknown_file_type(filename); + return; + } + + LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_ptr(); + LoaderFileType *requested_type = + reg->get_type_from_extension(extension); + + if (requested_type != (LoaderFileType *)NULL) { + requested_type->resolve_filename(filename); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Loader::request_load +// Access: Public +// Description: Requests an asynchronous load of a file. The request +// will be queued and served by the asynchronous thread. +// If event_name is nonempty, it is the name of the +// event that will be thrown (with the uint id as its +// single parameter) when the loading is completed later. +// +// The return value is an integer which can be used to +// identify this particular request later to +// fetch_load(), or 0 if there has been an error. +//////////////////////////////////////////////////////////////////// +uint Loader:: +request_load(const Filename &filename, const string &event_name) { + if (!_file_types_loaded) { + load_file_types(); + } + + PT(LoaderToken) tok; + if (asynchronous_loads) { + + // Make sure we actually are threaded + if (!_threaded) { + loader_cat.info() + << "Loader::request_load() - create_thread() was " + << "never called! Calling it now..." << endl; + create_thread(); + } + + // We need to grab the lock in order to signal the condition variable + _lock.lock(); + + if (_token_board->_waiting.is_full()) { + loader_cat.error() + << "Loader::request_load() - Too many pending requests\n"; + return 0; + } + + if (loader_cat.is_debug()) { + loader_cat.debug() + << "Load requested for file: " << filename << "\n"; + } + + tok = new LoaderToken(_next_token++, filename, event_name); + _token_board->_waiting.insert(tok); + + _request_cond->signal(); + + _lock.unlock(); + + } else { + // If we're not running asynchronously, process the load request + // directly now. + if (_token_board->_waiting.is_full()) { + loader_cat.error() + << "Loader::request_load() - Too many pending requests\n"; + return 0; + } + + if (loader_cat.is_debug()) { + loader_cat.debug() + << "Load requested for file: " << filename << "\n"; + } + + tok = new LoaderToken(_next_token++, filename, event_name); + _token_board->_waiting.insert(tok); + process_request(); + } + + return tok->_id; +} + +//////////////////////////////////////////////////////////////////// +// Function: Loader::check_load +// Access: Public +// Description: Returns true if the indicated load-request has +// completed and not yet been fetched, false otherwise. +//////////////////////////////////////////////////////////////////// +bool Loader:: +check_load(uint id) { + return _token_board->is_done_token(id); +} + +//////////////////////////////////////////////////////////////////// +// Function: Loader::fetch_load +// Access: Public +// Description: Returns the Node associated with the indicated id +// number (returned by a previous call to request_load), +// or NULL if the request has not yet completed. +//////////////////////////////////////////////////////////////////// +PT_Node Loader:: +fetch_load(uint id) { + PT(LoaderToken) tok = _token_board->get_done_token(id); + if (tok.is_null()) { + loader_cat.debug() + << "Request to fetch id " << id << " which has not yet completed.\n"; + return NULL; + } + PT_Node node = tok->_node; + return node; +} + +//////////////////////////////////////////////////////////////////// +// Function: Loader::load_file_types +// Access: Private, Static +// Description: Loads up all of the dynamic libraries named in a +// load-file-type Configure variable. Presumably this +// will make the various file types available for +// runtime loading. +//////////////////////////////////////////////////////////////////// +void Loader:: +load_file_types() { + nassertv(load_file_type != (Config::ConfigTable::Symbol *)NULL); + + if (!_file_types_loaded) { + Config::ConfigTable::Symbol::iterator ti; + for (ti = load_file_type->begin(); ti != load_file_type->end(); ++ti) { + Filename dlname = Filename::dso_filename("lib" + (*ti).Val() + ".so"); + loader_cat.info() + << "loading file type module: " << dlname.to_os_specific() << endl; + void *tmp = load_dso(dlname.to_os_specific()); + if (tmp == (void *)NULL) { + loader_cat.info() + << "Unable to load: " << load_dso_error() << endl; + } + } + _file_types_loaded = true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Loader::process_request +// Access: Private +// Description: Serves any requests on the token board, moving them +// to the done queue. +//////////////////////////////////////////////////////////////////// +bool Loader:: +process_request() { + if (_shutdown) { + if (loader_cat.is_debug()) + loader_cat.debug() + << "Loader shutting down...\n"; + return false; + } + + // If there is actually a request token - process it + while (!_token_board->_waiting.is_empty()) { + PT(LoaderToken) tok = _token_board->_waiting.extract(); + tok->_node = load_file(tok->_path); + if (tok->_node == NULL) { + loader_cat.error() + << "Loader::callback() - couldn't find file: " + << tok->_path << "\n"; + } else { + _token_board->_done.insert(tok); + + // Throw a "done" event now. + if (!tok->_event_name.empty()) { + PT_Event done = new Event(tok->_event_name); + done->add_parameter(EventParameter((int)tok->_id)); + throw_event(done); + } + } + + if (loader_cat.is_debug()) { + loader_cat.debug() + << "loading complete for " << tok->_path << "\n"; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Loader::load_file +// Access: Private +// Description: Loads a single scene graph file, if possible. +// Returns the Node that is the root of the file, or +// NULL if the file cannot be loaded. +//////////////////////////////////////////////////////////////////// +PT_Node Loader:: +load_file(const Filename &filename) const { + string extension = filename.get_extension(); + + if (extension.empty()) { + return load_unknown_file_type(filename); + } + + LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_ptr(); + LoaderFileType *requested_type = + reg->get_type_from_extension(extension); + + if (requested_type == (LoaderFileType *)NULL) { + loader_cat.error() + << "Extension of file " << filename + << " is unrecognized; cannot load.\n"; + loader_cat.error(false) + << "Currently known scene file types are:\n"; + reg->write_types(loader_cat.error(false), 2); + return NULL; + } + + Filename requested_filename = filename; + // Ask the loader type to look for the file along its paths. + requested_type->resolve_filename(requested_filename); + + if (loader_cat.is_debug()) { + loader_cat.debug() + << "Loading " << requested_type->get_name() << " file: " + << requested_filename << "\n"; + } + + PT_Node result = requested_type->load_file(requested_filename, true); + return result; +} + +class LoaderConsiderFile { +public: + Filename _path; + LoaderFileType *_type; + + bool operator < (const LoaderConsiderFile &other) const { + return _path.compare_timestamps(other._path) > 0; + } +}; + + +//////////////////////////////////////////////////////////////////// +// Function: Loader::load_unknown_file_type +// Access: Private +// Description: Attempts to guess which file is meant when a file +// with no extension is given. Looks around for a file +// with a suitable extension for each of our known file +// types, and loads the most recent file available of +// any file type. +//////////////////////////////////////////////////////////////////// +PT_Node Loader:: +load_unknown_file_type(const Filename &filename) const { + typedef vector Files; + Files files; + + // First, build up a list of all of the possible files it could be. + LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_ptr(); + int num_types = reg->get_num_types(); + + if (num_types == 0) { + loader_cat.error() + << "Can't load file " << filename + << "; no scene file types are known.\n"; + return (Node *)NULL; + } + + for (int i = 0; i < num_types; i++) { + LoaderConsiderFile consider; + consider._type = reg->get_type(i); + consider._path = filename; + consider._path.set_extension(consider._type->get_extension()); + + // Ask the loader type to look for the file along its paths. + consider._type->resolve_filename(consider._path); + + if (consider._path.exists()) { + files.push_back(consider); + } + } + + if (files.empty()) { + loader_cat.error() + << "Couldn't find file " << filename << " as:\n"; + for (int i = 0; i < num_types; i++) { + Filename p = filename; + p.set_extension(reg->get_type(i)->get_extension()); + loader_cat.error(false) + << " " << p << "\n"; + } + return (Node *)NULL; + } + + // Now sort the list into order by timestamp, from newest to oldest. + sort(files.begin(), files.end()); + + // And try to load each file one at a time. + Files::const_iterator fi; + + if (loader_cat.is_debug()) { + loader_cat.debug() + << "Loading " << filename << ", one of " << files.size() + << " possible types:\n"; + for (fi = files.begin(); fi != files.end(); ++fi) { + loader_cat.debug(false) + << " " << (*fi)._path << "\n"; + } + } + + for (fi = files.begin(); fi != files.end(); ++fi) { + const LoaderConsiderFile &consider = (*fi); + PT_Node result = consider._type->load_file(consider._path, false); + if (result != (Node *)NULL) { + return result; + } + if (loader_cat.is_debug()) { + loader_cat.debug() + << "Couldn't read " << consider._type->get_name() + << " file " << consider._path << "\n"; + } + } + + loader_cat.error() + << "Cannot read " << files.front()._path << "\n"; + + return (Node *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: Loader::resolve_unknown_file_type +// Access: Private +// Description: Attempts to guess which file is meant when a file +// with no extension is given. Looks around for a file +// with a suitable extension for each of our known file +// types, and updates the filename if a suitable match +// is found. +//////////////////////////////////////////////////////////////////// +void Loader:: +resolve_unknown_file_type(Filename &filename) const { + typedef vector Files; + Files files; + + // First, build up a list of all of the possible files it could be. + LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_ptr(); + int num_types = reg->get_num_types(); + + if (num_types == 0) { + // No known file types! + return; + } + + for (int i = 0; i < num_types; i++) { + LoaderConsiderFile consider; + consider._type = reg->get_type(i); + consider._path = filename; + consider._path.set_extension(consider._type->get_extension()); + + // Ask the loader type to look for the file along its paths. + consider._type->resolve_filename(consider._path); + + if (consider._path.exists()) { + files.push_back(consider); + } + } + + if (files.empty()) { + // Couldn't find it anywhere. + return; + } + + // Now sort the list into order by timestamp, from newest to oldest. + sort(files.begin(), files.end()); + + // And get the first one. + filename = files.front()._path; +} diff --git a/panda/src/loader/loader.h b/panda/src/loader/loader.h new file mode 100644 index 0000000000..688db18551 --- /dev/null +++ b/panda/src/loader/loader.h @@ -0,0 +1,56 @@ +// Filename: loader.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef LOADER_H +#define LOADER_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include +#include +#include +#include + +class LoaderToken; + +//////////////////////////////////////////////////////////////////// +// Class : Loader +// Description : Handles database loading through asynchronous +// threading +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Loader : public AsyncUtility { +public: + Loader(); + ~Loader(); + + void resolve_filename(Filename &filename) const; + + INLINE PT_Node load_sync(const Filename &filename) const; + + uint request_load(const Filename &filename, const string &event_name); + bool check_load(uint id); + PT_Node fetch_load(uint id); + +private: + static void load_file_types(); + static bool _file_types_loaded; + + virtual bool process_request(void); + PT_Node load_file(const Filename &filename) const; + PT_Node load_unknown_file_type(const Filename &filename) const; + void resolve_unknown_file_type(Filename &filename) const; + + typedef TokenBoard LoaderTokenBoard; + LoaderTokenBoard *_token_board; +}; + +#include "loader.I" + +#endif diff --git a/panda/src/loader/loaderFileType.cxx b/panda/src/loader/loaderFileType.cxx new file mode 100644 index 0000000000..fc848cde77 --- /dev/null +++ b/panda/src/loader/loaderFileType.cxx @@ -0,0 +1,40 @@ +// Filename: loaderFileType.cxx +// Created by: drose (20Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "loaderFileType.h" + +TypeHandle LoaderFileType::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileType::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +LoaderFileType:: +LoaderFileType() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileType::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +LoaderFileType:: +~LoaderFileType() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileType::resolve_filename +// Access: Public, Virtual +// Description: Searches for the indicated filename on whatever paths +// are appropriate to this file type, and updates it if +// it is found. It is not necessary to call this before +// calling load_file(), but it doesn't hurt; this is +// useful for when the loader needs to know the full +// pathname to the exact file it will be loading. +//////////////////////////////////////////////////////////////////// +void LoaderFileType:: +resolve_filename(Filename &) const { +} diff --git a/panda/src/loader/loaderFileType.h b/panda/src/loader/loaderFileType.h new file mode 100644 index 0000000000..e3734d1fcc --- /dev/null +++ b/panda/src/loader/loaderFileType.h @@ -0,0 +1,55 @@ +// Filename: loaderFileType.h +// Created by: drose (20Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LOADERFILETYPE_H +#define LOADERFILETYPE_H + +#include + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : LoaderFileType +// Description : This is the base class for a family of scene-graph +// file types that the Loader supports. Each kind of +// loader that's available should define a corresponding +// LoaderFileType object and register itself. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA LoaderFileType : public TypedObject { +protected: + LoaderFileType(); + +public: + virtual ~LoaderFileType(); + + virtual string get_name() const=0; + virtual string get_extension() const=0; + + virtual void resolve_filename(Filename &path) const; + virtual PT_Node load_file(const Filename &path, bool report_errors) const=0; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedObject::init_type(); + register_type(_type_handle, "LoaderFileType", + TypedObject::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif + diff --git a/panda/src/loader/loaderFileTypeBam.cxx b/panda/src/loader/loaderFileTypeBam.cxx new file mode 100644 index 0000000000..591f5da598 --- /dev/null +++ b/panda/src/loader/loaderFileTypeBam.cxx @@ -0,0 +1,106 @@ +// Filename: loaderFileTypeBam.cxx +// Created by: jason (21Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "loaderFileTypeBam.h" +#include "config_loader.h" +#include "bamFile.h" + +#include +#include + +TypeHandle LoaderFileTypeBam::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeBam::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +LoaderFileTypeBam:: +LoaderFileTypeBam() +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeBam::get_name +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string LoaderFileTypeBam:: +get_name() const +{ + return "Bam"; +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeBam::get_extension +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string LoaderFileTypeBam:: +get_extension() const +{ + return "bam"; +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeBam::resolve_filename +// Access: Public, Virtual +// Description: Searches for the indicated filename on whatever paths +// are appropriate to this file type, and updates it if +// it is found. It is not necessary to call this before +// calling load_file(), but it doesn't hurt; this is +// useful for when the loader needs to know the full +// pathname to the exact file it will be loading. +//////////////////////////////////////////////////////////////////// +void LoaderFileTypeBam:: +resolve_filename(Filename &path) const { + path.resolve_filename(get_bam_path()); + path.resolve_filename(get_model_path()); +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeBam::load_file +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PT_Node LoaderFileTypeBam:: +load_file(const Filename &path, bool report_errors) const +{ + BamFile bam_file; + if (!bam_file.open_read(path, report_errors)) { + return NULL; + } + + PT_Node result; + + TypedWriteable *object = bam_file.read_object(); + if (object == TypedWriteable::Null) { + if (report_errors) { + loader_cat.error() << "Bam file " << path << " is empty.\n"; + } + + } else if (!object->is_of_type(Node::get_class_type())) { + if (report_errors) { + loader_cat.error() + << "Bam file " << path + << " contains a " << object->get_type() << ", not a Node.\n"; + } + + } else { + result = DCAST(Node, object); + + if (report_errors) { + if (bam_file.read_object() != TypedWriteable::Null) { + loader_cat.warning() + << "Ignoring extra objects in " << path << "\n"; + } + } + } + + bam_file.resolve(); + + return result; +} + diff --git a/panda/src/loader/loaderFileTypeBam.h b/panda/src/loader/loaderFileTypeBam.h new file mode 100644 index 0000000000..f3704b49ce --- /dev/null +++ b/panda/src/loader/loaderFileTypeBam.h @@ -0,0 +1,46 @@ +// Filename: loaderFileTypeBam.h +// Created by: drose (20Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LOADERFILETYPEBAM_H +#define LOADERFILETYPEBAM_H + +#include + +#include "loaderFileType.h" + +//////////////////////////////////////////////////////////////////// +// Class : LoaderFileTypeEgg +// Description : This defines the Loader interface to read Egg files. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA LoaderFileTypeBam : public LoaderFileType { +public: + LoaderFileTypeBam(); + + virtual string get_name() const; + virtual string get_extension() const; + + virtual void resolve_filename(Filename &path) const; + virtual PT_Node load_file(const Filename &path, bool report_errors) const; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + LoaderFileType::init_type(); + register_type(_type_handle, "LoaderFileTypeBam", + LoaderFileType::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif + diff --git a/panda/src/loader/loaderFileTypeRegistry.cxx b/panda/src/loader/loaderFileTypeRegistry.cxx new file mode 100644 index 0000000000..4785a90977 --- /dev/null +++ b/panda/src/loader/loaderFileTypeRegistry.cxx @@ -0,0 +1,138 @@ +// Filename: loaderFileTypeRegistry.cxx +// Created by: drose (20Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "loaderFileTypeRegistry.h" +#include "loaderFileType.h" +#include "config_loader.h" + +#include +#include + +#include + +LoaderFileTypeRegistry *LoaderFileTypeRegistry::_global_ptr; + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeRegistry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +LoaderFileTypeRegistry:: +LoaderFileTypeRegistry() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeRegistry::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +LoaderFileTypeRegistry:: +~LoaderFileTypeRegistry() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeRegistry::get_ptr +// Access: Public, Static +// Description: Returns a pointer to the global LoaderFileTypeRegistry +// object. +//////////////////////////////////////////////////////////////////// +LoaderFileTypeRegistry *LoaderFileTypeRegistry:: +get_ptr() { + if (_global_ptr == (LoaderFileTypeRegistry *)NULL) { + _global_ptr = new LoaderFileTypeRegistry; + } + return _global_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeRegistry::get_num_types +// Access: Public +// Description: Returns the total number of types registered. +//////////////////////////////////////////////////////////////////// +int LoaderFileTypeRegistry:: +get_num_types() const { + return _types.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeRegistry::get_type +// Access: Public +// Description: Returns the nth type registered. +//////////////////////////////////////////////////////////////////// +LoaderFileType *LoaderFileTypeRegistry:: +get_type(int n) const { + nassertr(n >= 0 && n < _types.size(), NULL); + return _types[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeRegistry::get_type_from_extension +// Access: Public +// Description: Determines the type of the file based on the indicated +// extension (without a leading dot). Returns NULL if +// the extension matches no known file types. +//////////////////////////////////////////////////////////////////// +LoaderFileType *LoaderFileTypeRegistry:: +get_type_from_extension(const string &extension) const { + Extensions::const_iterator ei; + ei = _extensions.find(downcase(extension)); + if (ei == _extensions.end()) { + // Nothing matches that extension. + return NULL; + } + + return (*ei).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeRegistry::write_types +// Access: Public +// Description: Writes a list of supported file types to the +// indicated output stream, one per line. +//////////////////////////////////////////////////////////////////// +void LoaderFileTypeRegistry:: +write_types(ostream &out, int indent_level) const { + if (_types.empty()) { + indent(out, indent_level) << "(No file types are known).\n"; + } else { + Types::const_iterator ti; + for (ti = _types.begin(); ti != _types.end(); ++ti) { + LoaderFileType *type = (*ti); + string name = type->get_name(); + indent(out, indent_level) << name; + indent(out, max(30 - (int)name.length(), 0)) + << " ." << type->get_extension() << "\n"; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeRegistry::register_type +// Access: Public +// Description: Defines a new LoaderFileType in the universe. +//////////////////////////////////////////////////////////////////// +void LoaderFileTypeRegistry:: +register_type(LoaderFileType *type) { + // Make sure we haven't already registered this type. + if (find(_types.begin(), _types.end(), type) != _types.end()) { + loader_cat.warning() + << "Attempt to register LoaderFileType " << type->get_name() + << " (" << type->get_type() << ") more than once.\n"; + return; + } + + _types.push_back(type); + + string extension = downcase(type->get_extension()); + Extensions::const_iterator ei; + ei = _extensions.find(extension); + if (ei != _extensions.end()) { + loader_cat.warning() + << "Multiple LoaderFileTypes registered that use the extension " + << extension << "\n"; + } else { + _extensions.insert(Extensions::value_type(extension, type)); + } +} diff --git a/panda/src/loader/loaderFileTypeRegistry.h b/panda/src/loader/loaderFileTypeRegistry.h new file mode 100644 index 0000000000..0dce007152 --- /dev/null +++ b/panda/src/loader/loaderFileTypeRegistry.h @@ -0,0 +1,51 @@ +// Filename: loaderFileTypeRegistry.h +// Created by: drose (20Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LOADERFILETYPEREGISTRY_H +#define LOADERFILETYPEREGISTRY_H + +#include + +#include +#include + +class LoaderFileType; +class Filename; + +//////////////////////////////////////////////////////////////////// +// Class : LoaderFileTypeRegistry +// Description : This class maintains the set of all known +// LoaderFileTypes in the universe. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA LoaderFileTypeRegistry { +protected: + LoaderFileTypeRegistry(); + +public: + ~LoaderFileTypeRegistry(); + + static LoaderFileTypeRegistry *get_ptr(); + + int get_num_types() const; + LoaderFileType *get_type(int n) const; + + LoaderFileType *get_type_from_extension(const string &extension) const; + + void write_types(ostream &out, int indent_level = 0) const; + + void register_type(LoaderFileType *type); + +private: + typedef vector Types; + Types _types; + + typedef map Extensions; + Extensions _extensions; + + static LoaderFileTypeRegistry *_global_ptr; +}; + +#endif + diff --git a/panda/src/loader/modelPool.I b/panda/src/loader/modelPool.I new file mode 100644 index 0000000000..6d75701712 --- /dev/null +++ b/panda/src/loader/modelPool.I @@ -0,0 +1,93 @@ +// Filename: modelPool.I +// Created by: drose (25Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ModelPool::has_model +// Access: Public, Static +// Description: Returns true if the model has ever been loaded, +// false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool ModelPool:: +has_model(const string &filename) { + return get_ptr()->ns_has_model(filename); +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelPool::verify_model +// Access: Public, Static +// Description: Loads the given filename up as a model, if it has +// not already been loaded, and returns true to indicate +// success, or false to indicate failure. If this +// returns true, it is guaranteed that a subsequent call +// to load_model() with the same model name will +// return a valid Node pointer. +//////////////////////////////////////////////////////////////////// +INLINE bool ModelPool:: +verify_model(const string &filename) { + return load_model(filename) != (Node *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelPool::load_model +// Access: Public, Static +// Description: Loads the given filename up as a model, if it has +// not already been loaded, and returns the new model. +// If a model with the same filename was previously +// loaded, returns that one instead. If the model +// file cannot be found, returns NULL. +//////////////////////////////////////////////////////////////////// +INLINE PT_Node ModelPool:: +load_model(const string &filename) { + return get_ptr()->ns_load_model(filename); +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelPool::add_model +// Access: Public, Static +// Description: Adds the indicated already-loaded model to the +// pool. The model will always replace any +// previously-loaded model in the pool that had the +// same filename. +//////////////////////////////////////////////////////////////////// +INLINE void ModelPool:: +add_model(const string &filename, Node *model) { + get_ptr()->ns_add_model(filename, model); +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelPool::release_model +// Access: Public, Static +// Description: Removes the indicated model from the pool, +// indicating it will never be loaded again; the model +// may then be freed. If this function is never called, +// a reference count will be maintained on every model +// every loaded, and models will never be freed. +//////////////////////////////////////////////////////////////////// +INLINE void ModelPool:: +release_model(const string &filename) { + get_ptr()->ns_release_model(filename); +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelPool::release_all_models +// Access: Public, Static +// Description: Releases all models in the pool and restores the +// pool to the empty state. +//////////////////////////////////////////////////////////////////// +INLINE void ModelPool:: +release_all_models() { + get_ptr()->ns_release_all_models(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelPool::Constructor +// Access: Private +// Description: The constructor is not intended to be called +// directly; there's only supposed to be one ModelPool +// in the universe and it constructs itself. +//////////////////////////////////////////////////////////////////// +INLINE ModelPool:: +ModelPool() { +} diff --git a/panda/src/loader/modelPool.cxx b/panda/src/loader/modelPool.cxx new file mode 100644 index 0000000000..5372239a51 --- /dev/null +++ b/panda/src/loader/modelPool.cxx @@ -0,0 +1,105 @@ +// Filename: modelPool.cxx +// Created by: drose (25Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "modelPool.h" +#include "loader.h" +#include "config_loader.h" + + +ModelPool *ModelPool::_global_ptr = (ModelPool *)NULL; + +static Loader _model_loader; + +//////////////////////////////////////////////////////////////////// +// Function: ModelPool::ns_has_model +// Access: Private +// Description: The nonstatic implementation of has_model(). +//////////////////////////////////////////////////////////////////// +bool ModelPool:: +ns_has_model(const string &filename) { + Models::const_iterator ti; + ti = _models.find(filename); + if (ti != _models.end()) { + // This model was previously loaded. + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelPool::ns_load_model +// Access: Private +// Description: The nonstatic implementation of load_model(). +//////////////////////////////////////////////////////////////////// +PT_Node ModelPool:: +ns_load_model(const string &filename) { + Models::const_iterator ti; + ti = _models.find(filename); + if (ti != _models.end()) { + // This model was previously loaded. + return (*ti).second; + } + + loader_cat.info() + << "Loading model " << filename << "\n"; + PT(Node) node = _model_loader.load_sync(filename); + if (node.is_null()) { + // This model was not found. + return (Node *)NULL; + } + + _models[filename] = node; + return node; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelPool::ns_add_model +// Access: Private +// Description: The nonstatic implementation of add_model(). +//////////////////////////////////////////////////////////////////// +void ModelPool:: +ns_add_model(const string &filename, Node *model) { + // We blow away whatever model was there previously, if any. + _models[filename] = model; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelPool::ns_release_model +// Access: Private +// Description: The nonstatic implementation of release_model(). +//////////////////////////////////////////////////////////////////// +void ModelPool:: +ns_release_model(const string &filename) { + Models::iterator ti; + ti = _models.find(filename); + if (ti != _models.end()) { + _models.erase(ti); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelPool::ns_release_all_models +// Access: Private +// Description: The nonstatic implementation of release_all_models(). +//////////////////////////////////////////////////////////////////// +void ModelPool:: +ns_release_all_models() { + _models.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelPool::get_ptr +// Access: Private, Static +// Description: Initializes and/or returns the global pointer to the +// one ModelPool object in the system. +//////////////////////////////////////////////////////////////////// +ModelPool *ModelPool:: +get_ptr() { + if (_global_ptr == (ModelPool *)NULL) { + _global_ptr = new ModelPool; + } + return _global_ptr; +} diff --git a/panda/src/loader/modelPool.h b/panda/src/loader/modelPool.h new file mode 100644 index 0000000000..d79781623b --- /dev/null +++ b/panda/src/loader/modelPool.h @@ -0,0 +1,65 @@ +// Filename: modelPool.h +// Created by: drose (25Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MODELPOOL_H +#define MODELPOOL_H + +#include + +#include +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : ModelPool +// Description : This is the preferred interface for loading models. +// It unifies all references to the same filename, so +// that multiple attempts to load the same model will +// return the same pointer. Note that the default +// behavior is thus to make instances: use with caution. +// Use the copy_subgraph() method on Node (or use +// NodePath::copy_to) to make modifiable copies of the +// node. +// +// Unlike TexturePool, this class does not automatically +// resolve the model filenames before loading, so a +// relative path and an absolute path to the same model +// will appear to be different filenames. +// +// This does not presently support asynchronous loading, +// although it wouldn't be *too* difficult to add. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ModelPool { +public: + INLINE static bool has_model(const string &filename); + INLINE static bool verify_model(const string &filename); + INLINE static PT_Node load_model(const string &filename); + + INLINE static void add_model(const string &filename, Node *model); + INLINE static void release_model(const string &filename); + INLINE static void release_all_models(); + +private: + INLINE ModelPool(); + + bool ns_has_model(const string &filename); + PT_Node ns_load_model(const string &filename); + void ns_add_model(const string &filename, Node *model); + void ns_release_model(const string &filename); + void ns_release_all_models(); + + static ModelPool *get_ptr(); + + static ModelPool *_global_ptr; + typedef map Models; + Models _models; +}; + +#include "modelPool.I" + +#endif + + diff --git a/panda/src/mathutil/Sources.pp b/panda/src/mathutil/Sources.pp new file mode 100644 index 0000000000..8123add973 --- /dev/null +++ b/panda/src/mathutil/Sources.pp @@ -0,0 +1,41 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET mathutil + #define LOCAL_LIBS \ + linmath putil + + #define SOURCES \ + boundingHexahedron.I boundingHexahedron.cxx boundingHexahedron.h \ + boundingLine.I boundingLine.cxx boundingLine.h boundingSphere.I \ + boundingSphere.cxx boundingSphere.h boundingVolume.I \ + boundingVolume.cxx boundingVolume.h config_mathutil.cxx \ + config_mathutil.h finiteBoundingVolume.cxx finiteBoundingVolume.h \ + geometricBoundingVolume.I geometricBoundingVolume.cxx \ + geometricBoundingVolume.h look_at.I look_at.cxx look_at.h \ + omniBoundingVolume.I omniBoundingVolume.cxx omniBoundingVolume.h \ + plane.I plane.N plane.cxx plane.h rotate_to.cxx rotate_to.h + + #define INSTALL_HEADERS \ + boundingHexahedron.I boundingHexahedron.h boundingLine.I \ + boundingLine.h boundingSphere.I boundingSphere.h boundingVolume.I \ + boundingVolume.h config_mathutil.h finiteBoundingVolume.h frustum.I \ + frustum.h geometricBoundingVolume.I geometricBoundingVolume.h \ + look_at.I look_at.h mathHelpers.I mathHelpers.h mathutil.h \ + omniBoundingVolume.I omniBoundingVolume.h plane.I plane.h \ + rotate_to.h + + #define IGATESCAN all + +#end lib_target + +#begin test_bin_target + #define TARGET test_mathutil + #define LOCAL_LIBS \ + mathutil + + #define SOURCES \ + test_mathutil.cxx + +#end test_bin_target + diff --git a/panda/src/mathutil/boundingHexahedron.I b/panda/src/mathutil/boundingHexahedron.I new file mode 100644 index 0000000000..6a2069bfac --- /dev/null +++ b/panda/src/mathutil/boundingHexahedron.I @@ -0,0 +1,30 @@ +// Filename: boundingHexahedron.I +// Created by: drose (03Oct99) +// +//////////////////////////////////////////////////////////////////// + +INLINE BoundingHexahedron:: +BoundingHexahedron() { +} + +INLINE int BoundingHexahedron:: +get_num_points() const { + return num_points; +} + +INLINE LPoint3f BoundingHexahedron:: +get_point(int n) const { + nassertr(n >= 0 && n < num_points, LPoint3f(0.0, 0.0, 0.0)); + return _points[n]; +} + +INLINE int BoundingHexahedron:: +get_num_planes() const { + return num_planes; +} + +INLINE Planef BoundingHexahedron:: +get_plane(int n) const { + nassertr(n >= 0 && n < num_planes, Planef()); + return _planes[n]; +} diff --git a/panda/src/mathutil/boundingHexahedron.cxx b/panda/src/mathutil/boundingHexahedron.cxx new file mode 100644 index 0000000000..84206f1aea --- /dev/null +++ b/panda/src/mathutil/boundingHexahedron.cxx @@ -0,0 +1,336 @@ +// Filename: boundingHexahedron.cxx +// Created by: drose (03Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "boundingHexahedron.h" +#include "boundingSphere.h" +#include "config_mathutil.h" + +#include +#include + +TypeHandle BoundingHexahedron::_type_handle; + +BoundingHexahedron:: +BoundingHexahedron(const Frustumf &frustum, bool is_ortho, + CoordinateSystem cs) { + if (cs == CS_default) { + cs = default_coordinate_system; + } + + float fs = 1.0; + if (!is_ortho) { + fs = frustum._ffar / frustum._fnear; + } + + // We build the points based on a Z-up right-handed frustum. If the + // requested coordinate system is otherwise, we'll convert it in a + // second pass. + _points[0].set(frustum._l * fs, frustum._ffar, frustum._b * fs); + _points[1].set(frustum._r * fs, frustum._ffar, frustum._b * fs); + _points[2].set(frustum._r * fs, frustum._ffar, frustum._t * fs); + _points[3].set(frustum._l * fs, frustum._ffar, frustum._t * fs); + _points[4].set(frustum._l, frustum._fnear, frustum._b); + _points[5].set(frustum._r, frustum._fnear, frustum._b); + _points[6].set(frustum._r, frustum._fnear, frustum._t); + _points[7].set(frustum._l, frustum._fnear, frustum._t); + + _flags = 0; + + // Now fix the coordinate system, if necessary. + if (cs == CS_zup_right) { + set_centroid(); + set_planes(); + } else { + xform(LMatrix4f::convert_mat(CS_zup_right, cs)); + } +} + +BoundingVolume *BoundingHexahedron:: +make_copy() const { + return new BoundingHexahedron(*this); +} + +LPoint3f BoundingHexahedron:: +get_min() const { + nassertr(!is_empty(), LPoint3f(0.0, 0.0, 0.0)); + nassertr(!is_infinite(), LPoint3f(0.0, 0.0, 0.0)); + int i; + LPoint3f m = _points[0]; + for (i = 1; i < num_points; i++) { + m.set(min(m[0], _points[i][0]), + min(m[1], _points[i][1]), + min(m[2], _points[i][2])); + } + return m; +} + +LPoint3f BoundingHexahedron:: +get_max() const { + nassertr(!is_empty(), LPoint3f(0.0, 0.0, 0.0)); + nassertr(!is_infinite(), LPoint3f(0.0, 0.0, 0.0)); + int i; + LPoint3f m = _points[0]; + for (i = 1; i < num_points; i++) { + m.set(max(m[0], _points[i][0]), + max(m[1], _points[i][1]), + max(m[2], _points[i][2])); + } + return m; +} + +LPoint3f BoundingHexahedron:: +get_approx_center() const { + nassertr(!is_empty(), LPoint3f(0.0, 0.0, 0.0)); + nassertr(!is_infinite(), LPoint3f(0.0, 0.0, 0.0)); + return _centroid; +} + +void BoundingHexahedron:: +xform(const LMatrix4f &mat) { + if (!is_empty() && !is_infinite()) { + for (int i = 0; i < num_points; i++) { + _points[i] = _points[i] * mat; + } + set_centroid(); + set_planes(); + } +} + +void BoundingHexahedron:: +output(ostream &out) const { + if (is_empty()) { + out << "bhexahedron, empty"; + } else if (is_infinite()) { + out << "bhexahedron, infinite"; + } else { + out << "bhexahedron, min " << get_min() << " max " << get_max(); + } +} + +void BoundingHexahedron:: +write(ostream &out, int indent_level) const { + if (is_empty()) { + indent(out, indent_level) << "bhexahedron, empty\n"; + } else if (is_infinite()) { + out << "bhexahedron, infinite\n"; + } else { + indent(out, indent_level) + << "bhexahedron, min " << get_min() << " max " << get_max() << ":\n"; + int i; + for (i = 0; i < num_points; i++) { + indent(out, indent_level + 2) << _points[i] << "\n"; + } + indent(out, indent_level + 2) << "centroid is " << _centroid << "\n"; + } +} + +bool BoundingHexahedron:: +extend_other(BoundingVolume *other) const { + return other->extend_by_hexahedron(this); +} + +bool BoundingHexahedron:: +around_other(BoundingVolume *other, + const BoundingVolume **first, + const BoundingVolume **last) const { + return other->around_hexahedrons(first, last); +} + +int BoundingHexahedron:: +contains_other(const BoundingVolume *other) const { + return other->contains_hexahedron(this); +} + + +bool BoundingHexahedron:: +extend_by_point(const LPoint3f &) { + mathutil_cat.error() + << "BoundingHexahedron::extend_by_point() called\n"; + return false; +} + +bool BoundingHexahedron:: +extend_by_sphere(const BoundingSphere *) { + mathutil_cat.error() + << "BoundingHexahedron::extend_by_sphere() called\n"; + return false; +} + +bool BoundingHexahedron:: +extend_by_hexahedron(const BoundingHexahedron *) { + mathutil_cat.error() + << "BoundingHexahedron::extend_by_hexahedron() called\n"; + return false; +} + +bool BoundingHexahedron:: +around_points(const LPoint3f *, const LPoint3f *) { + mathutil_cat.error() + << "BoundingHexahedron::around_points() called\n"; + return false; +} + +bool BoundingHexahedron:: +around_spheres(const BoundingVolume **, + const BoundingVolume **) { + mathutil_cat.error() + << "BoundingHexahedron::around_spheres() called\n"; + return false; +} + +bool BoundingHexahedron:: +around_hexahedrons(const BoundingVolume **, + const BoundingVolume **) { + mathutil_cat.error() + << "BoundingHexahedron::around_hexahedrons() called\n"; + return false; +} + +int BoundingHexahedron:: +contains_point(const LPoint3f &point) const { + if (is_empty()) { + return IF_no_intersection; + + } else if (is_infinite()) { + return IF_possible | IF_some | IF_all; + + } else { + // The hexahedron contains the point iff the point is behind all of + // the planes. + for (int i = 0; i < num_planes; i++) { + const Planef &p = _planes[i]; + if (p.dist_to_plane(point) > 0.0) { + return IF_no_intersection; + } + } + return IF_possible | IF_some | IF_all; + } +} + +int BoundingHexahedron:: +contains_lineseg(const LPoint3f &a, const LPoint3f &b) const { + if (is_empty()) { + return IF_no_intersection; + + } else if (is_infinite()) { + return IF_possible | IF_some | IF_all; + + } else { + // The hexahedron does not contains the line segment if both points + // are in front of any one plane. + for (int i = 0; i < num_planes; i++) { + const Planef &p = _planes[i]; + if (p.dist_to_plane(a) > 0.0 || + p.dist_to_plane(b) > 0.0) { + return IF_no_intersection; + } + } + + // If there is no plane that both points are in front of, the + // hexahedron may or may not contain the line segment. For the + // moment, we won't bother to check that more thoroughly, though. + return IF_possible; + } +} + +int BoundingHexahedron:: +contains_sphere(const BoundingSphere *sphere) const { + nassertr(!is_empty(), 0); + + // The hexahedron contains the sphere iff the sphere is at least + // partly behind all of the planes. + const LPoint3f ¢er = sphere->get_center(); + float radius = sphere->get_radius(); + + int result = IF_possible | IF_some | IF_all; + + for (int i = 0; i < num_planes; i++) { + const Planef &p = _planes[i]; + float dist = p.dist_to_plane(center); + + if (dist > radius) { + // The sphere is completely in front of this plane; it's thus + // completely outside of the hexahedron. + return IF_no_intersection; + + } else if (dist > -radius) { + // The sphere is not completely behind this plane, but some of + // it is. + result &= ~IF_all; + } + } + + return result; +} + +int BoundingHexahedron:: +contains_hexahedron(const BoundingHexahedron *hexahedron) const { + nassertr(!is_empty(), 0); + nassertr(!hexahedron->is_empty(), 0); + + // Check minmax. + LPoint3f min1 = get_min(); + LPoint3f min2 = hexahedron->get_min(); + LPoint3f max1 = get_max(); + LPoint3f max2 = hexahedron->get_max(); + + if (min1[0] > max2[0] || min1[1] > max2[1] || min1[2] > max2[2] || + min2[0] > max1[0] || min2[1] > max1[1] || min2[2] > max1[2] || + max1[0] < min2[0] || max1[1] < min2[1] || max1[2] < min2[2] || + max2[0] < min1[0] || max2[1] < min1[1] || max2[2] < min1[2]) { + return IF_no_intersection; + } + + int result = IF_possible | IF_all; + + for (int i = 0; i < num_points; i++) { + if (contains_point(hexahedron->_points[i])) { + result |= IF_some; + } else { + result &= ~IF_all; + } + } + + return result; +} + +void BoundingHexahedron:: +set_planes() { + _planes[0] = Planef(_points[0], _points[3], _points[2]); + + // Test to see if we have accidentally inverted our frustum by + // transforming it with a -1 matrix. We do this by ensuring that + // the centroid is in front of all of the planes (actually, we only + // need to test the first plane). + if (_planes[0].dist_to_plane(_centroid) >= 0) { + // Oops! We're flipped! Rebuild the planes in the opposite + // direction. + _planes[0] = Planef(_points[0], _points[2], _points[3]); + _planes[1] = Planef(_points[0], _points[5], _points[1]); + _planes[2] = Planef(_points[1], _points[6], _points[2]); + _planes[3] = Planef(_points[2], _points[7], _points[3]); + _planes[4] = Planef(_points[3], _points[4], _points[0]); + _planes[5] = Planef(_points[4], _points[7], _points[6]); + + nassertv(_planes[0].dist_to_plane(_centroid) < 0); + + } else { + // No, a perfectly sane universe. + _planes[1] = Planef(_points[0], _points[1], _points[5]); + _planes[2] = Planef(_points[1], _points[2], _points[6]); + _planes[3] = Planef(_points[2], _points[3], _points[7]); + _planes[4] = Planef(_points[3], _points[0], _points[4]); + _planes[5] = Planef(_points[4], _points[6], _points[7]); + } +} + +void BoundingHexahedron:: +set_centroid() { + LPoint3f net = _points[0]; + for (int i = 1; i < num_points; i++) { + net += _points[i]; + } + _centroid = net / (float)num_points; +} diff --git a/panda/src/mathutil/boundingHexahedron.h b/panda/src/mathutil/boundingHexahedron.h new file mode 100644 index 0000000000..88a695337f --- /dev/null +++ b/panda/src/mathutil/boundingHexahedron.h @@ -0,0 +1,109 @@ +// Filename: boundingHexahedron.h +// Created by: drose (03Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BOUNDINGHEXAHEDRON_H +#define BOUNDINGHEXAHEDRON_H + +#include + +#include "finiteBoundingVolume.h" +#include "frustum.h" +#include "plane.h" + +#include + + +/////////////////////////////////////////////////////////////////// +// Class : BoundingHexahedron +// Description : This defines a bounding convex hexahedron. It is +// typically used to represent a frustum, but may +// represent any enclosing convex hexahedron. +// +// This class does not support any of the around() or +// extend_by() functions, but all other functionality +// should be well-defined. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA BoundingHexahedron : public FiniteBoundingVolume { +public: + INLINE BoundingHexahedron(); + BoundingHexahedron(const Frustumf &frustum, bool is_ortho, + CoordinateSystem cs = CS_default); + virtual BoundingVolume *make_copy() const; + + virtual LPoint3f get_min() const; + virtual LPoint3f get_max() const; + + virtual LPoint3f get_approx_center() const; + virtual void xform(const LMatrix4f &mat); + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + + INLINE int get_num_points() const; + INLINE LPoint3f get_point(int n) const; + INLINE int get_num_planes() const; + INLINE Planef get_plane(int n) const; + +protected: + virtual bool extend_other(BoundingVolume *other) const; + virtual bool around_other(BoundingVolume *other, + const BoundingVolume **first, + const BoundingVolume **last) const; + virtual int contains_other(const BoundingVolume *other) const; + + + virtual bool extend_by_point(const LPoint3f &point); + virtual bool extend_by_sphere(const BoundingSphere *sphere); + virtual bool extend_by_hexahedron(const BoundingHexahedron *hexahedron); + + virtual bool around_points(const LPoint3f *first, + const LPoint3f *last); + virtual bool around_spheres(const BoundingVolume **first, + const BoundingVolume **last); + virtual bool around_hexahedrons(const BoundingVolume **first, + const BoundingVolume **last); + + virtual int contains_point(const LPoint3f &point) const; + virtual int contains_lineseg(const LPoint3f &a, const LPoint3f &b) const; + virtual int contains_sphere(const BoundingSphere *sphere) const; + virtual int contains_hexahedron(const BoundingHexahedron *hexahedron) const; + +private: + void set_planes(); + void set_centroid(); + +private: + enum { + num_points = 8, + num_planes = 6 + }; + LPoint3f _points[num_points]; + Planef _planes[num_planes]; + LPoint3f _centroid; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + FiniteBoundingVolume::init_type(); + register_type(_type_handle, "BoundingHexahedron", + FiniteBoundingVolume::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend class BoundingSphere; +}; + +#include "boundingHexahedron.I" + +#endif diff --git a/panda/src/mathutil/boundingLine.I b/panda/src/mathutil/boundingLine.I new file mode 100644 index 0000000000..9ef228ecc9 --- /dev/null +++ b/panda/src/mathutil/boundingLine.I @@ -0,0 +1,33 @@ +// Filename: boundingLine.I +// Created by: drose (04Jul00) +// +//////////////////////////////////////////////////////////////////// + +INLINE BoundingLine:: +BoundingLine() { +} + +INLINE BoundingLine:: +BoundingLine(const LPoint3f &a, const LPoint3f &b) : + _origin(a), _vector(b - a) +{ + _vector.normalize(); + _flags = 0; + nassertd(!_origin.is_nan() && !_vector.is_nan()) { + _flags = F_empty; + } +} + +INLINE const LPoint3f &BoundingLine:: +get_point_a() const { + nassertr(!is_empty(), _origin); + nassertr(!is_infinite(), _origin); + return _origin; +} + +INLINE LPoint3f BoundingLine:: +get_point_b() const { + nassertr(!is_empty(), _origin); + nassertr(!is_infinite(), _origin); + return _origin + _vector; +} diff --git a/panda/src/mathutil/boundingLine.cxx b/panda/src/mathutil/boundingLine.cxx new file mode 100644 index 0000000000..ec60fe013b --- /dev/null +++ b/panda/src/mathutil/boundingLine.cxx @@ -0,0 +1,114 @@ +// Filename: boundingLine.cxx +// Created by: drose (04Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "boundingLine.h" +#include "boundingSphere.h" +#include "config_mathutil.h" + +#include + +TypeHandle BoundingLine::_type_handle; + +BoundingVolume *BoundingLine:: +make_copy() const { + return new BoundingLine(*this); +} + +LPoint3f BoundingLine:: +get_approx_center() const { + nassertr(!is_empty(), LPoint3f(0.0, 0.0, 0.0)); + nassertr(!is_infinite(), LPoint3f(0.0, 0.0, 0.0)); + return (get_point_a() + get_point_b()) / 2.0; +} + +void BoundingLine:: +xform(const LMatrix4f &mat) { + nassertv(!mat.is_nan()); + + if (!is_empty() && !is_infinite()) { + _origin = _origin * mat; + _vector = _vector * mat; + _vector.normalize(); + } +} + +void BoundingLine:: +output(ostream &out) const { + if (is_empty()) { + out << "bline, empty"; + } else if (is_infinite()) { + out << "bline, infinite"; + } else { + out << "bline, (" << _origin << ") - (" << _origin + _vector << ")"; + } +} + +bool BoundingLine:: +extend_other(BoundingVolume *other) const { + return other->extend_by_line(this); +} + +bool BoundingLine:: +around_other(BoundingVolume *other, + const BoundingVolume **first, + const BoundingVolume **last) const { + return other->around_lines(first, last); +} + +int BoundingLine:: +contains_other(const BoundingVolume *other) const { + return other->contains_line(this); +} + + +bool BoundingLine:: +extend_by_line(const BoundingLine *line) { + nassertr(!line->is_empty() && !line->is_infinite(), false); + nassertr(!is_infinite(), false); + + if (is_empty()) { + _origin = line->_origin; + _vector = line->_vector; + _flags = 0; + } else { + _flags = F_infinite; + } + return true; +} + +int BoundingLine:: +contains_sphere(const BoundingSphere *sphere) const { + nassertr(!is_empty() && !is_infinite(), 0); + nassertr(!sphere->is_empty() && !sphere->is_infinite(), 0); + + float r = sphere->get_radius(); + + if (r * r >= sqr_dist_to_line(sphere->get_center())) { + return IF_possible | IF_some; + } else { + return IF_no_intersection; + } +} + +float BoundingLine:: +sqr_dist_to_line(const LPoint3f &point) const { + nassertr(!point.is_nan(), 0.0); + nassertr(!is_empty() && !is_infinite(), 0.0); + nassertr(!_vector.almost_equal(LVector3f(0.0, 0.0, 0.0)), 0.0); + + // The formula for the distance from a point to the line based on + // the quadratic equation. + + float A = dot(_vector, _vector); + nassertr(A != 0.0, 0.0); + LVector3f fc = _origin - point; + float B = 2.0 * dot(_vector, fc); + float fc_d2 = dot(fc, fc); + + float r2 = fc_d2 - B*B / 4.0*A; + + nassertr(!cnan(r2), 0.0); + return r2; +} diff --git a/panda/src/mathutil/boundingLine.h b/panda/src/mathutil/boundingLine.h new file mode 100644 index 0000000000..26b35f4b0a --- /dev/null +++ b/panda/src/mathutil/boundingLine.h @@ -0,0 +1,78 @@ +// Filename: boundingLine.h +// Created by: drose (04Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BOUNDINGLINE_H +#define BOUNDINGLINE_H + +#include + +#include "geometricBoundingVolume.h" + +/////////////////////////////////////////////////////////////////// +// Class : BoundingLine +// Description : This funny bounding volume is an infinite line with +// no thickness and extending to infinity in both +// directions. +// +// Note that it *always* extends in both directions, +// despite the fact that you specify two points to the +// constructor. These are not endpoints, they are two +// arbitrary points on the line. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA BoundingLine : public GeometricBoundingVolume { +public: + INLINE BoundingLine(); + INLINE BoundingLine(const LPoint3f &a, const LPoint3f &b); + virtual BoundingVolume *make_copy() const; + + virtual LPoint3f get_approx_center() const; + virtual void xform(const LMatrix4f &mat); + + virtual void output(ostream &out) const; + + INLINE const LPoint3f &get_point_a() const; + INLINE LPoint3f get_point_b() const; + +protected: + virtual bool extend_other(BoundingVolume *other) const; + virtual bool around_other(BoundingVolume *other, + const BoundingVolume **first, + const BoundingVolume **last) const; + virtual int contains_other(const BoundingVolume *other) const; + + virtual bool extend_by_line(const BoundingLine *line); + + virtual int contains_sphere(const BoundingSphere *sphere) const; + + float sqr_dist_to_line(const LPoint3f &point) const; + +private: + LPoint3f _origin; + LVector3f _vector; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GeometricBoundingVolume::init_type(); + register_type(_type_handle, "BoundingLine", + GeometricBoundingVolume::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend class BoundingSphere; +}; + +#include "boundingLine.I" + +#endif diff --git a/panda/src/mathutil/boundingSphere.I b/panda/src/mathutil/boundingSphere.I new file mode 100644 index 0000000000..40a20580b5 --- /dev/null +++ b/panda/src/mathutil/boundingSphere.I @@ -0,0 +1,33 @@ +// Filename: boundingSphere.I +// Created by: drose (02Oct99) +// +//////////////////////////////////////////////////////////////////// + +INLINE BoundingSphere:: +BoundingSphere() { +} + +INLINE BoundingSphere:: +BoundingSphere(const LPoint3f ¢er, float radius) : + _center(center), _radius(radius) +{ + _flags = 0; + nassertd(!_center.is_nan() && !cnan(_radius)) { + _flags = F_empty; + } +} + +INLINE const LPoint3f &BoundingSphere:: +get_center() const { + nassertr(!is_empty(), _center); + nassertr(!is_infinite(), _center); + return _center; +} + +INLINE float BoundingSphere:: +get_radius() const { + nassertr(!is_empty(), 0.0); + nassertr(!is_infinite(), 0.0); + return _radius; +} + diff --git a/panda/src/mathutil/boundingSphere.cxx b/panda/src/mathutil/boundingSphere.cxx new file mode 100644 index 0000000000..0d5da76cea --- /dev/null +++ b/panda/src/mathutil/boundingSphere.cxx @@ -0,0 +1,452 @@ +// Filename: boundingSphere.cxx +// Created by: drose (01Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "boundingSphere.h" +#include "boundingHexahedron.h" +#include "boundingLine.h" +#include "config_mathutil.h" + +#include +#include + +TypeHandle BoundingSphere::_type_handle; + +BoundingVolume *BoundingSphere:: +make_copy() const { + return new BoundingSphere(*this); +} + +LPoint3f BoundingSphere:: +get_min() const { + nassertr(!is_empty(), LPoint3f(0.0, 0.0, 0.0)); + nassertr(!is_infinite(), LPoint3f(0.0, 0.0, 0.0)); + return LPoint3f(_center[0] - _radius, + _center[1] - _radius, + _center[2] - _radius); +} + +LPoint3f BoundingSphere:: +get_max() const { + nassertr(!is_empty(), LPoint3f(0.0, 0.0, 0.0)); + nassertr(!is_infinite(), LPoint3f(0.0, 0.0, 0.0)); + return LPoint3f(_center[0] + _radius, + _center[1] + _radius, + _center[2] + _radius); +} + +LPoint3f BoundingSphere:: +get_approx_center() const { + nassertr(!is_empty(), LPoint3f(0.0, 0.0, 0.0)); + nassertr(!is_infinite(), LPoint3f(0.0, 0.0, 0.0)); + return get_center(); +} + +void BoundingSphere:: +xform(const LMatrix4f &mat) { + nassertv(!mat.is_nan()); + + if (!is_empty() && !is_infinite()) { + // First, determine the longest axis of the matrix, in case it + // contains a non-proportionate scale. + LVector3f x = mat.get_row3(0); + LVector3f y = mat.get_row3(1); + LVector3f z = mat.get_row3(2); + float xd = dot(x, x); + float yd = dot(y, y); + float zd = dot(z, z); + + float scale; + if (xd < yd) { + if (yd < zd) { + scale = sqrtf(zd); + } else { + scale = sqrtf(yd); + } + } else { + if (xd < zd) { + scale = sqrtf(zd); + } else { + scale = sqrtf(xd); + } + } + + // Transform the center + _center = _center * mat; + + // And the radius. + _radius *= scale; + } +} + +void BoundingSphere:: +output(ostream &out) const { + if (is_empty()) { + out << "bsphere, empty"; + } else if (is_infinite()) { + out << "bsphere, infinite"; + } else { + out << "bsphere, c (" << _center << "), r " << _radius; + } +} + +bool BoundingSphere:: +extend_other(BoundingVolume *other) const { + return other->extend_by_sphere(this); +} + +bool BoundingSphere:: +around_other(BoundingVolume *other, + const BoundingVolume **first, + const BoundingVolume **last) const { + return other->around_spheres(first, last); +} + +int BoundingSphere:: +contains_other(const BoundingVolume *other) const { + return other->contains_sphere(this); +} + + +bool BoundingSphere:: +extend_by_point(const LPoint3f &point) { + nassertr(!point.is_nan(), false); + + if (is_empty()) { + _center = point; + _radius = 0.0; + _flags = 0; + } else if (!is_infinite()) { + LVector3f v = point - _center; + float dist2 = dot(v, v); + if (dist2 > _radius * _radius) { + _radius = sqrtf(dist2); + } + } + return true; +} + +bool BoundingSphere:: +extend_by_sphere(const BoundingSphere *sphere) { + nassertr(!sphere->is_empty() && !sphere->is_infinite(), false); + nassertr(!is_infinite(), false); + + if (is_empty()) { + _center = sphere->_center; + _radius = sphere->_radius; + _flags = 0; + } else { + float dist = length(sphere->_center - _center); + + _radius = max(_radius, dist + sphere->_radius); + } + return true; +} + +bool BoundingSphere:: +extend_by_hexahedron(const BoundingHexahedron *hexahedron) { + return extend_by_finite(hexahedron); +} + +bool BoundingSphere:: +extend_by_finite(const FiniteBoundingVolume *volume) { + nassertr(!volume->is_empty(), false); + + LVector3f min1 = volume->get_min(); + LVector3f max1 = volume->get_max(); + + if (is_empty()) { + _center = (min1 + max1) / 2.0; + _radius = length(LVector3f(max1 - _center)); + _flags = 0; + } else { + LVector3f v = max1 - _center; + float dist2 = dot(v, v); + + if (dist2 > _radius * _radius) { + _radius = sqrtf(dist2); + } + } + + return true; +} + +bool BoundingSphere:: +around_points(const LPoint3f *first, const LPoint3f *last) { + nassertr(first != last, false); + + // First, get the minmax of all the points to construct a bounding + // box. + const LPoint3f *p = first; + +#ifndef NDEBUG + // Skip any NaN points. + int skipped_nan = 0; + while (p != last && (*p).is_nan()) { + ++p; + ++skipped_nan; + } + if (p == last) { + mathutil_cat.warning() + << "BoundingSphere around NaN\n"; + return false; + } +#endif + + LPoint3f min_box = *p; + LPoint3f max_box = *p; + ++p; + +#ifndef NDEBUG + // Skip more NaN points. + while (p != last && (*p).is_nan()) { + ++p; + ++skipped_nan; + } +#endif + + if (p == last) { + // Only one point; we have a radius of zero. This is not the same + // thing as an empty sphere, because our volume contains one + // point; an empty sphere contains no points. + _radius = 0.0; + + } else { + // More than one point; we have a nonzero radius. + while (p != last) { +#ifndef NDEBUG + // Skip more NaN points. + if ((*p).is_nan()) { + ++skipped_nan; + } else +#endif + { + min_box.set(min(min_box[0], (*p)[0]), + min(min_box[1], (*p)[1]), + min(min_box[2], (*p)[2])); + max_box.set(max(max_box[0], (*p)[0]), + max(max_box[1], (*p)[1]), + max(max_box[2], (*p)[2])); + } + ++p; + } + + // Now take the center of the bounding box as the center of the sphere. + _center = (min_box + max_box) / 2.0; + + // Now walk back through to get the max distance from center. + float max_dist2 = 0.0; + for (p = first; p != last; ++p) { + LVector3f v = (*p) - _center; + float dist2 = dot(v, v); + max_dist2 = max(max_dist2, dist2); + } + + _radius = sqrtf(max_dist2); + } + +#ifndef NDEBUG + if (skipped_nan != 0) { + mathutil_cat.warning() + << "BoundingSphere ignored " << skipped_nan << " NaN points of " + << (last - first) << " total.\n"; + } +#endif + + _flags = 0; + + return true; +} + +bool BoundingSphere:: +around_spheres(const BoundingVolume **first, + const BoundingVolume **last) { + return around_finite(first, last); +} + +bool BoundingSphere:: +around_hexahedrons(const BoundingVolume **first, + const BoundingVolume **last) { + return around_finite(first, last); +} + +bool BoundingSphere:: +around_finite(const BoundingVolume **first, + const BoundingVolume **last) { + nassertr(first != last, false); + + // We're given a set of bounding volumes, at least the first one of + // which is guaranteed to be finite and nonempty. Some others may + // not be. + + // First, get the minmax of all the points to construct a bounding + // box. + const BoundingVolume **p = first; + nassertr(!(*p)->is_empty() && !(*p)->is_infinite(), false); + nassertr((*p)->is_of_type(FiniteBoundingVolume::get_class_type()), false); + const FiniteBoundingVolume *vol = DCAST(FiniteBoundingVolume, *p); + LPoint3f min_box = vol->get_min(); + LPoint3f max_box = vol->get_max(); + + bool any_unknown = false; + + for (++p; p != last; ++p) { + nassertr(!(*p)->is_infinite(), false); + if (!(*p)->is_empty() && + (*p)->is_of_type(FiniteBoundingVolume::get_class_type())) { + const FiniteBoundingVolume *vol = DCAST(FiniteBoundingVolume, *p); + LPoint3f min1 = vol->get_min(); + LPoint3f max1 = vol->get_max(); + min_box.set(min(min_box[0], min1[0]), + min(min_box[1], min1[1]), + min(min_box[2], min1[2])); + max_box.set(max(max_box[0], max1[0]), + max(max_box[1], max1[1]), + max(max_box[2], max1[2])); + + if (!(*p)->is_of_type(BoundingSphere::get_class_type())) { + any_unknown = true; + } + } + } + + // Now take the center of the bounding box as the center of the sphere. + _center = (min_box + max_box) / 2.0; + + if (any_unknown) { + // If we have any volumes in the list that we don't know what to + // do with, we'll have to make the bounding sphere large enough to + // enclose the bounding box. Less than ideal, but too bad. + _radius = length(max_box - _center); + + } else { + // Otherwise, we do understand all the volumes in the list; make + // the sphere as tight as we can. + _radius = 0.0; + for (p = first; p != last; ++p) { + if (!(*p)->is_empty()) { + if ((*p)->is_of_type(BoundingSphere::get_class_type())) { + const BoundingSphere *sphere = DCAST(BoundingSphere, *p); + float dist = length(sphere->_center - _center); + _radius = max(_radius, dist + sphere->_radius); + } else { + // Shouldn't get here, unless we missed a type from above. + mathutil_cat.error() + << "Unexpected type in BoundingSphere::around_finite()\n"; + nassertr(false, false); + } + } + } + } + + _flags = 0; + return true; +} + +int BoundingSphere:: +contains_point(const LPoint3f &point) const { + nassertr(!point.is_nan(), IF_no_intersection); + + if (is_empty()) { + return IF_no_intersection; + + } else if (is_infinite()) { + return IF_possible | IF_some | IF_all; + + } else { + LVector3f v = point - _center; + float dist2 = dot(v, v); + return (dist2 <= _radius * _radius) ? + IF_possible | IF_some | IF_all : IF_no_intersection; + } +} + +int BoundingSphere:: +contains_lineseg(const LPoint3f &a, const LPoint3f &b) const { + nassertr(!a.is_nan() && !b.is_nan(), IF_no_intersection); + + if (a == b) { + return contains_point(a); + } + if (is_empty()) { + return IF_no_intersection; + + } else if (is_infinite()) { + return IF_possible | IF_some | IF_all; + + } else { + LPoint3f from = a; + LVector3f delta = b - a; + float t1, t2; + + // Solve the equation for the intersection of a line with a sphere + // using the quadratic equation. + float A = dot(delta, delta); + + nassertr(A != 0.0, 0); // Trivial line segment. + + LVector3f fc = from - _center; + float B = 2.0 * dot(delta, fc); + float C = dot(fc, fc) - _radius * _radius; + + float radical = B*B - 4.0*A*C; + + if (IS_NEARLY_ZERO(radical)) { + // Tangent. + t1 = t2 = -B / (2.0*A); + return (t1 >= 0.0 && t1 <= 1.0) ? + IF_possible | IF_some : IF_no_intersection; + } + + if (radical < 0.0) { + // No real roots: no intersection with the line. + return IF_no_intersection; + } + + float sqrt_radical = sqrtf(radical); + t1 = ( -B - sqrt_radical ) / (2.0*A); + t2 = ( -B + sqrt_radical ) / (2.0*A); + + if (t1 >= 0.0 && t2 <= 1.0) { + return IF_possible | IF_some | IF_all; + } else if (t1 <= 1.0 && t2 >= 0.0) { + return IF_possible | IF_some; + } else { + return IF_no_intersection; + } + } +} + +int BoundingSphere:: +contains_sphere(const BoundingSphere *sphere) const { + nassertr(!is_empty() && !is_infinite(), 0); + nassertr(!sphere->is_empty() && !sphere->is_infinite(), 0); + + LVector3f v = sphere->_center - _center; + float dist2 = dot(v, v); + + if (_radius >= sphere->_radius && + dist2 <= (_radius - sphere->_radius) * (_radius - sphere->_radius)) { + // The other sphere is completely within this sphere. + return IF_possible | IF_some | IF_all; + + } else if (dist2 > (_radius + sphere->_radius) * (_radius + sphere->_radius)) { + // The other sphere is completely outside this sphere. + return IF_no_intersection; + + } else { + // The other sphere is partially within this sphere. + return IF_possible | IF_some; + } +} + +int BoundingSphere:: +contains_hexahedron(const BoundingHexahedron *hexahedron) const { + return hexahedron->contains_sphere(this) & ~IF_all; +} + +int BoundingSphere:: +contains_line(const BoundingLine *line) const { + return line->contains_sphere(this) & ~IF_all; +} diff --git a/panda/src/mathutil/boundingSphere.h b/panda/src/mathutil/boundingSphere.h new file mode 100644 index 0000000000..13bb7ae33a --- /dev/null +++ b/panda/src/mathutil/boundingSphere.h @@ -0,0 +1,91 @@ +// Filename: boundingSphere.h +// Created by: drose (01Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BOUNDINGSPHERE_H +#define BOUNDINGSPHERE_H + +#include + +#include "finiteBoundingVolume.h" + +/////////////////////////////////////////////////////////////////// +// Class : BoundingSphere +// Description : This defines a bounding sphere, consisting of a +// center and a radius. It is always a sphere, and +// never an ellipsoid or other quadric. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA BoundingSphere : public FiniteBoundingVolume { +public: + INLINE BoundingSphere(); + INLINE BoundingSphere(const LPoint3f ¢er, float radius); + virtual BoundingVolume *make_copy() const; + + virtual LPoint3f get_min() const; + virtual LPoint3f get_max() const; + + virtual LPoint3f get_approx_center() const; + virtual void xform(const LMatrix4f &mat); + + virtual void output(ostream &out) const; + + INLINE const LPoint3f &get_center() const; + INLINE float get_radius() const; + +protected: + virtual bool extend_other(BoundingVolume *other) const; + virtual bool around_other(BoundingVolume *other, + const BoundingVolume **first, + const BoundingVolume **last) const; + virtual int contains_other(const BoundingVolume *other) const; + + + virtual bool extend_by_point(const LPoint3f &point); + virtual bool extend_by_sphere(const BoundingSphere *sphere); + virtual bool extend_by_hexahedron(const BoundingHexahedron *hexahedron); + bool extend_by_finite(const FiniteBoundingVolume *volume); + + virtual bool around_points(const LPoint3f *first, + const LPoint3f *last); + virtual bool around_spheres(const BoundingVolume **first, + const BoundingVolume **last); + virtual bool around_hexahedrons(const BoundingVolume **first, + const BoundingVolume **last); + bool around_finite(const BoundingVolume **first, + const BoundingVolume **last); + + virtual int contains_point(const LPoint3f &point) const; + virtual int contains_lineseg(const LPoint3f &a, const LPoint3f &b) const; + virtual int contains_hexahedron(const BoundingHexahedron *hexahedron) const; + virtual int contains_sphere(const BoundingSphere *sphere) const; + virtual int contains_line(const BoundingLine *line) const; + +private: + LPoint3f _center; + float _radius; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + FiniteBoundingVolume::init_type(); + register_type(_type_handle, "BoundingSphere", + FiniteBoundingVolume::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend class BoundingHexahedron; +}; + +#include "boundingSphere.I" + +#endif diff --git a/panda/src/mathutil/boundingVolume.I b/panda/src/mathutil/boundingVolume.I new file mode 100644 index 0000000000..9f6a497184 --- /dev/null +++ b/panda/src/mathutil/boundingVolume.I @@ -0,0 +1,112 @@ +// Filename: boundingVolume.I +// Created by: drose (01Oct99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BoundingVolume:: +BoundingVolume() { + _flags = F_empty; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::is_empty +// Access: Public +// Description: Any kind of volume might be empty. This is a +// degenerate volume that contains no points; it's not +// the same as, for instance, a sphere with radius zero, +// since that contains one point (the center). It +// intersects with no other volumes. +//////////////////////////////////////////////////////////////////// +INLINE bool BoundingVolume:: +is_empty() const { + return (_flags & F_empty) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::is_infinite +// Access: Public +// Description: The other side of the empty coin is an infinite +// volume. This is a degenerate state of a normally +// finite volume that contains all points. (Note that +// some kinds of infinite bounding volumes, like binary +// separating planes, do not contain all points and thus +// correctly return is_infinite() == false, even though +// they are technically infinite. This is a special +// case of the word 'infinite' meaning the volume covers +// all points in space.) +// +// It completely intersects with all other volumes +// except empty volumes. +//////////////////////////////////////////////////////////////////// +INLINE bool BoundingVolume:: +is_infinite() const { + return (_flags & F_infinite) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::set_infinite +// Access: Public +// Description: Marks the volume as infinite, even if it is normally +// finite. You can think of this as an infinite +// extend_by() operation. +//////////////////////////////////////////////////////////////////// +INLINE void BoundingVolume:: +set_infinite() { + _flags = F_infinite; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::extend_by +// Access: Public +// Description: Increases the size of the volume to include the given +// volume. +//////////////////////////////////////////////////////////////////// +INLINE bool BoundingVolume:: +extend_by(const BoundingVolume *vol) { + if (vol->is_infinite()) { + set_infinite(); + + } else if (!vol->is_empty()) { + // This is a double-dispatch. We call this virtual function on the + // volume we were given, which will in turn call the appropriate + // virtual function in our own class to perform the operation. + return vol->extend_other(this); + } + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::contains +// Access: Public +// Description: Returns the appropriate set of IntersectionFlags to +// indicate the amount of intersection with the +// indicated volume. +//////////////////////////////////////////////////////////////////// +INLINE int BoundingVolume:: +contains(const BoundingVolume *vol) const { + if (is_empty() || vol->is_empty()) { + return IF_no_intersection; + + } else if (is_infinite()) { + return IF_possible | IF_some | IF_all; + + } else if (vol->is_infinite()) { + return IF_possible | IF_some; + } + + // This is a double-dispatch. We call this virtual function on the + // volume we were given, which will in turn call the appropriate + // virtual function in our own class to perform the operation. + return vol->contains_other(this); +} + +INLINE ostream &operator << (ostream &out, const BoundingVolume &bound) { + bound.output(out); + return out; +} diff --git a/panda/src/mathutil/boundingVolume.cxx b/panda/src/mathutil/boundingVolume.cxx new file mode 100644 index 0000000000..a32f14acda --- /dev/null +++ b/panda/src/mathutil/boundingVolume.cxx @@ -0,0 +1,191 @@ +// Filename: boundingVolume.cxx +// Created by: drose (01Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "boundingVolume.h" +#include "finiteBoundingVolume.h" + +#include + +TypeHandle BoundingVolume::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::around +// Access: Public +// Description: Resets the volume to enclose only the volumes +// indicated. Returns true if successful, false if the +// volume doesn't know how to do that or can't do that. +//////////////////////////////////////////////////////////////////// +bool BoundingVolume:: +around(const BoundingVolume **first, const BoundingVolume **last) { + _flags = F_empty; + + // Skip any empty volumes at the beginning of the list. We want to + // get to the first real volume. + while (first != last && (*first)->is_empty()) { + if ((*first)->is_infinite()) { + // If we go around an infinite volume, we're infinite too. + _flags = F_infinite; + return true; + } + ++first; + } + + bool okflag = true; + + if (first != last) { + // Check for more infinite bounding volumes in the list. + const BoundingVolume **bvi; + for (bvi = first; bvi != last; ++bvi) { + if ((*bvi)->is_infinite()) { + _flags = F_infinite; + return true; + } + } + + // This is a double-dispatch. We call this virtual function on + // the volume we were given, which will in turn call the + // appropriate virtual function in our own class to perform the + // operation. + if (!(*first)->around_other(this, first, last)) { + okflag = false; + } + } + + return okflag; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void BoundingVolume:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << *this << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::extend_by_sphere +// Access: Protected, Virtual +// Description: Double-dispatch support: called by extend_other() +// when the type we're extending by is known to be a +// sphere. +//////////////////////////////////////////////////////////////////// +bool BoundingVolume:: +extend_by_sphere(const BoundingSphere *) { + _flags = F_infinite; + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::extend_by_hexahedron +// Access: Protected, Virtual +// Description: Double-dispatch support: called by extend_other() +// when the type we're extending by is known to be a +// hexahedron. +//////////////////////////////////////////////////////////////////// +bool BoundingVolume:: +extend_by_hexahedron(const BoundingHexahedron *) { + _flags = F_infinite; + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::extend_by_line +// Access: Protected, Virtual +// Description: Double-dispatch support: called by extend_other() +// when the type we're extending by is known to be a +// line. +//////////////////////////////////////////////////////////////////// +bool BoundingVolume:: +extend_by_line(const BoundingLine *) { + _flags = F_infinite; + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::around_spheres +// Access: Protected, Virtual +// Description: Double-dispatch support: called by around_other() +// when the type of the first element in the list is +// known to be a nonempty sphere. +//////////////////////////////////////////////////////////////////// +bool BoundingVolume:: +around_spheres(const BoundingVolume **, const BoundingVolume **) { + _flags = F_infinite; + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::around_hexahedrons +// Access: Protected, Virtual +// Description: Double-dispatch support: called by around_other() +// when the type of the first element in the list is +// known to be a nonempty hexahedron. +//////////////////////////////////////////////////////////////////// +bool BoundingVolume:: +around_hexahedrons(const BoundingVolume **, const BoundingVolume **) { + _flags = F_infinite; + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::around_lines +// Access: Protected, Virtual +// Description: Double-dispatch support: called by around_other() +// when the type of the first element in the list is +// known to be a nonempty line. +//////////////////////////////////////////////////////////////////// +bool BoundingVolume:: +around_lines(const BoundingVolume **, const BoundingVolume **) { + _flags = F_infinite; + if (is_of_type(FiniteBoundingVolume::get_class_type())) { + // If it's a FiniteBoundingVolume, we can't do any better than + // making it infinite. So we return true. + return true; + } + + // Otherwise, we might do better, and we require each class to + // define a function. If we get here, the function isn't defined, + // so we return false to indicate this. + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::contains_sphere +// Access: Protected, Virtual +// Description: Double-dispatch support: called by contains_other() +// when the type we're testing for intersection is known +// to be a sphere. +//////////////////////////////////////////////////////////////////// +int BoundingVolume:: +contains_sphere(const BoundingSphere *) const { + return IF_dont_understand; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::contains_hexahedron +// Access: Protected, Virtual +// Description: Double-dispatch support: called by contains_other() +// when the type we're testing for intersection is known +// to be a hexahedron. +//////////////////////////////////////////////////////////////////// +int BoundingVolume:: +contains_hexahedron(const BoundingHexahedron *) const { + return IF_dont_understand; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingVolume::contains_line +// Access: Protected, Virtual +// Description: Double-dispatch support: called by contains_other() +// when the type we're testing for intersection is known +// to be a line. +//////////////////////////////////////////////////////////////////// +int BoundingVolume:: +contains_line(const BoundingLine *) const { + return IF_dont_understand; +} diff --git a/panda/src/mathutil/boundingVolume.h b/panda/src/mathutil/boundingVolume.h new file mode 100644 index 0000000000..14289b3986 --- /dev/null +++ b/panda/src/mathutil/boundingVolume.h @@ -0,0 +1,147 @@ +// Filename: boundingVolume.h +// Created by: drose (01Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BOUNDINGVOLUME_H +#define BOUNDINGVOLUME_H + +#include + +#include +#include + +class BoundingSphere; +class BoundingHexahedron; +class BoundingLine; + + +/////////////////////////////////////////////////////////////////// +// Class : BoundingVolume +// Description : This is an abstract class for any volume in any sense +// which can be said to define the locality of reference +// of a node in a graph, along with all of its +// descendants. It is not necessarily a geometric +// volume (although see GeometricBoundingVolume); this +// is simply an abstract interface for bounds of any +// sort. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA BoundingVolume : public TypedReferenceCount { +public: + INLINE BoundingVolume(); + virtual BoundingVolume *make_copy() const=0; + + INLINE bool is_empty() const; + INLINE bool is_infinite() const; + + INLINE void set_infinite(); + + INLINE bool extend_by(const BoundingVolume *vol); + + // It might be nice to make these template member functions so we + // could have true STL-style first/last iterators, but that's + // impossible for virtual functions. + bool around(const BoundingVolume **first, + const BoundingVolume **last); + + // The contains() functions return the union of one or more of these + // bits. + enum IntersectionFlags { + // If no bits are set, it is known that there is no intersection. + IF_no_intersection = 0, + + // IF_possible is set if there might be an intersection. + IF_possible = 0x01, + + // IF_some is set if there is definitely an intersection. In this + // case, IF_possible will also be set. + IF_some = 0x02, + + // IF_all is set if the other bounding volume is known to be + // completely within this bounding volume: that is, there is no + // part of the other bounding volume that does not intersect this + // one. It does *not* indicate the inverse; it is possible that + // some part of this bounding volume does not intersect the other. + + // Also, the converse is not implied: if IF_all is not set, you + // simply don't know whether the other volume is completely + // contained within this one or not. + + // When IF_all is set, both IF_possible and IF_some will also be + // set. + IF_all = 0x04, + + // IF_dont_understand is set if the particular volume/volume + // intersection test has not been implemented. + IF_dont_understand = 0x08 + }; + + INLINE int contains(const BoundingVolume *vol) const; + + virtual void output(ostream &out) const=0; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + enum Flags { + F_empty = 0x01, + F_infinite = 0x02 + }; + int _flags; + +protected: + // The following functions support double-dispatch of virtual + // methods, so we can easily extend_by() various types of bounding + // volumes. + + // These functions are the first dispatch point. + virtual bool extend_other(BoundingVolume *other) const=0; + virtual bool around_other(BoundingVolume *other, + const BoundingVolume **first, + const BoundingVolume **last) const=0; + virtual int contains_other(const BoundingVolume *other) const=0; + + // These functions are the second dispatch point. They actually do + // the work. + virtual bool extend_by_sphere(const BoundingSphere *sphere); + virtual bool extend_by_hexahedron(const BoundingHexahedron *hexahedron); + virtual bool extend_by_line(const BoundingLine *line); + + virtual bool around_spheres(const BoundingVolume **first, + const BoundingVolume **last); + virtual bool around_hexahedrons(const BoundingVolume **first, + const BoundingVolume **last); + virtual bool around_lines(const BoundingVolume **first, + const BoundingVolume **last); + + virtual int contains_sphere(const BoundingSphere *sphere) const; + virtual int contains_hexahedron(const BoundingHexahedron *hexahedron) const; + virtual int contains_line(const BoundingLine *line) const; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "BoundingVolume", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend class BoundingSphere; + friend class BoundingHexahedron; + friend class BoundingLine; +}; + +INLINE ostream &operator << (ostream &out, const BoundingVolume &bound); + +#include "boundingVolume.I" + +#endif diff --git a/panda/src/mathutil/config_mathutil.cxx b/panda/src/mathutil/config_mathutil.cxx new file mode 100644 index 0000000000..0c3e0612f0 --- /dev/null +++ b/panda/src/mathutil/config_mathutil.cxx @@ -0,0 +1,28 @@ +// Filename: config_mathutil.cxx +// Created by: drose (01Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_mathutil.h" +#include "boundingVolume.h" +#include "geometricBoundingVolume.h" +#include "finiteBoundingVolume.h" +#include "omniBoundingVolume.h" +#include "boundingSphere.h" +#include "boundingHexahedron.h" +#include "boundingLine.h" +#include + +Configure(config_mathutil); +NotifyCategoryDef(mathutil, ""); + +ConfigureFn(config_mathutil) { + BoundingHexahedron::init_type(); + BoundingSphere::init_type(); + BoundingVolume::init_type(); + FiniteBoundingVolume::init_type(); + GeometricBoundingVolume::init_type(); + OmniBoundingVolume::init_type(); + BoundingLine::init_type(); +} + diff --git a/panda/src/mathutil/config_mathutil.h b/panda/src/mathutil/config_mathutil.h new file mode 100644 index 0000000000..83911f0ace --- /dev/null +++ b/panda/src/mathutil/config_mathutil.h @@ -0,0 +1,16 @@ +// Filename: config_mathutil.h +// Created by: drose (01Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_MATHUTIL_H +#define CONFIG_MATHUTIL_H + +#include +#include + +NotifyCategoryDecl(mathutil, EXPCL_PANDA, EXPTP_PANDA); + +#endif + + diff --git a/panda/src/mathutil/finiteBoundingVolume.cxx b/panda/src/mathutil/finiteBoundingVolume.cxx new file mode 100644 index 0000000000..216096c741 --- /dev/null +++ b/panda/src/mathutil/finiteBoundingVolume.cxx @@ -0,0 +1,8 @@ +// Filename: finiteBoundingVolume.cxx +// Created by: drose (02Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "finiteBoundingVolume.h" + +TypeHandle FiniteBoundingVolume::_type_handle; diff --git a/panda/src/mathutil/finiteBoundingVolume.h b/panda/src/mathutil/finiteBoundingVolume.h new file mode 100644 index 0000000000..515f7feee8 --- /dev/null +++ b/panda/src/mathutil/finiteBoundingVolume.h @@ -0,0 +1,63 @@ +// Filename: finiteBoundingVolume.h +// Created by: drose (02Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FINITEBOUNDINGVOLUME_H +#define FINITEBOUNDINGVOLUME_H + +#include + +#include "geometricBoundingVolume.h" + + +/////////////////////////////////////////////////////////////////// +// Class : FiniteBoundingVolume +// Description : A special kind of GeometricBoundingVolume that is +// known to be finite. It is possible to query this +// kind of volume for its minimum and maximum extents. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA FiniteBoundingVolume : public GeometricBoundingVolume { +public: + virtual LPoint3f get_min() const=0; + virtual LPoint3f get_max() const=0; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GeometricBoundingVolume::init_type(); + register_type(_type_handle, "FiniteBoundingVolume", + GeometricBoundingVolume::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif + + + + + + + + + + + + + + + + + + + diff --git a/panda/src/mathutil/frustum.I b/panda/src/mathutil/frustum.I new file mode 100644 index 0000000000..702bc332c7 --- /dev/null +++ b/panda/src/mathutil/frustum.I @@ -0,0 +1,266 @@ +// Filename: frustum.I +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "frustum.h" +#include "mathutil.h" +#include "config_mathutil.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +template +Frustum:: +Frustum() { + _fnear = 1.4142; + _ffar = 10.0; + _l = -1; + _r = 1; + _t = 1; + _b = -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: make_ortho_2D +// Access: +// Description: Sets up a two-dimensional orthographic frustum +//////////////////////////////////////////////////////////////////// +template +void Frustum::make_ortho_2D(void) { + make_ortho(-1, 1); +} + +//////////////////////////////////////////////////////////////////// +// Function: make_ortho_2D +// Access: +// Description: Sets up a two-dimensional orthographic frustum +//////////////////////////////////////////////////////////////////// +template +void Frustum:: +make_ortho_2D(P_numtype l, P_numtype r, P_numtype t, P_numtype b) { + make_ortho(-1, 1, l, r, t, b); +} + +//////////////////////////////////////////////////////////////////// +// Function: make_ortho_2D +// Access: +// Description: Behaves like gluOrtho +//////////////////////////////////////////////////////////////////// +template +void Frustum::make_ortho(P_numtype fnear, P_numtype ffar) { + _fnear = fnear; + _ffar = ffar; + _l = -1; + _r = 1; + _t = 1; + _b = -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: make_ortho_2D +// Access: +// Description: Behaves like gluOrtho +//////////////////////////////////////////////////////////////////// +template +void Frustum:: +make_ortho(P_numtype fnear, P_numtype ffar, P_numtype l, P_numtype r, + P_numtype t, P_numtype b) { + _fnear = fnear; + _ffar = ffar; + _l = l; + _r = r; + _t = t; + _b = b; +} + +//////////////////////////////////////////////////////////////////// +// Function: make_perspective +// Access: +// Description: Behaves like gluPerspective (Aspect = width/height, +// Yfov in degrees) +// aspect +// +------------+ +// | | +// 1 | | yfov +// | | +// +------------+ +// +// -------+------ +// \ | / +// \ | / +// \ | / +// \ | / +// \ | / +// \|/ +// W yfov +// +//////////////////////////////////////////////////////////////////// +template +void Frustum:: +make_perspective_hfov(P_numtype hfov, P_numtype aspect, P_numtype fnear, + P_numtype ffar) { + _fnear = fnear; + _ffar = ffar; + _r = tan(deg_2_rad(hfov) * 0.5) * _fnear; + _l = -_r; + _t = _r / aspect; + _b = -_t; +} + +template +void Frustum:: +make_perspective_vfov(P_numtype yfov, P_numtype aspect, P_numtype fnear, + P_numtype ffar) { + _fnear = fnear; + _ffar = ffar; + _t = tan(deg_2_rad(yfov) * 0.5) * _fnear; + _b = -_t; + _r = _t * aspect; + _l = -_r; +} + +template +void Frustum:: +make_perspective(P_numtype xfov, P_numtype yfov, P_numtype fnear, + P_numtype ffar) { + _fnear = fnear; + _ffar = ffar; + _t = tan(deg_2_rad(yfov) * 0.5) * _fnear; + _b = -_t; + _r = tan(deg_2_rad(xfov) * 0.5) * _fnear; + _l = -_r; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_perspective_params +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +template +void Frustum:: +get_perspective_params(P_numtype& yfov, P_numtype& aspect, + P_numtype& fnear, P_numtype& ffar) const { + yfov = rad_2_deg(atan(_t / _fnear)) * 2.0; + aspect = _r / _t; + fnear = _fnear; + ffar = _ffar; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_perspective_params +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +template +void Frustum:: +get_perspective_params(P_numtype& xfov, P_numtype& yfov, P_numtype& aspect, + P_numtype& fnear, P_numtype& ffar) const { + xfov = rad_2_deg(atan(_r / _fnear)) * 2.0; + get_perspective_params(yfov, aspect, fnear, ffar); +} + +//////////////////////////////////////////////////////////////////// +// Function: get_perspective_projection_mat +// Access: Public +// Description: This computes a transform matrix that performs the +// perspective transform defined by the frustum, +// accordinate to the indicated coordinate system. +//////////////////////////////////////////////////////////////////// +template +LMatrix4 Frustum:: +get_perspective_projection_mat(CoordinateSystem cs) const { + if (cs == CS_default) { + cs = default_coordinate_system; + } + + P_numtype a = (2.0 * _fnear) / (_r - _l); + P_numtype b = (_t + _b) / (_t - _b); + P_numtype c = (_ffar + _fnear) / (_ffar - _fnear); + P_numtype d = (_r + _l) / (_r - _l); + P_numtype e = (2.0 * _fnear) / (_t - _b); + P_numtype f = (-2.0 * _ffar * _fnear) / (_ffar - _fnear); + + switch (cs) { + case CS_zup_right: + return LMatrix4( a, 0.0, 0.0, 0.0, + 0.0, -b, c, 1.0, + d, e, 0.0, 0.0, + 0.0, 0.0, f, 0.0); + + case CS_yup_right: + return LMatrix4( a, 0.0, 0.0, 0.0, + 0.0, e, 0.0, 0.0, + d, b, -c,-1.0, + 0.0, 0.0, f, 0.0); + + case CS_zup_left: + return LMatrix4::convert_mat(CS_zup_right, CS_zup_left) * + get_perspective_projection_mat(CS_zup_right); + + case CS_yup_left: + return LMatrix4::convert_mat(CS_yup_right, CS_yup_left) * + get_perspective_projection_mat(CS_yup_right); + } + + mathutil_cat.error() + << "Invalid coordinate system!\n"; + return LMatrix4::ident_mat(); +} + +//////////////////////////////////////////////////////////////////// +// Function: get_ortho_projection_mat +// Access: Public +// Description: This computes a transform matrix that performs the +// orthographic transform defined by the frustum, +// accordinate to the indicated coordinate system. +//////////////////////////////////////////////////////////////////// +template +LMatrix4 Frustum:: +get_ortho_projection_mat(CoordinateSystem cs) const { + if (cs == CS_default) { + cs = default_coordinate_system; + } + + P_numtype a = 2.0 / (_r - _l); + P_numtype b = 2.0 / (_t - _b); + P_numtype c = 2.0 / (_ffar - _fnear); + P_numtype d = (_r + _l) / (_r - _l); + P_numtype e = (_t + _b) / (_t - _b); + P_numtype f = (_ffar + _fnear) / (_ffar - _fnear); + + switch (cs) { + case CS_zup_right: + return LMatrix4::convert_mat(CS_yup_right, CS_zup_right) * + get_ortho_projection_mat(CS_yup_right); + + case CS_yup_right: + return LMatrix4( a, 0.0, 0.0, 0.0, + 0.0, b, 0.0, 0.0, + 0.0, 0.0, -c, 0.0, + -d, -e, -f, 1.0); + + case CS_zup_left: + return LMatrix4::convert_mat(CS_zup_right, CS_zup_left) * + get_ortho_projection_mat(CS_zup_right); + + case CS_yup_left: + return LMatrix4::convert_mat(CS_yup_right, CS_yup_left) * + get_ortho_projection_mat(CS_yup_right); + } + + mathutil_cat.error() + << "Invalid coordinate system!\n"; + return LMatrix4::ident_mat(); +} diff --git a/panda/src/mathutil/frustum.h b/panda/src/mathutil/frustum.h new file mode 100644 index 0000000000..f961ceeb00 --- /dev/null +++ b/panda/src/mathutil/frustum.h @@ -0,0 +1,64 @@ +// Filename: frustum.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef FRUSTUM_H +#define FRUSTUM_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : Frustum +// Description : +//////////////////////////////////////////////////////////////////// +template +class EXPCL_PANDA Frustum { +public: + Frustum(); + + void make_ortho_2D(void); + void make_ortho_2D(P_numtype l, P_numtype r, P_numtype t, P_numtype b); + + void make_ortho(P_numtype fnear, P_numtype ffar); + void make_ortho(P_numtype fnear, P_numtype ffar, + P_numtype l, P_numtype r, P_numtype t, P_numtype b); + + void make_perspective_hfov(P_numtype xfov, P_numtype aspect, + P_numtype fnear, P_numtype ffar); + void make_perspective_vfov(P_numtype yfov, P_numtype aspect, + P_numtype fnear, P_numtype ffar); + void make_perspective(P_numtype xfov, P_numtype yfov, P_numtype fnear, + P_numtype ffar); + void get_perspective_params(P_numtype &yfov, P_numtype &aspect, + P_numtype &fnear, P_numtype &ffar) const; + void get_perspective_params(P_numtype &xfov, P_numtype &yfov, + P_numtype &aspect, P_numtype &fnear, + P_numtype &ffar) const; + + LMatrix4 + get_perspective_projection_mat(CoordinateSystem cs = CS_default) const; + + LMatrix4 + get_ortho_projection_mat(CoordinateSystem cs = CS_default) const; + +public: + P_numtype _l, _r, _b, _t; + P_numtype _fnear, _ffar; +}; + +#include "frustum.I" + +typedef Frustum Frustumf; +typedef Frustum Frustumd; + +#endif diff --git a/panda/src/mathutil/geometricBoundingVolume.I b/panda/src/mathutil/geometricBoundingVolume.I new file mode 100644 index 0000000000..253c64d7e6 --- /dev/null +++ b/panda/src/mathutil/geometricBoundingVolume.I @@ -0,0 +1,108 @@ +// Filename: geometricBoundingVolume.I +// Created by: drose (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: GeometricBoundingVolume::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GeometricBoundingVolume:: +GeometricBoundingVolume() { +} + +//////////////////////////////////////////////////////////////////// +// Function: GeometricBoundingVolume::extend_by +// Access: Public +// Description: Increases the size of the volume to include the given +// volume. +//////////////////////////////////////////////////////////////////// +INLINE bool GeometricBoundingVolume:: +extend_by(const GeometricBoundingVolume *vol) { + return BoundingVolume::extend_by(vol); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeometricBoundingVolume::extend_by +// Access: Public +// Description: Increases the size of the volume to include the given +// point. +//////////////////////////////////////////////////////////////////// +INLINE bool GeometricBoundingVolume:: +extend_by(const LPoint3f &point) { + return extend_by_point(point); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeometricBoundingVolume::around +// Access: Public +// Description: Resets the volume to enclose only the volumes +// indicated. +//////////////////////////////////////////////////////////////////// +INLINE bool GeometricBoundingVolume:: +around(const GeometricBoundingVolume **first, + const GeometricBoundingVolume **last) { + return BoundingVolume::around((const BoundingVolume **)first, + (const BoundingVolume **)last); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeometricBoundingVolume::around +// Access: Public +// Description: Resets the volume to enclose only the points +// indicated. +//////////////////////////////////////////////////////////////////// +INLINE bool GeometricBoundingVolume:: +around(const LPoint3f *first, const LPoint3f *last) { + _flags = F_empty; + if (first != last) { + return around_points(first, last); + } + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: GeometricBoundingVolume::contains +// Access: Public +// Description: Returns the appropriate set of IntersectionFlags to +// indicate the amount of intersection with the +// indicated volume. +//////////////////////////////////////////////////////////////////// +INLINE int GeometricBoundingVolume:: +contains(const GeometricBoundingVolume *vol) const { + return BoundingVolume::contains(vol); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeometricBoundingVolume::contains +// Access: Public +// Description: Returns the appropriate set of IntersectionFlags to +// indicate the amount of intersection with the +// indicated point. +//////////////////////////////////////////////////////////////////// +INLINE int GeometricBoundingVolume:: +contains(const LPoint3f &point) const { + if (is_empty()) { + return IF_no_intersection; + } + + return contains_point(point); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeometricBoundingVolume::contains +// Access: Public +// Description: Returns the appropriate set of IntersectionFlags to +// indicate the amount of intersection with the +// indicated line segment. +//////////////////////////////////////////////////////////////////// +INLINE int GeometricBoundingVolume:: +contains(const LPoint3f &a, const LPoint3f &b) const { + if (is_empty()) { + return IF_no_intersection; + } + + return contains_lineseg(a, b); +} diff --git a/panda/src/mathutil/geometricBoundingVolume.cxx b/panda/src/mathutil/geometricBoundingVolume.cxx new file mode 100644 index 0000000000..f8275607a5 --- /dev/null +++ b/panda/src/mathutil/geometricBoundingVolume.cxx @@ -0,0 +1,30 @@ +// Filename: geometricBoundingVolume.cxx +// Created by: drose (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "geometricBoundingVolume.h" + +TypeHandle GeometricBoundingVolume::_type_handle; + + +bool GeometricBoundingVolume:: +extend_by_point(const LPoint3f &) { + return false; +} + +bool GeometricBoundingVolume:: +around_points(const LPoint3f *, const LPoint3f *) { + _flags = F_empty; + return false; +} + +int GeometricBoundingVolume:: +contains_point(const LPoint3f &) const { + return IF_dont_understand; +} + +int GeometricBoundingVolume:: +contains_lineseg(const LPoint3f &, const LPoint3f &) const { + return IF_dont_understand; +} diff --git a/panda/src/mathutil/geometricBoundingVolume.h b/panda/src/mathutil/geometricBoundingVolume.h new file mode 100644 index 0000000000..649a2e61ff --- /dev/null +++ b/panda/src/mathutil/geometricBoundingVolume.h @@ -0,0 +1,75 @@ +// Filename: geometricBoundingVolume.h +// Created by: drose (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMETRICBOUNDINGVOLUME_H +#define GEOMETRICBOUNDINGVOLUME_H + +#include + +#include "boundingVolume.h" + +#include +#include + +/////////////////////////////////////////////////////////////////// +// Class : GeometricBoundingVolume +// Description : This is another abstract class, for a general class +// of bounding volumes that actually enclose points in +// 3-d space, such as BSP's and bounding spheres. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeometricBoundingVolume : public BoundingVolume { +public: + INLINE GeometricBoundingVolume(); + + INLINE bool extend_by(const GeometricBoundingVolume *vol); + INLINE bool extend_by(const LPoint3f &point); + + // It might be nice to make these template member functions so we + // could have true STL-style first/last iterators, but that's + // impossible for virtual functions. + INLINE bool around(const GeometricBoundingVolume **first, + const GeometricBoundingVolume **last); + INLINE bool around(const LPoint3f *first, + const LPoint3f *last); + + INLINE int contains(const GeometricBoundingVolume *vol) const; + INLINE int contains(const LPoint3f &point) const; + INLINE int contains(const LPoint3f &a, const LPoint3f &b) const; + + virtual LPoint3f get_approx_center() const=0; + virtual void xform(const LMatrix4f &mat)=0; + +protected: + // Some virtual functions to implement fundamental bounding + // operations on points in 3-d space. + + virtual bool extend_by_point(const LPoint3f &point); + virtual bool around_points(const LPoint3f *first, + const LPoint3f *last); + virtual int contains_point(const LPoint3f &point) const; + virtual int contains_lineseg(const LPoint3f &a, const LPoint3f &b) const; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + BoundingVolume::init_type(); + register_type(_type_handle, "GeometricBoundingVolume", + BoundingVolume::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "geometricBoundingVolume.I" + +#endif diff --git a/panda/src/mathutil/look_at.I b/panda/src/mathutil/look_at.I new file mode 100644 index 0000000000..43ed0b67be --- /dev/null +++ b/panda/src/mathutil/look_at.I @@ -0,0 +1,79 @@ +// Filename: look_at.I +// Created by: drose (25Sep99) +// +//////////////////////////////////////////////////////////////////// + +INLINE void +heads_up(LMatrix3f &mat, const LVector3f &fwd, CoordinateSystem cs) { + heads_up(mat, fwd, LVector3f::up(cs), cs); +} + +INLINE void +look_at(LMatrix3f &mat, const LVector3f &fwd, CoordinateSystem cs) { + look_at(mat, fwd, LVector3f::up(cs), cs); +} + +INLINE void +heads_up(LMatrix4f &mat, const LVector3f &fwd, + const LVector3f &up, CoordinateSystem cs) { + LMatrix3f mat3; + heads_up(mat3, fwd, up, cs); + mat = LMatrix4f(mat3); +} + +INLINE void +look_at(LMatrix4f &mat, const LVector3f &fwd, + const LVector3f &up, CoordinateSystem cs) { + LMatrix3f mat3; + look_at(mat3, fwd, up, cs); + mat = LMatrix4f(mat3); +} + +INLINE void +heads_up(LMatrix4f &mat, const LVector3f &fwd, CoordinateSystem cs) { + heads_up(mat, fwd, LVector3f::up(cs), cs); +} + +INLINE void +look_at(LMatrix4f &mat, const LVector3f &fwd, CoordinateSystem cs) { + look_at(mat, fwd, LVector3f::up(cs), cs); +} + + + +INLINE void +heads_up(LMatrix3d &mat, const LVector3d &fwd, CoordinateSystem cs) { + heads_up(mat, fwd, LVector3d::up(cs), cs); +} + +INLINE void +look_at(LMatrix3d &mat, const LVector3d &fwd, CoordinateSystem cs) { + look_at(mat, fwd, LVector3d::up(cs), cs); +} + +INLINE void +heads_up(LMatrix4d &mat, const LVector3d &fwd, + const LVector3d &up, CoordinateSystem cs) { + LMatrix3d mat3; + heads_up(mat3, fwd, up, cs); + mat = LMatrix4d(mat3); +} + +INLINE void +look_at(LMatrix4d &mat, const LVector3d &fwd, + const LVector3d &up, CoordinateSystem cs) { + LMatrix3d mat3; + look_at(mat3, fwd, up, cs); + mat = LMatrix4d(mat3); +} + +INLINE void +heads_up(LMatrix4d &mat, const LVector3d &fwd, CoordinateSystem cs) { + heads_up(mat, fwd, LVector3d::up(cs), cs); +} + +INLINE void +look_at(LMatrix4d &mat, const LVector3d &fwd, CoordinateSystem cs) { + look_at(mat, fwd, LVector3d::up(cs), cs); +} + diff --git a/panda/src/mathutil/look_at.cxx b/panda/src/mathutil/look_at.cxx new file mode 100644 index 0000000000..ff7df16b80 --- /dev/null +++ b/panda/src/mathutil/look_at.cxx @@ -0,0 +1,338 @@ +// Filename: lookAt.cxx +// Created by: drose (25Apr97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "look_at.h" +#include + +template +INLINE LMatrix3 +make_xi_mat(const LVector2 &x) { + return LMatrix3(1, 0, 0, + 0, x[0], x[1], + 0, -x[1], x[0]); +} + +template +INLINE LMatrix3 +make_x_mat(const LVector2 &x) { + return LMatrix3(1, 0, 0, + 0, x[1], x[0], + 0, -x[0], x[1]); +} + +template +INLINE LMatrix3 +make_y_mat(const LVector2 &y) { + return LMatrix3(y[1], 0, -y[0], + 0, 1, 0, + y[0], 0, y[1]); +} + +template +INLINE LMatrix3 +make_z_mat(const LVector2 &z) { + return LMatrix3(z[1], -z[0], 0, + z[0], z[1], 0, + 0, 0, 1); +} + +//////////////////////////////////////////////////////////////////// +// Function: heads_up +// Description: Given two vectors defining a forward direction and an +// up vector, constructs the matrix that rotates things +// from the defined coordinate system to y-forward and +// z-up. The up vector will be rotated to z-up first, +// then the forward vector will be rotated as nearly to +// y-forward as possible. This will only have a +// different effect from look_at() if the forward and up +// vectors are not perpendicular. +//////////////////////////////////////////////////////////////////// +template +static void +_heads_up(LMatrix3 &mat, const LVector3 &fwd, + const LVector3 &up, CoordinateSystem cs) { + if (cs == CS_zup_right || cs == CS_zup_left) { + // Z-up. + + // y is the projection of the up vector into the XZ plane. Its + // angle to the Z axis is the amount to rotate about the Y axis to + // bring the up vector into the YZ plane. + + LVector2 y(up[0], up[2]); + NumType d = dot(y, y); + if (d==0.0) { + y = LVector2(0.0, 1.0); + } else { + y /= csqrt(d); + } + + // x is the up vector rotated into the YZ plane. Its angle to the Z + // axis is the amount to rotate about the X axis to bring the up + // vector to the Z axis. + + LVector2 x(up[1], up[0]*y[0]+up[2]*y[1]); + d = dot(x, x); + if (d==0.0) { + x = LVector2(0.0, 1.0); + } else { + x /= csqrt(d); + } + + // Now apply both rotations to the forward vector. This will rotate + // the forward vector by the same amount we would have had to rotate + // the up vector to bring it to the Z axis. If the vectors were + // perpendicular, this will put the forward vector somewhere in the + // XY plane. + + // z is the projection of the newly rotated fwd vector into the XY + // plane. Its angle to the Y axis is the amount to rotate about the + // Z axis in order to bring the fwd vector to the Y axis. + LVector2 z(fwd[0]*y[1] - fwd[2]*y[0], + -fwd[0]*y[0]*x[0] + fwd[1]*x[1] - fwd[2]*y[1]*x[0]); + d = dot(z, z); + if (d==0.0) { + z = LVector2(0.0, 1.0); + } else { + z /= csqrt(d); + } + + // Now build the net rotation matrix. + if (cs == CS_zup_right) { + mat = + make_z_mat(z) * + make_x_mat(x) * + make_y_mat(y); + } else { // cs == CS_zup_left + mat = + make_z_mat(z) * + make_x_mat(-x) * + make_y_mat(-y); + } + } else { + // Y-up. + + // z is the projection of the forward vector into the XY plane. Its + // angle to the Y axis is the amount to rotate about the Z axis to + // bring the forward vector into the YZ plane. + + LVector2 z(up[0], up[1]); + NumType d = dot(z, z); + if (d==0.0) { + z = LVector2(0.0, 1.0); + } else { + z /= csqrt(d); + } + + // x is the forward vector rotated into the YZ plane. Its angle to + // the Y axis is the amount to rotate about the X axis to bring the + // forward vector to the Y axis. + + LVector2 x(up[0]*z[0] + up[1]*z[1], up[2]); + d = dot(x, x); + if (d==0.0) { + x = LVector2(1.0, 0.0); + } else { + x /= csqrt(d); + } + + // Now apply both rotations to the up vector. This will rotate + // the up vector by the same amount we would have had to rotate + // the forward vector to bring it to the Y axis. If the vectors were + // perpendicular, this will put the up vector somewhere in the + // XZ plane. + + // y is the projection of the newly rotated up vector into the XZ + // plane. Its angle to the Z axis is the amount to rotate about the + // Y axis in order to bring the up vector to the Z axis. + LVector2 y(fwd[0]*z[1] - fwd[1]*z[0], + -fwd[0]*x[1]*z[0] - fwd[1]*x[1]*z[1] + fwd[2]*x[0]); + d = dot(y, y); + if (d==0.0) { + y = LVector2(0.0, 1.0); + } else { + y /= csqrt(d); + } + + // Now build the net rotation matrix. + if (cs == CS_yup_right) { + mat = + make_y_mat(y) * + make_xi_mat(-x) * + make_z_mat(-z); + } else { // cs == CS_yup_left + mat = + make_y_mat(y) * + make_xi_mat(x) * + make_z_mat(z); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: look_at +// Description: Given two vectors defining a forward direction and an +// up vector, constructs the matrix that rotates things +// from the defined coordinate system to y-forward and +// z-up. The forward vector will be rotated to +// y-forward first, then the up vector will be rotated +// as nearly to z-up as possible. This will only have a +// different effect from heads_up() if the forward and +// up vectors are not perpendicular. +//////////////////////////////////////////////////////////////////// +template +static void +_look_at(LMatrix3 &mat, const LVector3 &fwd, + const LVector3 &up, CoordinateSystem cs) { + if (cs == CS_default) { + cs = default_coordinate_system; + } + + if (cs == CS_zup_right || cs == CS_zup_left) { + // Z-up. + + // z is the projection of the forward vector into the XY plane. Its + // angle to the Y axis is the amount to rotate about the Z axis to + // bring the forward vector into the YZ plane. + + LVector2 z(fwd[0], fwd[1]); + NumType d = dot(z, z); + if (d==0.0) { + z = LVector2(0.0, 1.0); + } else { + z /= csqrt(d); + } + + // x is the forward vector rotated into the YZ plane. Its angle to + // the Y axis is the amount to rotate about the X axis to bring the + // forward vector to the Y axis. + + LVector2 x(fwd[0]*z[0] + fwd[1]*z[1], fwd[2]); + d = dot(x, x); + if (d==0.0) { + x = LVector2(1.0, 0.0); + } else { + x /= csqrt(d); + } + + // Now apply both rotations to the up vector. This will rotate + // the up vector by the same amount we would have had to rotate + // the forward vector to bring it to the Y axis. If the vectors were + // perpendicular, this will put the up vector somewhere in the + // XZ plane. + + // y is the projection of the newly rotated up vector into the XZ + // plane. Its angle to the Z axis is the amount to rotate about the + // Y axis in order to bring the up vector to the Z axis. + LVector2 y(up[0]*z[1] - up[1]*z[0], + -up[0]*x[1]*z[0] - up[1]*x[1]*z[1] + up[2]*x[0]); + d = dot(y, y); + if (d==0.0) { + y = LVector2(0.0, 1.0); + } else { + y /= csqrt(d); + } + + // Now build the net rotation matrix. + if (cs == CS_zup_right) { + mat = + make_y_mat(y) * + make_xi_mat(x) * + make_z_mat(z); + } else { // cs == CS_zup_left + mat = + make_y_mat(-y) * + make_xi_mat(-x) * + make_z_mat(z); + } + } else { + // Y-up. + + // y is the projection of the up vector into the XZ plane. Its + // angle to the Z axis is the amount to rotate about the Y axis to + // bring the up vector into the YZ plane. + + LVector2 y(fwd[0], fwd[2]); + NumType d = dot(y, y); + if (d==0.0) { + y = LVector2(0.0, 1.0); + } else { + y /= csqrt(d); + } + + // x is the up vector rotated into the YZ plane. Its angle to the Z + // axis is the amount to rotate about the X axis to bring the up + // vector to the Z axis. + + LVector2 x(fwd[1], fwd[0]*y[0]+fwd[2]*y[1]); + d = dot(x, x); + if (d==0.0) { + x = LVector2(0.0, 1.0); + } else { + x /= csqrt(d); + } + + // Now apply both rotations to the forward vector. This will rotate + // the forward vector by the same amount we would have had to rotate + // the up vector to bring it to the Z axis. If the vectors were + // perpendicular, this will put the forward vector somewhere in the + // XY plane. + + // z is the projection of the newly rotated fwd vector into the XY + // plane. Its angle to the Y axis is the amount to rotate about the + // Z axis in order to bring the fwd vector to the Y axis. + LVector2 z(up[0]*y[1] - up[2]*y[0], + -up[0]*y[0]*x[0] + up[1]*x[1] - up[2]*y[1]*x[0]); + d = dot(z, z); + if (d==0.0) { + z = LVector2(0.0, 1.0); + } else { + z /= csqrt(d); + } + + // Now build the net rotation matrix. + if (cs == CS_yup_right) { + mat = + make_z_mat(z) * + make_x_mat(x) * + make_y_mat(-y); + } else { // cs == CS_yup_left + mat = + make_z_mat(-z) * + make_x_mat(-x) * + make_y_mat(-y); + } + } +} + +// The following functions are the non-template functions that are +// actually exported. + +void +heads_up(LMatrix3f &mat, const LVector3f &fwd, + const LVector3f &up, CoordinateSystem cs) { + _heads_up(mat, fwd, up, cs); +} + +void +look_at(LMatrix3f &mat, const LVector3f &fwd, + const LVector3f &up, CoordinateSystem cs) { + _look_at(mat, fwd, up, cs); +} + +void +heads_up(LMatrix3d &mat, const LVector3d &fwd, + const LVector3d &up, CoordinateSystem cs) { + _heads_up(mat, fwd, up, cs); +} + +void +look_at(LMatrix3d &mat, const LVector3d &fwd, + const LVector3d &up, CoordinateSystem cs) { + _look_at(mat, fwd, up, cs); +} diff --git a/panda/src/mathutil/look_at.h b/panda/src/mathutil/look_at.h new file mode 100644 index 0000000000..5c425a2030 --- /dev/null +++ b/panda/src/mathutil/look_at.h @@ -0,0 +1,92 @@ +// Filename: look_at.h +// Created by: drose (25Apr97) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LOOKAT_H +#define LOOKAT_H + +//////////////////////////////////////////////////////////////////// +// Includes +/////////////////////////////////////////////////////////////////// +#include + +#include +#include + +// These functions return a matrix that rotates between a coordinate +// system defined with the given forward and up vectors, and the +// standard coordinate system with y-forward and z-up. They differ +// only in their behavior when the supplied forward and up vectors are +// not perpendicular; in this case, look_at will match the forward +// vector precisely, while heads_up will match the up vector +// precisely. + +// Since these functions only return a rotation matrix, the +// translation component is always zero. There are flavors of these +// functions that simply return the upper 3x3 part of the matrix, and +// flavors that return the whole 4x4 matrix with a zero bottom row. + + +// Flavors for float-type arithmetic. + +EXPCL_PANDA void +heads_up(LMatrix3f &mat, const LVector3f &fwd, + const LVector3f &up = LVector3f::up(), + CoordinateSystem cs = CS_default); +EXPCL_PANDA void +look_at(LMatrix3f &mat, const LVector3f &fwd, + const LVector3f &up = LVector3f::up(), + CoordinateSystem cs = CS_default); + +INLINE void heads_up(LMatrix3f &mat, const LVector3f &fwd, + CoordinateSystem cs); +INLINE void look_at(LMatrix3f &mat, const LVector3f &fwd, + CoordinateSystem cs); + + +INLINE void heads_up(LMatrix4f &mat, const LVector3f &fwd, + const LVector3f &up = LVector3f::up(), + CoordinateSystem cs = CS_default); +INLINE void look_at(LMatrix4f &mat, const LVector3f &fwd, + const LVector3f &up = LVector3f::up(), + CoordinateSystem cs = CS_default); + +INLINE void heads_up(LMatrix4f &mat, const LVector3f &fwd, + CoordinateSystem cs); +INLINE void look_at(LMatrix4f &mat, const LVector3f &fwd, + CoordinateSystem cs); + + +// Flavors for double-type arithmetic. + +EXPCL_PANDA void +heads_up(LMatrix3d &mat, const LVector3d &fwd, + const LVector3d &up = LVector3d::up(), + CoordinateSystem cs = CS_default); +EXPCL_PANDA void +look_at(LMatrix3d &mat, const LVector3d &fwd, + const LVector3d &up = LVector3d::up(), + CoordinateSystem cs = CS_default); + +INLINE void heads_up(LMatrix3d &mat, const LVector3d &fwd, + CoordinateSystem cs); +INLINE void look_at(LMatrix3d &mat, const LVector3d &fwd, + CoordinateSystem cs); + + +INLINE void heads_up(LMatrix4d &mat, const LVector3d &fwd, + const LVector3d &up = LVector3d::up(), + CoordinateSystem cs = CS_default); +INLINE void look_at(LMatrix4d &mat, const LVector3d &fwd, + const LVector3d &up = LVector3d::up(), + CoordinateSystem cs = CS_default); + +INLINE void heads_up(LMatrix4d &mat, const LVector3d &fwd, + CoordinateSystem cs); +INLINE void look_at(LMatrix4d &mat, const LVector3d &fwd, + CoordinateSystem cs); + +#include "look_at.I" + +#endif diff --git a/panda/src/mathutil/mathHelpers.I b/panda/src/mathutil/mathHelpers.I new file mode 100644 index 0000000000..2698113d06 --- /dev/null +++ b/panda/src/mathutil/mathHelpers.I @@ -0,0 +1,14 @@ +// Filename: mathHelpers.I +// Created by: mike (25Apr97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +INLINE float distance(const LPoint3f &pos0, const LPoint3f &pos1) { + LVector3f v = pos0 - pos1; + return length(v); +} diff --git a/panda/src/mathutil/mathHelpers.h b/panda/src/mathutil/mathHelpers.h new file mode 100644 index 0000000000..4ec6ba48db --- /dev/null +++ b/panda/src/mathutil/mathHelpers.h @@ -0,0 +1,20 @@ +// Filename: mathHelpers.h +// Created by: mike (25Apr97) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MATHHELPERS_H +#define MATHHELPERS_H + +//////////////////////////////////////////////////////////////////// +// Includes +/////////////////////////////////////////////////////////////////// +#include + +#include + +INLINE float distance(const LPoint3f &pos0, const LPoint3f &pos1); + +#include "mathHelpers.I" + +#endif diff --git a/panda/src/mathutil/mathutil.h b/panda/src/mathutil/mathutil.h new file mode 100644 index 0000000000..1b26e8dd11 --- /dev/null +++ b/panda/src/mathutil/mathutil.h @@ -0,0 +1,23 @@ +// Filename: mathutil.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef MATHUTIL_H +#define MATHUTIL_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Functions +//////////////////////////////////////////////////////////////////// + +#define ABS(x) (((x)<0)?(-(x)):x) + +#endif diff --git a/panda/src/mathutil/omniBoundingVolume.I b/panda/src/mathutil/omniBoundingVolume.I new file mode 100644 index 0000000000..2882be4f2d --- /dev/null +++ b/panda/src/mathutil/omniBoundingVolume.I @@ -0,0 +1,14 @@ +// Filename: omniBoundingVolume.I +// Created by: drose (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE OmniBoundingVolume:: +OmniBoundingVolume() { + _flags = F_infinite; +} diff --git a/panda/src/mathutil/omniBoundingVolume.cxx b/panda/src/mathutil/omniBoundingVolume.cxx new file mode 100644 index 0000000000..c88ec2157e --- /dev/null +++ b/panda/src/mathutil/omniBoundingVolume.cxx @@ -0,0 +1,187 @@ +// Filename: omniBoundingVolume.cxx +// Created by: drose (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "omniBoundingVolume.h" +#include "boundingHexahedron.h" +#include "config_mathutil.h" + +#include + +TypeHandle OmniBoundingVolume::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +BoundingVolume *OmniBoundingVolume:: +make_copy() const { + return new OmniBoundingVolume(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::get_approx_center +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +LPoint3f OmniBoundingVolume:: +get_approx_center() const { + return LPoint3f(0.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::xform +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void OmniBoundingVolume:: +xform(const LMatrix4f &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void OmniBoundingVolume:: +output(ostream &out) const { + out << "omni"; +} + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::extend_other +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool OmniBoundingVolume:: +extend_other(BoundingVolume *other) const { + other->set_infinite(); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::around_other +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool OmniBoundingVolume:: +around_other(BoundingVolume *other, + const BoundingVolume **, + const BoundingVolume **) const { + other->set_infinite(); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::contains_other +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int OmniBoundingVolume:: +contains_other(const BoundingVolume *) const { + return IF_possible | IF_some | IF_all; +} + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::extend_by_point +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool OmniBoundingVolume:: +extend_by_point(const LPoint3f &) { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::extend_by_sphere +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool OmniBoundingVolume:: +extend_by_sphere(const BoundingSphere *) { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::extend_by_hexahedron +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool OmniBoundingVolume:: +extend_by_hexahedron(const BoundingHexahedron *) { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::around_points +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool OmniBoundingVolume:: +around_points(const LPoint3f *, const LPoint3f *) { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::around_spheres +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool OmniBoundingVolume:: +around_spheres(const BoundingVolume **, + const BoundingVolume **) { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::around_hexahedrons +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool OmniBoundingVolume:: +around_hexahedrons(const BoundingVolume **, + const BoundingVolume **) { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::contains_point +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int OmniBoundingVolume:: +contains_point(const LPoint3f &) const { + return IF_possible | IF_some | IF_all; +} + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::contains_lineseg +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int OmniBoundingVolume:: +contains_lineseg(const LPoint3f &, const LPoint3f &) const { + return IF_possible | IF_some | IF_all; +} + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::contains_sphere +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int OmniBoundingVolume:: +contains_sphere(const BoundingSphere *) const { + return IF_possible | IF_some | IF_all; +} + +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::contains_hexahedron +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int OmniBoundingVolume:: +contains_hexahedron(const BoundingHexahedron *) const { + return IF_possible | IF_some | IF_all; +} diff --git a/panda/src/mathutil/omniBoundingVolume.h b/panda/src/mathutil/omniBoundingVolume.h new file mode 100644 index 0000000000..c78e0f13a6 --- /dev/null +++ b/panda/src/mathutil/omniBoundingVolume.h @@ -0,0 +1,74 @@ +// Filename: omniBoundingVolume.h +// Created by: drose (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef OMNIBOUNDINGVOLUME_H +#define OMNIBOUNDINGVOLUME_H + +#include + +#include "geometricBoundingVolume.h" + +/////////////////////////////////////////////////////////////////// +// Class : OmniBoundingVolume +// Description : This is a special kind of GeometricBoundingVolume +// that fills all of space. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA OmniBoundingVolume : public GeometricBoundingVolume { +public: + INLINE OmniBoundingVolume(); + virtual BoundingVolume *make_copy() const; + + virtual LPoint3f get_approx_center() const; + virtual void xform(const LMatrix4f &mat); + + virtual void output(ostream &out) const; + +protected: + virtual bool extend_other(BoundingVolume *other) const; + virtual bool around_other(BoundingVolume *other, + const BoundingVolume **first, + const BoundingVolume **last) const; + virtual int contains_other(const BoundingVolume *other) const; + + + virtual bool extend_by_point(const LPoint3f &point); + virtual bool extend_by_sphere(const BoundingSphere *sphere); + virtual bool extend_by_hexahedron(const BoundingHexahedron *hexahedron); + + virtual bool around_points(const LPoint3f *first, + const LPoint3f *last); + virtual bool around_spheres(const BoundingVolume **first, + const BoundingVolume **last); + virtual bool around_hexahedrons(const BoundingVolume **first, + const BoundingVolume **last); + + virtual int contains_point(const LPoint3f &point) const; + virtual int contains_lineseg(const LPoint3f &a, const LPoint3f &b) const; + virtual int contains_hexahedron(const BoundingHexahedron *hexahedron) const; + virtual int contains_sphere(const BoundingSphere *sphere) const; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + GeometricBoundingVolume::init_type(); + register_type(_type_handle, "OmniBoundingVolume", + GeometricBoundingVolume::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend class BoundingHexahedron; +}; + +#include "omniBoundingVolume.I" + +#endif diff --git a/panda/src/mathutil/plane.I b/panda/src/mathutil/plane.I new file mode 100644 index 0000000000..f8602ffef0 --- /dev/null +++ b/panda/src/mathutil/plane.I @@ -0,0 +1,306 @@ +// Filename: plane.I +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "plane.h" +#include "mathutil.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Plane::Constructor +// Access: Public +// Description: Creates a default plane. This plane happens to +// intersect the origin, perpendicular to the Z axis. +// It's not clear how useful a default plane is. +//////////////////////////////////////////////////////////////////// +template +INLINE Plane:: +Plane(void) { + _a = 0.0; + _b = 0.0; + _c = 1.0; + _d = 0.0; +} + +//////////////////////////////////////////////////////////////////// +// Function: Plane::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE Plane:: +Plane(const Plane ©) : + _a(copy._a), + _b(copy._b), + _c(copy._c), + _d(copy._d) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: Plane::Constructor +// Access: Public +// Description: Constructs a plane given three counter-clockwise +// points, as seen from the front of the plane (that is, +// viewed from the end of the normal vector, looking +// down). +//////////////////////////////////////////////////////////////////// +template +INLINE Plane:: +Plane(const LPoint3 &a, const LPoint3 &b, + const LPoint3 &c) { + LVector3 u = b - a; + LVector3 v = c - a; + LVector3 p = normalize(cross(u, v)); + + _a = p[0]; + _b = p[1]; + _c = p[2]; + _d = -dot(p, a); +} + +//////////////////////////////////////////////////////////////////// +// Function: Plane::Constructor +// Access: Public +// Description: Constructs a plane given a surface normal vector and +// a point within the plane. +//////////////////////////////////////////////////////////////////// +template +INLINE Plane:: +Plane(const LVector3 &normal, const LPoint3 &point) { + LVector3 p = normalize(normal); + + _a = p[0]; + _b = p[1]; + _c = p[2]; + _d = -dot(p, point); +} + +//////////////////////////////////////////////////////////////////// +// Function: Plane::Operator = +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE Plane& Plane:: +operator = (const Plane& p) { + _a = p._a; + _b = p._b; + _c = p._c; + _d = p._d; + return (*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: Plane::Operator * LMatrix3 +// Access: Public +// Description: Transforms the plane by the indicated matrix. +//////////////////////////////////////////////////////////////////// +template +INLINE Plane Plane:: +operator * (const LMatrix3 &mat) const { + LVector3 new_normal = get_normal() * mat; + LPoint3 new_point = get_point() * mat; + return Plane(new_normal, new_point); +} + +//////////////////////////////////////////////////////////////////// +// Function: Plane::Operator * LMatrix4 +// Access: Public +// Description: Transforms the plane by the indicated matrix. +//////////////////////////////////////////////////////////////////// +template +INLINE Plane Plane:: +operator * (const LMatrix4 &mat) const { + LVector3 new_normal = get_normal() * mat; + LPoint3 new_point = get_point() * mat; + return Plane(new_normal, new_point); +} + +//////////////////////////////////////////////////////////////////// +// Function: Plane::get_reflection_mat +// Access: Public +// Description: This computes a transform matrix that performs the +// perspective transform defined by the frustum, +// accordinate to the indicated coordinate system. +//////////////////////////////////////////////////////////////////// +template +LMatrix4 Plane:: +get_reflection_mat(void) const { + + NumType aa = _a * _a; NumType ab = _a * _b; NumType ac = _a * _c; + NumType ad = _a * _d; + NumType bb = _b * _b; NumType bc = _b * _c; NumType bd = _b * _d; + NumType cc = _c * _c; NumType cd = _c * _d; + + return LMatrix4( 1-2*aa, -2*ab, -2*ac, 0, + -2*ab, 1-2*bb, -2*bc, 0, + -2*ac, -2*bc, 1-2*cc, 0, + -2*ad, -2*bd, -2*cd, 1 ); +} + +//////////////////////////////////////////////////////////////////// +// Function: Plane::get_normal +// Access: Public +// Description: Returns the surface normal of the plane. +//////////////////////////////////////////////////////////////////// +template +INLINE LVector3 Plane:: +get_normal() const { + return LVector3(_a, _b, _c); +} + +//////////////////////////////////////////////////////////////////// +// Function: Plane::get_point +// Access: Public +// Description: Returns an arbitrary point in the plane. This can be +// used along with the normal returned by get_normal() +// to reconstruct the plane. +//////////////////////////////////////////////////////////////////// +template +LPoint3 Plane:: +get_point() const { + // Choose the denominator based on the largest axis in the normal. + if (cabs(_a) >= cabs(_b) && cabs(_a) >= cabs(_c)) { + nassertr(_a != 0.0, LPoint3(0.0, 0.0, 0.0)); + return LPoint3(-_d / _a, 0.0, 0.0); + } else if (cabs(_b) >= cabs(_c)) { + nassertr(_b != 0.0, LPoint3(0.0, 0.0, 0.0)); + return LPoint3(0.0, -_d / _b, 0.0); + } else { + nassertr(_c != 0.0, LPoint3(0.0, 0.0, 0.0)); + return LPoint3(0.0, 0.0, -_d / _c); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Plane::dist_to_plane +// Access: Public +// Description: Returns the straight-line shortest distance from the +// point to the plane. The returned value is positive +// if the point is in front of the plane (on the side +// with the normal), or negative in the point is behind +// the plane (on the opposite side from the normal). +// It's zero if the point is exactly in the plane. +//////////////////////////////////////////////////////////////////// +template +INLINE NumType Plane:: +dist_to_plane(const LPoint3 &point) const { + return (_a * point[0] + _b * point[1] + _c * point[2] + _d); +} + +//////////////////////////////////////////////////////////////////// +// Function: Plane::intersects_line +// Access: Public +// Description: Returns true if the plane intersects the infinite +// line passing through points p1 and p2, false if the +// line is parallel. The points p1 and p2 are used only +// to define the Euclidean line; they have no other +// bearing on the intersection test. If true, sets +// intersection_point to the point of intersection. +//////////////////////////////////////////////////////////////////// +template +INLINE bool Plane:: +intersects_line(LPoint3 &intersection_point, + const LPoint3 &p1, + const LPoint3 &p2) const { + NumType t; + if (!intersects_line(t, p1, p2 - p1)) { + return false; + } + intersection_point = p1 + t * (p2 - p1); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Plane::intersects_line +// Access: Public +// Description: This flavor of intersects_line() returns a bit more +// information about the nature of the intersecting +// point. The line is defined via the parametric +// equation from + t * delta for all real values of t. +// +// If there is no intersection with the plane, the +// function returns false and leaves t undefined. If +// there is an intersection with the plane, the function +// returns true and sets t to the parametric value that +// defines the point of intersection. That is, t == 0.0 +// implies that the intersection occurred exactly at +// point from, and t == 1.0 implies at point from + +// delta, with other values of t accordingly. +//////////////////////////////////////////////////////////////////// +template +INLINE bool Plane:: +intersects_line(NumType &t, + const LPoint3 &from, + const LVector3 &delta) const { + NumType denom = dot(get_normal(), delta); + if (IS_NEARLY_ZERO(denom)) { + return false; + } + + t = -(dist_to_plane(from) / denom); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Plane::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void Plane:: +output(ostream &out) const { + out << "Plane(" << _a << " " << _b << " " << _c << " " << _d << ")"; +} + +//////////////////////////////////////////////////////////////////// +// Function: Plane::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void Plane:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << *this << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: Plane::write_datagram +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void Plane:: +write_datagram(Datagram &dest) +{ + dest.add_float64(_a); + dest.add_float64(_b); + dest.add_float64(_c); + dest.add_float64(_d); +} + +//////////////////////////////////////////////////////////////////// +// Function: Plane::read_datagram +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void Plane:: +read_datagram(DatagramIterator &source) +{ + _a = source.get_float64(); + _b = source.get_float64(); + _c = source.get_float64(); + _d = source.get_float64(); +} diff --git a/panda/src/mathutil/plane.N b/panda/src/mathutil/plane.N new file mode 100644 index 0000000000..9b8394214f --- /dev/null +++ b/panda/src/mathutil/plane.N @@ -0,0 +1,2 @@ +forcetype Plane +forcetype Plane diff --git a/panda/src/mathutil/plane.cxx b/panda/src/mathutil/plane.cxx new file mode 100644 index 0000000000..e0a36c39f8 --- /dev/null +++ b/panda/src/mathutil/plane.cxx @@ -0,0 +1,14 @@ +// Filename: plane.cxx +// Created by: drose (19May00) +// +//////////////////////////////////////////////////////////////////// + +#include "plane.h" + +// This tells GCC to explicitly instantiate the templates defined in +// plane.h and leave them here. +#ifdef __GNUC__ +#pragma implementation +#endif + + diff --git a/panda/src/mathutil/plane.h b/panda/src/mathutil/plane.h new file mode 100644 index 0000000000..c09aabdecd --- /dev/null +++ b/panda/src/mathutil/plane.h @@ -0,0 +1,88 @@ +// Filename: plane.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef PLANE_H +#define PLANE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include + +class Datagram; +class DatagramIterator; + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : Plane +// Description : +//////////////////////////////////////////////////////////////////// +template +class Plane { +public: + INLINE Plane(void); + INLINE Plane(const Plane ©); + INLINE Plane(const LPoint3 &a, const LPoint3 &b, + const LPoint3 &c); + INLINE Plane(const LVector3 &normal, + const LPoint3 &point); + + INLINE Plane& operator = (const Plane& copy); + + INLINE Plane operator * (const LMatrix3 &mat) const; + INLINE Plane operator * (const LMatrix4 &mat) const; + + + LMatrix4 + get_reflection_mat(void) const; + + INLINE LVector3 get_normal() const; + LPoint3 get_point() const; + INLINE NumType dist_to_plane(const LPoint3 &point) const; + INLINE bool intersects_line(LPoint3 &intersection_point, + const LPoint3 &p1, + const LPoint3 &p2) const; + INLINE bool intersects_line(NumType &t, + const LPoint3 &from, + const LVector3 &delta) const; + + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + +public: + INLINE void write_datagram(Datagram &dest); + INLINE void read_datagram(DatagramIterator &source); + +public: + NumType _a, _b, _c, _d; +}; + +template +INLINE ostream &operator << (ostream &out, const Plane &p) { + p.output(out); + return out; +} + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, Plane) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, Plane) + +typedef Plane Planef; +typedef Plane Planed; + +#include "plane.I" + + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/mathutil/rotate_to.cxx b/panda/src/mathutil/rotate_to.cxx new file mode 100644 index 0000000000..917b2c3ee7 --- /dev/null +++ b/panda/src/mathutil/rotate_to.cxx @@ -0,0 +1,92 @@ +// Filename: rotate_to.cxx +// Created by: drose (04Nov99) +// +//////////////////////////////////////////////////////////////////// + +#include +#include "rotate_to.h" +#include "luse.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: _rotate_to +// Description: Computes the matrix necessary to rotate vector a onto +// vector b. It is assumed that both vectors are +// normalized. +//////////////////////////////////////////////////////////////////// +template +static void +_rotate_to(LMatrix3 &mat, + const LVector3 &a, const LVector3 &b) { + NumType cos_theta = a.dot(b); + + LVector3 axis = a.cross(b); + NumType sin_theta = length(axis); + + // Check for collinear vectors + if (sin_theta < 0.0001) { + // The vectors are collinear. + + if (cos_theta < 0.0) { + // The vectors are opposite; choose an arbitrary axis + // perpendicular to a. + LVector3 absa(fabs(a[0]), fabs(a[1]), fabs(a[2])); + LVector3 lca(0., 0., 0.); + lca[absa[0]<=absa[1] ? absa[0]<=absa[2] ? 0 : 2 + : absa[1]<=absa[2] ? 1 : 2] = 1.0; + + axis = normalize(a.cross(lca)); + } else { + mat = LMatrix3::ident_mat(); + return; + } + + } else { + // The vectors are not collinear; determine the best axis. + axis /= sin_theta; + } + + NumType x = axis[0]; + NumType y = axis[1]; + NumType z = axis[2]; + + NumType t = 1.0 - cos_theta; + + mat(0, 0) = t * x * x + cos_theta; + mat(0, 1) = t * x * y + sin_theta * z; + mat(0, 2) = t * x * z - sin_theta * y; + + mat(1, 0) = t * y * x - sin_theta * z; + mat(1, 1) = t * y * y + cos_theta; + mat(1, 2) = t * y * z + sin_theta * x; + + mat(2, 0) = t * z * x + sin_theta * y; + mat(2, 1) = t * z * y - sin_theta * x; + mat(2, 2) = t * z * z + cos_theta; +} + + +void +rotate_to(LMatrix3f &mat, const LVector3f &a, const LVector3f &b) { + _rotate_to(mat, a, b); +} + +void +rotate_to(LMatrix3d &mat, const LVector3d &a, const LVector3d &b) { + _rotate_to(mat, a, b); +} + +void +rotate_to(LMatrix4f &mat, const LVector3f &a, const LVector3f &b) { + LMatrix3f m3; + _rotate_to(m3, a, b); + mat = LMatrix4f(m3); +} + +void +rotate_to(LMatrix4d &mat, const LVector3d &a, const LVector3d &b) { + LMatrix3d m3; + _rotate_to(m3, a, b); + mat = LMatrix4d(m3); +} diff --git a/panda/src/mathutil/rotate_to.h b/panda/src/mathutil/rotate_to.h new file mode 100644 index 0000000000..089967641d --- /dev/null +++ b/panda/src/mathutil/rotate_to.h @@ -0,0 +1,31 @@ +// Filename: rotate_to.h +// Created by: drose (04Nov99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ROTATE_TO_H +#define ROTATE_TO_H + +//////////////////////////////////////////////////////////////////// +// +// rotate_to() +// +// This function computes a suitable rotation matrix to rotate vector +// a onto vector b. That is, it computes mat so that a * mat = b. +// The rotation axis is chosen to give the smallest possible rotation +// angle. +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "lmatrix.h" +#include "luse.h" + +void rotate_to(LMatrix3f &mat, const LVector3f &a, const LVector3f &b); +void rotate_to(LMatrix3d &mat, const LVector3d &a, const LVector3d &b); + +void rotate_to(LMatrix4f &mat, const LVector3f &a, const LVector3f &b); +void rotate_to(LMatrix4d &mat, const LVector3d &a, const LVector3d &b); + +#endif diff --git a/panda/src/mathutil/test_mathutil.cxx b/panda/src/mathutil/test_mathutil.cxx new file mode 100644 index 0000000000..62896f1f59 --- /dev/null +++ b/panda/src/mathutil/test_mathutil.cxx @@ -0,0 +1,53 @@ +// Filename: test_mathutil.cxx +// Created by: drose (16Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include "rotate_to.h" +#include "boundingLine.h" +#include "boundingSphere.h" + +int +main() { + /* + LVector3d a(1.0, 0.0, 0.0); + LVector3d b = normalize(LVector3d(0.5, 0.5, 0.0)); + + LMatrix3d rot; + rotate_to(rot, a, b); + + nout << "a = " << a << "\n" + << "b = " << b << "\n" + << "rot =\n"; + rot.write(nout, 2); + nout << "a * rot = " << a * rot << " length " << length(a * rot) << "\n" + << "a * invert(rot) = " << a * invert(rot) + << " length " << length(a * invert(rot)) << "\n" + << "b * rot = " << b * rot + << " length " << length(b * rot) << "\n" + << "b * invert(rot) = " << b * invert(rot) + << " length " << length(a * invert(rot)) << "\n"; + */ + + BoundingLine line(LPoint3f(0, 0, 1), LPoint3f(0, 0, 0)); + + BoundingSphere s1(LPoint3f(0, 0, 10), 1); + BoundingSphere s2(LPoint3f(10, 0, 10), 1); + BoundingSphere s3(LPoint3f(1, 0, 0), 1); + BoundingSphere s4(LPoint3f(-1, -1, -1), 1); + + line.contains(&s1); + line.contains(&s2); + line.contains(&s3); + line.contains(&s4); + + /* + s1.contains(LPoint3f(0, 0, 1), LPoint3f(0, 0, 0)); + s2.contains(LPoint3f(0, 0, 1), LPoint3f(0, 0, 0)); + s3.contains(LPoint3f(0, 0, 1), LPoint3f(0, 0, 0)); + s4.contains(LPoint3f(0, 0, 1), LPoint3f(0, 0, 0)); + */ + + return (0); +} diff --git a/panda/src/net/Sources.pp b/panda/src/net/Sources.pp new file mode 100644 index 0000000000..767233aa5a --- /dev/null +++ b/panda/src/net/Sources.pp @@ -0,0 +1,88 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET net + #define LOCAL_LIBS \ + putil + + #define SOURCES \ + config_net.cxx config_net.h connection.cxx connection.h \ + connectionListener.cxx connectionListener.h connectionManager.N \ + connectionManager.cxx connectionManager.h connectionReader.cxx \ + connectionReader.h connectionWriter.cxx connectionWriter.h \ + datagramQueue.cxx datagramQueue.h datagramTCPHeader.cxx \ + datagramTCPHeader.h datagramUDPHeader.cxx datagramUDPHeader.h \ + netAddress.cxx netAddress.h netDatagram.I netDatagram.cxx \ + netDatagram.h pprerror.cxx pprerror.h queuedConnectionListener.I \ + queuedConnectionListener.cxx queuedConnectionListener.h \ + queuedConnectionManager.cxx queuedConnectionManager.h \ + queuedConnectionReader.cxx queuedConnectionReader.h \ + recentConnectionReader.cxx recentConnectionReader.h + + #define INSTALL_HEADERS \ + config_net.h connection.h connectionListener.h connectionManager.h \ + connectionReader.h connectionWriter.h datagramQueue.h \ + datagramTCPHeader.h datagramUDPHeader.h netAddress.h netDatagram.I \ + netDatagram.h pprerror.h queuedConnectionListener.I \ + queuedConnectionListener.h queuedConnectionManager.h \ + queuedConnectionReader.h queuedReturn.I queuedReturn.h \ + recentConnectionReader.h + + #define IGATESCAN all + +#end lib_target + +#begin test_bin_target + #define TARGET test_datagram + #define LOCAL_LIBS net + + #define SOURCES \ + test_datagram.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET test_spam_client + #define LOCAL_LIBS net + + #define SOURCES \ + datagram_ui.cxx datagram_ui.h test_spam_client.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET test_spam_server + #define LOCAL_LIBS net + + #define SOURCES \ + datagram_ui.cxx datagram_ui.h test_spam_server.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET test_tcp_client + #define LOCAL_LIBS net + + #define SOURCES \ + datagram_ui.cxx datagram_ui.h test_tcp_client.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET test_tcp_server + #define LOCAL_LIBS net + + #define SOURCES \ + datagram_ui.cxx datagram_ui.h test_tcp_server.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET test_udp + #define LOCAL_LIBS net + + #define SOURCES \ + datagram_ui.cxx datagram_ui.h test_udp.cxx + +#end test_bin_target + diff --git a/panda/src/net/config_net.cxx b/panda/src/net/config_net.cxx new file mode 100644 index 0000000000..f622465e28 --- /dev/null +++ b/panda/src/net/config_net.cxx @@ -0,0 +1,40 @@ +// Filename: config_net.cxx +// Created by: drose (25Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_net.h" + +#include "netDatagram.h" + +#include + +Configure(config_net); +NotifyCategoryDef(net, ""); + +ConfigureFn(config_net) { + NetDatagram::init_type(); +} + +// The following two maximum queue sizes are totally arbitrary and +// serve only to provide sanity caps on the various queues in the net +// package. You can set them to any sane values you like. Also see +// the set_max_queue_size() methods in the various classes, which you +// can change at runtime on a particular instance. + +// This one limits the number of datagrams in a ConnectionWriter's +// output queue. +int get_net_max_write_queue() { + return config_net.GetInt("net-max-write-queue", 10000); +} + +// This one limits the number of datagrams, messages, what have you, +// in the various QueuedConnectionReader, QueuedConnectionListener, +// and QueuedConnectionManager classes. +int get_net_max_response_queue() { + return config_net.GetInt("net-max-response-queue", 10000); +} + +bool get_net_error_abort() { + return config_net.GetBool("net-error-abort", false); +} diff --git a/panda/src/net/config_net.h b/panda/src/net/config_net.h new file mode 100644 index 0000000000..20b744ee8e --- /dev/null +++ b/panda/src/net/config_net.h @@ -0,0 +1,20 @@ +// Filename: config_net.h +// Created by: drose (25Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_NET_H +#define CONFIG_NET_H + +#include +#include + +// Configure variables for net package. + +NotifyCategoryDecl(net, EXPCL_PANDA, EXPTP_PANDA); + +extern int get_net_max_write_queue(); +extern int get_net_max_response_queue(); +extern bool get_net_error_abort(); + +#endif diff --git a/panda/src/net/connection.cxx b/panda/src/net/connection.cxx new file mode 100644 index 0000000000..9669166b4a --- /dev/null +++ b/panda/src/net/connection.cxx @@ -0,0 +1,150 @@ +// Filename: connection.cxx +// Created by: jns (07Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "connection.h" +#include "connectionManager.h" +#include "netDatagram.h" +#include "datagramTCPHeader.h" +#include "datagramUDPHeader.h" +#include "pprerror.h" +#include "config_net.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: Connection::Constructor +// Access: Public +// Description: Creates a connection. Normally this constructor +// should not be used directly by user code; use one of +// the methods in ConnectionManager to make a new +// connection. +//////////////////////////////////////////////////////////////////// +Connection:: +Connection(ConnectionManager *manager, PRFileDesc *socket) : + _manager(manager), + _socket(socket) +{ + _write_mutex = PR_NewLock(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Connection::Destructor +// Access: Public +// Description: Closes a connection. +//////////////////////////////////////////////////////////////////// +Connection:: +~Connection() { + net_cat.info() + << "Deleting connection " << (void *)this << "\n"; + + if (_socket != (PRFileDesc *)NULL) { + PRStatus result = PR_Close(_socket); + if (result != PR_SUCCESS) { + pprerror("PR_Close"); + } + } + + PR_DestroyLock(_write_mutex); +} + +//////////////////////////////////////////////////////////////////// +// Function: Connection::get_address +// Access: Public +// Description: Returns the address bound to this connection, if it +// is a TCP connection. +//////////////////////////////////////////////////////////////////// +NetAddress Connection:: +get_address() const { + PRNetAddr addr; + if (PR_GetSockName(_socket, &addr) != PR_SUCCESS) { + pprerror("PR_GetSockName"); + } + + return NetAddress(addr); +} + +//////////////////////////////////////////////////////////////////// +// Function: Connection::get_manager +// Access: Public +// Description: Returns a pointer to the ConnectionManager object +// that serves this connection. +//////////////////////////////////////////////////////////////////// +ConnectionManager *Connection:: +get_manager() const { + return _manager; +} + +//////////////////////////////////////////////////////////////////// +// Function: Connection::get_socket +// Access: Public +// Description: Returns the internal NSPR pointer that defines the +// connection. +//////////////////////////////////////////////////////////////////// +PRFileDesc *Connection:: +get_socket() const { + return _socket; +} + +//////////////////////////////////////////////////////////////////// +// Function: Connection::send_datagram +// Access: Private +// Description: This method is intended only to be called by +// ConnectionWriter. It atomically writes the given +// datagram to the socket, returning true on success, +// false on failure. If the socket seems to be closed, +// it notifies the ConnectionManager. +//////////////////////////////////////////////////////////////////// +bool Connection:: +send_datagram(const NetDatagram &datagram) { + nassertr(_socket != (PRFileDesc *)NULL, false); + + PR_Lock(_write_mutex); + + PRInt32 bytes_sent; + PRInt32 result; + if (PR_GetDescType(_socket) == PR_DESC_SOCKET_UDP) { + DatagramUDPHeader header(datagram); + string data = header.get_header() + datagram.get_message(); + bytes_sent = data.length(); + result = PR_SendTo(_socket, + data.data(), bytes_sent, + 0, + datagram.get_address().get_addr(), + PR_INTERVAL_NO_TIMEOUT); + } else { + DatagramTCPHeader header(datagram); + string data = header.get_header() + datagram.get_message(); + bytes_sent = data.length(); + result = PR_Send(_socket, + data.data(), bytes_sent, + 0, + PR_INTERVAL_NO_TIMEOUT); + } + + PRErrorCode errcode = PR_GetError(); + + PR_Unlock(_write_mutex); + + if (result < 0) { + if (errcode == PR_CONNECT_RESET_ERROR) { + // The connection has been reset; tell our manager about it + // and ignore it. + if (_manager != (ConnectionManager *)NULL) { + _manager->connection_reset(this); + } + + } else if (errcode != PR_PENDING_INTERRUPT_ERROR) { + pprerror("PR_SendTo"); + } + + return false; + + } else if (result != bytes_sent) { + net_cat.error() << "Not enough bytes sent to socket.\n"; + return false; + } + + return true; +} diff --git a/panda/src/net/connection.h b/panda/src/net/connection.h new file mode 100644 index 0000000000..25a23fa47f --- /dev/null +++ b/panda/src/net/connection.h @@ -0,0 +1,46 @@ +// Filename: connection.h +// Created by: jns (07Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONNECTION_H +#define CONNECTION_H + +#include + +#include + +#include "netAddress.h" + +#include +#include + +class ConnectionManager; +class NetDatagram; + +//////////////////////////////////////////////////////////////////// +// Class : Connection +// Description : Represents a single TCP or UDP socket for input or +// output. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Connection : public ReferenceCount { +public: + Connection(ConnectionManager *manager, PRFileDesc *socket); + ~Connection(); + + NetAddress get_address() const; + ConnectionManager *get_manager() const; + + PRFileDesc *get_socket() const; + +private: + bool send_datagram(const NetDatagram &datagram); + + ConnectionManager *_manager; + PRFileDesc *_socket; + PRLock *_write_mutex; + +friend class ConnectionWriter; +}; + +#endif diff --git a/panda/src/net/connectionListener.cxx b/panda/src/net/connectionListener.cxx new file mode 100644 index 0000000000..e4677f3237 --- /dev/null +++ b/panda/src/net/connectionListener.cxx @@ -0,0 +1,69 @@ +// Filename: connectionListener.cxx +// Created by: drose (09Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "connectionListener.h" +#include "connection.h" +#include "connectionManager.h" +#include "netAddress.h" +#include "pprerror.h" +#include "config_net.h" + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionListener::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ConnectionListener:: +ConnectionListener(ConnectionManager *manager, int num_threads) : + ConnectionReader(manager, num_threads) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionListener::receive_datagram +// Access: Protected, Virtual +// Description: This function must be declared because it is pure +// virtual in the base class, but it isn't used in this +// class and doesn't do anything. +//////////////////////////////////////////////////////////////////// +void ConnectionListener:: +receive_datagram(const NetDatagram &) { + net_cat.error() + << "ConnectionListener::receive_datagram called.\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionListener::process_incoming_data +// Access: Protected, Virtual +// Description: This is the function that is called when activity is +// detected on a rendezvous port. In this case, it +// performs the accept(). +//////////////////////////////////////////////////////////////////// +void ConnectionListener:: +process_incoming_data(SocketInfo *sinfo) { + PRNetAddr addr; + + PRFileDesc *socket = + PR_Accept(sinfo->get_socket(), &addr, PR_INTERVAL_NO_TIMEOUT); + + if (socket == (PRFileDesc *)NULL) { + pprerror("PR_Accept"); + + } else { + NetAddress net_addr(addr); + net_cat.info() + << "Received TCP connection from client " << net_addr.get_ip() + << " on port " << sinfo->_connection->get_address().get_port() + << "\n"; + + PT(Connection) new_connection = new Connection(_manager, socket); + if (_manager != (ConnectionManager *)NULL) { + _manager->new_connection(new_connection); + } + connection_opened(sinfo->_connection, net_addr, new_connection); + } + + finish_socket(sinfo); +} diff --git a/panda/src/net/connectionListener.h b/panda/src/net/connectionListener.h new file mode 100644 index 0000000000..bd58b58a80 --- /dev/null +++ b/panda/src/net/connectionListener.h @@ -0,0 +1,41 @@ +// Filename: connectionListener.h +// Created by: drose (09Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONNECTIONLISTENER_H +#define CONNECTIONLISTENER_H + +#include + +#include "connectionReader.h" + +class NetAddress; + +//////////////////////////////////////////////////////////////////// +// Class : ConnectionListener +// Description : This is a special kind of ConnectionReader that waits +// for activity on a rendezvous port and accepts a TCP +// connection (instead of attempting to read a datagram +// from the rendezvous port). +// +// It is itself an abstract class, as it doesn't define +// what to do with the established connection. See +// QueuedConnectionListener. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ConnectionListener : public ConnectionReader { +public: + ConnectionListener(ConnectionManager *manager, int num_threads); + +protected: + virtual void receive_datagram(const NetDatagram &datagram); + virtual void connection_opened(const PT(Connection) &rendezvous, + const NetAddress &address, + const PT(Connection) &new_connection)=0; + + virtual void process_incoming_data(SocketInfo *sinfo); + +private: +}; + +#endif diff --git a/panda/src/net/connectionManager.N b/panda/src/net/connectionManager.N new file mode 100644 index 0000000000..6842b1753f --- /dev/null +++ b/panda/src/net/connectionManager.N @@ -0,0 +1 @@ +forcetype PointerTo diff --git a/panda/src/net/connectionManager.cxx b/panda/src/net/connectionManager.cxx new file mode 100644 index 0000000000..f59257dd5b --- /dev/null +++ b/panda/src/net/connectionManager.cxx @@ -0,0 +1,316 @@ +// Filename: connectionManager.cxx +// Created by: jns (07Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "connectionManager.h" +#include "connection.h" +#include "connectionReader.h" +#include "connectionWriter.h" +#include "netAddress.h" +#include "pprerror.h" +#include "config_net.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionManager::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ConnectionManager:: +ConnectionManager() { + _set_mutex = PR_NewLock(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionManager::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +ConnectionManager:: +~ConnectionManager() { + // Notify all of our associated readers and writers that we're gone. + Readers::iterator ri; + for (ri = _readers.begin(); ri != _readers.end(); ++ri) { + (*ri)->clear_manager(); + } + Writers::iterator wi; + for (wi = _writers.begin(); wi != _writers.end(); ++wi) { + (*wi)->clear_manager(); + } + + PR_DestroyLock(_set_mutex); +} + + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionManager::open_UDP_connection +// Access: Public +// Description: Opens a socket for sending and/or receiving UDP +// packets. If the port is non-negative, it will be +// bound to the connection; this is primarily a useful +// to do when the UDP connection will be used for +// reading. Use a ConnectionReader and +// ConnectionWriter to handle the actual communication. +//////////////////////////////////////////////////////////////////// +PT(Connection) ConnectionManager:: +open_UDP_connection(int port) { + NetAddress address; + address.set_any(port); + + PRFileDesc *socket = PR_NewUDPSocket(); + if (socket == (PRFileDesc *)NULL) { + pprerror("PR_NewUDPSocket"); + return PT(Connection)(); + } + + if (port >= 0) { + PRStatus result = PR_Bind(socket, address.get_addr()); + if (result != PR_SUCCESS) { + pprerror("PR_Bind"); + PR_Close(socket); + return PT(Connection)(); + } + + net_cat.info() + << "Creating UDP connection for port " << port << "\n"; + } else { + net_cat.info() + << "Creating UDP connection\n"; + } + + PT(Connection) connection = new Connection(this, socket); + new_connection(connection); + return connection; +} + + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionManager::open_TCP_server_rendezvous +// Access: Public +// Description: Creates a socket to be used as a rendezvous socket +// for a server to listen for TCP connections. The +// socket returned by this call should only be added to +// a ConnectionListener (not to a generic +// ConnectionReader). +// +// backlog is the maximum length of the queue of pending +// connections. +//////////////////////////////////////////////////////////////////// +PT(Connection) ConnectionManager:: +open_TCP_server_rendezvous(int port, int backlog) { + NetAddress address; + address.set_any(port); + + PRFileDesc *socket = PR_NewTCPSocket(); + if (socket == (PRFileDesc *)NULL) { + pprerror("PR_NewTCPSocket"); + return PT(Connection)(); + } + + PRStatus result = PR_Bind(socket, address.get_addr()); + if (result != PR_SUCCESS) { + pprerror("PR_Bind"); + net_cat.info() + << "Unable to bind to port " << port << " for TCP.\n"; + PR_Close(socket); + return PT(Connection)(); + } + + result = PR_Listen(socket, backlog); + if (result != PR_SUCCESS) { + pprerror("PR_Listen"); + net_cat.info() + << "Unable to listen to port " << port << " for TCP.\n"; + PR_Close(socket); + return PT(Connection)(); + } + + net_cat.info() + << "Listening for TCP connections on port " << port << "\n"; + + PT(Connection) connection = new Connection(this, socket); + new_connection(connection); + return connection; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionManager::open_TCP_client_connection +// Access: Public +// Description: Attempts to establish a TCP client connection to a +// server at the indicated address. If the connection +// is not established within timeout_ms milliseconds, a +// null connection is returned. +//////////////////////////////////////////////////////////////////// +PT(Connection) ConnectionManager:: +open_TCP_client_connection(const NetAddress &address, int timeout_ms) { + PRFileDesc *socket = PR_NewTCPSocket(); + if (socket == (PRFileDesc *)NULL) { + pprerror("PR_NewTCPSocket"); + return PT(Connection)(); + } + + PRStatus result = PR_Connect(socket, address.get_addr(), + PR_MillisecondsToInterval(timeout_ms)); + if (result != PR_SUCCESS) { + if (PR_GetError() != PR_CONNECT_RESET_ERROR) { + pprerror("PR_Connect"); + } + net_cat.info() + << "Unable to open TCP connection to server " + << address.get_ip() << " on port " << address.get_port() << "\n"; + PR_Close(socket); + return PT(Connection)(); + } + + net_cat.info() + << "Opened TCP connection to server " << address.get_ip() << " " + << " on port " << address.get_port() << "\n"; + + PT(Connection) connection = new Connection(this, socket); + new_connection(connection); + return connection; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionManager::open_TCP_client_connection +// Access: Public +// Description: This is a shorthand version of the function to +// directly establish communcations to a named host and +// port. +//////////////////////////////////////////////////////////////////// +PT(Connection) ConnectionManager:: +open_TCP_client_connection(const string &hostname, int port, + int timeout_ms) { + NetAddress address; + if (!address.set_host(hostname, port)) { + return PT(Connection)(); + } + + return open_TCP_client_connection(address, timeout_ms); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionManager::close_connection +// Access: Public +// Description: Terminates a UDP or TCP socket previously opened. +// This also removes it from any associated +// ConnectionReader or ConnectionListeners. +// +// The socket itself may not be immediately closed--it +// will not be closed until all outstanding pointers to +// it are cleared, including any pointers remaining in +// NetDatagrams recently received from the socket. +// +// The return value is true if the connection was marked +// to be closed, or false if close_connection() had +// already been called (or the connection did not belong +// to this ConnectionManager). In neither case can you +// infer anything about whether the connection has +// *actually* been closed yet based on the return value. +//////////////////////////////////////////////////////////////////// +bool ConnectionManager:: +close_connection(const PT(Connection) &connection) { + PR_Lock(_set_mutex); + Connections::iterator ci = _connections.find(connection); + if (ci == _connections.end()) { + // Already closed, or not part of this ConnectionManager. + PR_Unlock(_set_mutex); + return false; + } + _connections.erase(ci); + + Readers::iterator ri; + for (ri = _readers.begin(); ri != _readers.end(); ++ri) { + (*ri)->remove_connection(connection); + } + PR_Unlock(_set_mutex); + + PRFileDesc *socket = connection->get_socket(); + + if (PR_GetDescType(socket) == PR_DESC_SOCKET_TCP) { + // We can't *actually* close the connection right now, because + // there might be outstanding pointers to it. But we can at least + // shut it down. It will be eventually closed when all the + // pointers let go. + + PRStatus result = PR_Shutdown(socket, PR_SHUTDOWN_BOTH); + if (result != PR_SUCCESS) { + PRErrorCode errcode = PR_GetError(); + if (errcode != PR_NOT_CONNECTED_ERROR) { + pprerror("PR_Shutdown"); + } + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionManager::new_connection +// Access: Protected +// Description: This internal function is called whenever a new +// connection is established. It allows the +// ConnectionManager to save all of the pointers to open +// connections so they can't be inadvertently deleted +// until close_connection() is called. +//////////////////////////////////////////////////////////////////// +void ConnectionManager:: +new_connection(const PT(Connection) &connection) { + PR_Lock(_set_mutex); + _connections.insert(connection); + PR_Unlock(_set_mutex); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionManager::add_reader +// Access: Protected +// Description: This internal function is called by ConnectionReader +// when it is constructed. +//////////////////////////////////////////////////////////////////// +void ConnectionManager:: +add_reader(ConnectionReader *reader) { + PR_Lock(_set_mutex); + _readers.insert(reader); + PR_Unlock(_set_mutex); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionManager::remove_reader +// Access: Protected +// Description: This internal function is called by ConnectionReader +// when it is destructed. +//////////////////////////////////////////////////////////////////// +void ConnectionManager:: +remove_reader(ConnectionReader *reader) { + PR_Lock(_set_mutex); + _readers.erase(reader); + PR_Unlock(_set_mutex); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionManager::add_writer +// Access: Protected +// Description: This internal function is called by ConnectionWriter +// when it is constructed. +//////////////////////////////////////////////////////////////////// +void ConnectionManager:: +add_writer(ConnectionWriter *writer) { + PR_Lock(_set_mutex); + _writers.insert(writer); + PR_Unlock(_set_mutex); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionManager::remove_writer +// Access: Protected +// Description: This internal function is called by ConnectionWriter +// when it is destructed. +//////////////////////////////////////////////////////////////////// +void ConnectionManager:: +remove_writer(ConnectionWriter *writer) { + PR_Lock(_set_mutex); + _writers.erase(writer); + PR_Unlock(_set_mutex); +} diff --git a/panda/src/net/connectionManager.h b/panda/src/net/connectionManager.h new file mode 100644 index 0000000000..fbc3c7a043 --- /dev/null +++ b/panda/src/net/connectionManager.h @@ -0,0 +1,77 @@ +// Filename: connectionManager.h +// Created by: jns (07Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONNECTIONMANAGER_H +#define CONNECTIONMANAGER_H + +#include + +#include "netDatagram.h" +#include "connection.h" + +#include + +#include +#include + +class NetAddress; +class ConnectionReader; +class ConnectionWriter; + +//////////////////////////////////////////////////////////////////// +// Class : ConnectionManager +// Description : The primary interface to the low-level networking +// layer in this package. A ConnectionManager is used +// to establish and destroy TCP and UDP connections. +// Communication on these connections, once established, +// is handled via ConnectionReader, ConnectionWriter, +// and ConnectionListener. +// +// This is actually an abstract class, since it does not +// define what to do when a connection is externally +// reset (i.e. closed on the other end, or dropped +// because of network errors). See +// QueuedConnectionManager. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ConnectionManager { +public: + ConnectionManager(); + virtual ~ConnectionManager(); + + PT(Connection) open_UDP_connection(int port = -1); + + PT(Connection) open_TCP_server_rendezvous(int port, int backlog); + PT(Connection) open_TCP_client_connection(const NetAddress &address, + int timeout_ms); + PT(Connection) open_TCP_client_connection(const string &hostname, int port, + int timeout_ms); + + bool close_connection(const PT(Connection) &connection); + +protected: + void new_connection(const PT(Connection) &connection); + virtual void connection_reset(const PT(Connection) &connection)=0; + + void add_reader(ConnectionReader *reader); + void remove_reader(ConnectionReader *reader); + void add_writer(ConnectionWriter *writer); + void remove_writer(ConnectionWriter *writer); + + typedef set Connections; + typedef set Readers; + typedef set Writers; + Connections _connections; + Readers _readers; + Writers _writers; + PRLock *_set_mutex; + +private: + friend class ConnectionReader; + friend class ConnectionWriter; + friend class ConnectionListener; + friend class Connection; +}; + +#endif diff --git a/panda/src/net/connectionReader.cxx b/panda/src/net/connectionReader.cxx new file mode 100644 index 0000000000..7926211435 --- /dev/null +++ b/panda/src/net/connectionReader.cxx @@ -0,0 +1,834 @@ +// Filename: connectionReader.cxx +// Created by: drose (08Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "connectionReader.h" +#include "connectionManager.h" +#include "netDatagram.h" +#include "datagramTCPHeader.h" +#include "datagramUDPHeader.h" +#include "pprerror.h" +#include "config_net.h" + +#include +#include +#include +#include + +static const int read_buffer_size = maximum_udp_datagram + datagram_udp_header_size; + +// We have to impose a maximum timeout on the PR_Poll() call because +// PR_Poll() doesn't seem to be interruptible! (!) +static const PRUint32 max_timeout_ms = 100; + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::SocketInfo::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ConnectionReader::SocketInfo:: +SocketInfo(const PT(Connection) &connection) : + _connection(connection) +{ + _busy = false; + _error = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::SocketInfo::is_udp +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool ConnectionReader::SocketInfo:: +is_udp() const { + return (PR_GetDescType(_connection->get_socket()) == PR_DESC_SOCKET_UDP); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::SocketInfo::get_socket +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PRFileDesc *ConnectionReader::SocketInfo:: +get_socket() const { + return _connection->get_socket(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::Constructor +// Access: Public +// Description: Creates a new ConnectionReader with the indicated +// number of threads to handle requests. If num_threads +// is 0, the sockets will only be read by polling, +// during an explicit poll() call. +// (QueuedConnectionReader will do this automatically.) +//////////////////////////////////////////////////////////////////// +ConnectionReader:: +ConnectionReader(ConnectionManager *manager, int num_threads) : + _manager(manager) +{ + _polling = (num_threads <= 0); + + _shutdown = false; + _startup_mutex = PR_NewLock(); + + _next_index = 0; + _num_results = 0; + _select_mutex = PR_NewLock(); + + _currently_polling_thread = -1; + + _reexamine_sockets = false; + _sockets_mutex = PR_NewLock(); + + // Before we create all the threads, grab _startup_mutex. That will + // prevent our new threads from trying to look themselves up in the + // _threads array before we have filled it up. + PR_Lock(_startup_mutex); + + for (int i = 0; i < num_threads; i++) { + PRThread *thread = + PR_CreateThread(PR_USER_THREAD, + thread_start, (void *)this, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, // Since thread will mostly do I/O. + PR_JOINABLE_THREAD, + 0); // Select a suitable stack size. + + nassertv(thread != (PRThread *)NULL); + _threads.push_back(thread); + } + + PR_Unlock(_startup_mutex); + + _manager->add_reader(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +ConnectionReader:: +~ConnectionReader() { + if (_manager != (ConnectionManager *)NULL) { + _manager->remove_reader(this); + } + + shutdown(); + + // Delete all of our old sockets. + Sockets::iterator si; + for (si = _sockets.begin(); si != _sockets.end(); ++si) { + delete (*si); + } + for (si = _removed_sockets.begin(); si != _removed_sockets.end(); ++si) { + SocketInfo *sinfo = (*si); + if (!sinfo->_busy) { + delete sinfo; + } else { + net_cat.error() + << "Reentrant deletion of ConnectionReader--don't delete these\n" + << "in response to connection_reset().\n"; + + // We'll have to do the best we can to recover. + sinfo->_connection.clear(); + } + } + + PR_DestroyLock(_startup_mutex); + PR_DestroyLock(_select_mutex); + PR_DestroyLock(_sockets_mutex); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::add_connection +// Access: Public +// Description: Adds a new socket to the list of sockets the +// ConnectionReader will monitor. A datagram that comes +// in on any of the monitored sockets will be reported. +// In the case of a ConnectionListener, this adds a new +// rendezvous socket; any activity on any of the +// monitored sockets will cause a connection to be +// accepted. +// +// The return value is true if the connection was added, +// false if it was already there. +// +// add_connection() is thread-safe, and may be called at +// will by any thread. +//////////////////////////////////////////////////////////////////// +bool ConnectionReader:: +add_connection(const PT(Connection) &connection) { + nassertr(connection != (Connection *)NULL, false); + + PR_Lock(_sockets_mutex); + + // Make sure it's not already on the _sockets list. + Sockets::const_iterator si; + for (si = _sockets.begin(); si != _sockets.end(); ++si) { + if ((*si)->_connection == connection) { + // Whoops, already there. + PR_Unlock(_sockets_mutex); + return false; + } + } + + _sockets.push_back(new SocketInfo(connection)); + _reexamine_sockets = true; + PR_Unlock(_sockets_mutex); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::remove_connection +// Access: Public +// Description: Removes a socket from the list of sockets being +// monitored. Returns true if the socket was correctly +// removed, false if it was not on the list in the first +// place. +// +// remove_connection() is thread-safe, and may be called +// at will by any thread. +//////////////////////////////////////////////////////////////////// +bool ConnectionReader:: +remove_connection(const PT(Connection) &connection) { + PR_Lock(_sockets_mutex); + + // Walk through the list of sockets to find the one we're removing. + Sockets::iterator si; + si = _sockets.begin(); + while (si != _sockets.end() && (*si)->_connection != connection) { + ++si; + } + if (si == _sockets.end()) { + PR_Unlock(_sockets_mutex); + return false; + } + + _removed_sockets.push_back(*si); + _sockets.erase(si); + _reexamine_sockets = true; + PR_Unlock(_sockets_mutex); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::is_connection_ok +// Access: Public +// Description: Returns true if the indicated connection has been +// added to the ConnectionReader and is being monitored +// properly, false if it is not known, or if there was +// some error condition detected on the connection. (If +// there was an error condition, normally the +// ConnectionManager would have been informed and closed +// the connection.) +//////////////////////////////////////////////////////////////////// +bool ConnectionReader:: +is_connection_ok(const PT(Connection) &connection) { + PR_Lock(_sockets_mutex); + + // Walk through the list of sockets to find the one we're asking + // about. + Sockets::iterator si; + si = _sockets.begin(); + while (si != _sockets.end() && (*si)->_connection != connection) { + ++si; + } + if (si == _sockets.end()) { + // Don't know that connection. + PR_Unlock(_sockets_mutex); + return false; + } + + SocketInfo *sinfo = (*si); + bool is_ok = !sinfo->_error; + PR_Unlock(_sockets_mutex); + + return is_ok; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::poll +// Access: Public +// Description: Explicitly polls the available sockets to see if any +// of them have any noise. This function does nothing +// unless this is a polling-type ConnectionReader, +// i.e. it was created with zero threads (and +// is_polling() will return true). +// +// It is not necessary to call this explicitly for a +// QueuedConnectionReader. +//////////////////////////////////////////////////////////////////// +void ConnectionReader:: +poll() { + if (!_polling) { + return; + } + + SocketInfo *sinfo = get_next_available_socket(PR_INTERVAL_NO_WAIT, -2); + if (sinfo != (SocketInfo *)NULL) { + process_incoming_data(sinfo); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::get_manager +// Access: Public +// Description: Returns a pointer to the ConnectionManager object +// that serves this ConnectionReader. +//////////////////////////////////////////////////////////////////// +ConnectionManager *ConnectionReader:: +get_manager() const { + return _manager; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::is_polling +// Access: Public +// Description: Returns true if the reader is a polling reader, +// i.e. it has no threads. +//////////////////////////////////////////////////////////////////// +bool ConnectionReader:: +is_polling() const { + return _polling; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::get_num_threads +// Access: Public +// Description: Returns the number of threads the ConnectionReader +// has been created with. +//////////////////////////////////////////////////////////////////// +int ConnectionReader:: +get_num_threads() const { + return _threads.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::shutdown +// Access: Protected +// Description: Terminates all threads cleanly. Normally this is +// only called by the destructor. +//////////////////////////////////////////////////////////////////// +void ConnectionReader:: +shutdown() { + if (_shutdown) { + return; + } + + // First, begin the shutdown. This will tell our threads we want + // them to quit. + _shutdown = true; + + // Now wait for all of our threads to terminate. + Threads::iterator ti; + for (ti = _threads.begin(); ti != _threads.end(); ++ti) { + // Interrupt the thread so it can notice the shutdown. + PRStatus result = PR_Interrupt(*ti); + if (result != PR_SUCCESS) { + pprerror("PR_Interrupt"); + } + + result = PR_JoinThread(*ti); + if (result != PR_SUCCESS) { + pprerror("PR_JoinThread"); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::clear_manager +// Access: Protected +// Description: This should normally only be called when the +// associated ConnectionManager destructs. It resets +// the ConnectionManager pointer to NULL so we don't +// have a floating pointer. This makes the +// ConnectionReader invalid; presumably it also will be +// destructed momentarily. +//////////////////////////////////////////////////////////////////// +void ConnectionReader:: +clear_manager() { + _manager = (ConnectionManager *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::finish_socket +// Access: Protected +// Description: To be called when a socket has been fully read and is +// ready for polling for additional data. +//////////////////////////////////////////////////////////////////// +void ConnectionReader:: +finish_socket(SocketInfo *sinfo) { + nassertv(sinfo->_busy); + + // By marking the SocketInfo nonbusy, we make it available for + // future polls. + sinfo->_busy = false; + _reexamine_sockets = true; + + // However, someone might be already blocking on an + // earlier-established PR_Poll() that doesn't involve this socket. + // That complicates things. It means we'll have to wake that thread + // up so it can rebuild the poll with the new socket. + + // Actually, don't bother, since it turns out that PR_Poll() isn't + // interruptible anyway. Sigh. Maybe we'll revisit this later, but + // in the meantime it means we have to rig up the PR_Poll() to + // return every so often and check the _reexamine_sockets flag by + // itself. + + /* + int thread = _currently_polling_thread; + if (thread != -1) { + nassertv(thread >= 0 && thread < _threads.size()); + PR_Interrupt(_threads[thread]); + } + */ +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::process_incoming_data +// Access: Protected, Virtual +// Description: This is run within a thread when the call to +// PR_Poll() indicates there is data available +// on a socket. +//////////////////////////////////////////////////////////////////// +void ConnectionReader:: +process_incoming_data(SocketInfo *sinfo) { + if (sinfo->is_udp()) { + process_incoming_udp_data(sinfo); + } else { + process_incoming_tcp_data(sinfo); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::process_incoming_udp_data +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +void ConnectionReader:: +process_incoming_udp_data(SocketInfo *sinfo) { + PRFileDesc *socket = sinfo->get_socket(); + PRNetAddr addr; + + // Read as many bytes as we can. + PRInt8 buffer[read_buffer_size]; + PRInt32 bytes_read; + + bytes_read = PR_RecvFrom(socket, buffer, read_buffer_size, 0, + &addr, PR_INTERVAL_NO_TIMEOUT); + + if (bytes_read < 0) { + PRErrorCode errcode = PR_GetError(); + if (errcode != PR_PENDING_INTERRUPT_ERROR) { + pprerror("PR_RecvFrom"); + } + finish_socket(sinfo); + return; + + } else if (bytes_read == 0) { + // The socket was closed (!). This shouldn't happen with a UDP + // connection. Oh well. Report that and return. + if (_manager != (ConnectionManager *)NULL) { + _manager->connection_reset(sinfo->_connection); + } + finish_socket(sinfo); + return; + } + + // Now we must decode the header to determine how big the datagram + // is. This means we must have read at least a full header. + if (bytes_read < datagram_udp_header_size) { + net_cat.error() + << "Did not read entire header, discarding UDP datagram.\n"; + finish_socket(sinfo); + return; + } + + DatagramUDPHeader header(buffer); + PRInt32 size = header.get_datagram_size(); + + PRInt8 *dp = buffer + datagram_udp_header_size; + bytes_read -= datagram_udp_header_size; + + PRInt32 datagram_bytes = min(bytes_read, size); + NetDatagram datagram(dp, datagram_bytes); + + if (_shutdown) { + finish_socket(sinfo); + return; + } + + if (bytes_read > datagram_bytes) { + // There were some extra bytes at the end of the datagram. Huh. + net_cat.error() + << "Discarding " << bytes_read - datagram_bytes + << " bytes following UDP datagram.\n"; + } + + // Now that we've read all the data, it's time to finish the socket + // so another thread can read the next datagram. + finish_socket(sinfo); + + // And now do whatever we need to do to process the datagram. + if (!header.verify_datagram(datagram)) { + net_cat.error() + << "Ignoring invalid UDP datagram.\n"; + } else { + datagram.set_connection(sinfo->_connection); + datagram.set_address(NetAddress(addr)); + receive_datagram(datagram); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::process_incoming_tcp_data +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +void ConnectionReader:: +process_incoming_tcp_data(SocketInfo *sinfo) { + PRFileDesc *socket = sinfo->get_socket(); + PRNetAddr addr; + + // Read only the header bytes to start with. + PRInt8 buffer[read_buffer_size]; + PRInt32 header_bytes_read = 0; + + if (PR_GetSockName(socket, &addr) != PR_SUCCESS) { + pprerror("PR_GetSockName"); + } + + // First, we have to read the first datagram_tcp_header_size bytes. + while (header_bytes_read < datagram_tcp_header_size) { + PRInt32 bytes_read = + PR_Recv(socket, buffer + header_bytes_read, + datagram_tcp_header_size - header_bytes_read, 0, + PR_INTERVAL_NO_TIMEOUT); + + if (bytes_read < 0) { + PRErrorCode errcode = PR_GetError(); + if (errcode == PR_CONNECT_RESET_ERROR) { + // The socket was closed. + if (_manager != (ConnectionManager *)NULL) { + _manager->connection_reset(sinfo->_connection); + } + + } else if (errcode != PR_PENDING_INTERRUPT_ERROR) { + pprerror("PR_Recv"); + } + finish_socket(sinfo); + return; + + } else if (bytes_read == 0) { + // The socket was closed. Report that and return. + if (_manager != (ConnectionManager *)NULL) { + _manager->connection_reset(sinfo->_connection); + } + finish_socket(sinfo); + return; + } + + header_bytes_read += bytes_read; + } + + // Now we must decode the header to determine how big the datagram + // is. This means we must have read at least a full header. + if (header_bytes_read != datagram_tcp_header_size) { + // This should actually be impossible, by the read-loop logic + // above. + net_cat.error() + << "Did not read entire header, discarding TCP datagram.\n"; + finish_socket(sinfo); + return; + } + + DatagramTCPHeader header(buffer); + PRInt32 size = header.get_datagram_size(); + + // We have to loop until the entire datagram is read. + NetDatagram datagram; + + while (!_shutdown && datagram.get_length() < size) { + PRInt32 bytes_read; + + bytes_read = + PR_Recv(socket, buffer, + min((PRInt32)read_buffer_size, + (PRInt32)(size - datagram.get_length())), + 0, PR_INTERVAL_NO_TIMEOUT); + PRInt8 *dp = buffer; + + if (bytes_read < 0) { + PRErrorCode errcode = PR_GetError(); + if (errcode == PR_CONNECT_RESET_ERROR) { + // The socket was closed. + if (_manager != (ConnectionManager *)NULL) { + _manager->connection_reset(sinfo->_connection); + } + + } else if (errcode != PR_PENDING_INTERRUPT_ERROR) { + pprerror("PR_Recv"); + } + finish_socket(sinfo); + return; + + } else if (bytes_read == 0) { + // The socket was closed. Report that and return. + if (_manager != (ConnectionManager *)NULL) { + _manager->connection_reset(sinfo->_connection); + } + finish_socket(sinfo); + return; + } + + PRInt32 datagram_bytes = + min(bytes_read, (PRInt32)(size - datagram.get_length())); + datagram.append_data(dp, datagram_bytes); + + if (bytes_read > datagram_bytes) { + // There were some extra bytes at the end of the datagram. Maybe + // the beginning of the next datagram? Huh. + net_cat.error() + << "Discarding " << bytes_read - datagram_bytes + << " bytes following TCP datagram.\n"; + } + } + + if (_shutdown) { + finish_socket(sinfo); + return; + } + + // Now that we've read all the data, it's time to finish the socket + // so another thread can read the next datagram. + finish_socket(sinfo); + + // And now do whatever we need to do to process the datagram. + if (!header.verify_datagram(datagram)) { + net_cat.error() + << "Ignoring invalid TCP datagram.\n"; + } else { + datagram.set_connection(sinfo->_connection); + datagram.set_address(NetAddress(addr)); + receive_datagram(datagram); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::thread_start +// Access: Private, Static +// Description: The static wrapper around the thread's executing +// function. This must be a static member function +// because it is passed through the C interface to +// PR_CreateThread(). +//////////////////////////////////////////////////////////////////// +void ConnectionReader:: +thread_start(void *data) { + ((ConnectionReader *)data)->thread_run(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::thread_run +// Access: Private +// Description: This is the actual executing function for each +// thread. +//////////////////////////////////////////////////////////////////// +void ConnectionReader:: +thread_run() { + nassertv(!_polling); + + // First determine our own thread index. + PR_Lock(_startup_mutex); + Threads::const_iterator ti = + find(_threads.begin(), _threads.end(), PR_GetCurrentThread()); + + nassertv(ti != _threads.end()); + PRInt32 current_thread_index = (ti - _threads.begin()); + + nassertv(_threads[current_thread_index] == PR_GetCurrentThread()); + PR_Unlock(_startup_mutex); + + while (!_shutdown) { + SocketInfo *sinfo = + get_next_available_socket(PR_INTERVAL_NO_TIMEOUT, + current_thread_index); + if (sinfo != (SocketInfo *)NULL) { + process_incoming_data(sinfo); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::get_next_available_socket +// Access: Private +// Description: Polls the known connections for activity and returns +// the next one known to have activity, or NULL if no +// activity is detected within the timeout interval. +// +// This function may block indefinitely if it is being +// called by multiple threads; if there are no other +// threads, it may block only if timeout != +// PR_INTERVAL_NO_WAIT. +//////////////////////////////////////////////////////////////////// +ConnectionReader::SocketInfo *ConnectionReader:: +get_next_available_socket(PRIntervalTime timeout, + PRInt32 current_thread_index) { + // Go to sleep on the select() mutex. This guarantees that only one + // thread is in this function at a time. + PR_Lock(_select_mutex); + + int num_sockets = _polled_sockets.size(); + nassertr(num_sockets == _poll.size(), NULL); + + do { + // First, check the result from the previous PR_Poll() call. If + // there are any sockets remaining there, process them first. + while (!_shutdown && _num_results > 0) { + nassertr(_next_index < num_sockets, NULL); + int i = _next_index; + _next_index++; + + if (_poll[i].out_flags != 0) { + _num_results--; + SocketInfo *sinfo = _polled_sockets[i]; + + if ((_poll[i].out_flags & PR_POLL_READ) != 0) { + // Some genuine noise on the port. + sinfo->_busy = true; + _reexamine_sockets = true; + PR_Unlock(_select_mutex); + PR_Sleep(PR_INTERVAL_NO_WAIT); + return sinfo; + + } else if ((_poll[i].out_flags & + (PR_POLL_ERR | PR_POLL_NVAL | PR_POLL_HUP)) != 0) { + // Something bad happened to this socket. Tell the + // ConnectionManager to drop it. + if (_manager != (ConnectionManager *)NULL) { + _manager->connection_reset(sinfo->_connection); + } + sinfo->_error = true; + _reexamine_sockets = true; + } + } + } + + bool interrupted; + do { + interrupted = false; + + // Ok, no results from previous PR_Poll() calls. Prepare to set + // up for a new poll. + + // First, report to anyone else who cares that we're the thread + // about to do the poll. That way, if any new sockets come + // available while we're polling, we can service them. + PR_AtomicSet(&_currently_polling_thread, current_thread_index); + + if (_reexamine_sockets) { + _reexamine_sockets = false; + rebuild_poll_list(); + num_sockets = _polled_sockets.size(); + nassertr(num_sockets == _poll.size(), NULL); + } + + // Now we can execute PR_Poll(). This basically maps to a Unix + // select() call. + _num_results = 0; + _next_index = 0; + + if (!_shutdown) { + PRIntervalTime poll_timeout = + PR_MillisecondsToInterval(max_timeout_ms); + if (timeout != PR_INTERVAL_NO_TIMEOUT) { + poll_timeout = min(timeout, poll_timeout); + } + + _num_results = PR_Poll(&_poll[0], num_sockets, poll_timeout); + } + + if (_num_results == 0 && timeout == PR_INTERVAL_NO_TIMEOUT) { + // If we reached max_timeout_ms, but the caller didn't request + // a timeout, consider that an interrupt: go back and + // reconsider. (This is a kludge around the fact that + // PR_Poll() appears to be non-interruptible.) + interrupted = true; + + } else if (_num_results < 0) { + // If our poll was interrupted by another thread, rebuild the + // list and poll again. + PRErrorCode errcode = PR_GetError(); + if (errcode == PR_PENDING_INTERRUPT_ERROR) { + interrupted = true; + } else { + pprerror("PR_Poll"); + } + } + } while (!_shutdown && interrupted); + + PR_AtomicSet(&_currently_polling_thread, -1); + // Just in case someone interrupted us while we were polling and + // we didn't catch it, clear it now--we don't care any more. + PR_ClearInterrupt(); + + // Repeat the above until we (a) find a socket with actual noise + // on it, or (b) return from PR_Poll() with no sockets available. + } while (!_shutdown && _num_results > 0); + + PR_Unlock(_select_mutex); + return (SocketInfo *)NULL; +} + + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionReader::rebuild_poll_list +// Access: Private +// Description: Rebuilds the _poll and _polled_sockets arrays based +// on the sockets that are currently available for +// polling. +//////////////////////////////////////////////////////////////////// +void ConnectionReader:: +rebuild_poll_list() { + _poll.clear(); + _polled_sockets.clear(); + + PR_Lock(_sockets_mutex); + Sockets::const_iterator si; + for (si = _sockets.begin(); si != _sockets.end(); ++si) { + SocketInfo *sinfo = (*si); + if (!sinfo->_busy && !sinfo->_error) { + PRPollDesc pd; + pd.fd = sinfo->get_socket(); + pd.in_flags = PR_POLL_READ; + pd.out_flags = 0; + + _poll.push_back(pd); + _polled_sockets.push_back(sinfo); + } + } + + // This is also a fine time to delete the contents of the + // _removed_sockets list. + if (!_removed_sockets.empty()) { + Sockets still_busy_sockets; + for (si = _removed_sockets.begin(); si != _removed_sockets.end(); ++si) { + SocketInfo *sinfo = (*si); + if (sinfo->_busy) { + still_busy_sockets.push_back(sinfo); + } else { + delete sinfo; + } + } + _removed_sockets.swap(still_busy_sockets); + } + + PR_Unlock(_sockets_mutex); +} diff --git a/panda/src/net/connectionReader.h b/panda/src/net/connectionReader.h new file mode 100644 index 0000000000..ba1b91cdc2 --- /dev/null +++ b/panda/src/net/connectionReader.h @@ -0,0 +1,147 @@ +// Filename: connectionReader.h +// Created by: drose (08Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONNECTIONREADER_H +#define CONNECTIONREADER_H + +#include + +#include "connection.h" + +#include + +#include +#include +#include +#include +#include + +class NetDatagram; +class ConnectionManager; + +//////////////////////////////////////////////////////////////////// +// Class : ConnectionReader +// Description : This is an abstract base class for a family of +// classes that listen for activity on a socket and +// respond to it, for instance by reading a datagram and +// serving it (or queueing it up for later service). +// +// A ConnectionReader may define an arbitrary number of +// threads (at least one) to process datagrams coming in +// from an arbitrary number of sockets that it is +// monitoring. The number of threads is specified at +// construction time and cannot be changed, but the set +// of sockets that is to be monitored may be constantly +// modified at will. +// +// This is an abstract class because it doesn't define +// how to process each received datagram. See +// QueuedConnectionReader. Also note that +// ConnectionListener derives from this class, extending +// it to accept connections on a rendezvous socket +// rather than read datagrams. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ConnectionReader { +public: + // The implementation here used to involve NSPR's multi-wait + // interface, but that got too complicated to manage. It turns out + // to be difficult to protect against memory leaks caused by race + // conditions in that interface, as designed. + + // Instead, we do our own multi-wait type stuff. Only one thread at + // a time can extract the next-available socket with activity on it. + // That thread will either (a) simply extract the next socket from + // the arrays returned by a previous call to PR_Poll(), or (b) + // execute (and possibly block on) a new call to PR_Poll(). + + ConnectionReader(ConnectionManager *manager, int num_threads); + virtual ~ConnectionReader(); + + bool add_connection(const PT(Connection) &connection); + bool remove_connection(const PT(Connection) &connection); + bool is_connection_ok(const PT(Connection) &connection); + + void poll(); + + ConnectionManager *get_manager() const; + bool is_polling() const; + int get_num_threads() const; + +protected: + virtual void receive_datagram(const NetDatagram &datagram)=0; + + class SocketInfo { + public: + SocketInfo(const PT(Connection) &connection); + bool is_udp() const; + PRFileDesc *get_socket() const; + + PT(Connection) _connection; + bool _busy; + bool _error; + }; + + void shutdown(); + void clear_manager(); + void finish_socket(SocketInfo *sinfo); + + virtual void process_incoming_data(SocketInfo *sinfo); + virtual void process_incoming_udp_data(SocketInfo *sinfo); + virtual void process_incoming_tcp_data(SocketInfo *sinfo); + +private: + static void thread_start(void *data); + void thread_run(); + + SocketInfo *get_next_available_socket(PRIntervalTime timeout, + PRInt32 current_thread_index); + + void rebuild_poll_list(); + +protected: + ConnectionManager *_manager; + +private: + bool _shutdown; + + typedef vector Threads; + Threads _threads; + PRLock *_startup_mutex; + bool _polling; + + // These structures are used to manage polling for noise on + // available sockets. + typedef vector Poll; + typedef vector Sockets; + Poll _poll; + Sockets _polled_sockets; + int _next_index; + int _num_results; + // Threads go to sleep on this mutex waiting for their chance to + // read a socket. + PRLock *_select_mutex; + + // This is atomically updated with the index (in _threads) of the + // thread that is currently waiting on the PR_Poll() call. It + // contains -1 if no thread is so waiting. + PRInt32 _currently_polling_thread; + + // These structures track the total set of sockets (connections) we + // know about. + Sockets _sockets; + // This is the list of recently-removed sockets. We can't actually + // delete them until they're no longer _busy. + Sockets _removed_sockets; + // Threads may set this true to force the polling thread to rebuild + // its Poll() list. + bool _reexamine_sockets; + // Any operations on _sockets are protected by this mutex. + PRLock *_sockets_mutex; + + +friend class ConnectionManager; +}; + +#endif diff --git a/panda/src/net/connectionWriter.cxx b/panda/src/net/connectionWriter.cxx new file mode 100644 index 0000000000..bd26cf796b --- /dev/null +++ b/panda/src/net/connectionWriter.cxx @@ -0,0 +1,236 @@ +// Filename: connectionWriter.cxx +// Created by: drose (08Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "connectionWriter.h" +#include "connectionManager.h" +#include "pprerror.h" +#include "config_net.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionWriter::Constructor +// Access: Public +// Description: Creates a new ConnectionWriter with the indicated +// number of threads to handle output. +// +// If num_threads is 0, all datagrams will be sent +// immediately instead of queueing for later +// transmission by a thread. +//////////////////////////////////////////////////////////////////// +ConnectionWriter:: +ConnectionWriter(ConnectionManager *manager, int num_threads) : + _manager(manager) +{ + _immediate = (num_threads <= 0); + + for (int i = 0; i < num_threads; i++) { + PRThread *thread = + PR_CreateThread(PR_USER_THREAD, + thread_start, (void *)this, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, // Since thread will mostly do I/O. + PR_JOINABLE_THREAD, + 0); // Select a suitable stack size. + + nassertv(thread != (PRThread *)NULL); + _threads.push_back(thread); + } + + _manager->add_writer(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionWriter::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ConnectionWriter:: +~ConnectionWriter() { + if (_manager != (ConnectionManager *)NULL) { + _manager->remove_writer(this); + } + + // First, shutdown the queue. This will tell our threads they're + // done. + _queue.shutdown(); + + // Now wait for all threads to terminate. + Threads::iterator ti; + for (ti = _threads.begin(); ti != _threads.end(); ++ti) { + // Interrupt the thread just in case it was stuck waiting for I/O. + PRStatus result = PR_Interrupt(*ti); + if (result != PR_SUCCESS) { + pprerror("PR_Interrupt"); + } + + result = PR_JoinThread(*ti); + if (result != PR_SUCCESS) { + pprerror("PR_JoinThread"); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionWriter::send +// Access: Public +// Description: Enqueues a datagram for transmittal on the indicated +// socket. Since the host address is not specified with +// this form, this function should only be used for +// sending TCP packets. Use the other send() method for +// sending UDP packets. +// +// Returns true if successful, false if there was an +// error. In the normal, threaded case, this function +// only returns false if the send queue is filled; it's +// impossible to detect a transmission error at this +// point. +//////////////////////////////////////////////////////////////////// +bool ConnectionWriter:: +send(const Datagram &datagram, const PT(Connection) &connection) { + nassertr(connection != (Connection *)NULL, false); + nassertr(PR_GetDescType(connection->get_socket()) == PR_DESC_SOCKET_TCP, false); + + if (net_cat.is_debug()) { + net_cat.debug() + << "Sending TCP datagram of " << datagram.get_length() + << " bytes\n"; + } + + NetDatagram copy(datagram); + copy.set_connection(connection); + + if (_immediate) { + return connection->send_datagram(copy); + } else { + return _queue.insert(copy); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionWriter::send +// Access: Public +// Description: Enqueues a datagram for transmittal on the indicated +// socket. This form of the function allows the +// specification of a destination host address, and so +// is appropriate for UDP packets. Use the other send() +// method for sending TCP packets. +// +// Returns true if successful, false if there was an +// error. In the normal, threaded case, this function +// only returns false if the send queue is filled; it's +// impossible to detect a transmission error at this +// point. +//////////////////////////////////////////////////////////////////// +bool ConnectionWriter:: +send(const Datagram &datagram, const PT(Connection) &connection, + const NetAddress &address) { + nassertr(connection != (Connection *)NULL, false); + nassertr(PR_GetDescType(connection->get_socket()) == PR_DESC_SOCKET_UDP, false); + + if (net_cat.is_debug()) { + net_cat.debug() + << "Sending UDP datagram of " << datagram.get_length() + << " bytes\n"; + } + + if (PR_GetDescType(connection->get_socket()) == PR_DESC_SOCKET_UDP && + datagram.get_length() > maximum_udp_datagram) { + net_cat.warning() + << "Attempt to send UDP datagram of " << datagram.get_length() + << " bytes, more than the\n" + << "currently defined maximum of " << maximum_udp_datagram + << " bytes.\n"; + } + + NetDatagram copy(datagram); + copy.set_connection(connection); + copy.set_address(address); + + if (_immediate) { + return connection->send_datagram(copy); + } else { + return _queue.insert(copy); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionWriter::get_manager +// Access: Public +// Description: Returns a pointer to the ConnectionManager object +// that serves this ConnectionWriter. +//////////////////////////////////////////////////////////////////// +ConnectionManager *ConnectionWriter:: +get_manager() const { + return _manager; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionWriter::is_immediate +// Access: Public +// Description: Returns true if the writer is an immediate writer, +// i.e. it has no threads. +//////////////////////////////////////////////////////////////////// +bool ConnectionWriter:: +is_immediate() const { + return _immediate; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionWriter::get_num_threads +// Access: Public +// Description: Returns the number of threads the ConnectionWriter +// has been created with. +//////////////////////////////////////////////////////////////////// +int ConnectionWriter:: +get_num_threads() const { + return _threads.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionWriter::clear_manager +// Access: Protected +// Description: This should normally only be called when the +// associated ConnectionManager destructs. It resets +// the ConnectionManager pointer to NULL so we don't +// have a floating pointer. This makes the +// ConnectionWriter invalid; presumably it also will be +// destructed momentarily. +//////////////////////////////////////////////////////////////////// +void ConnectionWriter:: +clear_manager() { + _manager = (ConnectionManager *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionWriter::thread_start +// Access: Private, Static +// Description: The static wrapper around the thread's executing +// function. This must be a static member function +// because it is passed through the C interface to +// PR_CreateThread(). +//////////////////////////////////////////////////////////////////// +void ConnectionWriter:: +thread_start(void *data) { + ((ConnectionWriter *)data)->thread_run(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionWriter::thread_run +// Access: Private +// Description: This is the actual executing function for each +// thread. +//////////////////////////////////////////////////////////////////// +void ConnectionWriter:: +thread_run() { + nassertv(!_immediate); + + NetDatagram datagram; + while (_queue.extract(datagram)) { + datagram.get_connection()->send_datagram(datagram); + } +} diff --git a/panda/src/net/connectionWriter.h b/panda/src/net/connectionWriter.h new file mode 100644 index 0000000000..ffe85641ba --- /dev/null +++ b/panda/src/net/connectionWriter.h @@ -0,0 +1,71 @@ +// Filename: connectionWriter.h +// Created by: drose (08Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONNECTIONWRITER_H +#define CONNECTIONWRITER_H + +#include + +#include "datagramQueue.h" +#include "connection.h" + +#include + +#include +#include + +class ConnectionManager; +class NetAddress; + +//////////////////////////////////////////////////////////////////// +// Class : ConnectionWriter +// Description : This class handles threaded delivery of datagrams to +// various TCP or UDP sockets. +// +// A ConnectionWriter may define an arbitrary number of +// threads (at least one) to write its datagrams to +// sockets. The number of threads is specified at +// construction time and cannot be changed. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ConnectionWriter { +public: + ConnectionWriter(ConnectionManager *manager, int num_threads); + ~ConnectionWriter(); + + bool send(const Datagram &datagram, + const PT(Connection) &connection); + + bool send(const Datagram &datagram, + const PT(Connection) &connection, + const NetAddress &address); + + ConnectionManager *get_manager() const; + bool is_immediate() const; + int get_num_threads() const; + +protected: + void clear_manager(); + +private: + static void thread_start(void *data); + void thread_run(); + bool send_datagram(const NetDatagram &datagram); + +protected: + ConnectionManager *_manager; + +private: + DatagramQueue _queue; + + typedef vector Threads; + Threads _threads; + bool _immediate; + +friend class ConnectionManager; +}; + +#endif + + diff --git a/panda/src/net/datagramQueue.cxx b/panda/src/net/datagramQueue.cxx new file mode 100644 index 0000000000..e369724619 --- /dev/null +++ b/panda/src/net/datagramQueue.cxx @@ -0,0 +1,163 @@ +// Filename: datagramQueue.cxx +// Created by: drose (08Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "datagramQueue.h" +#include "config_net.h" + +//////////////////////////////////////////////////////////////////// +// Function: DatagramQueue::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DatagramQueue:: +DatagramQueue() { + _shutdown = false; + _cvlock = PR_NewLock(); + _cv = PR_NewCondVar(_cvlock); + _max_queue_size = get_net_max_write_queue(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramQueue::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DatagramQueue:: +~DatagramQueue() { + // It's an error to delete a DatagramQueue without first shutting it + // down (and waiting for any associated threads to terminate). + nassertv(_shutdown); + + PR_DestroyCondVar(_cv); + PR_DestroyLock(_cvlock); +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramQueue::shutdown +// Access: Public +// Description: Marks the queue as shutting down, which will +// eventually cause all threads blocking on extract() to +// return false. The normal way to delete a +// DatagramQueue will be to call first shutdown() and +// then wait for all known threads to terminate. Then +// it is safe to delete the queue. +//////////////////////////////////////////////////////////////////// +void DatagramQueue:: +shutdown() { + // Notify all of our threads that we're shutting down. This will + // cause any thread blocking on extract() to return false. + PR_Lock(_cvlock); + _shutdown = true; + PR_NotifyAllCondVar(_cv); + PR_Unlock(_cvlock); +} + + +//////////////////////////////////////////////////////////////////// +// Function: DatagramQueue::insert +// Access: Public +// Description: Inserts the indicated datagram onto the end of the +// queue, and returns. If the queue is empty and any +// threads are waiting on the queue, this will wake one +// of them up. Returns true if successful, false if the +// queue was full. +//////////////////////////////////////////////////////////////////// +bool DatagramQueue:: +insert(const NetDatagram &data) { + PR_Lock(_cvlock); + bool enqueue_ok = (_queue.size() < _max_queue_size); + if (enqueue_ok) { + _queue.push_back(data); + } + PR_NotifyCondVar(_cv); + PR_Unlock(_cvlock); + return enqueue_ok; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DatagramQueue::extract +// Access: Public +// Description: Extracts a datagram from the head of the queue, if +// one is available. If a datagram is available, this +// will immediately return; otherwise, it will block +// until a datagram becomes available. Multiple threads +// may simultaneously block on extract(); when a +// datagram is subsequently inserted into the queue, one +// of the threads will return from extract() with the +// datagram. +// +// The return value is true if the datagram is +// successfully extracted, or false if the queue was +// destroyed while waiting. (In the case of a false +// return, the thread should not attempt to operate on +// the queue again.) +//////////////////////////////////////////////////////////////////// +bool DatagramQueue:: +extract(NetDatagram &result) { + // First, clear the datagram result in case it's got an outstanding + // connection pointer--we're about to go to sleep for a while. + result.clear(); + + PR_Lock(_cvlock); + while (_queue.empty() && !_shutdown) { + PR_WaitCondVar(_cv, PR_INTERVAL_NO_TIMEOUT); + } + + if (_shutdown) { + PR_Unlock(_cvlock); + return false; + } + + nassertr(!_queue.empty(), false); + result = _queue.front(); + _queue.pop_front(); + + PR_Unlock(_cvlock); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramQueue::set_max_queue_size +// Access: Public +// Description: Sets the maximum size the queue is allowed to grow +// to. This is primarily for a sanity check; this is a +// limit beyond which we can assume something bad has +// happened. +// +// It's also a crude check against unfortunate seg +// faults due to the queue filling up and quietly +// consuming all available memory. +//////////////////////////////////////////////////////////////////// +void DatagramQueue:: +set_max_queue_size(int max_size) { + PR_Lock(_cvlock); + _max_queue_size = max_size; + PR_Unlock(_cvlock); +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramQueue::get_max_queue_size +// Access: Public +// Description: Returns the maximum size the queue is allowed to grow +// to. See set_max_queue_size(). +//////////////////////////////////////////////////////////////////// +int DatagramQueue:: +get_max_queue_size() const { + return _max_queue_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramQueue::get_current_queue_size +// Access: Public +// Description: Returns the current number of things in the queue. +//////////////////////////////////////////////////////////////////// +int DatagramQueue:: +get_current_queue_size() const { + PR_Lock(_cvlock); + int size = _queue.size(); + PR_Unlock(_cvlock); + return size; +} diff --git a/panda/src/net/datagramQueue.h b/panda/src/net/datagramQueue.h new file mode 100644 index 0000000000..aaad259b65 --- /dev/null +++ b/panda/src/net/datagramQueue.h @@ -0,0 +1,45 @@ +// Filename: datagramQueue.h +// Created by: drose (07Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DATAGRAMQUEUE_H +#define DATAGRAMQUEUE_H + +#include + +#include "netDatagram.h" + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : DatagramQueue +// Description : A thread-safe, FIFO queue of NetDatagrams. This is used +// by ConnectionWriter for queuing up datagrams for +// its various threads to write to sockets. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DatagramQueue { +public: + DatagramQueue(); + ~DatagramQueue(); + void shutdown(); + + bool insert(const NetDatagram &data); + bool extract(NetDatagram &result); + + void set_max_queue_size(int max_size); + int get_max_queue_size() const; + int get_current_queue_size() const; + +private: + PRLock *_cvlock; + PRCondVar *_cv; + deque _queue; + bool _shutdown; + int _max_queue_size; +}; + +#endif + diff --git a/panda/src/net/datagramTCPHeader.cxx b/panda/src/net/datagramTCPHeader.cxx new file mode 100644 index 0000000000..0eec313ce1 --- /dev/null +++ b/panda/src/net/datagramTCPHeader.cxx @@ -0,0 +1,101 @@ +// Filename: datagramTCPHeader.cxx +// Created by: drose (08Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "datagramTCPHeader.h" +#include "netDatagram.h" +#include "datagramIterator.h" +#include "config_net.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: DatagramTCPHeader::Constructor +// Access: Public +// Description: This constructor creates a header based on an +// already-constructed NetDatagram. +//////////////////////////////////////////////////////////////////// +DatagramTCPHeader:: +DatagramTCPHeader(const NetDatagram &datagram) { + const string &str = datagram.get_message(); + PRUint16 size = str.length(); + nassertv(size == str.length()); + + // Now pack the header. + _header.add_uint16(size); + nassertv(_header.get_length() == datagram_tcp_header_size); +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramTCPHeader::Constructor +// Access: Public +// Description: This constructor decodes a header from a block of +// data of length datagram_tcp_header_size, presumably +// just read from a socket. +//////////////////////////////////////////////////////////////////// +DatagramTCPHeader:: +DatagramTCPHeader(const void *data) : _header(data, datagram_tcp_header_size) { +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramTCPHeader::get_datagram_size +// Access: Public +// Description: Returns the number of bytes in the associated +// datagram. +//////////////////////////////////////////////////////////////////// +int DatagramTCPHeader:: +get_datagram_size() const { + DatagramIterator di(_header); + return di.get_uint16(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramTCPHeader::get_header +// Access: Public +// Description: Returns a pointer to a block of data of length +// datagram_tcp_header_size, which can be written to the +// network as the header information. +//////////////////////////////////////////////////////////////////// +const string &DatagramTCPHeader:: +get_header() const { + return _header.get_message(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramTCPHeader::verify_datagram +// Access: Public +// Description: Verifies that the indicated datagram has the +// appropriate length. Returns true if it matches, +// false otherwise. +//////////////////////////////////////////////////////////////////// +bool DatagramTCPHeader:: +verify_datagram(const NetDatagram &datagram) const { + const string &str = datagram.get_message(); + PRUint16 size = str.length(); + nassertr(size == str.length(), false); + + if (size == get_datagram_size()) { + return true; + } + + if (net_cat.is_debug()) { + net_cat.debug() + << "Invalid datagram!\n"; + if (size != get_datagram_size()) { + net_cat.debug() + << " size is " << size << " bytes, header reports " + << get_datagram_size() << "\n"; + } + + // We write the hex dump into a ostringstream first, to guarantee + // an atomic write to the output stream in case we're threaded. + + ostringstream hex; + datagram.dump_hex(hex); + hex << "\n"; + net_cat.debug() << hex.str(); + } + + return false; +} diff --git a/panda/src/net/datagramTCPHeader.h b/panda/src/net/datagramTCPHeader.h new file mode 100644 index 0000000000..03b6adf5ed --- /dev/null +++ b/panda/src/net/datagramTCPHeader.h @@ -0,0 +1,47 @@ +// Filename: datagramTCPHeader.h +// Created by: drose (08Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DATAGRAMTCPHEADER_H +#define DATAGRAMTCPHEADER_H + +#include + +#include "netDatagram.h" + +#include + +static const int datagram_tcp_header_size = sizeof(PRUint16); + +class NetDatagram; + +//////////////////////////////////////////////////////////////////// +// Class : DatagramTCPHeader +// Description : A class that encapsulates the extra bytes that are +// sent in front of each datagram to identify it when it +// is sent on TCP. This is similar to +// DatagramUDPHeader, except it does not include a +// checksum, since this is unnecessary on UDP. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DatagramTCPHeader { +public: + DatagramTCPHeader(const NetDatagram &datagram); + DatagramTCPHeader(const void *data); + + int get_datagram_size() const; + + const string &get_header() const; + + bool verify_datagram(const NetDatagram &datagram) const; + +private: + // The actual data for the header is stored (somewhat recursively) + // in its own NetDatagram object. This is just for convenience of + // packing and unpacking the header. + NetDatagram _header; +}; + +#endif + + diff --git a/panda/src/net/datagramUDPHeader.cxx b/panda/src/net/datagramUDPHeader.cxx new file mode 100644 index 0000000000..30b797eb6b --- /dev/null +++ b/panda/src/net/datagramUDPHeader.cxx @@ -0,0 +1,129 @@ +// Filename: datagramUDPHeader.cxx +// Created by: drose (08Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "datagramUDPHeader.h" +#include "netDatagram.h" +#include "datagramIterator.h" +#include "config_net.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: DatagramUDPHeader::Constructor +// Access: Public +// Description: This constructor creates a header based on an +// already-constructed NetDatagram. +//////////////////////////////////////////////////////////////////// +DatagramUDPHeader:: +DatagramUDPHeader(const NetDatagram &datagram) { + const string &str = datagram.get_message(); + PRUint16 size = str.length(); + nassertv(size == str.length()); + + PRUint16 checksum = 0; + for (size_t p = 0; p < str.size(); p++) { + checksum += (PRUint16)(PRUint8)str[p]; + } + + // Now pack the header. + _header.add_uint16(size); + _header.add_uint16(checksum); + nassertv(_header.get_length() == datagram_udp_header_size); +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramUDPHeader::Constructor +// Access: Public +// Description: This constructor decodes a header from a block of +// data of length datagram_udp_header_size, presumably +// just read from a socket. +//////////////////////////////////////////////////////////////////// +DatagramUDPHeader:: +DatagramUDPHeader(const void *data) : _header(data, datagram_udp_header_size) { +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramUDPHeader::get_datagram_size +// Access: Public +// Description: Returns the number of bytes in the associated +// datagram. +//////////////////////////////////////////////////////////////////// +int DatagramUDPHeader:: +get_datagram_size() const { + DatagramIterator di(_header); + return di.get_uint16(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramUDPHeader::get_datagram_checksum +// Access: Public +// Description: Returns the checksum appropriate for the indicated +// datagram. +//////////////////////////////////////////////////////////////////// +int DatagramUDPHeader:: +get_datagram_checksum() const { + DatagramIterator di(_header, sizeof(PRUint16)); + return di.get_uint16(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramUDPHeader::get_header +// Access: Public +// Description: Returns a pointer to a block of data of length +// datagram_udp_header_size, which can be written to the +// network as the header information. +//////////////////////////////////////////////////////////////////// +const string &DatagramUDPHeader:: +get_header() const { + return _header.get_message(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramUDPHeader::verify_datagram +// Access: Public +// Description: Verifies that the indicated datagram has the +// appropriate length and checksum. Returns true if it +// matches, false otherwise. +//////////////////////////////////////////////////////////////////// +bool DatagramUDPHeader:: +verify_datagram(const NetDatagram &datagram) const { + const string &str = datagram.get_message(); + PRUint16 size = str.length(); + nassertr(size == str.length(), false); + + PRUint16 checksum = 0; + for (size_t p = 0; p < str.size(); p++) { + checksum += (PRUint16)(PRUint8)str[p]; + } + + if (size == get_datagram_size() && checksum == get_datagram_checksum()) { + return true; + } + + if (net_cat.is_debug()) { + net_cat.debug() + << "Invalid datagram!\n"; + if (size != get_datagram_size()) { + net_cat.debug() + << " size is " << size << " bytes, header reports " + << get_datagram_size() << "\n"; + } + if (checksum != get_datagram_checksum()) { + net_cat.debug() + << " checksum is " << checksum << ", header reports " + << get_datagram_checksum() << "\n"; + } + + // We write the hex dump into a ostringstream first, to guarantee + // an atomic write to the output stream in case we're threaded. + + ostringstream hex; + datagram.dump_hex(hex); + hex << "\n"; + net_cat.debug() << hex.str(); + } + + return false; +} diff --git a/panda/src/net/datagramUDPHeader.h b/panda/src/net/datagramUDPHeader.h new file mode 100644 index 0000000000..440f000fca --- /dev/null +++ b/panda/src/net/datagramUDPHeader.h @@ -0,0 +1,49 @@ +// Filename: datagramUDPHeader.h +// Created by: drose (08Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DATAGRAMUDPHEADER_H +#define DATAGRAMUDPHEADER_H + +#include + +#include "netDatagram.h" + +#include + +static const int datagram_udp_header_size = + sizeof(PRUint16) + sizeof(PRUint16); + +class NetDatagram; + +//////////////////////////////////////////////////////////////////// +// Class : DatagramUDPHeader +// Description : A class that encapsulates the extra bytes that are +// sent in front of each datagram to identify it when it +// is sent on UDP. Like NetDatagram, this class +// automatically handles converting its data to and from +// the network byte ordering. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DatagramUDPHeader { +public: + DatagramUDPHeader(const NetDatagram &datagram); + DatagramUDPHeader(const void *data); + + int get_datagram_size() const; + int get_datagram_checksum() const; + + const string &get_header() const; + + bool verify_datagram(const NetDatagram &datagram) const; + +private: + // The actual data for the header is stored (somewhat recursively) + // in its own NetDatagram object. This is just for convenience of + // packing and unpacking the header. + NetDatagram _header; +}; + +#endif + + diff --git a/panda/src/net/datagram_ui.cxx b/panda/src/net/datagram_ui.cxx new file mode 100644 index 0000000000..bba10e63ba --- /dev/null +++ b/panda/src/net/datagram_ui.cxx @@ -0,0 +1,115 @@ +// Filename: datagram_ui.cxx +// Created by: drose (09Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "datagram_ui.h" +#include "datagramIterator.h" + +#include +#include +#include + +enum DatagramElement { + DE_int32, + DE_float64, + DE_string, + DE_end +}; + +istream & +operator >> (istream &in, NetDatagram &datagram) { + datagram.clear(); + + // First, read a line of text. + string line; + getline(in, line); + + // Now parse the text. + size_t p = 0; + while (p < line.length()) { + // Skip whitespace + while (p < line.length() && isspace(line[p])) { + p++; + } + + // What have we got? + if (p < line.length()) { + if (isdigit(line[p]) || line[p] == '-') { + // A number. + size_t start = p; + p++; + while (p < line.length() && isdigit(line[p])) { + p++; + } + if (p < line.length() && line[p] == '.') { + // A floating-point number. + p++; + while (p < line.length() && isdigit(line[p])) { + p++; + } + double num = atof(line.substr(start, p - start).c_str()); + datagram.add_int8(DE_float64); + datagram.add_float64(num); + } else { + // An integer. + int num = atoi(line.substr(start, p - start).c_str()); + datagram.add_int8(DE_int32); + datagram.add_int32(num); + } + + } else if (line[p] == '"') { + // A quoted string. + p++; + size_t start = p; + while (p < line.length() && line[p] != '"') { + p++; + } + string str = line.substr(start, p - start); + datagram.add_int8(DE_string); + datagram.add_string(str); + p++; + + } else { + // An unquoted string. + size_t start = p; + while (p < line.length() && !isspace(line[p])) { + p++; + } + string str = line.substr(start, p - start); + datagram.add_int8(DE_string); + datagram.add_string(str); + } + } + } + datagram.add_int8(DE_end); + return in; +} + +ostream & +operator << (ostream &out, const NetDatagram &datagram) { + DatagramIterator di(datagram); + + DatagramElement de = (DatagramElement)di.get_int8(); + while (de != DE_end) { + switch (de) { + case DE_int32: + out << di.get_int32() << " "; + break; + + case DE_float64: + out << di.get_float64() << " "; + break; + + case DE_string: + out << "\"" << di.get_string() << "\" "; + break; + + default: + out << "(invalid datagram)"; + return out; + } + de = (DatagramElement)di.get_int8(); + } + return out; +} diff --git a/panda/src/net/datagram_ui.h b/panda/src/net/datagram_ui.h new file mode 100644 index 0000000000..2965837148 --- /dev/null +++ b/panda/src/net/datagram_ui.h @@ -0,0 +1,28 @@ +// Filename: datagram_ui.h +// Created by: drose (09Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DATAGRAM_UI_H +#define DATAGRAM_UI_H + +//////////////////////////////////////////////////////////////////// +// +// The functions defined here are used for testing purposes only by +// some of the various test_* programs in this directory. They are +// not compiled into the package library, libnet.so. +// +// These functions are handy for getting and reporting a datagram from +// and to the user. They extend a datagram by encoding information +// about the types of values stored in it. +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "netDatagram.h" + +istream &operator >> (istream &in, NetDatagram &datagram); +ostream &operator << (ostream &out, const NetDatagram &datagram); + +#endif diff --git a/panda/src/net/netAddress.cxx b/panda/src/net/netAddress.cxx new file mode 100644 index 0000000000..7a4e73f02d --- /dev/null +++ b/panda/src/net/netAddress.cxx @@ -0,0 +1,168 @@ +// Filename: netAddress.cxx +// Created by: drose (08Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "netAddress.h" +#include "pprerror.h" +#include "config_net.h" + +#include +#include + + +//////////////////////////////////////////////////////////////////// +// Function: NetAddress::Constructor +// Access: Public +// Description: Constructs an unspecified address. +//////////////////////////////////////////////////////////////////// +NetAddress:: +NetAddress() { + PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &_addr); +} + +//////////////////////////////////////////////////////////////////// +// Function: NetAddress::Constructor +// Access: Public +// Description: Constructs an address from a given PRNetAddr. +// Normally, this constructor should not be used by user +// code; instead, create a default NetAddress and use +// one of the set_*() functions to set up an address. +//////////////////////////////////////////////////////////////////// +NetAddress:: +NetAddress(const PRNetAddr &addr) : _addr(addr) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: NetAddress::set_any +// Access: Public +// Description: Sets the address up to refer to a particular port, +// but not to any particular IP. Returns true if +// successful, false otherwise (currently, this only +// returns true). +//////////////////////////////////////////////////////////////////// +bool NetAddress:: +set_any(int port) { + PR_InitializeNetAddr(PR_IpAddrAny, port, &_addr); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NetAddress::set_localhost +// Access: Public +// Description: Sets the address up to refer to a particular port, +// on this host. +//////////////////////////////////////////////////////////////////// +bool NetAddress:: +set_localhost(int port) { + PR_InitializeNetAddr(PR_IpAddrLoopback, port, &_addr); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NetAddress::set_host +// Access: Public +// Description: Sets the address up to refer to a particular port +// on a particular host. Returns true if the hostname +// is known, false otherwise. +//////////////////////////////////////////////////////////////////// +bool NetAddress:: +set_host(const string &hostname, int port) { + char buf[PR_NETDB_BUF_SIZE]; + PRHostEnt host; + PRStatus result = + PR_GetHostByName(hostname.c_str(), buf, PR_NETDB_BUF_SIZE, &host); + if (result != PR_SUCCESS) { + pprerror("PR_GetHostByName"); + return false; + } + + PRIntn next = PR_EnumerateHostEnt(0, &host, port, &_addr); + + if (next == -1) { + pprerror("PR_EnumerateHostEnt"); + return false; + } else if (next == 0) { + net_cat.error() + << "No addresses available for " << hostname << ".\n"; + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NetAddress::clear +// Access: Public +// Description: Resets the NetAddress to its initial state. +//////////////////////////////////////////////////////////////////// +void NetAddress:: +clear() { + PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &_addr); +} + +//////////////////////////////////////////////////////////////////// +// Function: NetAddress::get_port +// Access: Public +// Description: Returns the port number to which this address refers. +//////////////////////////////////////////////////////////////////// +int NetAddress:: +get_port() const { + return PR_ntohs(_addr.inet.port); +} + +//////////////////////////////////////////////////////////////////// +// Function: NetAddress::set_port +// Access: Public +// Description: Resets the port number without otherwise changing the +// address. +//////////////////////////////////////////////////////////////////// +void NetAddress:: +set_port(int port) { + PR_InitializeNetAddr(PR_IpAddrNull, port, &_addr); +} + +//////////////////////////////////////////////////////////////////// +// Function: NetAddress::get_ip +// Access: Public +// Description: Returns the IP address to which this address refers, +// formatted as a string. +//////////////////////////////////////////////////////////////////// +string NetAddress:: +get_ip() const { + static const int buf_len = 1024; + char buf[buf_len]; + + PRStatus result = + PR_NetAddrToString(&_addr, buf, buf_len); + if (result != PR_SUCCESS) { + pprerror("PR_NetAddrToString"); + return "error"; + } + + return string(buf); +} + + +//////////////////////////////////////////////////////////////////// +// Function: NetAddress::get_addr +// Access: Public +// Description: Returns the PRNetAddr for this address. +//////////////////////////////////////////////////////////////////// +PRNetAddr *NetAddress:: +get_addr() const { + return (PRNetAddr *)&_addr; +} + +//////////////////////////////////////////////////////////////////// +// Function: NetAddress::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NetAddress:: +output(ostream &out) const { + out << get_ip(); +} diff --git a/panda/src/net/netAddress.h b/panda/src/net/netAddress.h new file mode 100644 index 0000000000..39c5bd8832 --- /dev/null +++ b/panda/src/net/netAddress.h @@ -0,0 +1,47 @@ +// Filename: netAddress.h +// Created by: drose (08Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NETADDRESS_H +#define NETADDRESS_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : NetAddress +// Description : Represents a network address to which UDP packets may +// be sent or to which a TCP socket may be bound. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NetAddress { +public: + NetAddress(); + NetAddress(const PRNetAddr &addr); + + bool set_any(int port); + bool set_localhost(int port); + bool set_host(const string &hostname, int port); + + void clear(); + + int get_port() const; + void set_port(int port); + string get_ip() const; + + PRNetAddr *get_addr() const; + + void output(ostream &out) const; + +private: + PRNetAddr _addr; +}; + +INLINE ostream &operator << (ostream &out, const NetAddress &addr) { + addr.output(out); + return out; +} + +#endif + diff --git a/panda/src/net/netDatagram.I b/panda/src/net/netDatagram.I new file mode 100644 index 0000000000..00868da3ca --- /dev/null +++ b/panda/src/net/netDatagram.I @@ -0,0 +1,34 @@ +// Filename: datagram.I +// Created by: drose (17May00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: NetDatagram::operator == +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NetDatagram:: +operator == (const NetDatagram &) const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NetDatagram::operator != +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NetDatagram:: +operator != (const NetDatagram &) const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: NetDatagram::operator < +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NetDatagram:: +operator < (const NetDatagram &) const { + return false; +} diff --git a/panda/src/net/netDatagram.cxx b/panda/src/net/netDatagram.cxx new file mode 100644 index 0000000000..9672b9f2ae --- /dev/null +++ b/panda/src/net/netDatagram.cxx @@ -0,0 +1,132 @@ +// Filename: datagram.cxx +// Created by: jns (07Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "netDatagram.h" + +TypeHandle NetDatagram::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: NetDatagram::Constructor +// Access: Public +// Description: Constructs an empty datagram. +//////////////////////////////////////////////////////////////////// +NetDatagram:: +NetDatagram() { +} + +//////////////////////////////////////////////////////////////////// +// Function: NetDatagram::Constructor +// Access: Public +// Description: Constructs a datagram from an existing block of data. +//////////////////////////////////////////////////////////////////// +NetDatagram:: +NetDatagram(const void *data, size_t size) : + Datagram(data, size) { +} + +//////////////////////////////////////////////////////////////////// +// Function: NetDatagram::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +NetDatagram:: +NetDatagram(const Datagram ©) : + Datagram(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NetDatagram::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +NetDatagram:: +NetDatagram(const NetDatagram ©) : + Datagram(copy), + _connection(copy._connection), + _address(copy._address) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NetDatagram::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NetDatagram:: +operator = (const Datagram ©) { + Datagram::operator = (copy); + _connection.clear(); + _address.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NetDatagram::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NetDatagram:: +operator = (const NetDatagram ©) { + Datagram::operator = (copy); + _connection = copy._connection; + _address = copy._address; +} + +//////////////////////////////////////////////////////////////////// +// Function: NetDatagram::clear +// Access: Public, Virtual +// Description: Resets the datagram to empty, in preparation for +// building up a new datagram. +//////////////////////////////////////////////////////////////////// +void NetDatagram:: +clear() { + Datagram::clear(); + _connection.clear(); + _address.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NetDatagram::set_connection +// Access: Public +// Description: Specifies the socket to which the datagram should be +// written. +//////////////////////////////////////////////////////////////////// +void NetDatagram:: +set_connection(const PT(Connection) &connection) { + _connection = connection; +} + +//////////////////////////////////////////////////////////////////// +// Function: NetDatagram::set_connection +// Access: Public +// Description: Retrieves the socket from which the datagram was +// read, or to which it is scheduled to be written. +//////////////////////////////////////////////////////////////////// +PT(Connection) NetDatagram:: +get_connection() const { + return _connection; +} + +//////////////////////////////////////////////////////////////////// +// Function: NetDatagram::set_address +// Access: Public +// Description: Specifies the host to which the datagram should be +// sent. +//////////////////////////////////////////////////////////////////// +void NetDatagram:: +set_address(const NetAddress &address) { + _address = address; +} + +//////////////////////////////////////////////////////////////////// +// Function: NetDatagram::set_address +// Access: Public +// Description: Retrieves the host from which the datagram was +// read, or to which it is scheduled to be sent. +//////////////////////////////////////////////////////////////////// +const NetAddress &NetDatagram:: +get_address() const { + return _address; +} diff --git a/panda/src/net/netDatagram.h b/panda/src/net/netDatagram.h new file mode 100644 index 0000000000..cbcb2e0c58 --- /dev/null +++ b/panda/src/net/netDatagram.h @@ -0,0 +1,84 @@ +// Filename: netDatagram.h +// Created by: jns (07Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NETDATAGRAM_H +#define NETDATAGRAM_H + +#include + +#include "connection.h" +#include "netAddress.h" + +#include +#include + +#include + +// This determines the size of the read buffer used to read UDP +// packets. It places a limit on the maximum receivable size of a UDP +// packet (although it doesn't limit TCP packets at all). However, +// there's no real reason this can't be set arbitrarily large, +// although there's not much point in making it larger than the system +// MTU, which also limits the maximum size of a UDP packet. +static const int maximum_udp_datagram = 1024; + +//////////////////////////////////////////////////////////////////// +// Class : NetDatagram +// Description : A specific kind of Datagram, especially for sending +// across or receiving from a network. It's different +// only in that it knows which Connection and/or +// NetAddress it is to be sent to or was received from. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NetDatagram : public Datagram { +public: + NetDatagram(); + NetDatagram(const void *data, size_t size); + NetDatagram(const Datagram ©); + NetDatagram(const NetDatagram ©); + void operator = (const Datagram ©); + void operator = (const NetDatagram ©); + + virtual void clear(); + + void set_connection(const PT(Connection) &connection); + PT(Connection) get_connection() const; + + void set_address(const NetAddress &address); + const NetAddress &get_address() const; + +public: + // We need these methods to make VC++ happy when we try to + // instantiate a QueuedReturn. They don't do anything + // useful. + INLINE bool operator == (const NetDatagram &other) const; + INLINE bool operator != (const NetDatagram &other) const; + INLINE bool operator < (const NetDatagram &other) const; + +private: + PT(Connection) _connection; + NetAddress _address; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Datagram::init_type(); + register_type(_type_handle, "NetDatagram", + Datagram::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "netDatagram.I" + +#endif diff --git a/panda/src/net/pprerror.cxx b/panda/src/net/pprerror.cxx new file mode 100644 index 0000000000..6b03e26fd4 --- /dev/null +++ b/panda/src/net/pprerror.cxx @@ -0,0 +1,209 @@ +// Filename: pprerror.cxx +// Created by: drose (08Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include "pprerror.h" +#include "config_net.h" + +#include +#include +#include + +#include +#include + +static const char *get_error_message(PRErrorCode code); + +//////////////////////////////////////////////////////////////////// +// Function: pprerror +// Description: A handy function like perror(). It outputs the +// printf-style format string and its arguments, +// followed by a colon and the NSPR-given description of +// its current error state. +//////////////////////////////////////////////////////////////////// +void +pprerror(const char *format, ...) { + va_list ap; + va_start(ap, format); + + char *str = PR_vsprintf_append(NULL, format, ap); + if (str == (char *)NULL) { + net_cat.error() + << string("Invalid format string: ") + format + "\n"; + return; + } + + PRErrorCode code = PR_GetError(); + const char *error_message = get_error_message(code); + + if (error_message == (const char *)NULL) { + str = PR_sprintf_append(str, ": (%d)\n", code); + } else { + str = PR_sprintf_append(str, ": %s (%d)\n", error_message, code); + } + + net_cat.error() << str; + + PR_Free(str); + if (get_net_error_abort()) { + abort(); + } + va_end(ap); +} + +//////////////////////////////////////////////////////////////////// +// Function: get_error_message +// Description: A local function to this module, it returns a +// sensibly formatted string to describe the given NSPR +// error code. The strings in this function were +// extracted from the NSPR documentation web site. +//////////////////////////////////////////////////////////////////// +static const char * +get_error_message(PRErrorCode code) { + switch (code) { + case PR_OUT_OF_MEMORY_ERROR: + return "PR_OUT_OF_MEMORY_ERROR--Insufficient memory to perform request."; + case PR_BAD_DESCRIPTOR_ERROR: + return "PR_BAD_DESCRIPTOR_ERROR--The file descriptor used as an argument in the preceding function is invalid."; + case PR_WOULD_BLOCK_ERROR: + return "PR_WOULD_BLOCK_ERROR--The operation would have blocked, which conflicts with the semantics that have been established."; + case PR_ACCESS_FAULT_ERROR: + return "PR_ACCESS_FAULT_ERROR--One of the arguments of the preceding function specified an invalid memory address."; + case PR_INVALID_METHOD_ERROR: + return "PR_INVALID_METHOD_ERROR--The preceding function is invalid for the type of file descriptor used."; + case PR_ILLEGAL_ACCESS_ERROR: + return "PR_ILLEGAL_ACCESS_ERROR--One of the arguments of the preceding function specified an invalid memory address."; + case PR_UNKNOWN_ERROR: + return "PR_UNKNOWN_ERROR--Some unknown error has occurred."; + case PR_PENDING_INTERRUPT_ERROR: + return "PR_PENDING_INTERRUPT_ERROR--The operation terminated because another thread has interrupted it with PR_Interrupt."; + case PR_NOT_IMPLEMENTED_ERROR: + return "PR_NOT_IMPLEMENTED_ERROR--The preceding function has not been implemented."; + case PR_IO_ERROR: + return "PR_IO_ERROR--The preceding I/O function encountered some sort of an error, perhaps an invalid device."; + case PR_IO_TIMEOUT_ERROR: + return "PR_IO_TIMEOUT_ERROR--The I/O operation has not completed in the time specified for the preceding function."; + case PR_IO_PENDING_ERROR: + return "PR_IO_PENDING_ERROR--An I/O operation has been attempted on a file descriptor that is currently busy with another operation."; + case PR_DIRECTORY_OPEN_ERROR: + return "PR_DIRECTORY_OPEN_ERROR--The directory could not be opened."; + case PR_INVALID_ARGUMENT_ERROR: + return "PR_INVALID_ARGUMENT_ERROR--One or more of the arguments to the function is invalid."; + case PR_ADDRESS_NOT_AVAILABLE_ERROR: + return "PR_ADDRESS_NOT_AVAILABLE_ERROR--The network address (PRNetAddr) is not available (probably in use)."; + case PR_ADDRESS_NOT_SUPPORTED_ERROR: + return "PR_ADDRESS_NOT_SUPPORTED_ERROR--The type of network address specified is not supported."; + case PR_IS_CONNECTED_ERROR: + return "PR_IS_CONNECTED_ERROR--An attempt to connect on an already connected network file descriptor."; + case PR_BAD_ADDRESS_ERROR: + return "PR_BAD_ADDRESS_ERROR--The network address specified is invalid (as reported by the network)."; + case PR_ADDRESS_IN_USE_ERROR: + return "PR_ADDRESS_IN_USE_ERROR--Network address specified (PRNetAddr) is in use."; + case PR_CONNECT_REFUSED_ERROR: + return "PR_CONNECT_REFUSED_ERROR--The peer has refused to allow the connection to be established."; + case PR_NETWORK_UNREACHABLE_ERROR: + return "PR_NETWORK_UNREACHABLE_ERROR--The network address specifies a host that is unreachable (perhaps temporary)."; + case PR_CONNECT_TIMEOUT_ERROR: + return "PR_CONNECT_TIMEOUT_ERROR--The connection attempt did not complete in a reasonable period of time."; + case PR_NOT_CONNECTED_ERROR: + return "PR_NOT_CONNECTED_ERROR--The preceding function attempted to use connected semantics on a network file descriptor that was not connected."; + case PR_LOAD_LIBRARY_ERROR: + return "PR_LOAD_LIBRARY_ERROR--Failure to load a dynamic library."; + case PR_UNLOAD_LIBRARY_ERROR: + return "PR_UNLOAD_LIBRARY_ERROR--Failure to unload a dynamic library."; + case PR_FIND_SYMBOL_ERROR: + return "PR_FIND_SYMBOL_ERROR--Symbol could not be found in the specified library."; + case PR_INSUFFICIENT_RESOURCES_ERROR: + return "PR_INSUFFICIENT_RESOURCES_ERROR--There are insufficient system resources to process the request."; + case PR_DIRECTORY_LOOKUP_ERROR: + return "PR_DIRECTORY_LOOKUP_ERROR--A directory lookup on a network address has failed."; + case PR_TPD_RANGE_ERROR: + return "PR_TPD_RANGE_ERROR--Attempt to access a thread-private data index that is out of range of any index that has been allocated to the process."; + case PR_PROC_DESC_TABLE_FULL_ERROR: + return "PR_PROC_DESC_TABLE_FULL_ERROR--The process' table for holding open file descriptors is full."; + case PR_SYS_DESC_TABLE_FULL_ERROR: + return "PR_SYS_DESC_TABLE_FULL_ERROR--The system's table for holding open file descriptors has been exceeded."; + case PR_NOT_SOCKET_ERROR: + return "PR_NOT_SOCKET_ERROR--An attempt to use a non-network file descriptor on a network-only operation."; + case PR_NOT_TCP_SOCKET_ERROR: + return "PR_NOT_TCP_SOCKET_ERROR--Attempt to perform a TCP specific function on a non-TCP file descriptor."; + case PR_SOCKET_ADDRESS_IS_BOUND_ERROR: + return "PR_SOCKET_ADDRESS_IS_BOUND_ERROR--Attempt to bind an address to a TCP file descriptor that is already bound."; + case PR_NO_ACCESS_RIGHTS_ERROR: + return "PR_NO_ACCESS_RIGHTS_ERROR--Calling thread does not have privilege to perform the operation requested."; + case PR_OPERATION_NOT_SUPPORTED_ERROR: + return "PR_OPERATION_NOT_SUPPORTED_ERROR--The requested operation is not supported by the platform."; + case PR_PROTOCOL_NOT_SUPPORTED_ERROR: + return "PR_PROTOCOL_NOT_SUPPORTED_ERROR--The host operating system does not support the protocol requested."; + case PR_REMOTE_FILE_ERROR: + return "PR_REMOTE_FILE_ERROR--Access to the remote file has been severed."; + case PR_BUFFER_OVERFLOW_ERROR: + return "PR_BUFFER_OVERFLOW_ERROR--The value retrieved is too large to be stored in the buffer provided."; + case PR_CONNECT_RESET_ERROR: + return "PR_CONNECT_RESET_ERROR--The (TCP) connection has been reset by the peer."; + case PR_RANGE_ERROR: + return "PR_RANGE_ERROR--Unused."; + case PR_DEADLOCK_ERROR: + return "PR_DEADLOCK_ERROR--Performing the requested operation would have caused a deadlock. The deadlock was avoided."; + case PR_FILE_IS_LOCKED_ERROR: + return "PR_FILE_IS_LOCKED_ERROR--An attempt to acquire a lock on a file has failed because the file is already locked."; + case PR_FILE_TOO_BIG_ERROR: + return "PR_FILE_TOO_BIG_ERROR--Completing the write or seek operation would have resulted in a file larger than the system could handle."; + case PR_NO_DEVICE_SPACE_ERROR: + return "PR_NO_DEVICE_SPACE_ERROR--The device for storing the file is full."; + case PR_PIPE_ERROR: + return "PR_PIPE_ERROR--Unused."; + case PR_NO_SEEK_DEVICE_ERROR: + return "PR_NO_SEEK_DEVICE_ERROR--Unused."; + case PR_IS_DIRECTORY_ERROR: + return "PR_IS_DIRECTORY_ERROR--Attempt to perform a normal file operation on a directory."; + case PR_LOOP_ERROR: + return "PR_LOOP_ERROR--Symbolic link loop."; + case PR_NAME_TOO_LONG_ERROR: + return "PR_NAME_TOO_LONG_ERROR--Filename is longer than allowed by the host operating system."; + case PR_FILE_NOT_FOUND_ERROR: + return "PR_FILE_NOT_FOUND_ERROR--The requested file was not found."; + case PR_NOT_DIRECTORY_ERROR: + return "PR_NOT_DIRECTORY_ERROR--Attempt to perform directory specific operations on a normal file."; + case PR_READ_ONLY_FILESYSTEM_ERROR: + return "PR_READ_ONLY_FILESYSTEM_ERROR--Attempt to write to a read-only file system."; + case PR_DIRECTORY_NOT_EMPTY_ERROR: + return "PR_DIRECTORY_NOT_EMPTY_ERROR--Attempt to delete a directory that is not empty."; + case PR_FILESYSTEM_MOUNTED_ERROR: + return "PR_FILESYSTEM_MOUNTED_ERROR--Attempt to delete or rename a file object while the file system is busy."; + case PR_NOT_SAME_DEVICE_ERROR: + return "PR_NOT_SAME_DEVICE_ERROR--Request to rename a file to a file system on another device."; + case PR_DIRECTORY_CORRUPTED_ERROR: + return "PR_DIRECTORY_CORRUPTED_ERROR--The directory object in the file system is corrupted."; + case PR_FILE_EXISTS_ERROR: + return "PR_FILE_EXISTS_ERROR--Attempt to create or rename a file when the new name is already being used."; + case PR_MAX_DIRECTORY_ENTRIES_ERROR: + return "PR_MAX_DIRECTORY_ENTRIES_ERROR--Attempt to add new filename to directory would exceed the limit allowed."; + case PR_INVALID_DEVICE_STATE_ERROR: + return "PR_INVALID_DEVICE_STATE_ERROR--The device was in an invalid state to complete the desired operation."; + case PR_DEVICE_IS_LOCKED_ERROR: + return "PR_DEVICE_IS_LOCKED_ERROR--The device needed to perform the desired request is locked."; + case PR_NO_MORE_FILES_ERROR: + return "PR_NO_MORE_FILES_ERROR--There are no more entries in the directory."; + case PR_END_OF_FILE_ERROR: + return "PR_END_OF_FILE_ERROR--Unexpectedly encountered end of file (Mac OS only)."; + case PR_FILE_SEEK_ERROR: + return "PR_FILE_SEEK_ERROR--An unexpected seek error (Mac OS only)."; + case PR_FILE_IS_BUSY_ERROR: + return "PR_FILE_IS_BUSY_ERROR--The file is busy and the operation cannot be performed (Mac OS only)."; + case PR_IN_PROGRESS_ERROR: + return "PR_IN_PROGRESS_ERROR--The operation is still in progress (probably a nonblocking connect)."; + case PR_ALREADY_INITIATED_ERROR: + return "PR_ALREADY_INITIATED_ERROR--The (retried) operation has already been initiated (probably a nonblocking connect)."; + case PR_GROUP_EMPTY_ERROR: + return "PR_GROUP_EMPTY_ERROR--The wait group is empty."; + case PR_INVALID_STATE_ERROR: + return "PR_INVALID_STATE_ERROR--The attempted operation is on an object that was in an improper state to perform the request."; + case PR_MAX_ERROR: + return "PR_MAX_ERROR--Placeholder for the end of the list."; + } + + return (const char *)NULL; +} diff --git a/panda/src/net/pprerror.h b/panda/src/net/pprerror.h new file mode 100644 index 0000000000..512f3c233d --- /dev/null +++ b/panda/src/net/pprerror.h @@ -0,0 +1,14 @@ +// Filename: pprerror.h +// Created by: drose (08Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PPRERROR_H +#define PPRERROR_H + +#include + +void pprerror(const char *format, ...); + +#endif + diff --git a/panda/src/net/queuedConnectionListener.I b/panda/src/net/queuedConnectionListener.I new file mode 100644 index 0000000000..08597d8930 --- /dev/null +++ b/panda/src/net/queuedConnectionListener.I @@ -0,0 +1,34 @@ +// Filename: queuedConnectionListener.I +// Created by: drose (17May00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionListenerData::operator == +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ConnectionListenerData:: +operator == (const ConnectionListenerData &) const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionListenerData::operator != +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ConnectionListenerData:: +operator != (const ConnectionListenerData &) const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConnectionListenerData::operator < +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ConnectionListenerData:: +operator < (const ConnectionListenerData &) const { + return false; +} diff --git a/panda/src/net/queuedConnectionListener.cxx b/panda/src/net/queuedConnectionListener.cxx new file mode 100644 index 0000000000..88efd18c7d --- /dev/null +++ b/panda/src/net/queuedConnectionListener.cxx @@ -0,0 +1,118 @@ +// Filename: queuedConnectionListener.cxx +// Created by: drose (09Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "queuedConnectionListener.h" +#include "config_net.h" + +//////////////////////////////////////////////////////////////////// +// Function: QueuedConnectionListener::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +QueuedConnectionListener:: +QueuedConnectionListener(ConnectionManager *manager, int num_threads) : + ConnectionListener(manager, num_threads) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedConnectionListener::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +QueuedConnectionListener:: +~QueuedConnectionListener() { + // We call shutdown() here to guarantee that all threads are gone + // before the QueuedReturn destructs. + shutdown(); +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedConnectionListener::new_connection_available +// Access: Public +// Description: Returns true if a new connection was recently +// established; the connection information may then be +// retrieved via get_new_connection(). +//////////////////////////////////////////////////////////////////// +bool QueuedConnectionListener:: +new_connection_available() { + poll(); + return thing_available(); +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedConnectionListener::get_new_connection +// Access: Public +// Description: If a previous call to new_connection_available() +// returned true, this function will return information +// about the newly established connection. +// +// The rendezvous parameter is the particular rendezvous +// socket this new connection originally communicated +// with; it is provided in case the ConnectionListener +// was monitorind more than one and you care which one +// it was. The address parameter is the net address of +// the new client, and new_connection is the socket of +// the newly established connection. +// +// The return value is true if a connection was +// successfully returned, or false if there was, in +// fact, no new connection. (This may happen if there +// are multiple threads accessing the +// QueuedConnectionListener). +//////////////////////////////////////////////////////////////////// +bool QueuedConnectionListener:: +get_new_connection(PT(Connection) &rendezvous, + NetAddress &address, + PT(Connection) &new_connection) { + ConnectionListenerData result; + if (!get_thing(result)) { + return false; + } + + rendezvous = result._rendezvous; + address = result._address; + new_connection = result._new_connection; + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedConnectionListener::get_new_connection +// Access: Public +// Description: This flavor of get_new_connection() simply returns a +// new connection, assuming the user doesn't care about +// the rendezvous socket that originated it or the +// address it came from. +//////////////////////////////////////////////////////////////////// +bool QueuedConnectionListener:: +get_new_connection(PT(Connection) &new_connection) { + PT(Connection) rendezvous; + NetAddress address; + return get_new_connection(rendezvous, address, new_connection); +} + + +//////////////////////////////////////////////////////////////////// +// Function: QueuedConnectionListener::connection_opened +// Access: Protected, Virtual +// Description: An internal function called by ConnectionListener() +// when a new TCP connection has been established. The +// QueuedConnectionListener simply queues up this fact +// for later retrieval by get_new_connection(). +//////////////////////////////////////////////////////////////////// +void QueuedConnectionListener:: +connection_opened(const PT(Connection) &rendezvous, + const NetAddress &address, + const PT(Connection) &new_connection) { + ConnectionListenerData nc; + nc._rendezvous = rendezvous; + nc._address = address; + nc._new_connection = new_connection; + + if (!enqueue_thing(nc)) { + net_cat.error() + << "QueuedConnectionListener queue full!\n"; + } +} diff --git a/panda/src/net/queuedConnectionListener.h b/panda/src/net/queuedConnectionListener.h new file mode 100644 index 0000000000..ed34a843c8 --- /dev/null +++ b/panda/src/net/queuedConnectionListener.h @@ -0,0 +1,62 @@ +// Filename: queuedConnectionListener.h +// Created by: drose (09Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef QUEUEDCONNECTIONLISTENER_H +#define QUEUEDCONNECTIONLISTENER_H + +#include + +#include "connectionListener.h" +#include "connection.h" +#include "netAddress.h" +#include "queuedReturn.h" + +#include +#include + + +class EXPCL_PANDA ConnectionListenerData { +public: + // We need these methods to make VC++ happy when we try to + // instantiate the template, below. They don't do anything useful. + INLINE bool operator == (const ConnectionListenerData &other) const; + INLINE bool operator != (const ConnectionListenerData &other) const; + INLINE bool operator < (const ConnectionListenerData &other) const; + + PT(Connection) _rendezvous; + NetAddress _address; + PT(Connection) _new_connection; +}; + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, QueuedReturn); + +//////////////////////////////////////////////////////////////////// +// Class : QueuedConnectionListener +// Description : This flavor of ConnectionListener will queue up all +// of the TCP connections it established for later +// detection by the client code. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA QueuedConnectionListener : public ConnectionListener, + public QueuedReturn { +public: + QueuedConnectionListener(ConnectionManager *manager, int num_threads); + virtual ~QueuedConnectionListener(); + + bool new_connection_available(); + bool get_new_connection(PT(Connection) &rendezvous, + NetAddress &address, + PT(Connection) &new_connection); + bool get_new_connection(PT(Connection) &new_connection); + +protected: + virtual void connection_opened(const PT(Connection) &rendezvous, + const NetAddress &address, + const PT(Connection) &new_connection); +}; + +#include "queuedConnectionListener.I" + +#endif + diff --git a/panda/src/net/queuedConnectionManager.cxx b/panda/src/net/queuedConnectionManager.cxx new file mode 100644 index 0000000000..adeee74a6c --- /dev/null +++ b/panda/src/net/queuedConnectionManager.cxx @@ -0,0 +1,96 @@ +// Filename: queuedConnectionManager.cxx +// Created by: drose (09Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "queuedConnectionManager.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: QueuedConnectionManager::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +QueuedConnectionManager:: +QueuedConnectionManager() { +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedConnectionManager::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +QueuedConnectionManager:: +~QueuedConnectionManager() { +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedConnectionManager::reset_connection_available +// Access: Public +// Description: Returns true if one of the readers/writers/listeners +// reported a connection reset recently. If so, the +// particular connection that has been reset can be +// extracted via get_reset_connection(). +// +// Only connections which were externally reset are +// certain to appear in this list. Those which were +// explicitly closed via a call to close_connection() +// may or may not be reported. Furthermore, it is the +// responsibility of the caller to subsequently call +// close_connection() with any connection reported reset +// by this call. (There is no harm in calling +// close_connection() more than once on a given socket.) +//////////////////////////////////////////////////////////////////// +bool QueuedConnectionManager:: +reset_connection_available() const { + return thing_available(); +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedConnectionManager::get_reset_connection +// Access: Public +// Description: If a previous call to reset_connection_available() +// returned true, this function will return information +// about the newly reset connection. +// +// Only connections which were externally reset are +// certain to appear in this list. Those which were +// explicitly closed via a call to close_connection() +// may or may not be reported. Furthermore, it is the +// responsibility of the caller to subsequently call +// close_connection() with any connection reported reset +// by this call. (There is no harm in calling +// close_connection() more than once on a given socket.) +// +// The return value is true if a connection was +// successfully returned, or false if there was, in +// fact, no reset connection. (This may happen if +// there are multiple threads accessing the +// QueuedConnectionManager). +//////////////////////////////////////////////////////////////////// +bool QueuedConnectionManager:: +get_reset_connection(PT(Connection) &connection) { + return get_thing(connection); +} + + +//////////////////////////////////////////////////////////////////// +// Function: QueuedConnectionManager::connection_reset +// Access: Protected, Virtual +// Description: An internal function called by the ConnectionReader, +// ConnectionWriter, or ConnectionListener when a +// connection has been externally reset. This adds the +// connection to the queue of those which have recently +// been reset. +//////////////////////////////////////////////////////////////////// +void QueuedConnectionManager:: +connection_reset(const PT(Connection) &connection) { + // Largely, we don't care if this particular queue fills up. If it + // does, it probably just means the user isn't bothering to track + // this. + if (enqueue_unique_thing(connection)) { + net_cat.info() + << "Lost connection\n"; + } +} diff --git a/panda/src/net/queuedConnectionManager.h b/panda/src/net/queuedConnectionManager.h new file mode 100644 index 0000000000..0c734878e3 --- /dev/null +++ b/panda/src/net/queuedConnectionManager.h @@ -0,0 +1,45 @@ +// Filename: queuedConnectionManager.h +// Created by: drose (09Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef QUEUEDCONNECTIONMANAGER_H +#define QUEUEDCONNECTIONMANAGER_H + +#include + +#include "connectionManager.h" +#include "queuedReturn.h" + +#include +#include + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, QueuedReturn< PT(Connection) >); + +//////////////////////////////////////////////////////////////////// +// Class : QueuedConnectionManager +// Description : This flavor of ConnectionManager will queue up all of +// the reset-connection messages from the +// ConnectionReaders and ConnectionWriters and report +// them to the client on demand. +// +// When a reset connection has been discovered via +// reset_connection_available()/get_reset_connection(), +// it is still the responsibility of the client to call +// close_connection() on that connection to free up its +// resources. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA QueuedConnectionManager : public ConnectionManager, + public QueuedReturn { +public: + QueuedConnectionManager(); + ~QueuedConnectionManager(); + + bool reset_connection_available() const; + bool get_reset_connection(PT(Connection) &connection); + +protected: + virtual void connection_reset(const PT(Connection) &connection); +}; + +#endif diff --git a/panda/src/net/queuedConnectionReader.cxx b/panda/src/net/queuedConnectionReader.cxx new file mode 100644 index 0000000000..d9afe5f107 --- /dev/null +++ b/panda/src/net/queuedConnectionReader.cxx @@ -0,0 +1,102 @@ +// Filename: queuedConnectionReader.cxx +// Created by: drose (08Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "queuedConnectionReader.h" +#include "config_net.h" + +//////////////////////////////////////////////////////////////////// +// Function: QueuedConnectionReader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +QueuedConnectionReader:: +QueuedConnectionReader(ConnectionManager *manager, int num_threads) : + ConnectionReader(manager, num_threads) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedConnectionReader::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +QueuedConnectionReader:: +~QueuedConnectionReader() { + // We call shutdown() here to guarantee that all threads are gone + // before the QueuedReturn destructs. + shutdown(); +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedConnectionReader::data_available +// Access: Public +// Description: Returns true if a datagram is available on the queue; +// call get_data() to extract the datagram. +//////////////////////////////////////////////////////////////////// +bool QueuedConnectionReader:: +data_available() { + poll(); + return thing_available(); +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedConnectionReader::get_data +// Access: Public +// Description: If a previous call to data_available() returned +// true, this function will return the datagram that has +// become available. +// +// The return value is true if a datagram was +// successfully returned, or false if there was, in +// fact, no datagram available. (This may happen if +// there are multiple threads accessing the +// QueuedConnectionReader). +//////////////////////////////////////////////////////////////////// +bool QueuedConnectionReader:: +get_data(NetDatagram &result) { + return get_thing(result); +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedConnectionReader::get_data +// Access: Public +// Description: This flavor of QueuedConnectionReader::get_data(), +// works like the other, except that it only fills a +// Datagram object, not a NetDatagram object. This +// means that the Datagram cannot be queried for its +// source Connection and/or NetAddress, but it is useful +// in all other respects. +//////////////////////////////////////////////////////////////////// +bool QueuedConnectionReader:: +get_data(Datagram &result) { + NetDatagram nd; + if (!get_thing(nd)) { + return false; + } + result = nd; + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedConnectionReader::receive_datagram +// Access: Protected, Virtual +// Description: An internal function called by ConnectionReader() +// when a new datagram has become available. The +// QueuedConnectionReader simply queues it up for later +// retrieval by get_data(). +//////////////////////////////////////////////////////////////////// +void QueuedConnectionReader:: +receive_datagram(const NetDatagram &datagram) { + if (net_cat.is_debug()) { + net_cat.debug() + << "Received datagram of " << datagram.get_length() + << " bytes\n"; + } + + if (!enqueue_thing(datagram)) { + net_cat.error() + << "QueuedConnectionReader queue full!\n"; + } +} diff --git a/panda/src/net/queuedConnectionReader.h b/panda/src/net/queuedConnectionReader.h new file mode 100644 index 0000000000..bac966fc7a --- /dev/null +++ b/panda/src/net/queuedConnectionReader.h @@ -0,0 +1,44 @@ +// Filename: queuedConnectionReader.h +// Created by: drose (08Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef QUEUEDCONNECTIONREADER_H +#define QUEUEDCONNECTIONREADER_H + +#include + +#include "connectionReader.h" +#include "netDatagram.h" +#include "queuedReturn.h" + +#include +#include + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, QueuedReturn); + +//////////////////////////////////////////////////////////////////// +// Class : QueuedConnectionReader +// Description : This flavor of ConnectionReader will read from its +// sockets and queue up all of the datagrams read for +// later receipt by the client code. This class is +// useful for client code that doesn't want to deal with +// threading and is willing to poll for datagrams at its +// convenience. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA QueuedConnectionReader : public ConnectionReader, + public QueuedReturn { +public: + QueuedConnectionReader(ConnectionManager *manager, int num_threads); + virtual ~QueuedConnectionReader(); + + bool data_available(); + bool get_data(NetDatagram &result); + bool get_data(Datagram &result); + +protected: + virtual void receive_datagram(const NetDatagram &datagram); +}; + +#endif + diff --git a/panda/src/net/queuedReturn.I b/panda/src/net/queuedReturn.I new file mode 100644 index 0000000000..a36351bbf5 --- /dev/null +++ b/panda/src/net/queuedReturn.I @@ -0,0 +1,171 @@ +// Filename: queuedReturn.I +// Created by: drose (25Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_net.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: QueuedReturn::set_max_queue_size +// Access: Public +// Description: Sets the maximum size the queue is allowed to grow +// to. This is primarily for a sanity check; this is a +// limit beyond which we can assume something bad has +// happened. +// +// It's also a crude check against unfortunate seg +// faults due to the queue filling up and quietly +// consuming all available memory. +//////////////////////////////////////////////////////////////////// +template +void QueuedReturn:: +set_max_queue_size(int max_size) { + PR_Lock(_mutex); + _max_queue_size = max_size; + PR_Unlock(_mutex); +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedReturn::get_max_queue_size +// Access: Public +// Description: Returns the maximum size the queue is allowed to grow +// to. See set_max_queue_size(). +//////////////////////////////////////////////////////////////////// +template +int QueuedReturn:: +get_max_queue_size() const { + return _max_queue_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedReturn::get_current_queue_size +// Access: Public +// Description: Returns the current number of things in the queue. +//////////////////////////////////////////////////////////////////// +template +int QueuedReturn:: +get_current_queue_size() const { + PR_Lock(_mutex); + int size = _things.size(); + PR_Unlock(_mutex); + return size; +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedReturn::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +template +QueuedReturn:: +QueuedReturn() { + _mutex = PR_NewLock(); + _available = false; + _max_queue_size = get_net_max_response_queue(); +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedReturn::Destructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +template +QueuedReturn:: +~QueuedReturn() { + PR_DestroyLock(_mutex); +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedReturn::thing_available +// Access: Protected +// Description: Returns true if a thing is available on the queue; +// call get_thing() to extract the thing. +//////////////////////////////////////////////////////////////////// +template +INLINE bool QueuedReturn:: +thing_available() const { + return _available; +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedReturn::get_thing +// Access: Protected +// Description: If a previous call to thing_available() returned +// true, this function will return the thing that has +// become available. +// +// The return value is true if a thing was +// successfully returned, or false if there was, in +// fact, no thing available. (This may happen if +// there are multiple threads accessing the +// QueuedReturn). +//////////////////////////////////////////////////////////////////// +template +bool QueuedReturn:: +get_thing(Thing &result) { + PR_Lock(_mutex); + if (_things.empty()) { + // Huh. Nothing after all. + _available = false; + PR_Unlock(_mutex); + return false; + } + + result = _things.front(); + _things.pop_front(); + _available = !_things.empty(); + PR_Unlock(_mutex); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedReturn::enqueue_thing +// Access: Protected +// Description: Adds a new thing to the queue for later retrieval. +// Returns true if successful, false if the queue is +// full (i.e. has reached _max_queue_size). +//////////////////////////////////////////////////////////////////// +template +bool QueuedReturn:: +enqueue_thing(const Thing &thing) { + PR_Lock(_mutex); + bool enqueue_ok = (_things.size() < _max_queue_size); + if (enqueue_ok) { + _things.push_back(thing); + } + _available = true; + PR_Unlock(_mutex); + + return enqueue_ok; +} + +//////////////////////////////////////////////////////////////////// +// Function: QueuedReturn::enqueue_unique_thing +// Access: Protected +// Description: The same as enqueue_thing(), except the queue is +// first checked that it doesn't already have something +// like thing. The return value is true if the enqueue +// operation was successful, false if the queue was full +// or the thing was already on the queue. +//////////////////////////////////////////////////////////////////// +template +bool QueuedReturn:: +enqueue_unique_thing(const Thing &thing) { + PR_Lock(_mutex); + bool enqueue_ok = (_things.size() < _max_queue_size); + if (enqueue_ok) { + if (find(_things.begin(), _things.end(), thing) == _things.end()) { + // It wasn't there already; add it now. + _things.push_back(thing); + } else { + // It was already there; return false to indicate this. + enqueue_ok = false; + } + } + _available = true; + PR_Unlock(_mutex); + + return enqueue_ok; +} diff --git a/panda/src/net/queuedReturn.h b/panda/src/net/queuedReturn.h new file mode 100644 index 0000000000..6f839b058d --- /dev/null +++ b/panda/src/net/queuedReturn.h @@ -0,0 +1,52 @@ +// Filename: queuedReturn.h +// Created by: drose (25Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef QUEUEDRETURN_H +#define QUEUEDRETURN_H + +#include + +#include "connectionListener.h" +#include "connection.h" +#include "netAddress.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : QueuedReturn +// Description : This is the implementation of a family of things that +// queue up their return values for later retrieval by +// client code, like QueuedConnectionReader, +// QueuedConnectionListener, QueuedConnectionManager. +//////////////////////////////////////////////////////////////////// +template +class QueuedReturn { +public: + void set_max_queue_size(int max_size); + int get_max_queue_size() const; + int get_current_queue_size() const; + +protected: + QueuedReturn(); + ~QueuedReturn(); + + INLINE bool thing_available() const; + bool get_thing(Thing &thing); + + bool enqueue_thing(const Thing &thing); + bool enqueue_unique_thing(const Thing &thing); + +private: + PRLock *_mutex; + deque _things; + bool _available; + int _max_queue_size; +}; + +#include "queuedReturn.I" + +#endif + diff --git a/panda/src/net/recentConnectionReader.cxx b/panda/src/net/recentConnectionReader.cxx new file mode 100644 index 0000000000..673f63356f --- /dev/null +++ b/panda/src/net/recentConnectionReader.cxx @@ -0,0 +1,119 @@ +// Filename: recentConnectionReader.cxx +// Created by: drose (23Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "recentConnectionReader.h" +#include "config_net.h" + +//////////////////////////////////////////////////////////////////// +// Function: RecentConnectionReader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +RecentConnectionReader:: +RecentConnectionReader(ConnectionManager *manager) : + ConnectionReader(manager, 1) +{ + // We should not receive any datagrams before the constructor is + // done initializing, or our thread may get confused. Fortunately + // this should be impossible, because we can't receive datagrams + // before we call add_connection(). + _available = false; + _mutex = PR_NewLock(); +} + +//////////////////////////////////////////////////////////////////// +// Function: RecentConnectionReader::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +RecentConnectionReader:: +~RecentConnectionReader() { + // We call shutdown() here to guarantee that all threads are gone + // before the RecentConnectionReader destructs. + shutdown(); + + PR_DestroyLock(_mutex); +} + +//////////////////////////////////////////////////////////////////// +// Function: RecentConnectionReader::data_available +// Access: Public +// Description: Returns true if a datagram is available on the queue; +// call get_data() to extract the datagram. +//////////////////////////////////////////////////////////////////// +bool RecentConnectionReader:: +data_available() { + return _available; +} + +//////////////////////////////////////////////////////////////////// +// Function: RecentConnectionReader::get_data +// Access: Public +// Description: If a previous call to data_available() returned +// true, this function will return the datagram that has +// become available. +// +// The return value is true if a datagram was +// successfully returned, or false if there was, in +// fact, no datagram available. (This may happen if +// there are multiple threads accessing the +// RecentConnectionReader). +//////////////////////////////////////////////////////////////////// +bool RecentConnectionReader:: +get_data(NetDatagram &result) { + PR_Lock(_mutex); + if (!_available) { + // Huh. Nothing after all. + PR_Unlock(_mutex); + return false; + } + + result = _datagram; + _available = false; + PR_Unlock(_mutex); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: RecentConnectionReader::get_data +// Access: Public +// Description: This flavor of RecentConnectionReader::get_data(), +// works like the other, except that it only fills a +// Datagram object, not a NetDatagram object. This +// means that the Datagram cannot be queried for its +// source Connection and/or NetAddress, but it is useful +// in all other respects. +//////////////////////////////////////////////////////////////////// +bool RecentConnectionReader:: +get_data(Datagram &result) { + NetDatagram nd; + if (!get_data(nd)) { + return false; + } + result = nd; + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: RecentConnectionReader::receive_datagram +// Access: Protected, Virtual +// Description: An internal function called by ConnectionReader() +// when a new datagram has become available. The +// RecentConnectionReader simply queues it up for later +// retrieval by get_data(). +//////////////////////////////////////////////////////////////////// +void RecentConnectionReader:: +receive_datagram(const NetDatagram &datagram) { + if (net_cat.is_debug()) { + net_cat.debug() + << "Received datagram of " << datagram.get_length() + << " bytes\n"; + } + + PR_Lock(_mutex); + _datagram = datagram; + _available = true; + PR_Unlock(_mutex); +} diff --git a/panda/src/net/recentConnectionReader.h b/panda/src/net/recentConnectionReader.h new file mode 100644 index 0000000000..2e047c581e --- /dev/null +++ b/panda/src/net/recentConnectionReader.h @@ -0,0 +1,47 @@ +// Filename: recentConnectionReader.h +// Created by: drose (23Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef RECENTCONNECTIONREADER_H +#define RECENTCONNECTIONREADER_H + +#include + +#include "connectionReader.h" +#include "netDatagram.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : RecentConnectionReader +// Description : This flavor of ConnectionReader will read from its +// sockets and retain only the single most recent +// datagram for inspection by client code. It's useful +// particularly for reading telemetry-type data from UDP +// sockets where you don't care about getting every last +// socket, and in fact if the sockets are coming too +// fast you'd prefer to skip some of them. +// +// This class will always create one thread for itself. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA RecentConnectionReader : public ConnectionReader { +public: + RecentConnectionReader(ConnectionManager *manager); + virtual ~RecentConnectionReader(); + + bool data_available(); + bool get_data(NetDatagram &result); + bool get_data(Datagram &result); + +protected: + virtual void receive_datagram(const NetDatagram &datagram); + +private: + bool _available; + Datagram _datagram; + PRLock *_mutex; +}; + +#endif + diff --git a/panda/src/net/test_datagram.cxx b/panda/src/net/test_datagram.cxx new file mode 100644 index 0000000000..3099437247 --- /dev/null +++ b/panda/src/net/test_datagram.cxx @@ -0,0 +1,44 @@ +// Filename: test_datagram.cxx +// Created by: jns (07Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +#include "netDatagram.h" +#include "datagramIterator.h" + +main() +{ + NetDatagram dg; + + dg.add_int8((signed char) -10); + dg.add_uint8((unsigned char) 200); + dg.add_int16(-32000); + dg.add_uint16(64000); + dg.add_int32(100000); + dg.add_float64(3.14159); + dg.add_float64(3.141592654378765689009887765567); + dg.add_string("Monkey"); + dg.add_string("Purple Monkey!"); + dg.add_int32(200000); + + cout << "Length of datagram: " << dg.get_length() << endl; + + DatagramIterator dgi(dg); + + cout << "int8: " << (int)dgi.get_int8() << endl; + cout << "uint8: " << (int)dgi.get_uint8() << endl; + cout << "int16: " << dgi.get_int16() << endl; + cout << "uint16: " << dgi.get_uint16() << endl; + cout << "int32: " << dgi.get_int32() << endl; + cout << "float64: " << dgi.get_float64() << endl; + cout << "float64: " << dgi.get_float64() << endl; + cout << "string: " << dgi.get_string() << endl; + cout << "string: " << dgi.get_string() << endl; + cout << "int: " << dgi.get_int32() << endl; + +} diff --git a/panda/src/net/test_spam_client.cxx b/panda/src/net/test_spam_client.cxx new file mode 100644 index 0000000000..0ccf3b8897 --- /dev/null +++ b/panda/src/net/test_spam_client.cxx @@ -0,0 +1,106 @@ +// Filename: test_spam_client.cxx +// Created by: drose (24Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "queuedConnectionManager.h" +#include "queuedConnectionReader.h" +#include "connectionWriter.h" +#include "netAddress.h" +#include "connection.h" +#include "netDatagram.h" + +#include "datagram_ui.h" + +#include + +int +main(int argc, char *argv[]) { + if (argc != 3) { + nout << "test_spam_client host port\n"; + exit(1); + } + + string hostname = argv[1]; + int port = atoi(argv[2]); + + NetAddress host; + if (!host.set_host(hostname, port)) { + nout << "Unknown host: " << hostname << "\n"; + } + + QueuedConnectionManager cm; + PT(Connection) c = cm.open_TCP_client_connection(host, 5000); + + if (c.is_null()) { + nout << "No connection.\n"; + exit(1); + } + + nout << "Successfully opened TCP connection to " << hostname + << " on port " << port << "\n"; + + QueuedConnectionReader reader(&cm, 10); + reader.add_connection(c); + ConnectionWriter writer(&cm, 10); + + bool lost_connection = false; + + NetDatagram datagram; + cout << "Enter a datagram.\n"; + cin >> datagram; + + nout << "Read datagram " << datagram << "\n"; + datagram.dump_hex(nout); + nout << "\n"; + + int num_sent = 0; + int num_received = 0; + PRIntervalTime last_reported_time = PR_IntervalNow(); + PRIntervalTime report_interval = PR_SecondsToInterval(5); + + while (!lost_connection) { + // Send the datagram. + if (writer.send(datagram, c, host)) { + num_sent++; + } + + // Check for a lost connection. + while (cm.reset_connection_available()) { + PT(Connection) connection; + if (cm.get_reset_connection(connection)) { + nout << "Lost connection from " + << connection->get_address().get_ip() << "\n"; + cm.close_connection(connection); + if (connection == c) { + lost_connection = true; + } + } + } + + // Now poll for new datagrams on the socket. + if (reader.data_available()) { + NetDatagram new_datagram; + if (reader.get_data(new_datagram)) { + num_received++; + } + } + + PRIntervalTime now = PR_IntervalNow(); + if ((PRIntervalTime)(now - last_reported_time) > report_interval) { + nout << "Sent " << num_sent << ", received " + << num_received << " datagrams.\n"; + last_reported_time = now; + } + + // Yield the timeslice before we poll again. + // PR_Sleep(PR_MillisecondsToInterval(1)); + } + + return (0); +} + + + + + diff --git a/panda/src/net/test_spam_server.cxx b/panda/src/net/test_spam_server.cxx new file mode 100644 index 0000000000..1f694b05d3 --- /dev/null +++ b/panda/src/net/test_spam_server.cxx @@ -0,0 +1,114 @@ +// Filename: test_spam_server.cxx +// Created by: drose (24Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "queuedConnectionManager.h" +#include "queuedConnectionListener.h" +#include "queuedConnectionReader.h" +#include "connectionWriter.h" +#include "netAddress.h" +#include "connection.h" +#include "netDatagram.h" + +#include "datagram_ui.h" + +#include + +#include +#include +#include +#include + +int +main(int argc, char *argv[]) { + if (argc != 2) { + nout << "test_spam_server port\n"; + exit(1); + } + + int port = atoi(argv[1]); + + QueuedConnectionManager cm; + PT(Connection) rendezvous = cm.open_TCP_server_rendezvous(port, 5); + + if (rendezvous.is_null()) { + nout << "Cannot grab port " << port << ".\n"; + exit(1); + } + + nout << "Listening for connections on port " << port << "\n"; + + QueuedConnectionListener listener(&cm, 1); + listener.add_connection(rendezvous); + + typedef set Clients; + Clients clients; + + QueuedConnectionReader reader(&cm, 10); + ConnectionWriter writer(&cm, 10); + + int num_sent = 0; + int num_received = 0; + PRIntervalTime last_reported_time = PR_IntervalNow(); + PRIntervalTime report_interval = PR_SecondsToInterval(5); + + bool shutdown = false; + while (!shutdown) { + // Check for new clients. + while (listener.new_connection_available()) { + PT(Connection) rv; + NetAddress address; + PT(Connection) new_connection; + if (listener.get_new_connection(rv, address, new_connection)) { + nout << "Got connection from " << address.get_ip() << "\n"; + reader.add_connection(new_connection); + clients.insert(new_connection); + } + } + + // Check for reset clients. + while (cm.reset_connection_available()) { + PT(Connection) connection; + if (cm.get_reset_connection(connection)) { + nout << "Lost connection from " + << connection->get_address().get_ip() << "\n"; + clients.erase(connection); + cm.close_connection(connection); + } + } + + // Process all available datagrams. + while (reader.data_available()) { + NetDatagram datagram; + if (reader.get_data(datagram)) { + num_received++; + Clients::iterator ci; + for (ci = clients.begin(); ci != clients.end(); ++ci) { + if (writer.send(datagram, (*ci))) { + num_sent++; + } + } + } + } + + PRIntervalTime now = PR_IntervalNow(); + if ((PRIntervalTime)(now - last_reported_time) > report_interval) { + nout << "Sent " << num_sent << ", received " + << num_received << " datagrams.\n"; + last_reported_time = now; + } + + // Yield the timeslice before we poll again. + // PR_Sleep(PR_MillisecondsToInterval(1)); + } + + return (0); +} + + + + + diff --git a/panda/src/net/test_tcp_client.cxx b/panda/src/net/test_tcp_client.cxx new file mode 100644 index 0000000000..982af496b8 --- /dev/null +++ b/panda/src/net/test_tcp_client.cxx @@ -0,0 +1,89 @@ +// Filename: test_tcp_client.cxx +// Created by: drose (09Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "queuedConnectionManager.h" +#include "queuedConnectionReader.h" +#include "connectionWriter.h" +#include "netAddress.h" +#include "connection.h" +#include "netDatagram.h" + +#include "datagram_ui.h" + +int +main(int argc, char *argv[]) { + if (argc != 3) { + nout << "test_tcp_client host port\n"; + exit(1); + } + + string hostname = argv[1]; + int port = atoi(argv[2]); + + NetAddress host; + if (!host.set_host(hostname, port)) { + nout << "Unknown host: " << hostname << "\n"; + } + + QueuedConnectionManager cm; + PT(Connection) c = cm.open_TCP_client_connection(host, 5000); + + if (c.is_null()) { + nout << "No connection.\n"; + exit(1); + } + + nout << "Successfully opened TCP connection to " << hostname + << " on port " << port << "\n"; + + QueuedConnectionReader reader(&cm, 0); + reader.add_connection(c); + ConnectionWriter writer(&cm, 0); + + NetDatagram datagram; + cout << "Enter a datagram.\n"; + cin >> datagram; + + bool lost_connection = false; + + while (!cin.fail() && !lost_connection) { + // Send the datagram. + writer.send(datagram, c); + + // Check for a lost connection. + while (cm.reset_connection_available()) { + PT(Connection) connection; + if (cm.get_reset_connection(connection)) { + nout << "Lost connection from " + << connection->get_address().get_ip() << "\n"; + cm.close_connection(connection); + if (connection == c) { + lost_connection = true; + } + } + } + + // Now poll for new datagrams on the socket. + while (reader.data_available()) { + if (reader.get_data(datagram)) { + nout << "Got datagram " << datagram << "from " + << datagram.get_address().get_ip() << "\n"; + } + } + + if (!lost_connection) { + cout << "\nEnter a datagram.\n"; + cin >> datagram; + } + } + nout << "Exiting\n"; + + return (0); +} + + + + + diff --git a/panda/src/net/test_tcp_server.cxx b/panda/src/net/test_tcp_server.cxx new file mode 100644 index 0000000000..73610c356d --- /dev/null +++ b/panda/src/net/test_tcp_server.cxx @@ -0,0 +1,117 @@ +// Filename: test_tcp_server.cxx +// Created by: drose (09Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "queuedConnectionManager.h" +#include "queuedConnectionListener.h" +#include "queuedConnectionReader.h" +#include "connectionWriter.h" +#include "netAddress.h" +#include "connection.h" +#include "netDatagram.h" + +#include "datagram_ui.h" + +#include + +int +main(int argc, char *argv[]) { + if (argc != 2) { + nout << "test_tcp_server port\n"; + exit(1); + } + + int port = atoi(argv[1]); + + QueuedConnectionManager cm; + PT(Connection) rendezvous = cm.open_TCP_server_rendezvous(port, 5); + + if (rendezvous.is_null()) { + nout << "Cannot grab port " << port << ".\n"; + exit(1); + } + + nout << "Listening for connections on port " << port << "\n"; + + QueuedConnectionListener listener(&cm, 0); + listener.add_connection(rendezvous); + + typedef set Clients; + Clients clients; + + QueuedConnectionReader reader(&cm, 1); + ConnectionWriter writer(&cm, 1); + + bool shutdown = false; + while (!shutdown) { + // Check for new clients. + while (listener.new_connection_available()) { + PT(Connection) rv; + NetAddress address; + PT(Connection) new_connection; + if (listener.get_new_connection(rv, address, new_connection)) { + nout << "Got connection from " << address.get_ip() << "\n"; + reader.add_connection(new_connection); + clients.insert(new_connection); + } + } + + // Check for reset clients. + while (cm.reset_connection_available()) { + PT(Connection) connection; + if (cm.get_reset_connection(connection)) { + nout << "Lost connection from " + << connection->get_address().get_ip() << "\n"; + clients.erase(connection); + cm.close_connection(connection); + } + } + + // Process all available datagrams. + while (reader.data_available()) { + NetDatagram datagram; + if (reader.get_data(datagram)) { + nout << "Got datagram " << datagram << "from " + << datagram.get_address().get_ip() << ", sending to " + << clients.size() << " clients.\n"; + + Clients::iterator ci; + for (ci = clients.begin(); ci != clients.end(); ++ci) { + writer.send(datagram, (*ci)); + } + + if (datagram.get_length() <= 1) { + /* + // An empty datagram means to close the connection. + PT(Connection) connection = datagram.get_connection(); + if (connection.is_null()) { + nout << "Empty datagram from a null connection.\n"; + } else { + nout << "Closing connection from " + << connection->get_address().get_ip() << "\n"; + clients.erase(connection); + cm.close_connection(connection); + nout << "Closed " << connection << "\n"; + } + */ + + // No, an empty datagram means to shut down the server. + shutdown = true; + } + } + } + + // Yield the timeslice before we poll again. + PR_Sleep(PR_MillisecondsToInterval(100)); + } + + return (0); +} + + + + + diff --git a/panda/src/net/test_udp.cxx b/panda/src/net/test_udp.cxx new file mode 100644 index 0000000000..1e6c4a746e --- /dev/null +++ b/panda/src/net/test_udp.cxx @@ -0,0 +1,86 @@ +// Filename: test_udp.cxx +// Created by: drose (08Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "queuedConnectionManager.h" +#include "recentConnectionReader.h" +#include "connectionWriter.h" +#include "netAddress.h" +#include "connection.h" +#include "netDatagram.h" + +#include "datagram_ui.h" + +int +main(int argc, char *argv[]) { + if (argc != 3) { + nout << "test_udp host port\n"; + exit(1); + } + + string hostname = argv[1]; + int port = atoi(argv[2]); + + NetAddress host; + if (!host.set_host(hostname, port)) { + nout << "Unknown host: " << hostname << "\n"; + } + + QueuedConnectionManager cm; + PT(Connection) c = cm.open_UDP_connection(port); + + if (c.is_null()) { + nout << "No connection.\n"; + exit(1); + } + + nout << "Successfully opened UDP connection on port " << port << "\n"; + + RecentConnectionReader reader(&cm); + reader.add_connection(c); + ConnectionWriter writer(&cm, 1); + + NetDatagram datagram; + cout << "Enter a datagram.\n"; + cin >> datagram; + + bool lost_connection = false; + + while (!cin.fail() && !lost_connection) { + // Send the datagram. + writer.send(datagram, c, host); + + // Check for a lost connection. + while (cm.reset_connection_available()) { + PT(Connection) connection; + if (cm.get_reset_connection(connection)) { + nout << "Lost connection from " + << connection->get_address().get_ip() << "\n"; + cm.close_connection(connection); + if (connection == c) { + lost_connection = true; + } + } + } + + // Now poll for new datagrams on the socket. + while (reader.data_available()) { + if (reader.get_data(datagram)) { + nout << "Got datagram " << datagram << "from " + << datagram.get_address().get_ip() << "\n"; + } + } + + cout << "\nEnter a datagram.\n"; + cin >> datagram; + } + nout << "Exiting.\n"; + + return (0); +} + + + + + diff --git a/panda/src/pandabase/Sources.pp b/panda/src/pandabase/Sources.pp new file mode 100644 index 0000000000..ddefb33b42 --- /dev/null +++ b/panda/src/pandabase/Sources.pp @@ -0,0 +1,12 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil + +#begin lib_target + #define TARGET pandabase + + #define SOURCES \ + pandabase.cxx pandabase.h pandasymbols.h \ + + #define INSTALL_HEADERS \ + pandabase.h pandasymbols.h + +#end lib_target diff --git a/panda/src/pandabase/libpandabase.in b/panda/src/pandabase/libpandabase.in new file mode 100644 index 0000000000..004d56dacf --- /dev/null +++ b/panda/src/pandabase/libpandabase.in @@ -0,0 +1,30 @@ +970454637 +2 2 +12 libpandabase 4 iign 15 libpandaexpress +0 +0 +0 +22 +1 15 EXPCL_FRAMEWORK 0 0 0 0 0 +2 11 EXPCL_PANDA 0 0 0 0 0 +3 13 EXPCL_PANDADX 0 0 0 0 0 +4 14 EXPCL_PANDAEGG 0 0 0 0 0 +5 18 EXPCL_PANDAEXPRESS 0 0 0 0 0 +6 13 EXPCL_PANDAGL 0 0 0 0 0 +7 15 EXPCL_PANDAGLUT 0 0 0 0 0 +8 18 EXPCL_PANDAPHYSICS 0 0 0 0 0 +9 14 EXPCL_PANDARIB 0 0 0 0 0 +10 12 EXPCL_SHADER 0 0 0 0 0 +11 15 EXPTP_FRAMEWORK 0 0 0 0 0 +12 11 EXPTP_PANDA 0 0 0 0 0 +13 13 EXPTP_PANDADX 0 0 0 0 0 +14 14 EXPTP_PANDAEGG 0 0 0 0 0 +15 18 EXPTP_PANDAEXPRESS 0 0 0 0 0 +16 13 EXPTP_PANDAGL 0 0 0 0 0 +17 15 EXPTP_PANDAGLUT 0 0 0 0 0 +18 18 EXPTP_PANDAPHYSICS 0 0 0 0 0 +19 14 EXPTP_PANDARIB 0 0 0 0 0 +20 12 EXPTP_SHADER 0 0 0 0 0 +21 11 PANDABASE_H 0 0 0 0 0 +22 14 PANDASYMBOLS_H 0 0 0 0 0 +0 diff --git a/panda/src/pandabase/pandabase.cxx b/panda/src/pandabase/pandabase.cxx new file mode 100644 index 0000000000..8521cab47f --- /dev/null +++ b/panda/src/pandabase/pandabase.cxx @@ -0,0 +1,6 @@ +// Filename: pandabase.cc +// Created by: drose (15Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "pandabase.h" diff --git a/panda/src/pandabase/pandabase.h b/panda/src/pandabase/pandabase.h new file mode 100644 index 0000000000..b02586bc71 --- /dev/null +++ b/panda/src/pandabase/pandabase.h @@ -0,0 +1,18 @@ +/* + * Filename: pandabase.h + * Created by: drose (12Sep00) + * + */ + +/* This file is included at the beginning of every header file and/or + C or C++ file. It must be compilable for C as well as C++ files, + so no C++-specific code or syntax can be put here. */ + +#ifndef PANDABASE_H +#define PANDABASE_H + +#include +#include "pandasymbols.h" + +#endif + diff --git a/panda/src/pandabase/pandasymbols.h b/panda/src/pandabase/pandasymbols.h new file mode 100644 index 0000000000..7441611704 --- /dev/null +++ b/panda/src/pandabase/pandasymbols.h @@ -0,0 +1,129 @@ +/* +// Filename: pandasymbols.h +// Created by: drose (18Feb00) +// +//////////////////////////////////////////////////////////////////// +*/ + +#ifndef PANDASYMBOLS_H +#define PANDASYMBOLS_H + +/* See dtoolsymbols.h for a rant on the purpose of this file. */ + +#if defined(PENV_WIN32) && !defined(CPPPARSER) + +#ifdef BUILDING_PANDA + #define EXPCL_PANDA __declspec(dllexport) + #define EXPTP_PANDA +#else + #define EXPCL_PANDA __declspec(dllimport) + #define EXPTP_PANDA extern +#endif + +#ifdef BUILDING_PANDAEXPRESS + #define EXPCL_PANDAEXPRESS __declspec(dllexport) + #define EXPTP_PANDAEXPRESS +#else + #define EXPCL_PANDAEXPRESS __declspec(dllimport) + #define EXPTP_PANDAEXPRESS extern +#endif + +#ifdef BUILDING_PANDAEGG + #define EXPCL_PANDAEGG __declspec(dllexport) + #define EXPTP_PANDAEGG +#else + #define EXPCL_PANDAEGG __declspec(dllimport) + #define EXPTP_PANDAEGG extern +#endif + +#ifdef BUILDING_PANDAPHYSICS + #define EXPCL_PANDAPHYSICS __declspec(dllexport) + #define EXPTP_PANDAPHYSICS +#else + #define EXPCL_PANDAPHYSICS __declspec(dllimport) + #define EXPTP_PANDAPHYSICS extern +#endif + +#ifdef BUILDING_PANDAGL + #define EXPCL_PANDAGL __declspec(dllexport) + #define EXPTP_PANDAGL +#else + #define EXPCL_PANDAGL __declspec(dllimport) + #define EXPTP_PANDAGL extern +#endif + +#ifdef BUILDING_PANDAGLUT + #define EXPCL_PANDAGLUT __declspec(dllexport) + #define EXPTP_PANDAGLUT +#else + #define EXPCL_PANDAGLUT __declspec(dllimport) + #define EXPTP_PANDAGLUT extern +#endif + +#ifdef BUILDING_PANDADX + #define EXPCL_PANDADX __declspec(dllexport) + #define EXPTP_PANDADX +#else + #define EXPCL_PANDADX __declspec(dllimport) + #define EXPTP_PANDADX extern +#endif + +#ifdef BUILDING_PANDARIB + #define EXPCL_PANDARIB __declspec(dllexport) + #define EXPTP_PANDARIB +#else + #define EXPCL_PANDARIB __declspec(dllimport) + #define EXPTP_PANDARIB extern +#endif + +#ifdef BUILDING_SHADER + #define EXPCL_SHADER __declspec(dllexport) + #define EXPTP_SHADER +#else + #define EXPCL_SHADER __declspec(dllimport) + #define EXPTP_SHADER extern +#endif + +#ifdef BUILDING_FRAMEWORK + #define EXPCL_FRAMEWORK __declspec(dllexport) + #define EXPTP_FRAMEWORK +#else + #define EXPCL_FRAMEWORK __declspec(dllimport) + #define EXPTP_FRAMEWORK extern +#endif + +#else /* !PENV_WIN32 */ + +#define EXPCL_PANDA +#define EXPTP_PANDA + +#define EXPCL_PANDAEXPRESS +#define EXPTP_PANDAEXPRESS + +#define EXPCL_PANDAEGG +#define EXPTP_PANDAEGG + +#define EXPCL_PANDAPHYSICS +#define EXPTP_PANDAPHYSICS + +#define EXPCL_PANDAGL +#define EXPTP_PANDAGL + +#define EXPCL_PANDAGLUT +#define EXPTP_PANDAGLUT + +#define EXPCL_PANDADX +#define EXPTP_PANDADX + +#define EXPCL_PANDARIB +#define EXPTP_PANDARIB + +#define EXPCL_SHADER +#define EXPTP_SHADER + +#define EXPCL_FRAMEWORK +#define EXPTP_FRAMEWORK + +#endif /* PENV_WIN32 */ + +#endif diff --git a/panda/src/particlesystem/Sources.pp b/panda/src/particlesystem/Sources.pp new file mode 100644 index 0000000000..334d268aa5 --- /dev/null +++ b/panda/src/particlesystem/Sources.pp @@ -0,0 +1,61 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET particlesystem + #define LOCAL_LIBS \ + physics sgraph sgattrib graph sgraphutil + + #define SOURCES \ + baseParticle.I baseParticle.cxx baseParticle.h \ + baseParticleEmitter.I baseParticleEmitter.cxx baseParticleEmitter.h \ + baseParticleFactory.I baseParticleFactory.cxx baseParticleFactory.h \ + baseParticleRenderer.I baseParticleRenderer.cxx \ + baseParticleRenderer.h boxEmitter.I boxEmitter.cxx boxEmitter.h \ + config_particlesystem.cxx config_particlesystem.h discEmitter.I \ + discEmitter.cxx discEmitter.h geomParticleRenderer.I \ + geomParticleRenderer.cxx geomParticleRenderer.h lineEmitter.I \ + lineEmitter.cxx lineEmitter.h orientedParticle.I \ + orientedParticle.cxx orientedParticle.h orientedParticleFactory.I \ + orientedParticleFactory.cxx orientedParticleFactory.h \ + particleSystem.I particleSystem.cxx particleSystem.h \ + particleSystemManager.I particleSystemManager.cxx \ + particleSystemManager.h pointEmitter.I pointEmitter.cxx \ + pointEmitter.h pointParticle.cxx pointParticle.h \ + pointParticleFactory.cxx pointParticleFactory.h \ + pointParticleRenderer.I pointParticleRenderer.cxx \ + pointParticleRenderer.h rectangleEmitter.I rectangleEmitter.cxx \ + rectangleEmitter.h ringEmitter.I ringEmitter.cxx ringEmitter.h \ + sparkleParticleRenderer.I sparkleParticleRenderer.cxx \ + sparkleParticleRenderer.h sphereSurfaceEmitter.I \ + sphereSurfaceEmitter.cxx sphereSurfaceEmitter.h \ + sphereVolumeEmitter.I sphereVolumeEmitter.cxx sphereVolumeEmitter.h \ + spriteParticleRenderer.I spriteParticleRenderer.cxx \ + spriteParticleRenderer.h tangentRingEmitter.I \ + tangentRingEmitter.cxx tangentRingEmitter.h zSpinParticle.I \ + zSpinParticle.cxx zSpinParticle.h zSpinParticleFactory.I \ + zSpinParticleFactory.cxx zSpinParticleFactory.h + + #define INSTALL_HEADERS \ + baseParticle.I baseParticle.h baseParticleEmitter.I \ + baseParticleEmitter.h baseParticleFactory.I baseParticleFactory.h \ + baseParticleRenderer.I baseParticleRenderer.h boxEmitter.I \ + boxEmitter.h config_particlesystem.h discEmitter.I discEmitter.h \ + emitters.h geomParticleRenderer.I geomParticleRenderer.h \ + lineEmitter.I lineEmitter.h orientedParticle.I orientedParticle.h \ + orientedParticleFactory.I orientedParticleFactory.h \ + particleSystem.I particleSystem.h particleSystemManager.I \ + particleSystemManager.h particlefactories.h particles.h \ + pointEmitter.I pointEmitter.h pointParticle.h \ + pointParticleFactory.h pointParticleRenderer.I \ + pointParticleRenderer.h rectangleEmitter.I rectangleEmitter.h \ + ringEmitter.I ringEmitter.h sparkleParticleRenderer.I \ + sparkleParticleRenderer.h sphereSurfaceEmitter.I \ + sphereSurfaceEmitter.h sphereVolumeEmitter.I sphereVolumeEmitter.h \ + spriteParticleRenderer.I spriteParticleRenderer.h \ + tangentRingEmitter.I tangentRingEmitter.h zSpinParticle.I \ + zSpinParticle.h zSpinParticleFactory.I zSpinParticleFactory.h + + #define IGATESCAN all + +#end lib_target + diff --git a/panda/src/particlesystem/baseParticle.I b/panda/src/particlesystem/baseParticle.I new file mode 100644 index 0000000000..705521f3ec --- /dev/null +++ b/panda/src/particlesystem/baseParticle.I @@ -0,0 +1,36 @@ +// Filename: BaseParticle.I +// Created by: charles (16Jun00) +// +//////////////////////////////////////////////////////////////////// + +INLINE void BaseParticle::set_age(float age) { + _age = age; +} + +INLINE void BaseParticle::set_lifespan(float lifespan) { + _lifespan = lifespan; +} + +INLINE void BaseParticle::set_alive(bool alive) { + _alive = alive; +} + +INLINE float BaseParticle::get_age(void) const { + return _age; +} + +INLINE float BaseParticle::get_lifespan(void) const { + return _lifespan; +} + +INLINE bool BaseParticle::get_alive(void) const { + return _alive; +} + +INLINE float BaseParticle::get_parameterized_age(void) const { + return _age / _lifespan; +} + +INLINE float BaseParticle::get_parameterized_vel(void) const { + return (get_velocity().length()) / get_terminal_velocity(); +} diff --git a/panda/src/particlesystem/baseParticle.cxx b/panda/src/particlesystem/baseParticle.cxx new file mode 100644 index 0000000000..627decfada --- /dev/null +++ b/panda/src/particlesystem/baseParticle.cxx @@ -0,0 +1,47 @@ +// Filename: baseParticle.cxx +// Created by: charles (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "baseParticle.h" + +//////////////////////////////////////////////////////////////////// +// Function : BaseParticle +// Access : Public +// Description : Default Constructor +//////////////////////////////////////////////////////////////////// +BaseParticle:: +BaseParticle(int lifespan, bool alive) : + _age(0.0f), _lifespan(lifespan), _alive(alive) { +} + +//////////////////////////////////////////////////////////////////// +// Function : BaseParticle +// Access : Public +// Description : Copy Constructor +//////////////////////////////////////////////////////////////////// +BaseParticle:: +BaseParticle(const BaseParticle ©) { + _age = copy._age; + _lifespan = copy._lifespan; + _alive = copy._alive; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~BaseParticle +// Access : Public +// Description : Default Destructor +//////////////////////////////////////////////////////////////////// +BaseParticle:: +~BaseParticle(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : get_theta +// Access : Public +// Description : for spriteParticleRenderer +//////////////////////////////////////////////////////////////////// +float BaseParticle:: +get_theta(void) const { + return 0.0f; +} diff --git a/panda/src/particlesystem/baseParticle.h b/panda/src/particlesystem/baseParticle.h new file mode 100644 index 0000000000..e45616e890 --- /dev/null +++ b/panda/src/particlesystem/baseParticle.h @@ -0,0 +1,56 @@ +// Filename: baseParticle.h +// Created by: charles (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BASEPARTICLE_H +#define BASEPARTICLE_H + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : BaseParticle +// Description : An individual, physically-modelable particle +// abstract base class. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS BaseParticle : public PhysicsObject { +private: + // NOTE: age and lifespan are in seconds. + float _age; + float _lifespan; + bool _alive; + +protected: + BaseParticle(int lifespan = 0, bool alive = false); + BaseParticle(const BaseParticle ©); + virtual ~BaseParticle(void); + +public: + // local methods + INLINE void set_age(float age); + INLINE void set_lifespan(float lifespan); + INLINE void set_alive(bool alive); + + INLINE float get_age(void) const; + INLINE float get_lifespan(void) const; + INLINE bool get_alive(void) const; + + INLINE float get_parameterized_age(void) const; + INLINE float get_parameterized_vel(void) const; + + // child methods + virtual void init(void) = 0; + virtual void die(void) = 0; + virtual void update(void) = 0; + + // for spriteParticleRenderer + virtual float get_theta(void) const; + + // from PhysicsObject + virtual PhysicsObject *make_copy(void) const = 0; +}; + +#include "baseParticle.I" + +#endif // BASEPARTICLE_H diff --git a/panda/src/particlesystem/baseParticleEmitter.I b/panda/src/particlesystem/baseParticleEmitter.I new file mode 100644 index 0000000000..a566b24bfb --- /dev/null +++ b/panda/src/particlesystem/baseParticleEmitter.I @@ -0,0 +1,56 @@ +// Filename: baseParticleEmitter.I +// Created by: charles (26Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : generate +// Access : Public +// Description : parent generation function +//////////////////////////////////////////////////////////////////// +INLINE void BaseParticleEmitter:: +generate(LPoint3f& pos, LVector3f& vel) { + assign_initial_values(pos, vel); + vel *= _amplitude; + vel += _offset_force; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_amplitude +// Access : Public +// Description : amplitude assignment +//////////////////////////////////////////////////////////////////// +INLINE void BaseParticleEmitter:: +set_amplitude(float a) { + _amplitude = a; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_amplitude +// Access : Public +// Description : amplitude query +//////////////////////////////////////////////////////////////////// +INLINE float BaseParticleEmitter:: +get_amplitude(void) const { + return _amplitude; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_offset_force +// Access : Public +// Description : user-defined force +//////////////////////////////////////////////////////////////////// +INLINE void BaseParticleEmitter:: +set_offset_force(const LVector3f& of) { + _offset_force = of; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_offset_force +// Access : Public +// Description : user-defined force +//////////////////////////////////////////////////////////////////// +INLINE LVector3f BaseParticleEmitter:: +get_offset_force(void) const { + return _offset_force; +} diff --git a/panda/src/particlesystem/baseParticleEmitter.cxx b/panda/src/particlesystem/baseParticleEmitter.cxx new file mode 100644 index 0000000000..9d21e93733 --- /dev/null +++ b/panda/src/particlesystem/baseParticleEmitter.cxx @@ -0,0 +1,53 @@ +// Filename: baseParticleEmitter.cxx +// Created by: charles (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "baseParticleEmitter.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function : BaseParticleEmitter +// Access : Protected +// Description : constructor +//////////////////////////////////////////////////////////////////// +BaseParticleEmitter:: +BaseParticleEmitter(void) { + _amplitude = 1.0f; + _offset_force.set(0,0,0); +} + +//////////////////////////////////////////////////////////////////// +// Function : BaseParticleEmitter +// Access : Protected +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +BaseParticleEmitter:: +BaseParticleEmitter(const BaseParticleEmitter ©) { + _offset_force = copy._offset_force; + _amplitude = copy._amplitude; +} + +//////////////////////////////////////////////////////////////////// +// Function : BaseParticleEmitter +// Access : Protected +// Description : destructor +//////////////////////////////////////////////////////////////////// +BaseParticleEmitter:: +~BaseParticleEmitter(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : BaseParticleEmitter::bounded_rand +// Access : Private +// Description : Random number in [0, 1] +//////////////////////////////////////////////////////////////////// +float BaseParticleEmitter::bounded_rand(void) +{ + int value = rand() & 0x7fffffff; + float f_value = (float) value / (float) 0x7fffffff; + + return f_value; +} + diff --git a/panda/src/particlesystem/baseParticleEmitter.h b/panda/src/particlesystem/baseParticleEmitter.h new file mode 100644 index 0000000000..c1edae414b --- /dev/null +++ b/panda/src/particlesystem/baseParticleEmitter.h @@ -0,0 +1,47 @@ +// Filename: baseParticleEmitter.h +// Created by: charles (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BASEPARTICLEEMITTER_H +#define BASEPARTICLEEMITTER_H + +#include +#include +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : BaseParticleEmitter +// Description : Describes a physical region in space in which +// particles are randomly generated. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS BaseParticleEmitter : public ReferenceCount { +private: + virtual void assign_initial_values(LPoint3f& pos, LVector3f& vel) = 0; + LVector3f _offset_force; + +protected: + BaseParticleEmitter(void); + BaseParticleEmitter(const BaseParticleEmitter ©); + + float _amplitude; + float bounded_rand(void); + +public: + virtual ~BaseParticleEmitter(void); + virtual BaseParticleEmitter *make_copy(void) = 0; + + INLINE void set_offset_force(const LVector3f& of); + INLINE LVector3f get_offset_force(void) const; + + INLINE void set_amplitude(float a); + INLINE float get_amplitude(void) const; + + INLINE void generate(LPoint3f& pos, LVector3f& vel); +}; + +#include "baseParticleEmitter.I" + +#endif // BASEPARTICLEEMITTER_H diff --git a/panda/src/particlesystem/baseParticleFactory.I b/panda/src/particlesystem/baseParticleFactory.I new file mode 100644 index 0000000000..0f0a8f76bf --- /dev/null +++ b/panda/src/particlesystem/baseParticleFactory.I @@ -0,0 +1,121 @@ +// Filename: baseParticleFactory.I +// Created by: charles (05Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_lifespan_base +// Description : public +//////////////////////////////////////////////////////////////////// +INLINE void BaseParticleFactory:: +set_lifespan_base(float lb) { + _lifespan_base = lb; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_lifespan_delta +// Description : public +//////////////////////////////////////////////////////////////////// +INLINE void BaseParticleFactory:: +set_lifespan_delta(float ld) { + _lifespan_delta = ld; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_mass_base +// Description : public +//////////////////////////////////////////////////////////////////// +INLINE void BaseParticleFactory:: +set_mass_base(float mb) { + _mass_base = mb; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_mass_delta +// Description : public +//////////////////////////////////////////////////////////////////// +INLINE void BaseParticleFactory:: +set_mass_delta(float md) { + _mass_delta = md; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_terminal_velocity_base +// Description : public +//////////////////////////////////////////////////////////////////// +INLINE void BaseParticleFactory:: +set_terminal_velocity_base(float tvb) { + _terminal_velocity_base = tvb; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_terminal_velocity_delta +// Description : public +//////////////////////////////////////////////////////////////////// +INLINE void BaseParticleFactory:: +set_terminal_velocity_delta(float tvd) { + _terminal_velocity_delta = tvd; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_lifespan_base +// Description : public +//////////////////////////////////////////////////////////////////// +INLINE float BaseParticleFactory:: +get_lifespan_base(void) const { + return _lifespan_base; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_lifespan_delta +// Description : public +//////////////////////////////////////////////////////////////////// +INLINE float BaseParticleFactory:: +get_lifespan_delta(void) const { + return _lifespan_delta; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_mass_base +// Description : public +//////////////////////////////////////////////////////////////////// +INLINE float BaseParticleFactory:: +get_mass_base(void) const { + return _mass_base; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_mass_delta +// Description : public +//////////////////////////////////////////////////////////////////// +INLINE float BaseParticleFactory:: +get_mass_delta(void) const { + return _mass_delta; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_terminal_velocity_base +// Description : public +//////////////////////////////////////////////////////////////////// +INLINE float BaseParticleFactory:: +get_terminal_velocity_base(void) const { + return _terminal_velocity_base; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_terminal_velocity_delta +// Description : public +//////////////////////////////////////////////////////////////////// +INLINE float BaseParticleFactory:: +get_terminal_velocity_delta(void) const { + return _terminal_velocity_delta; +} + +//////////////////////////////////////////////////////////////////// +// Function : bounded_rand +// Description : public +//////////////////////////////////////////////////////////////////// +INLINE float BaseParticleFactory:: +bounded_rand(void) const { + return ((float) (rand() % 0x7fffffff) / (float) 0x7fffffff); +} diff --git a/panda/src/particlesystem/baseParticleFactory.cxx b/panda/src/particlesystem/baseParticleFactory.cxx new file mode 100644 index 0000000000..2b61f4b285 --- /dev/null +++ b/panda/src/particlesystem/baseParticleFactory.cxx @@ -0,0 +1,75 @@ +// Filename: baseParticleFactory.cxx +// Created by: charles (05Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "baseParticleFactory.h" + +//////////////////////////////////////////////////////////////////// +// Function : BaseParticleFactory +// Access : protected +// Description : constructor +//////////////////////////////////////////////////////////////////// +BaseParticleFactory:: +BaseParticleFactory(void) { + _terminal_velocity_base = PhysicsObject::_default_terminal_velocity; + _terminal_velocity_delta = 0.0f; + + _lifespan_base = 1.0f; + _lifespan_delta = 0.0f; +} + +//////////////////////////////////////////////////////////////////// +// Function : BaseParticleFactory +// Access : protected +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +BaseParticleFactory:: +BaseParticleFactory(const BaseParticleFactory ©) { + _terminal_velocity_base = copy._terminal_velocity_base; + _terminal_velocity_delta = copy._terminal_velocity_delta; + _lifespan_base = copy._lifespan_base; + _lifespan_delta = copy._lifespan_delta; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~BaseParticleFactory +// Access : public virtual +// Description : destructor +//////////////////////////////////////////////////////////////////// +BaseParticleFactory:: +~BaseParticleFactory(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_particle +// Description : public +//////////////////////////////////////////////////////////////////// +void BaseParticleFactory:: +populate_particle(BaseParticle *bp) { + float lifespan = _lifespan_base; + float mass = _mass_base; + float tv = _terminal_velocity_base; + float t; + + // lifespan + t = bounded_rand(); + lifespan += _lifespan_delta - (2.0f * t * _lifespan_delta); + + // mass + t = bounded_rand(); + mass += _mass_delta - (2.0f * t * _mass_delta); + + // tv + t = bounded_rand(); + tv += _terminal_velocity_delta - (2.0f * t * _terminal_velocity_delta); + + bp->set_lifespan(lifespan); + bp->set_mass(mass); + bp->set_terminal_velocity(tv); + bp->set_active(false); + bp->set_alive(false); + bp->set_age(0.0f); + + populate_child_particle(bp); +} diff --git a/panda/src/particlesystem/baseParticleFactory.h b/panda/src/particlesystem/baseParticleFactory.h new file mode 100644 index 0000000000..0d33e0971c --- /dev/null +++ b/panda/src/particlesystem/baseParticleFactory.h @@ -0,0 +1,61 @@ +// Filename: baseParticleFactory.h +// Created by: charles (05Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BASEPARTICLEFACTORY_H +#define BASEPARTICLEFACTORY_H + +#include +#include + +#include "baseParticle.h" + +//////////////////////////////////////////////////////////////////// +// Class : BaseParticleFactory +// Description : Pure Virtual base class for creating particles +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS BaseParticleFactory : public ReferenceCount { +private: + float _lifespan_base; + float _lifespan_delta; + + float _mass_base; + float _mass_delta; + + float _terminal_velocity_base; + float _terminal_velocity_delta; + + virtual void populate_child_particle(BaseParticle *bp) const = 0; + +protected: + BaseParticleFactory(void); + BaseParticleFactory(const BaseParticleFactory ©); + + INLINE float bounded_rand(void) const; + +public: + virtual ~BaseParticleFactory(void); + + INLINE void set_lifespan_base(float lb); + INLINE void set_lifespan_delta(float ld); + INLINE void set_mass_base(float mb); + INLINE void set_mass_delta(float md); + INLINE void set_terminal_velocity_base(float tvb); + INLINE void set_terminal_velocity_delta(float tvd); + + INLINE float get_lifespan_base(void) const; + INLINE float get_lifespan_delta(void) const; + INLINE float get_mass_base(void) const; + INLINE float get_mass_delta(void) const; + INLINE float get_terminal_velocity_base(void) const; + INLINE float get_terminal_velocity_delta(void) const; + + virtual BaseParticle *alloc_particle(void) const = 0; + + void populate_particle(BaseParticle* bp); +}; + +#include "baseParticleFactory.I" + +#endif // BASEPARTICLEFACTORY_H diff --git a/panda/src/particlesystem/baseParticleRenderer.I b/panda/src/particlesystem/baseParticleRenderer.I new file mode 100644 index 0000000000..c967b62551 --- /dev/null +++ b/panda/src/particlesystem/baseParticleRenderer.I @@ -0,0 +1,98 @@ +// Filename: baseParticleRenderer.I +// Created by: charles (20Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : get_render_node +// Class : Public +// Description : Query the geomnode pointer +//////////////////////////////////////////////////////////////////// + +INLINE GeomNode *BaseParticleRenderer:: +get_render_node(void) const { + return _render_node; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_alpha_decay +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE ParticleRendererAlphaDecay BaseParticleRenderer:: +get_alpha_decay(void) const { + return _alpha_decay; +} + +//////////////////////////////////////////////////////////////////// +// Function : disable_alpha +// Access : Private +// Description : kills the intermediate alpha node/arc +//////////////////////////////////////////////////////////////////// + +INLINE void BaseParticleRenderer:: +disable_alpha(void) { + _alpha_node.clear(); + + if (_alpha_arc.is_null() == false) { + remove_arc(_alpha_arc); + _alpha_arc.clear(); + } + + _interface_node = _render_node; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_alpha_decay +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE void BaseParticleRenderer:: +set_alpha_decay(ParticleRendererAlphaDecay ad) { + update_alpha_state(ad); + init_geoms(); +} + +//////////////////////////////////////////////////////////////////// +// Function : color_lerp +// Access : Protected +// Description : Linear interpolate 2 color vectors +//////////////////////////////////////////////////////////////////// + +INLINE Colorf BaseParticleRenderer:: +color_lerp(float t, const Colorf& c1, const Colorf& c2) { + return (c1 + (c2 - c1) * t); +} + +//////////////////////////////////////////////////////////////////// +// Function : color_clerp +// Access : Protected +// Description : Cubic interpolation between 2 color vectors. +//////////////////////////////////////////////////////////////////// + +INLINE Colorf BaseParticleRenderer:: +color_clerp(float t, const Colorf& c1, const Colorf& c2) { + float cubic_t = t * t * ((2 * t) - 3); + return color_lerp(cubic_t, c1, c2); +} + +//////////////////////////////////////////////////////////////////// +// Function : cubic_smooth +// Access : Protected +// Description : smooths out a linear parameterized t in [0,1] +//////////////////////////////////////////////////////////////////// + +INLINE float BaseParticleRenderer:: +cubic_smooth(float t) { + return t * t * (3 - (2 * t)); +} + +//////////////////////////////////////////////////////////////////// +// Function : lerp +// Access : Protected +//////////////////////////////////////////////////////////////////// + +INLINE float BaseParticleRenderer:: +lerp(float t, float x0, float x1) { + return x0 + (t * (x1 - x0)); +} diff --git a/panda/src/particlesystem/baseParticleRenderer.cxx b/panda/src/particlesystem/baseParticleRenderer.cxx new file mode 100644 index 0000000000..20c6173b0d --- /dev/null +++ b/panda/src/particlesystem/baseParticleRenderer.cxx @@ -0,0 +1,86 @@ +// Filename: baseParticleRenderer.cxx +// Created by: charles (20Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include + +#include "baseParticleRenderer.h" + +//////////////////////////////////////////////////////////////////// +// Function : BaseParticleRenderer +// Access : Public +// Description : Default Constructor +//////////////////////////////////////////////////////////////////// +BaseParticleRenderer:: +BaseParticleRenderer(ParticleRendererAlphaDecay alpha_decay) : + _alpha_arc((RenderRelation *) NULL), + _alpha_decay(PR_ALPHA_INVALID) { + _render_node = new GeomNode("BaseParticleRenderer render node"); + + update_alpha_state(alpha_decay); +} + +//////////////////////////////////////////////////////////////////// +// Function : BaseParticleRenderer +// Access : Public +// Description : Copy Constructor +//////////////////////////////////////////////////////////////////// +BaseParticleRenderer:: +BaseParticleRenderer(const BaseParticleRenderer& copy) : + _alpha_arc((RenderRelation *) NULL), + _alpha_decay(PR_ALPHA_INVALID) { + _render_node = new GeomNode("BaseParticleRenderer render node"); + + update_alpha_state(copy._alpha_decay); +} + +//////////////////////////////////////////////////////////////////// +// Function : ~BaseParticleRenderer +// Access : Public +// Description : Destructor +//////////////////////////////////////////////////////////////////// +BaseParticleRenderer:: +~BaseParticleRenderer(void) { + if (_alpha_decay == PR_ALPHA_OUT || _alpha_decay == PR_ALPHA_IN) + remove_arc(_alpha_arc); +} + +//////////////////////////////////////////////////////////////////// +// Function : enable_alpha +// Access : Private +// Description : Builds an intermediate node and transition that +// enables alpha channeling. +//////////////////////////////////////////////////////////////////// +void BaseParticleRenderer:: +enable_alpha(void) { + _render_node->clear(); + + _alpha_node = new GeomNode("BaseParticleRenderer alpha node"); + + _alpha_arc = new RenderRelation(_render_node, _alpha_node); + _alpha_arc->set_transition(new TransparencyTransition(TransparencyProperty::M_alpha)); + + _interface_node = _alpha_node; +} + +//////////////////////////////////////////////////////////////////// +// Function : update_alpha_state +// Access : Private +// Description : handles the base class part of alpha updating. +//////////////////////////////////////////////////////////////////// +void BaseParticleRenderer:: +update_alpha_state(ParticleRendererAlphaDecay ad) { + if (_alpha_decay == ad) + return; + + if (ad == PR_NO_ALPHA && _alpha_decay != PR_NO_ALPHA) + disable_alpha(); + else if ((_alpha_decay == PR_ALPHA_INVALID || _alpha_decay == PR_NO_ALPHA) && + ad != PR_NO_ALPHA) + enable_alpha(); + + _alpha_decay = ad; +} + diff --git a/panda/src/particlesystem/baseParticleRenderer.h b/panda/src/particlesystem/baseParticleRenderer.h new file mode 100644 index 0000000000..3cab3082d2 --- /dev/null +++ b/panda/src/particlesystem/baseParticleRenderer.h @@ -0,0 +1,98 @@ +// Filename: baseParticleRenderer.h +// Created by: charles (20Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BASEPARTICLERENDERER_H +#define BASEPARTICLERENDERER_H + +#include +#include +#include +#include +#include +#include +#include + +#include + +enum ParticleRendererAlphaDecay { + PR_NO_ALPHA, + PR_ALPHA_OUT, + PR_ALPHA_IN, + PR_ALPHA_USER, + PR_ALPHA_INVALID +}; + +enum ParticleRendererBlendMethod { + PP_NO_BLEND, + PP_BLEND_LINEAR, + PP_BLEND_CUBIC +}; + +//////////////////////////////////////////////////////////////////// +// Class : BaseParticleRenderer +// Description : Pure virtual particle renderer base class +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS BaseParticleRenderer : public ReferenceCount { +private: + PT(GeomNode) _render_node; + + // children of this class don't know anything about alpha. + // they should just interact with _interface_node, and link geometry + // from/to that. + + PT(GeomNode) _alpha_node; + PT(RenderRelation) _alpha_arc; + + // birth and kill particle are for renderers that might do maintenance + // faster if it was notified on a per-event basis. An example: + // geomParticleRenderer maintains an arc for every particle. Instead + // of visiting EVERY entry in the arc array, individual arcs are + // changed on birth and death. Brings it down a little from O(N) every + // update. + + virtual void birth_particle(int index) = 0; + virtual void kill_particle(int index) = 0; + + + virtual void init_geoms(void) = 0; + virtual void render(vector< PT(PhysicsObject) >& po_vector, + int ttl_particles) = 0; + +protected: + GeomNode *_interface_node; + ParticleRendererAlphaDecay _alpha_decay; + + BaseParticleRenderer(ParticleRendererAlphaDecay alpha_decay); + BaseParticleRenderer(const BaseParticleRenderer& copy); + + void update_alpha_state(ParticleRendererAlphaDecay ad); + + INLINE Colorf color_lerp(float t, const Colorf& c1, const Colorf& c2); + INLINE Colorf color_clerp(float t, const Colorf& c1, const Colorf& c2); + INLINE float cubic_smooth(float t); + INLINE float lerp(float t, float x0, float x1); + + void enable_alpha(void); + INLINE void disable_alpha(void); + + virtual void resize_pool(int new_size) = 0; + +public: + + virtual ~BaseParticleRenderer(void); + + INLINE GeomNode *get_render_node(void) const; + INLINE ParticleRendererAlphaDecay get_alpha_decay(void) const; + INLINE void set_alpha_decay(ParticleRendererAlphaDecay ad); + + virtual BaseParticleRenderer *make_copy(void) = 0; + + friend class ParticleSystem; +}; + +#include "baseParticleRenderer.I" + +#endif // BASEPARTICLERENDERER_H + diff --git a/panda/src/particlesystem/boxEmitter.I b/panda/src/particlesystem/boxEmitter.I new file mode 100644 index 0000000000..b36f62628f --- /dev/null +++ b/panda/src/particlesystem/boxEmitter.I @@ -0,0 +1,26 @@ +// Filename: boxEmitter.I +// Created by: charles (26Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_boundaries +// Access : Public +// Description : boundary assignment +//////////////////////////////////////////////////////////////////// + +INLINE void BoxEmitter:: +set_boundaries(const LPoint3f& vmin, const LPoint3f& vmax) { + _vmin = vmin; _vmax = vmax; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_launch_vec +// Access : Public +// Description : launch vec assignment +//////////////////////////////////////////////////////////////////// + +INLINE void BoxEmitter:: +set_launch_vec(const LVector3f& lv) { + _launch_vec = lv; +} diff --git a/panda/src/particlesystem/boxEmitter.cxx b/panda/src/particlesystem/boxEmitter.cxx new file mode 100644 index 0000000000..6da023dec3 --- /dev/null +++ b/panda/src/particlesystem/boxEmitter.cxx @@ -0,0 +1,73 @@ +// Filename: boxEmitter.cxx +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "boxEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Function : BoxEmitter +// Access : Public +// Description : constructor +//////////////////////////////////////////////////////////////////// +BoxEmitter:: +BoxEmitter(void) : + BaseParticleEmitter() { + _vmin.set(0.0f, 0.0f, 0.0f); + _vmax.set(0.0f, 0.0f, 0.0f); + _launch_vec.set(0.0f, 0.0f, 0.0f); +} + +//////////////////////////////////////////////////////////////////// +// Function : BoxEmitter +// Access : Public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +BoxEmitter:: +BoxEmitter(const BoxEmitter ©) : + BaseParticleEmitter(copy) { + _vmin = copy._vmin; + _vmax = copy._vmax; + _launch_vec = copy._launch_vec; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~BoxEmitter +// Access : Public +// Description : destructor +//////////////////////////////////////////////////////////////////// +BoxEmitter:: +~BoxEmitter(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : Public +// Description : copier +//////////////////////////////////////////////////////////////////// +BaseParticleEmitter *BoxEmitter:: +make_copy(void) { + return new BoxEmitter(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : BoxEmitter::create_particle_location +// Access : Public +// Description : Generates a location in the box +//////////////////////////////////////////////////////////////////// +void BoxEmitter:: +assign_initial_values(LPoint3f& pos, LVector3f& vel) +{ + float t_x = bounded_rand(); + float t_y = bounded_rand(); + float t_z = bounded_rand(); + + LVector3f v_diff = _vmax - _vmin; + + float lerp_x = _vmin[0] + t_x * v_diff[0]; + float lerp_y = _vmin[1] + t_y * v_diff[1]; + float lerp_z = _vmin[2] + t_z * v_diff[2]; + + pos.set(lerp_x, lerp_y, lerp_z); + vel = _launch_vec; +} diff --git a/panda/src/particlesystem/boxEmitter.h b/panda/src/particlesystem/boxEmitter.h new file mode 100644 index 0000000000..eded10f1be --- /dev/null +++ b/panda/src/particlesystem/boxEmitter.h @@ -0,0 +1,35 @@ +// Filename: boxEmitter.h +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BOXEMITTER_H +#define BOXEMITTER_H + +#include "baseParticleEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Class : BoxEmitter +// Description : Describes a voluminous box region in which +// particles are generated. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS BoxEmitter : public BaseParticleEmitter { +private: + LPoint3f _vmin, _vmax; + LVector3f _launch_vec; + + virtual void assign_initial_values(LPoint3f& pos, LVector3f& vel); + +public: + BoxEmitter(void); + BoxEmitter(const BoxEmitter ©); + virtual ~BoxEmitter(void); + + virtual BaseParticleEmitter *make_copy(void); + INLINE void set_boundaries(const LPoint3f& vmin, const LPoint3f& vmax); + INLINE void set_launch_vec(const LVector3f& lv); +}; + +#include "boxEmitter.I" + +#endif // BOXEMITTER_H diff --git a/panda/src/particlesystem/config_particlesystem.cxx b/panda/src/particlesystem/config_particlesystem.cxx new file mode 100644 index 0000000000..219dd8f1b5 --- /dev/null +++ b/panda/src/particlesystem/config_particlesystem.cxx @@ -0,0 +1,16 @@ +// Filename: config_particlesystem.cxx +// Created by: charles (05Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_particlesystem.h" +#include "particleSystem.h" +#include "geomParticleRenderer.h" +#include + +ConfigureDef(config_particlesystem); +NotifyCategoryDef(particlesystem, ""); + +ConfigureFn(config_particlesystem) { +} + diff --git a/panda/src/particlesystem/config_particlesystem.h b/panda/src/particlesystem/config_particlesystem.h new file mode 100644 index 0000000000..37db8fecfb --- /dev/null +++ b/panda/src/particlesystem/config_particlesystem.h @@ -0,0 +1,16 @@ +// Filename: config_particlesystem.h +// Created by: charles (05Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_PARTICLESYSTEM_H +#define CONFIG_PARTICLESYSTEM_H + +#include +#include +#include + +ConfigureDecl(config_particlesystem, EXPCL_PANDAPHYSICS, EXPTP_PANDAPHYSICS); +NotifyCategoryDecl(particlesystem, EXPCL_PANDAPHYSICS, EXPTP_PANDAPHYSICS); + +#endif // CONFIG_PARTICLESYSTEM_H diff --git a/panda/src/particlesystem/discEmitter.I b/panda/src/particlesystem/discEmitter.I new file mode 100644 index 0000000000..02b198e606 --- /dev/null +++ b/panda/src/particlesystem/discEmitter.I @@ -0,0 +1,92 @@ +// Filename: discEmitter.I +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_radius +// Access : Public +// Description : radius assignment +//////////////////////////////////////////////////////////////////// + +INLINE void DiscEmitter:: +set_radius(float r) { + _radius = r; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_outer_aoe +// Access : Public +// Description : aoe assignement +//////////////////////////////////////////////////////////////////// + +INLINE void DiscEmitter:: +set_outer_aoe(float o_aoe) { + _outer_aoe = o_aoe; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_inner_aoe +// Access : Public +// Description : aoe assignment +//////////////////////////////////////////////////////////////////// + +INLINE void DiscEmitter:: +set_inner_aoe(float i_aoe) { + _inner_aoe = i_aoe; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_outer_magnitude +// Access : public +// Description : mag assignment +//////////////////////////////////////////////////////////////////// + +INLINE void DiscEmitter:: +set_outer_magnitude(float o_mag) { + _outer_magnitude = o_mag; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_inner_magnitude +// Access : public +// Description : mag assignment +//////////////////////////////////////////////////////////////////// + +INLINE void DiscEmitter:: +set_inner_magnitude(float i_mag) { + _inner_magnitude = i_mag; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_cubic_lerping +// Access : public +// Description : clerp flag +//////////////////////////////////////////////////////////////////// + +INLINE void DiscEmitter:: +set_cubic_lerping(bool clerp) { + _cubic_lerping = clerp; +} + +//////////////////////////////////////////////////////////////////// +// Function : lerp +// Access : private +// Description : yali +//////////////////////////////////////////////////////////////////// + +INLINE float DiscEmitter:: +lerp(float t, float x0, float x1) { + return x0 + (t * (x1 - x0)); +} + +//////////////////////////////////////////////////////////////////// +// Function : cubic_lerp +// Access : private +// Description : yaci +//////////////////////////////////////////////////////////////////// + +INLINE float DiscEmitter:: +cubic_lerp(float t, float x0, float x1) { + return x0 + ((t * t * (3 - (2 * t))) * (x1 - x0)); +} diff --git a/panda/src/particlesystem/discEmitter.cxx b/panda/src/particlesystem/discEmitter.cxx new file mode 100644 index 0000000000..0ac9997131 --- /dev/null +++ b/panda/src/particlesystem/discEmitter.cxx @@ -0,0 +1,97 @@ +// Filename: discEmitter.cxx +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "discEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Function : DiscEmitter::DiscEmitter +// Access : Public +// Description : constructor +//////////////////////////////////////////////////////////////////// +DiscEmitter:: +DiscEmitter(void) { + _radius = 0.0f; + _inner_aoe = _outer_aoe = 0.0f; + _inner_magnitude = _outer_magnitude = 0.0f; + _cubic_lerping = false; +} + +//////////////////////////////////////////////////////////////////// +// Function : DiscEmitter::DiscEmitter +// Access : Public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +DiscEmitter:: +DiscEmitter(const DiscEmitter ©) : + BaseParticleEmitter(copy) { + _radius = copy._radius; + _inner_aoe = copy._inner_aoe; + _outer_aoe = copy._outer_aoe; + _inner_magnitude = copy._inner_magnitude; + _outer_magnitude = copy._outer_magnitude; + _cubic_lerping = copy._cubic_lerping; +} + +//////////////////////////////////////////////////////////////////// +// Function : DiscEmitter::~DiscEmitter +// Access : Public +// Description : destructor +//////////////////////////////////////////////////////////////////// +DiscEmitter:: +~DiscEmitter(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : Public +// Description : copier +//////////////////////////////////////////////////////////////////// +BaseParticleEmitter *DiscEmitter:: +make_copy(void) { + return new DiscEmitter(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : DiscEmitter::create_particle_location +// Access : Public +// Description : Generates a location on the disc +//////////////////////////////////////////////////////////////////// +void DiscEmitter:: +assign_initial_values(LPoint3f& pos, LVector3f& vel) { + // position + float theta = bounded_rand() * 2.0f * MathNumbers::pi; + + float t = bounded_rand(); + float r_scalar = t * _radius; + + float sinf_theta = sinf(theta); + float cosf_theta = cosf(theta); + + float new_x = cosf_theta * r_scalar; + float new_y = sinf_theta * r_scalar; + + pos.set(new_x, new_y, 0.0f); + + // lerp type + float aoe, mag; + + if (_cubic_lerping == true) { + aoe = cubic_lerp(t, _inner_aoe, _outer_aoe); + mag = cubic_lerp(t, _inner_magnitude, _outer_magnitude); + } + else { + aoe = lerp(t, _inner_aoe, _outer_aoe); + mag = lerp(t, _inner_magnitude, _outer_magnitude); + } + + // velocity + float vel_z = mag * sinf(aoe * (MathNumbers::pi / 180.0f)); + float abs_diff = fabs((mag * mag) - (vel_z * vel_z)); + float root_mag_minus_z_squared = sqrtf(abs_diff); + float vel_x = cosf_theta * root_mag_minus_z_squared; + float vel_y = sinf_theta * root_mag_minus_z_squared; + + vel.set(vel_x, vel_y, vel_z); +} diff --git a/panda/src/particlesystem/discEmitter.h b/panda/src/particlesystem/discEmitter.h new file mode 100644 index 0000000000..b16fbf9927 --- /dev/null +++ b/panda/src/particlesystem/discEmitter.h @@ -0,0 +1,45 @@ +// Filename: discEmitter.h +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DISCEMITTER_H +#define DISCEMITTER_H + +#include "baseParticleEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Class : DiscEmitter +// Description : Describes a planar disc region from which particles +// are generated +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS DiscEmitter : public BaseParticleEmitter { +private: + float _radius; + float _outer_aoe, _inner_aoe; + float _outer_magnitude, _inner_magnitude; + bool _cubic_lerping; + + virtual void assign_initial_values(LPoint3f& pos, LVector3f& vel); + + INLINE float lerp(float t, float x0, float x1); + INLINE float cubic_lerp(float t, float x0, float x1); + +public: + DiscEmitter(void); + DiscEmitter(const DiscEmitter ©); + virtual ~DiscEmitter(void); + + virtual BaseParticleEmitter *make_copy(void); + + INLINE void set_radius(float r); + INLINE void set_outer_aoe(float o_aoe); + INLINE void set_inner_aoe(float i_aoe); + INLINE void set_outer_magnitude(float o_mag); + INLINE void set_inner_magnitude(float i_mag); + INLINE void set_cubic_lerping(bool clerp); +}; + +#include "discEmitter.I" + +#endif // DISCEMITTER_H diff --git a/panda/src/particlesystem/emitters.h b/panda/src/particlesystem/emitters.h new file mode 100644 index 0000000000..66a701e770 --- /dev/null +++ b/panda/src/particlesystem/emitters.h @@ -0,0 +1,20 @@ +// Filename: emitters.h +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EMITTERS_H +#define EMITTERS_H + +#include "baseParticleEmitter.h" +#include "discEmitter.h" +#include "ringEmitter.h" +#include "sphereVolumeEmitter.h" +#include "sphereSurfaceEmitter.h" +#include "rectangleEmitter.h" +#include "boxEmitter.h" +#include "lineEmitter.h" +#include "pointEmitter.h" +#include "tangentRingEmitter.h" + +#endif // EMITTERS_H diff --git a/panda/src/particlesystem/geomParticleRenderer.I b/panda/src/particlesystem/geomParticleRenderer.I new file mode 100644 index 0000000000..b72901a390 --- /dev/null +++ b/panda/src/particlesystem/geomParticleRenderer.I @@ -0,0 +1,45 @@ +// Filename: geomParticleRenderer.I +// Created by: charles (05Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_geom_node +// Access : public +//////////////////////////////////////////////////////////////////// + +// we're forcing a pool resize to remove every arc in the vector. +// this is because arcs are reference-counted, and we have to +// explicitly remove them from the node they were previously +// pointing at. + +INLINE void GeomParticleRenderer:: +set_geom_node(Node *node) { + _geom_node = node; + resize_pool(_pool_size); +} + +//////////////////////////////////////////////////////////////////// +// Function : get_geom_node +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE Node *GeomParticleRenderer:: +get_geom_node(void) { + return _geom_node; +} + +//////////////////////////////////////////////////////////////////// +// Function : kill_arcs +// Access : private +//////////////////////////////////////////////////////////////////// + +INLINE void GeomParticleRenderer:: +kill_arcs(void) { + vector< PT(RenderRelation) >::iterator vec_iter = _arc_vector.begin(); + + for (; vec_iter != _arc_vector.end(); vec_iter++) + remove_arc(*vec_iter); + + _arc_vector.erase(_arc_vector.begin(), _arc_vector.end()); +} diff --git a/panda/src/particlesystem/geomParticleRenderer.cxx b/panda/src/particlesystem/geomParticleRenderer.cxx new file mode 100644 index 0000000000..edca0b7387 --- /dev/null +++ b/panda/src/particlesystem/geomParticleRenderer.cxx @@ -0,0 +1,178 @@ +// Filename: geomParticleRenderer.cxx +// Created by: charles (05Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "geomParticleRenderer.h" +#include "baseParticle.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function : GeomParticleRenderer +// Access : public +// Description : constructor +//////////////////////////////////////////////////////////////////// + +GeomParticleRenderer:: +GeomParticleRenderer(ParticleRendererAlphaDecay ad, Node *geom_node) : + _geom_node(geom_node), _pool_size(0), BaseParticleRenderer(ad) { + + _dead_particle_parent_node = new Node; + + if (_geom_node.is_null()) + _geom_node = _dead_particle_parent_node; +} + +//////////////////////////////////////////////////////////////////// +// Function : GeomParticleRenderer +// Access : public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// + +GeomParticleRenderer:: +GeomParticleRenderer(const GeomParticleRenderer& copy) : + _pool_size(0), BaseParticleRenderer(copy) { + + _dead_particle_parent_node = new Node; + + _geom_node = copy._geom_node; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~GeomParticleRenderer +// Access : public +// Description : destructor +//////////////////////////////////////////////////////////////////// + +GeomParticleRenderer:: +~GeomParticleRenderer(void) { + kill_arcs(); +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : public +// Description : dynamic copying +//////////////////////////////////////////////////////////////////// + +BaseParticleRenderer *GeomParticleRenderer:: +make_copy(void) { + return new GeomParticleRenderer(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : init_geoms +// Access : private +// Description : links the child nodes to the parent stuff +//////////////////////////////////////////////////////////////////// + +void GeomParticleRenderer:: +init_geoms(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : resize_pool +// Access : private +// Description : handles renderer-size resizing. +//////////////////////////////////////////////////////////////////// + +void GeomParticleRenderer:: +resize_pool(int new_size) { + kill_arcs(); + + // now repopulate the vector + + int i; + RenderRelation *rr; + + for (i = 0; i < new_size; i++) { + rr = new RenderRelation(_dead_particle_parent_node, _geom_node); + _arc_vector.push_back(rr); + } + + _pool_size = new_size; +} + +//////////////////////////////////////////////////////////////////// +// Function : birth_particle +// Access : Private, virtual +// Description : child birth +//////////////////////////////////////////////////////////////////// + +void GeomParticleRenderer:: +birth_particle(int index) { + _arc_vector[index]->change_parent(_interface_node); +} + +//////////////////////////////////////////////////////////////////// +// Function : kill_particle +// Access : Private, virtual +// Description : child kill +//////////////////////////////////////////////////////////////////// + +void GeomParticleRenderer:: +kill_particle(int index) { + _arc_vector[index]->change_parent(_dead_particle_parent_node); +} + +//////////////////////////////////////////////////////////////////// +// Function : render +// Access : private +// Description : sets the transitions on each arc +//////////////////////////////////////////////////////////////////// + +void GeomParticleRenderer:: +render(vector< PT(PhysicsObject) >& po_vector, int ttl_particles) { + BaseParticle *cur_particle; + LPoint3f pos; + int i, remaining_particles = ttl_particles; + + vector< PT(RenderRelation) >::iterator cur_arc_iter = _arc_vector.begin(); + + // run through the particle vector + + for (i = 0; i < po_vector.size(); i++) { + RenderRelation *cur_arc; + + cur_particle = (BaseParticle *) po_vector[i].p(); + cur_arc = *cur_arc_iter; + + if (cur_particle->get_alive() == true) { + // living particle + + pos = cur_particle->get_position(); + + PT(TransformTransition) xform; + PT(ColorTransition) alpha; + xform = new TransformTransition(LMatrix4f::translate_mat(pos)); + + if ((_alpha_decay != PR_ALPHA_INVALID) && (_alpha_decay != PR_NO_ALPHA) && + (_alpha_decay != PR_ALPHA_USER)) { + + float alpha_scalar = cur_particle->get_parameterized_age(); + + if (_alpha_decay == PR_ALPHA_IN) { + alpha = new ColorTransition(1.0f, 1.0f, 1.0f, alpha_scalar); + } + else if (_alpha_decay == PR_ALPHA_OUT) { + alpha = new ColorTransition(1.0f, 1.0f, 1.0f, 1.0f - alpha_scalar); + } + + cur_arc->set_transition(alpha); + } + + cur_arc->set_transition(xform); + + // maybe get out early if possible. + + remaining_particles--; + + if (remaining_particles == 0) + break; + } + + cur_arc_iter++; + } +} diff --git a/panda/src/particlesystem/geomParticleRenderer.h b/panda/src/particlesystem/geomParticleRenderer.h new file mode 100644 index 0000000000..9eb7e0b75e --- /dev/null +++ b/panda/src/particlesystem/geomParticleRenderer.h @@ -0,0 +1,57 @@ +// Filename: geomParticleRenderer.h +// Created by: charles (05Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMPARTICLERENDERER_H +#define GEOMPARTICLERENDERER_H + +#include "baseParticleRenderer.h" +#include "baseParticle.h" + +#include +#include +#include +#include + +#include + +class EXPCL_PANDAPHYSICS GeomParticleRenderer : public BaseParticleRenderer { +private: + + PT(Node) _geom_node; + PT(Node) _dead_particle_parent_node; + + vector< PT(RenderRelation) > _arc_vector; + + int _pool_size; + + INLINE void kill_arcs(void); + + // geomparticlerenderer takes advantage of the birth/death functions + + virtual void birth_particle(int index); + virtual void kill_particle(int index); + + virtual void init_geoms(void); + virtual void render(vector< PT(PhysicsObject) >& po_vector, + int ttl_particles); + + virtual void resize_pool(int new_size); + +public: + + GeomParticleRenderer(ParticleRendererAlphaDecay ad = PR_NO_ALPHA, + Node *geom_node = (Node *) NULL); + GeomParticleRenderer(const GeomParticleRenderer& copy); + virtual ~GeomParticleRenderer(void); + + INLINE void set_geom_node(Node *node); + INLINE Node *get_geom_node(void); + + virtual BaseParticleRenderer *make_copy(void); +}; + +#include "geomParticleRenderer.I" + +#endif // GEOMPARTICLERENDERER_H diff --git a/panda/src/particlesystem/lineEmitter.I b/panda/src/particlesystem/lineEmitter.I new file mode 100644 index 0000000000..d924f849da --- /dev/null +++ b/panda/src/particlesystem/lineEmitter.I @@ -0,0 +1,27 @@ +// Filename: lineEmitter.I +// Created by: charles (26Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_endpoints +// Access : Public +// Description : endpoint assignment +//////////////////////////////////////////////////////////////////// + +INLINE void LineEmitter:: +set_endpoints(const LPoint3f& vmin, const LPoint3f& vmax) { + _vmin = vmin; _vmax = vmax; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_launch_vec +// Access : Public +// Description : launch assignment +//////////////////////////////////////////////////////////////////// + +INLINE void LineEmitter:: +set_launch_vec(const LVector3f& lv) { + _launch_vec = lv; +} + diff --git a/panda/src/particlesystem/lineEmitter.cxx b/panda/src/particlesystem/lineEmitter.cxx new file mode 100644 index 0000000000..a1c1f83b29 --- /dev/null +++ b/panda/src/particlesystem/lineEmitter.cxx @@ -0,0 +1,71 @@ +// Filename: lineEmitter.cxx +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "lineEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Function : LineEmitter +// Access : Public +// Description : constructor +//////////////////////////////////////////////////////////////////// +LineEmitter:: +LineEmitter(void) : + BaseParticleEmitter() { + _vmin.set(0.0f, 0.0f, 0.0f); + _vmax.set(0.0f, 0.0f, 0.0f); + _launch_vec.set(0,0,0); +} + +//////////////////////////////////////////////////////////////////// +// Function : LineEmitter +// Access : Public +// Description : constructor +//////////////////////////////////////////////////////////////////// +LineEmitter:: +LineEmitter(const LineEmitter ©) : + BaseParticleEmitter(copy) { + _vmin = copy._vmin; + _vmax = copy._vmax; + _launch_vec = copy._launch_vec; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~LineEmitter +// Access : Public +// Description : constructor +//////////////////////////////////////////////////////////////////// +LineEmitter:: +~LineEmitter(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : Public +// Description : copier +//////////////////////////////////////////////////////////////////// +BaseParticleEmitter *LineEmitter:: +make_copy(void) { + return new LineEmitter(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : LineEmitter::create_particle_location +// Access : Public +// Description : Generates a location on the line +//////////////////////////////////////////////////////////////////// +void LineEmitter:: +assign_initial_values(LPoint3f& pos, LVector3f& vel) +{ + float t = bounded_rand(); + + LVector3f v_diff = _vmax - _vmin; + + float lerp_x = _vmin[0] + t * v_diff[0]; + float lerp_y = _vmin[1] + t * v_diff[1]; + float lerp_z = _vmin[2] + t * v_diff[2]; + + pos.set(lerp_x, lerp_y, lerp_z); + vel = _launch_vec; +} diff --git a/panda/src/particlesystem/lineEmitter.h b/panda/src/particlesystem/lineEmitter.h new file mode 100644 index 0000000000..b088e8ff37 --- /dev/null +++ b/panda/src/particlesystem/lineEmitter.h @@ -0,0 +1,36 @@ +// Filename: lineEmitter.h +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINEEMITTER_H +#define LINEEMITTER_H + +#include "baseParticleEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Class : LineEmitter +// Description : Describes a linear region in which +// particles are generated. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS LineEmitter : public BaseParticleEmitter { +private: + LPoint3f _vmin, _vmax; + LVector3f _launch_vec; + + virtual void assign_initial_values(LPoint3f& pos, LVector3f& vel); + +public: + LineEmitter(void); + LineEmitter(const LineEmitter ©); + virtual ~LineEmitter(void); + + virtual BaseParticleEmitter *make_copy(void); + + INLINE void set_endpoints(const LPoint3f& vmin, const LPoint3f& vmax); + INLINE void set_launch_vec(const LVector3f& lv); +}; + +#include "lineEmitter.I" + +#endif // LINEEMITTER_H diff --git a/panda/src/particlesystem/orientedParticle.I b/panda/src/particlesystem/orientedParticle.I new file mode 100644 index 0000000000..04e06690c5 --- /dev/null +++ b/panda/src/particlesystem/orientedParticle.I @@ -0,0 +1,22 @@ +// Filename: orientedParticle.I +// Created by: charles (04Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_velocity +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE void OrientedParticle:: +set_velocity(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : set_orientation +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE void OrientedParticle:: +set_orientation(void) { +} diff --git a/panda/src/particlesystem/orientedParticle.cxx b/panda/src/particlesystem/orientedParticle.cxx new file mode 100644 index 0000000000..cbd737959d --- /dev/null +++ b/panda/src/particlesystem/orientedParticle.cxx @@ -0,0 +1,75 @@ +// Filename: orientedParticle.cxx +// Created by: charles (19Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "orientedParticle.h" + +//////////////////////////////////////////////////////////////////// +// Function : OrientedParticle +// Access : public +// Description : simple constructor +//////////////////////////////////////////////////////////////////// +OrientedParticle:: +OrientedParticle(int lifespan, bool alive) : + BaseParticle(lifespan, alive) { + set_oriented(true); +} + +//////////////////////////////////////////////////////////////////// +// Function : OrientedParticle +// Access : public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +OrientedParticle:: +OrientedParticle(const OrientedParticle ©) : + BaseParticle(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function : ~OrientedParticle +// Access : public +// Description : simple destructor +//////////////////////////////////////////////////////////////////// +OrientedParticle:: +~OrientedParticle(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : public, virtual +// Description : simple destructor +//////////////////////////////////////////////////////////////////// +PhysicsObject *OrientedParticle:: +make_copy(void) const { + return new OrientedParticle(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : init +// Access : Public +// Description : particle init routine +//////////////////////////////////////////////////////////////////// +void OrientedParticle:: +init(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : die +// Access : public +// Description : particle death routine +//////////////////////////////////////////////////////////////////// +void OrientedParticle:: +die(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : update +// Access : public +// Description : particle update routine. +// This NEEDS to be filled in with quaternion slerp +// stuff, or oriented particles will not rotate. +//////////////////////////////////////////////////////////////////// +void OrientedParticle:: +update(void) { +} diff --git a/panda/src/particlesystem/orientedParticle.h b/panda/src/particlesystem/orientedParticle.h new file mode 100644 index 0000000000..a8bd104879 --- /dev/null +++ b/panda/src/particlesystem/orientedParticle.h @@ -0,0 +1,36 @@ +// Filename: orientedParticle.h +// Created by: charles (19Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ORIENTEDPARTICLE_H +#define ORIENTEDPARTICLE_H + +#include "baseParticle.h" + +#include + +/////////////////////////////////////////////////////////////////// +// Class : OrientedParticle +// Description : Describes a particle that has angular +// characteristics (velocity, orientation). +/////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS OrientedParticle : public BaseParticle { +public: + OrientedParticle(int lifespan = 0, bool alive = false); + OrientedParticle(const OrientedParticle ©); + virtual ~OrientedParticle(void); + + virtual PhysicsObject *make_copy(void) const; + + INLINE void set_velocity(void); + INLINE void set_orientation(void); + + virtual void init(void); + virtual void update(void); + virtual void die(void); +}; + +#include "orientedParticle.I" + +#endif // ORIENTEDPARTICLE_H diff --git a/panda/src/particlesystem/orientedParticleFactory.I b/panda/src/particlesystem/orientedParticleFactory.I new file mode 100644 index 0000000000..a73d3973d6 --- /dev/null +++ b/panda/src/particlesystem/orientedParticleFactory.I @@ -0,0 +1,40 @@ +// Filename: orientedParticleFactory.I +// Created by: charles (16Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_initial_orientation +// Access : public +//////////////////////////////////////////////////////////////////// +void OrientedParticleFactory:: +set_initial_orientation(const LOrientationf &o) { + _initial_orientation = o; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_final_orientation +// Access : public +//////////////////////////////////////////////////////////////////// +void OrientedParticleFactory:: +set_final_orientation(const LOrientationf &o) { + _final_orientation = o; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_initial_orientation +// Access : public +//////////////////////////////////////////////////////////////////// +LOrientationf OrientedParticleFactory:: +get_initial_orientation(void) const { + return _initial_orientation; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_final_orientation +// Access : public +//////////////////////////////////////////////////////////////////// +LOrientationf OrientedParticleFactory:: +get_final_orientation(void) const { + return _final_orientation; +} diff --git a/panda/src/particlesystem/orientedParticleFactory.cxx b/panda/src/particlesystem/orientedParticleFactory.cxx new file mode 100644 index 0000000000..e4e0a9ad17 --- /dev/null +++ b/panda/src/particlesystem/orientedParticleFactory.cxx @@ -0,0 +1,58 @@ +// Filename: orientedParticleFactory.cxx +// Created by: charles (05Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "orientedParticleFactory.h" +#include "orientedParticle.h" + +//////////////////////////////////////////////////////////////////// +// Function : OrientedParticleFactory +// Access : Public +// Description : Constructor +//////////////////////////////////////////////////////////////////// +OrientedParticleFactory:: +OrientedParticleFactory(void) : + BaseParticleFactory() { +} + +//////////////////////////////////////////////////////////////////// +// Function : OrientedParticleFactory +// Access : Public +// Description : copy Constructor +//////////////////////////////////////////////////////////////////// +OrientedParticleFactory:: +OrientedParticleFactory(const OrientedParticleFactory ©) : + BaseParticleFactory(copy) { + _initial_orientation = copy._initial_orientation; + _final_orientation = copy._final_orientation; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~OrientedParticleFactory +// Access : public +// Description : destructor +//////////////////////////////////////////////////////////////////// +OrientedParticleFactory:: +~OrientedParticleFactory(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : populate_child_particle +// Access : private +// Description : child spawn +//////////////////////////////////////////////////////////////////// +void OrientedParticleFactory:: +populate_child_particle(BaseParticle *bp) const { + bp->set_orientation(_initial_orientation); +} + +//////////////////////////////////////////////////////////////////// +// Function : alloc_particle +// Access : public +// Description : child particle generation function +//////////////////////////////////////////////////////////////////// +BaseParticle *OrientedParticleFactory:: +alloc_particle(void) const { + return new OrientedParticle; +} diff --git a/panda/src/particlesystem/orientedParticleFactory.h b/panda/src/particlesystem/orientedParticleFactory.h new file mode 100644 index 0000000000..0f6648da03 --- /dev/null +++ b/panda/src/particlesystem/orientedParticleFactory.h @@ -0,0 +1,39 @@ +// Filename: orientedParticleFactory.h +// Created by: charles (05Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ORIENTEDPARTICLEFACTORY_H +#define ORIENTEDPARTICLEFACTORY_H + +#include "baseParticleFactory.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : OrientedParticleFactory +// Description : Creates particles that are affected by angular +// forces. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS OrientedParticleFactory : public BaseParticleFactory { +private: + virtual void populate_child_particle(BaseParticle *bp) const; + virtual BaseParticle *alloc_particle(void) const; + + LOrientationf _initial_orientation; + LOrientationf _final_orientation; + +public: + OrientedParticleFactory(void); + OrientedParticleFactory(const OrientedParticleFactory ©); + virtual ~OrientedParticleFactory(void); + + INLINE void set_initial_orientation(const LOrientationf &o); + INLINE void set_final_orientation(const LOrientationf &o); + INLINE LOrientationf get_initial_orientation(void) const; + INLINE LOrientationf get_final_orientation(void) const; +}; + +#include "orientedParticleFactory.I" + +#endif // ORIENTEDPARTICLEFACTORY_H diff --git a/panda/src/particlesystem/particleSystem.I b/panda/src/particlesystem/particleSystem.I new file mode 100644 index 0000000000..207ce96034 --- /dev/null +++ b/panda/src/particlesystem/particleSystem.I @@ -0,0 +1,372 @@ +// Filename: particleSystem.I +// Created by: charles (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : render +// Access : Public +// Description : Populates an atteched GeomNode structure with the +// particle geometry for rendering. This is a +// wrapper for accessability. +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystem:: +render(void) { + _renderer->render(_physics_objects, _living_particles); +} + +//// /////////////////////////////////////////////////////// +//// SET METHODS /////////////////////////////////////////////////////// +//// /////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_pool_size +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystem:: +set_pool_size(int size) { + _particle_pool_size = size; + resize_pool(); +} + +//////////////////////////////////////////////////////////////////// +// Function : set_birth_rate +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystem:: +set_birth_rate(float new_br) { + _birth_rate = new_br; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_litter_size +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystem:: +set_litter_size(int new_ls) { + _litter_size = new_ls; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_litter_delta +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystem:: +set_litter_delta(int new_ld) { + _litter_delta = new_ld; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_renderer +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystem:: +set_renderer(BaseParticleRenderer *r) { + _renderer = r; + _renderer->resize_pool(_particle_pool_size); + + if (_render_arc.is_null() == false) + remove_arc(_render_arc); + + _render_arc = new RenderRelation(_render_parent, r->get_render_node()); +} + +//////////////////////////////////////////////////////////////////// +// Function : set_emitter +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystem:: +set_emitter(BaseParticleEmitter *e) { + _emitter = e; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_factory +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystem:: +set_factory(BaseParticleFactory *f) { + _factory = f; + clear_physics_objects(); + resize_pool(); +} + +//////////////////////////////////////////////////////////////////// +// Function : set_active_state +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystem:: +set_active_system_flag(bool a) { + _active_system_flag = a; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_local_velocity_flag +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystem:: +set_local_velocity_flag(bool lv) { + _local_velocity_flag = lv; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_spawn_on_death_flag +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystem:: +set_spawn_on_death_flag(bool sod) { + _spawn_on_death_flag = sod; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_system_grows_older_flag +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystem:: +set_system_grows_older_flag(bool sgo) { + _system_grows_older_flag = sgo; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_system_lifespan +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystem:: +set_system_lifespan(float sl) { + _system_lifespan = sl; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_system_age +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystem:: +set_system_age(float age) { + _system_age = age; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_spawn_render_node +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystem:: +set_spawn_render_node(Node *node) { + _spawn_render_node = node; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_render_parent +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystem:: +set_render_parent(Node *node) { + if (_render_arc.is_null() == false) { + remove_arc(_render_arc); + } + + _render_parent = node; + _render_arc = new RenderRelation(_render_parent, + _renderer->get_render_node()); +} + +//////////////////////////////////////////////////////////////////// +// Function : set_template_system_flag +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystem:: +set_template_system_flag(bool tsf) { + _template_system_flag = tsf; +} + +//////////////////////////////////////////////////////////////////// +// Function : add_spawn_template +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void ParticleSystem:: +add_spawn_template(ParticleSystem *ps) { + _spawn_templates.push_back(ps); +} + +//////////////////////////////////////////////////////////////////// +// Function : clear_spawn_templates +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void ParticleSystem:: +clear_spawn_templates(void) { + _spawn_templates.erase(_spawn_templates.begin(), + _spawn_templates.end()); +} + +//// ///////////////////////////////////////////////////// +//// GET METHODS ///////////////////////////////////////////////////// +//// ///////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : get_pool_size +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE int ParticleSystem:: +get_pool_size(void) const { + return _particle_pool_size; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_birth_rate +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE float ParticleSystem:: +get_birth_rate(void) const { + return _birth_rate; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_litter_size +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE int ParticleSystem:: +get_litter_size(void) const { + return _litter_size; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_litter_delta +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE int ParticleSystem:: +get_litter_delta(void) const { + return _litter_delta; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_renderer +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE BaseParticleRenderer *ParticleSystem:: +get_renderer(void) const { + return _renderer; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_emitter +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE BaseParticleEmitter *ParticleSystem:: +get_emitter(void) const { + return _emitter; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_factory +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE BaseParticleFactory *ParticleSystem:: +get_factory(void) const { + return _factory; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_living_particles +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE int ParticleSystem:: +get_living_particles(void) const { + return _living_particles; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_active_state +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE bool ParticleSystem:: +get_active_system_flag(void) const { + return _active_system_flag; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_local_velocity_flag +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE bool ParticleSystem:: +get_local_velocity_flag(void) const { + return _local_velocity_flag; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_spawn_on_death_flag +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE bool ParticleSystem:: +get_spawn_on_death_flag(void) const { + return _spawn_on_death_flag; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_system_grows_older_flag +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE bool ParticleSystem:: +get_system_grows_older_flag(void) const { + return _system_grows_older_flag; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_system_lifespan +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE float ParticleSystem:: +get_system_lifespan(void) const { + return _system_lifespan; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_system_age +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE float ParticleSystem:: +get_system_age(void) const { + return _system_age; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_i_was_spawned_flag +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE bool ParticleSystem:: +get_i_was_spawned_flag(void) const { + return _i_was_spawned_flag; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_spawn_render_node +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE Node *ParticleSystem:: +get_spawn_render_node(void) const { + return _spawn_render_node; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_render_parent +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE Node *ParticleSystem:: +get_render_parent(void) const { + return _render_parent; +} diff --git a/panda/src/particlesystem/particleSystem.cxx b/panda/src/particlesystem/particleSystem.cxx new file mode 100644 index 0000000000..5cf7c5dc94 --- /dev/null +++ b/panda/src/particlesystem/particleSystem.cxx @@ -0,0 +1,394 @@ +// Filename: particleSystem.cxx +// Created by: charles (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config_particlesystem.h" +#include "particleSystem.h" +#include "particleSystemManager.h" +#include "pointParticleRenderer.h" +#include "pointParticleFactory.h" +#include "sphereSurfaceEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Function : ParticleSystem +// Access : Public +// Description : Default Constructor. +//////////////////////////////////////////////////////////////////// +ParticleSystem:: +ParticleSystem(int pool_size) : + _particle_pool_size(pool_size), Physical(pool_size, false) +{ + _birth_rate = 0.0f; + _litter_size = 0; + _litter_delta = 0; + _living_particles = 0; + _active_system_flag = true; + _local_velocity_flag = true; + _spawn_on_death_flag = false; + _system_grows_older_flag = false; + _system_lifespan = 0.0f; + _i_was_spawned_flag = false; + + // just in case someone tries to do something that requires the + // use of an emitter, renderer, or factory before they've actually + // assigned one. This is ok, because assigning them (set_renderer(), + // set_emitter(), etc...) forces them to set themselves up for the + // system, keeping the pool sizes consistent. + + _render_arc.clear(); + _render_parent = new NamedNode("ParticleSystem default render parent"); + + set_emitter(new SphereSurfaceEmitter); + set_renderer(new PointParticleRenderer); + set_factory(new PointParticleFactory); +} + +//////////////////////////////////////////////////////////////////// +// Function : ParticleSystem +// Access : Public +// Description : Copy Constructor. +//////////////////////////////////////////////////////////////////// +ParticleSystem:: +ParticleSystem(const ParticleSystem& copy) : + _system_age(0.0f), + _template_system_flag(false), + Physical(copy) { + + _particle_pool_size = copy._particle_pool_size; + _birth_rate = copy._birth_rate; + _litter_size = copy._litter_size; + _litter_delta = copy._litter_delta; + _active_system_flag = copy._active_system_flag; + _local_velocity_flag = copy._local_velocity_flag; + _spawn_on_death_flag = copy._spawn_on_death_flag; + _i_was_spawned_flag = copy._i_was_spawned_flag; + _system_grows_older_flag = copy._system_grows_older_flag; + _emitter = copy._emitter; + _renderer = copy._renderer->make_copy(); + _factory = copy._factory; + + _render_arc = copy._render_arc; + + _render_parent = copy._render_parent; + + _tics_since_birth = _birth_rate; + _system_lifespan = copy._system_lifespan; + _living_particles = 0; + + resize_pool(); +} + +//////////////////////////////////////////////////////////////////// +// Function : ~ParticleSystem +// Access : Public +// Description : You get the ankles and I'll get the wrists. +//////////////////////////////////////////////////////////////////// +ParticleSystem:: +~ParticleSystem(void) { + if (_template_system_flag == false) { + _renderer.clear(); + + if (_render_arc.is_null() == false) + remove_arc(_render_arc); + } + + if (_i_was_spawned_flag == true) + remove_arc(_physical_node_arc); +} + +//////////////////////////////////////////////////////////////////// +// Function : birth_particle +// Access : Private +// Description : A new particle is born. This doesn't allocate, +// resets an element from the particle pool. +//////////////////////////////////////////////////////////////////// +bool ParticleSystem:: +birth_particle(void) { + int pool_index; + float lifespan; + float mass, t; + + // cout << "ParticleSystem::birth_particle" << endl; + + // make sure there's room for a new particle + if (_living_particles == _particle_pool_size) + return false; + + pool_index = _free_particle_fifo.back(); + _free_particle_fifo.pop_back(); + + // get a handle on our particle. + BaseParticle *bp = (BaseParticle *) _physics_objects[pool_index].p(); + + // start filling out the variables. + _factory->populate_particle(bp); + + bp->set_alive(true); + bp->set_active(true); + bp->init(); + + // get the location of the new particle. + LPoint3f new_pos, world_pos; + LVector3f new_vel; + LMatrix4f birth_to_render_xform; + GeomNode *render_node; + + _emitter->generate(new_pos, new_vel); + render_node = _renderer->get_render_node(); + + // go from birth space to render space + get_rel_mat(get_physical_node(), render_node, birth_to_render_xform); + world_pos = new_pos * birth_to_render_xform; + + // cout << "New particle at " << world_pos << endl; + + // possibly transform the initial velocity as well. + if (_local_velocity_flag == false) + new_vel = new_vel * birth_to_render_xform; + + bp->set_position(world_pos); + bp->set_velocity(new_vel); + + _living_particles++; + + // propogate information down to renderer + _renderer->birth_particle(pool_index); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function : birth_litter +// Access : Private +// Description : spawns a new batch of particles +//////////////////////////////////////////////////////////////////// +void ParticleSystem:: +birth_litter(void) { + int litter_size; + + litter_size = _litter_size; + + if (_litter_delta != 0) + litter_size += _litter_delta - (rand() % (2 * _litter_delta)); + + int i; + + for (i = 0; i < litter_size; i++) { + if (birth_particle() == false) + return; + } +} + +//////////////////////////////////////////////////////////////////// +// Function : spawn_child_system +// Access : private +// Description : Creates a new particle system based on local +// template info and adds it to the ps and physics +// managers +//////////////////////////////////////////////////////////////////// +void ParticleSystem:: +spawn_child_system(BaseParticle *bp) { + // first, make sure that the system exists in the graph via a + // physicalnode reference. + PhysicalNode *this_pn = get_physical_node(); + if (!this_pn) { + physics_cat.error() << "ParticleSystem::spawn_child_system: " + << "Spawning system is not in the scene graph," + << " aborting." << endl; + return; + } + + if (this_pn->get_num_parents(RenderRelation::get_class_type()) == 0) { + physics_cat.error() << "ParticleSystem::spawn_child_system: " + << "PhysicalNode this system is contained in " + << "has no parent, aborting." << endl; + return; + } + + NodeRelation *parent_relation = + this_pn->get_parent(RenderRelation::get_class_type(), 0); + + Node *parent = parent_relation->get_parent(); + + // handle the spawn templates + int new_ps_index = rand() % _spawn_templates.size(); + ParticleSystem *ps_template = _spawn_templates[new_ps_index]; + + // create a new particle system + PT(ParticleSystem) new_ps = new ParticleSystem(*ps_template); + new_ps->_i_was_spawned_flag = true; + + // first, set up the render node info. + new_ps->_render_parent = _spawn_render_node; + new_ps->_render_arc = new RenderRelation(new_ps->_render_parent, + new_ps->_renderer->get_render_node()); + + // now set up the new system's PhysicalNode. + PT(PhysicalNode) new_pn = new PhysicalNode; + new_pn->add_physical(new_ps); + + // the arc from the parent to the new child has to represent the + // transform from the current system up to its parent, and then + // subsequently down to the new child. + PT(RenderRelation) rr = new RenderRelation(parent, new_pn); + + LMatrix4f old_system_to_parent_xform; + get_rel_mat(get_physical_node(), parent, old_system_to_parent_xform); + + LMatrix4f child_space_xform = old_system_to_parent_xform * + bp->get_lcs(); + + rr->set_transition(new TransformTransition(child_space_xform)); + + // tack the new system onto the managers + _manager->attach_particlesystem(new_ps); + get_physics_manager()->attach_physical(new_ps); +} + +//////////////////////////////////////////////////////////////////// +// Function : kill_particle +// Access : Private +// Description : Kills a particle, returns its slot to the empty +// stack. +//////////////////////////////////////////////////////////////////// +void ParticleSystem:: +kill_particle(int pool_index) { + vector< PT(PhysicsObject) >::iterator cur; + + // get a handle on our particle + BaseParticle *bp = (BaseParticle *) _physics_objects[pool_index].p(); + + // create a new system where this one died, maybe. + if (_spawn_on_death_flag == true) + spawn_child_system(bp); + + // tell everyone that it's dead + bp->set_alive(false); + bp->set_active(false); + bp->die(); + + _free_particle_fifo.push_back(pool_index); + + // tell renderer + _renderer->kill_particle(pool_index); + _living_particles--; +} + +//////////////////////////////////////////////////////////////////// +// Function : resize_pool +// Access : Private +// Description : Resizes the particle pool according to _particle_pool_size +//////////////////////////////////////////////////////////////////// +void ParticleSystem:: +resize_pool(void) { + int i; + int delta = _particle_pool_size - _physics_objects.size(); + + if (_factory.is_null()) { + particlesystem_cat.error() << "ParticleSystem::resize_pool" + << " called with null _factory." << endl; + return; + } + + if (_renderer.is_null()) { + particlesystem_cat.error() << "ParticleSystem::resize_pool" + << " called with null _renderer." << endl; + + return; + } + + _renderer->resize_pool(_particle_pool_size); + + // disregard no change + if (delta == 0) + return; + + // update the pool + if (delta > 0) { + // add elements + for (i = 0; i < delta; i++) + { + int free_index = _physics_objects.size(); + + BaseParticle *new_particle = _factory->alloc_particle(); + _factory->populate_particle(new_particle); + + _physics_objects.push_back(new_particle); + _free_particle_fifo.push_front(free_index); + } + } + else { + // subtract elements + if (delta > _particle_pool_size) { + _physics_objects.erase(_physics_objects.begin(), _physics_objects.end()); + _free_particle_fifo.clear(); + } + else { + for (i = 0; i < delta; i++) { + _physics_objects.pop_back(); + if (_free_particle_fifo.empty() == false) + _free_particle_fifo.pop_back(); + } + } + } +} + +////////////////////////////////////////////////////////////////////// +// Function : update +// Access : Public +// Description : Updates the particle system. Call once per frame. +////////////////////////////////////////////////////////////////////// +void ParticleSystem:: +update(float dt) { + int ttl_updates_left = _living_particles; + int current_index = 0, index_counter = 0; + BaseParticle *bp; + bool done = false; + float age; + + // run through the particle array + while (!done) { + if (ttl_updates_left == 0) + break; + + current_index = index_counter; + index_counter++; + + // get the current particle. + bp = (BaseParticle *) _physics_objects[current_index].p(); + + if (bp->get_alive() == false) + continue; + + age = bp->get_age() + dt; + bp->set_age(age); + + if (age >= bp->get_lifespan()) + kill_particle(current_index); + else + bp->update(); + + // break out early if we're lucky + ttl_updates_left--; + } + + // generate new particles if necessary. + _tics_since_birth += dt; + + if (_tics_since_birth >= _birth_rate) { + birth_litter(); + _tics_since_birth = 0.0f; + } +} diff --git a/panda/src/particlesystem/particleSystem.h b/panda/src/particlesystem/particleSystem.h new file mode 100644 index 0000000000..68e73e7ca8 --- /dev/null +++ b/panda/src/particlesystem/particleSystem.h @@ -0,0 +1,153 @@ +// Filename: particleSystem.h +// Created by: charles (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARTICLESYSTEM_H +#define PARTICLESYSTEM_H + +#include +#include +#include +#include +#include +#include + +#include + +#include "baseParticle.h" +#include "baseParticleRenderer.h" +#include "baseParticleEmitter.h" +#include "baseParticleFactory.h" + +class ParticleSystemManager; + +//////////////////////////////////////////////////////////////////// +// Class : ParticleSystem +// Description : Contains and manages a particle system. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS ParticleSystem : public Physical { +private: + bool birth_particle(void); + void kill_particle(int pool_index); + void birth_litter(void); + void resize_pool(void); + + deque< int > _free_particle_fifo; + + int _particle_pool_size; + int _living_particles; + float _birth_rate; + float _tics_since_birth; + int _litter_size; + int _litter_delta; + float _system_age; + float _system_lifespan; + + PT(BaseParticleFactory) _factory; + PT(BaseParticleEmitter) _emitter; + PT(BaseParticleRenderer) _renderer; + ParticleSystemManager *_manager; + + bool _template_system_flag; + + // _render_parent is the ALREADY ALLOC'D node under which this + // system will render its particles. + + PT(Node) _render_parent; + PT(RenderRelation) _render_arc; + + bool _active_system_flag; + bool _local_velocity_flag; + bool _system_grows_older_flag; + + // information for systems that will spawn + + bool _spawn_on_death_flag; + PT(Node) _spawn_render_node; + vector< PT(ParticleSystem) > _spawn_templates; + + void spawn_child_system(BaseParticle *bp); + + // information for spawned systems + + bool _i_was_spawned_flag; + PT(RenderRelation) _physical_node_arc; + +public: + + // constructor/destructor + + ParticleSystem(int pool_size = 0); + ParticleSystem(const ParticleSystem& copy); + ~ParticleSystem(void); + + // access/queries + + INLINE void set_pool_size(int size); + INLINE int get_pool_size(void) const; + + INLINE void set_birth_rate(float new_br); + INLINE float get_birth_rate(void) const; + + INLINE void set_litter_size(int new_ls); + INLINE int get_litter_size(void) const; + + INLINE void set_litter_delta(int new_ld); + INLINE int get_litter_delta(void) const; + + INLINE void set_renderer(BaseParticleRenderer *r); + INLINE BaseParticleRenderer *get_renderer(void) const; + + INLINE void set_emitter(BaseParticleEmitter *e); + INLINE BaseParticleEmitter *get_emitter(void) const; + + INLINE void set_factory(BaseParticleFactory *f); + INLINE BaseParticleFactory *get_factory(void) const; + + INLINE void set_active_system_flag(bool a); + INLINE bool get_active_system_flag(void) const; + + INLINE void set_local_velocity_flag(bool lv); + INLINE bool get_local_velocity_flag(void) const; + + INLINE void set_spawn_on_death_flag(bool sod); + INLINE bool get_spawn_on_death_flag(void) const; + + INLINE void set_system_grows_older_flag(bool sgo); + INLINE bool get_system_grows_older_flag(void) const; + + INLINE void set_system_lifespan(float sl); + INLINE float get_system_lifespan(void) const; + + INLINE void set_system_age(float age); + INLINE float get_system_age(void) const; + + INLINE void set_spawn_render_node(Node *node); + INLINE Node *get_spawn_render_node(void) const; + + INLINE bool get_i_was_spawned_flag(void) const; + INLINE int get_living_particles(void) const; + + INLINE void set_template_system_flag(bool tsf); + + INLINE Node *get_render_parent(void) const; + INLINE void set_render_parent(Node *node); + + // particle template vector + + INLINE void add_spawn_template(ParticleSystem *ps); + INLINE void clear_spawn_templates(void); + + // methods + + INLINE void render(void); + void update(float dt); + + friend class ParticleSystemManager; +}; + +#include "particleSystem.I" + +#endif // PARTICLESYSTEM_H + diff --git a/panda/src/particlesystem/particleSystemManager.I b/panda/src/particlesystem/particleSystemManager.I new file mode 100644 index 0000000000..51731dd7b5 --- /dev/null +++ b/panda/src/particlesystem/particleSystemManager.I @@ -0,0 +1,45 @@ +// Filename: particleSystemManager.I +// Created by: charles (28Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_frame_stepping +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystemManager:: +set_frame_stepping(int every_nth_frame) { + _nth_frame = every_nth_frame; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_frame_stepping +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE int ParticleSystemManager:: +get_frame_stepping(void) const { + return _nth_frame; +} + +//////////////////////////////////////////////////////////////////// +// Function : attach_particlesystem +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystemManager:: +attach_particlesystem(ParticleSystem *ps) { + ps->_manager = this; + _ps_list.push_back(ps); +} + +//////////////////////////////////////////////////////////////////// +// Function : clear +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE void ParticleSystemManager:: +clear(void) { + _ps_list.erase(_ps_list.begin(), _ps_list.end()); +} diff --git a/panda/src/particlesystem/particleSystemManager.cxx b/panda/src/particlesystem/particleSystemManager.cxx new file mode 100644 index 0000000000..d2d8bd9511 --- /dev/null +++ b/panda/src/particlesystem/particleSystemManager.cxx @@ -0,0 +1,106 @@ +// Filename: particleSystemManager.cxx +// Created by: charles (28Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "particleSystemManager.h" +#include "particleSystem.h" + +#include +#include +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Function : ParticleSystemManager +// Access : public +// Description : default constructor +//////////////////////////////////////////////////////////////////// +ParticleSystemManager:: +ParticleSystemManager(int every_nth_frame) : + _nth_frame(every_nth_frame), _cur_frame(0) { +} + +//////////////////////////////////////////////////////////////////// +// Function : remove_particlesystem +// Access : public +// Description : removes a ps from the maintenance list +//////////////////////////////////////////////////////////////////// +void ParticleSystemManager:: +remove_particlesystem(ParticleSystem *ps) { + list< PT(ParticleSystem) >::iterator found; + + PT(ParticleSystem) ptps = ps; + found = find(_ps_list.begin(), _ps_list.end(), ptps); + + if (found == _ps_list.end()) + return; + + _ps_list.erase(found); +} + +//////////////////////////////////////////////////////////////////// +// Function : do_particles +// Access : public +// Description : does an update and render for each ps in the list. +// this is probably the one you want to use. Rendering +// is the expensive operation, and particles REALLY +// should at least be updated every frame, so nth_frame +// stepping applies only to rendering. +//////////////////////////////////////////////////////////////////// +void ParticleSystemManager:: +do_particles(float dt) { + // cout << "ParticlesystemManager::doparticles entering." << endl; + + list< PT(ParticleSystem) >::iterator cur; + + bool render_due = false; + + _cur_frame++; + + if (_cur_frame >= _nth_frame) { + _cur_frame = 0; + render_due = true; + } + + cur = _ps_list.begin(); + + // cout << "PSM::do_particles on a vector of size " << _ps_list.size() << endl; + int cs = 0; + + while (cur != _ps_list.end()) { + ParticleSystem *cur_ps = *cur; + + // update this system + if (cur_ps->get_active_system_flag() == true) { + // cout << " system " << cs++ << endl; + // cout << " count is: " << cur_ps->get_render_parent()->get_count() << endl; + cur_ps->update(dt); + + // handle age + if (cur_ps->get_system_grows_older_flag() == true) { + float age = cur_ps->get_system_age() + dt; + cur_ps->set_system_age(age); + + // handle death + if (age >= cur_ps->get_system_lifespan()) { + list< PT(ParticleSystem) >::iterator kill = cur; + cur++; + + _ps_list.erase(kill); + render_due = false; + } + } + + // handle render + if (render_due) { + cur_ps->render(); + } + } + + cur++; + } + // cout << "PSM::do_particles finished." << endl; + // cout << "ParticleSystemManager::doparticles exiting." << endl; +} diff --git a/panda/src/particlesystem/particleSystemManager.h b/panda/src/particlesystem/particleSystemManager.h new file mode 100644 index 0000000000..a70af3f0ef --- /dev/null +++ b/panda/src/particlesystem/particleSystemManager.h @@ -0,0 +1,45 @@ +// Filename: particleSystemManager.h +// Created by: charles (28Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARTICLESYSTEMMANAGER_H +#define PARTICLESYSTEMMANAGER_H + +#include +#include + +#include "particleSystem.h" + +//////////////////////////////////////////////////////////////////// +// Class : ParticleSystemManager +// Description : Manages a set of individual ParticleSystem objects, +// so that each individual one doesn't have to be +// updated and rendered every frame +//////////////////////////////////////////////////////////////////// + +class EXPCL_PANDAPHYSICS ParticleSystemManager { +private: + + list< PT(ParticleSystem) > _ps_list; + + int _nth_frame; + int _cur_frame; + +public: + + ParticleSystemManager(int every_nth_frame = 1); + + INLINE void set_frame_stepping(int every_nth_frame); + INLINE int get_frame_stepping(void) const; + + INLINE void attach_particlesystem(ParticleSystem *ps); + void remove_particlesystem(ParticleSystem *ps); + INLINE void clear(void); + + void do_particles(float dt); +}; + +#include "particleSystemManager.I" + +#endif // PARTICLESYSTEMMANAGER_H diff --git a/panda/src/particlesystem/particlefactories.h b/panda/src/particlesystem/particlefactories.h new file mode 100644 index 0000000000..a062ca2462 --- /dev/null +++ b/panda/src/particlesystem/particlefactories.h @@ -0,0 +1,13 @@ +// Filename: particlefactories.h +// Created by: charles (05Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARTICLEFACTORIES_H +#define PARTICLEFACTORIES_H + +#include "pointParticleFactory.h" +#include "orientedParticleFactory.h" +#include "zSpinParticleFactory.h" + +#endif // PARTICLEFACTORIES_H diff --git a/panda/src/particlesystem/particles.h b/panda/src/particlesystem/particles.h new file mode 100644 index 0000000000..4bf53b6640 --- /dev/null +++ b/panda/src/particlesystem/particles.h @@ -0,0 +1,12 @@ +// Filename: particles.h +// Created by: charles (20Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARTICLES_H +#define PARTICLES_H + +#include "pointParticle.h" +#include "orientedParticle.h" + +#endif // PARTICLES_H diff --git a/panda/src/particlesystem/particlesystems.txt b/panda/src/particlesystem/particlesystems.txt new file mode 100644 index 0000000000..e0cc8afcbf --- /dev/null +++ b/panda/src/particlesystem/particlesystems.txt @@ -0,0 +1,158 @@ +// Panda Particle System Documentation +// Charles Nicholson, Intern-at-large. 7/17/00 + + +------------------- +NOTE: +------------------- + +Oriented particles are not yet a good idea. Stick to point particles. + + +Particle Systems: +----------------- + +Panda3d has particle system support, allowing users to set up dynamic +particle systems for special effects. Link with libparticlesystem.so +and include the necessary/appropriate headers (detailed below) + +The particle system layout is as follows: + +---------------------------------------------------------------------- +Classes: +---------------------------------------------------------------------- + +ParticleSystem- This is the big one you want to make/use. + All of the pluggable/swappable components are + stored and managed in here. + +ParticleSystemManager- This manages all particle systems and should be + updated once per frame in the data-flow graph. + +BaseParticleRenderer- pure virtual abstract base class for the pluggable + renderers + +PointParticleRenderer- Renderer for drawing particles as point primitives. + +GeomParticleRenderer- Renderer for drawing particles as geometric instances + in the scene graph + +SparkleParticleRenderer- Renderer for drawing particles as sparkles + (axial lines, looking better than they sound). + +SpriteParticleRenderer- Renderer for drawing particles as sprites + (uses panda Textures). + +BaseParticleEmitter- pure virtual abstract base class for the pluggable + emitters + +PointEmitter- point particle emitter (0d) +LineEmitter- line particle emitter (1d) +RectangleEmitter- rectangle emitter (2d) +BoxEmitter- rectangular prism emitter (3d) +RingEmitter- circle emitter (2d) +DiscEmitter- disc emitter (2d) +SphereVolumeEmitter- Sphere emitter (3d) +SphereSurfaceEmitter- Sphere emitter (3d) + +BaseParticleFactory- pure virtual abstract base class for the pluggable + factories + +PointParticleFactory- factory for point particles (orientation-independent) + +OrientedParticleFactory- factory for oriented particles (angular + velocity and orientation) + +---------------------------------------------------------------------- +Usage: +---------------------------------------------------------------------- + +To function properly, a particle system must have 'plugged in' to it +3 things: a renderer, and emitter, and a factory. + +The factory is responsible for creating typed particles and assigning +lifespan and orientation. + +The emitter is responsible for generating an initial position and +velocity to each particle. + +The renderer is responsible for taking a set of particles and inserting +them properly into the scene graph. + +All of these are reference-counted, so it's ok to assign and forget. + +Particle systems REALLY should be attached to a ParticleSystemManager. +The ParticleSystem/ParticleSystemManager setup is analagous to the +Physical/PhysicsManager setup with the exception of the fact that there +really isn't a need (as far as i've seen, anyway) to have more than one +ParticleSystemManager at all. Simply attach all of your systems to the +manager, call update on the manager with the elapsed time each frame, and +you should be happy. + +Particle systems can grow older and die if you want them too, the manager +is responsible for aging and killing systems. + +Inserting a particle system into the scene graph can be a little tricky, +as there are two nodes that each system interacts with. Each system needs +to have assigned to it a birth parent node and a render parent node. + +The birth parent node, accessed with set/get_birth_node, is the node that +the system looks to when transforming a newly-born particle into world-space. +If i wanted to attach a particle system to jafar's hands or herc's head, for +instance, i would call set_birth_node on the appropriate node. This only +needs to be set once, but take care that the node doesn't get destroyed (BAD +things happen...). + +The render parent node, accessed with set/get_render_parent_node, is the +node under which the currently plugged-in renderer will display its contents. +If you want your particles rendered into the world with global coordinates and +no transforms, call set with 'render'. Otherwise, each particle will undergo +the current set of transforms in the scene graph before being rendered. + +One functionality of particle systems is their ability to, if specified, +spawn entirely new particle systems on the death of each particle. This +is a useful setup to create effects like fireworks or sparks. These spawning +systems need a little extra information, and take a little more work, but +are pretty cool. You'll probably want your spawned systems to die of age +(as opposed to explicit kill), so be sure to set your template system with +the system_grows_older flag as true, but you might not. + +Things you need to do (note here that the word template is overloaded, i +don't mean C++ templates but the particle system templates, instances +of which will be allocated at particle death time): + 1. Create your template system(s, multiple systems are supported and + picked at random when it becomes time to create a new system). This + entails creating a unique renderer for each one. Factories and emitters + can be shared if you want, this is fine. + 2. call set_template_system_flag(true) on each template system. + 3. call add_spawn_template on your spawnING system once for each template. + 4. call set_spawn_on_death_flag on your spawnING system. + 5. call set_spawn_render_node on your spawnING system. This call is VERY + important, as it explicitly specifies the node under which each newly- + spawned system will render (as you can't tell each spawnED system where + to render, the parent system needs to know this information...). + +The particle system that spawnS the new systems will attach them to the +ParticleSystemManager that it is currently attached to, and processing and +rendering for the new systems begins on the current frame. + +---------------------------------------------------------------------- +Files: +---------------------------------------------------------------------- + +If you need a particle system, include . + +If you need an emitter, include ___Emitter.h, where ___ is one of: + point, line, box, rectangle, ring, disc, sphereVolume, sphereSurface + +If you want all of the emitters, include + +If you need a renderer, include ___ParticleRenderer.h, where ___ is one of: + point, geom, sparkle, sprite + +If you need a factory, include ___ParticleFactory.h, where ___ is one of: + point, oriented + +If you want all of the particles, include + +-charles nicholson diff --git a/panda/src/particlesystem/pointEmitter.I b/panda/src/particlesystem/pointEmitter.I new file mode 100644 index 0000000000..b1425946ad --- /dev/null +++ b/panda/src/particlesystem/pointEmitter.I @@ -0,0 +1,24 @@ +// Filename: pointEmitter.I +// Created by: charles (26Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_point +// Access : public +// Description : point setting +//////////////////////////////////////////////////////////////////// +INLINE void PointEmitter:: +set_point(const LPoint3f& p) { + _point = p; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_launch_vec +// Access : public +// Description : launch_vec setting +//////////////////////////////////////////////////////////////////// +INLINE void PointEmitter:: +set_launch_vec(const LVector3f &v) { + _launch_vec = v; +} diff --git a/panda/src/particlesystem/pointEmitter.cxx b/panda/src/particlesystem/pointEmitter.cxx new file mode 100644 index 0000000000..beb1e3392a --- /dev/null +++ b/panda/src/particlesystem/pointEmitter.cxx @@ -0,0 +1,61 @@ +// Filename: pointEmitter.cxx +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pointEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Function : PointEmitter +// Access : Public +// Description : constructor +//////////////////////////////////////////////////////////////////// +PointEmitter:: +PointEmitter(void) : + BaseParticleEmitter() { + _point.set(0.0f, 0.0f, 0.0f); + _launch_vec.set(0, 0, 0); +} + +//////////////////////////////////////////////////////////////////// +// Function : PointEmitter +// Access : Public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +PointEmitter:: +PointEmitter(const PointEmitter ©) : + BaseParticleEmitter(copy) { + _point = copy._point; + _launch_vec = copy._launch_vec; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~PointEmitter +// Access : Public +// Description : destructor +//////////////////////////////////////////////////////////////////// +PointEmitter:: +~PointEmitter(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : Public +// Description : copier +//////////////////////////////////////////////////////////////////// +BaseParticleEmitter *PointEmitter:: +make_copy(void) { + return new PointEmitter(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : PointEmitter::create_particle_location +// Access : Public +// Description : Generates a location on the point +//////////////////////////////////////////////////////////////////// +void PointEmitter:: +assign_initial_values(LPoint3f& pos, LVector3f& vel) +{ + pos = _point; + vel = _launch_vec; +} diff --git a/panda/src/particlesystem/pointEmitter.h b/panda/src/particlesystem/pointEmitter.h new file mode 100644 index 0000000000..725e626ac8 --- /dev/null +++ b/panda/src/particlesystem/pointEmitter.h @@ -0,0 +1,36 @@ +// Filename: pointEmitter.h +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef POINTEMITTER_H +#define POINTEMITTER_H + +#include "baseParticleEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Class : PointEmitter +// Description : Describes a planar ring region in which +// particles are generated. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS PointEmitter : public BaseParticleEmitter { +private: + LPoint3f _point; + LVector3f _launch_vec; + + virtual void assign_initial_values(LPoint3f& pos, LVector3f& vel); + +public: + PointEmitter(void); + PointEmitter(const PointEmitter ©); + virtual ~PointEmitter(void); + + virtual BaseParticleEmitter *make_copy(void); + + INLINE void set_point(const LPoint3f& p); + INLINE void set_launch_vec(const LVector3f &v); +}; + +#include "pointEmitter.I" + +#endif // POINTEMITTER_H diff --git a/panda/src/particlesystem/pointParticle.cxx b/panda/src/particlesystem/pointParticle.cxx new file mode 100644 index 0000000000..d1258922ab --- /dev/null +++ b/panda/src/particlesystem/pointParticle.cxx @@ -0,0 +1,74 @@ +// Filename: pointParticle.cxx +// Created by: charles (19Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pointParticle.h" + +//////////////////////////////////////////////////////////////////// +// Function : PointParticle +// Access : Public +// Description : simple constructor +//////////////////////////////////////////////////////////////////// +PointParticle:: +PointParticle(int lifespan, bool alive) : + BaseParticle(lifespan, alive) { + set_oriented(false); +} + +//////////////////////////////////////////////////////////////////// +// Function : PointParticle +// Access : Public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +PointParticle:: +PointParticle(const PointParticle ©) : + BaseParticle(copy) { + set_oriented(false); +} + +//////////////////////////////////////////////////////////////////// +// Function : ~PointParticle +// Access : Public +// Description : simple destructor +//////////////////////////////////////////////////////////////////// +PointParticle:: +~PointParticle(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : Public +// Description : dynamic copier +//////////////////////////////////////////////////////////////////// +PhysicsObject *PointParticle:: +make_copy(void) const { + return new PointParticle(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : die +// Access : Public +// Description : particle death routine +//////////////////////////////////////////////////////////////////// +void PointParticle:: +die(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : init +// Access : Public +// Description : particle init routine +//////////////////////////////////////////////////////////////////// +void PointParticle:: +init(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : update +// Access : Public +// Description : particle update +//////////////////////////////////////////////////////////////////// +void PointParticle:: +update(void) { +} diff --git a/panda/src/particlesystem/pointParticle.h b/panda/src/particlesystem/pointParticle.h new file mode 100644 index 0000000000..ebdc1a98b1 --- /dev/null +++ b/panda/src/particlesystem/pointParticle.h @@ -0,0 +1,29 @@ +// Filename: pointParticle.h +// Created by: charles (19Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef POINTPARTICLE_H +#define POINTPARTICLE_H + +#include "baseParticle.h" + +//////////////////////////////////////////////////////////////////// +// Class : PointParticle +// Description : Describes a particle that requires representation +// by a point (pixel, sparkle, billboard) +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS PointParticle : public BaseParticle { +public: + PointParticle(int lifespan = 0, bool alive = false); + PointParticle(const PointParticle ©); + virtual ~PointParticle(void); + + virtual void init(void); + virtual void die(void); + virtual void update(void); + + virtual PhysicsObject *make_copy(void) const; +}; + +#endif // POINTPARTICLE_H diff --git a/panda/src/particlesystem/pointParticleFactory.cxx b/panda/src/particlesystem/pointParticleFactory.cxx new file mode 100644 index 0000000000..c9e285128b --- /dev/null +++ b/panda/src/particlesystem/pointParticleFactory.cxx @@ -0,0 +1,58 @@ +// Filename: pointParticleFactory.cxx +// Created by: charles (05Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "pointParticleFactory.h" +#include "pointParticle.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function : PointParticleFactory +// Access : public +// Description : default constructor +//////////////////////////////////////////////////////////////////// +PointParticleFactory:: +PointParticleFactory(void) : + BaseParticleFactory() { +} + +//////////////////////////////////////////////////////////////////// +// Function : PointParticleFactory +// Access : public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +PointParticleFactory:: +PointParticleFactory(const PointParticleFactory ©) : + BaseParticleFactory(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function : ~PointParticleFactory +// Access : public +// Description : destructor +//////////////////////////////////////////////////////////////////// +PointParticleFactory:: +~PointParticleFactory(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : populate_child_particle +// Access : public +// Description : child particle generation function +//////////////////////////////////////////////////////////////////// +void PointParticleFactory:: +populate_child_particle(BaseParticle *bp) const { + bp->set_oriented(false); +} + +//////////////////////////////////////////////////////////////////// +// Function : alloc_particle +// Access : public +// Description : child particle generation function +//////////////////////////////////////////////////////////////////// +BaseParticle *PointParticleFactory:: +alloc_particle(void) const { + return new PointParticle; +} diff --git a/panda/src/particlesystem/pointParticleFactory.h b/panda/src/particlesystem/pointParticleFactory.h new file mode 100644 index 0000000000..da1dc05ff3 --- /dev/null +++ b/panda/src/particlesystem/pointParticleFactory.h @@ -0,0 +1,27 @@ +// Filename: pointParticleFactory.h +// Created by: charles (05Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef POINTPARTICLEFACTORY_H +#define POINTPARTICLEFACTORY_H + +#include "baseParticleFactory.h" + +//////////////////////////////////////////////////////////////////// +// Class : PointParticleFactory +// Description : Creates point particles to user specs +//////////////////////////////////////////////////////////////////// + +class EXPCL_PANDAPHYSICS PointParticleFactory : public BaseParticleFactory { +private: + virtual BaseParticle *alloc_particle(void) const; + virtual void populate_child_particle(BaseParticle *bp) const; + +public: + PointParticleFactory(void); + PointParticleFactory(const PointParticleFactory ©); + virtual ~PointParticleFactory(void); +}; + +#endif // POINTPARTICLEFACTORY_H diff --git a/panda/src/particlesystem/pointParticleRenderer.I b/panda/src/particlesystem/pointParticleRenderer.I new file mode 100644 index 0000000000..404cefbce1 --- /dev/null +++ b/panda/src/particlesystem/pointParticleRenderer.I @@ -0,0 +1,105 @@ +// Filename: pointParticleRenderer.I +// Created by: charles (20Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_point_size +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE void PointParticleRenderer:: +set_point_size(float point_size) { + _point_primitive->set_size(point_size); + _point_size = point_size; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_color1 +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE void PointParticleRenderer:: +set_color1(const Colorf& c1) { + _color1 = c1; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_color2 +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE void PointParticleRenderer:: +set_color2(const Colorf& c2) { + _color2 = c2; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_blend_type +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE void PointParticleRenderer:: +set_blend_type(PointParticleBlendType bt) { + _blend_type = bt; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_blend_method +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE void PointParticleRenderer:: +set_blend_method(ParticleRendererBlendMethod bm) { + _blend_method = bm; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_point_size +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE float PointParticleRenderer:: +get_point_size(void) const { + return _point_size; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_color1 +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE const Colorf& PointParticleRenderer:: +get_color1(void) const { + return _color1; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_color2 +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE const Colorf& PointParticleRenderer:: +get_color2(void) const { + return _color2; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_blend_type +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE PointParticleBlendType PointParticleRenderer:: +get_blend_type(void) const { + return _blend_type; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_blend_method +// Access : Public +//////////////////////////////////////////////////////////////////// + +INLINE ParticleRendererBlendMethod PointParticleRenderer:: +get_blend_method(void) const { + return _blend_method; +} diff --git a/panda/src/particlesystem/pointParticleRenderer.cxx b/panda/src/particlesystem/pointParticleRenderer.cxx new file mode 100644 index 0000000000..ca6093406c --- /dev/null +++ b/panda/src/particlesystem/pointParticleRenderer.cxx @@ -0,0 +1,262 @@ +// Filename: pointParticleRenderer.cxx +// Created by: charles (20Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include "pointParticleRenderer.h" + +//////////////////////////////////////////////////////////////////// +// Function : PointParticleRenderer +// Access : Public +// Description : special constructor +//////////////////////////////////////////////////////////////////// + +PointParticleRenderer:: +PointParticleRenderer(ParticleRendererAlphaDecay ad, + float point_size, + PointParticleBlendType bt, + ParticleRendererBlendMethod bm, + const Colorf& c1, const Colorf& c2) : + _point_size(point_size), + _blend_type(bt), _blend_method(bm), + _color1(c1), _color2(c2), + BaseParticleRenderer(ad) { + + _point_primitive = new GeomPoint; + init_geoms(); +} + +//////////////////////////////////////////////////////////////////// +// Function : PointParticleRenderer +// Access : Public +// Description : Copy constructor +//////////////////////////////////////////////////////////////////// + +PointParticleRenderer:: +PointParticleRenderer(const PointParticleRenderer& copy) : + _max_pool_size(0), + BaseParticleRenderer(copy) { + + _blend_type = copy._blend_type; + _blend_method = copy._blend_method; + _color1 = copy._color1; + _color2 = copy._color2; + _point_primitive = new GeomPoint; + init_geoms(); +} + +//////////////////////////////////////////////////////////////////// +// Function : ~PointParticleRenderer +// Access : Public +// Description : Simple destructor +//////////////////////////////////////////////////////////////////// + +PointParticleRenderer:: +~PointParticleRenderer(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : Public +// Description : for spawning systems from dead particles +//////////////////////////////////////////////////////////////////// + +BaseParticleRenderer *PointParticleRenderer:: +make_copy(void) { + return new PointParticleRenderer(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : resize_pool +// Access : Public +// Description : reallocate the space for the vertex and color +// pools +//////////////////////////////////////////////////////////////////// + +void PointParticleRenderer:: +resize_pool(int new_size) { + if (new_size == _max_pool_size) + return; + + _max_pool_size = new_size; + + _vertex_array = PTA_Vertexf(new_size); + _color_array = PTA_Colorf(new_size); + + _point_primitive->set_coords(_vertex_array, G_PER_VERTEX); + _point_primitive->set_colors(_color_array, G_PER_VERTEX); +} + +//////////////////////////////////////////////////////////////////// +// Function : init_geoms +// Access : Private +// Description : On-construction initialization +//////////////////////////////////////////////////////////////////// + +void PointParticleRenderer:: +init_geoms(void) { + + _point_primitive->set_num_prims(0); + _point_primitive->set_size(_point_size); + + _interface_node->clear(); + _interface_node->add_geom(_point_primitive); +} + +//////////////////////////////////////////////////////////////////// +// Function : birth_particle +// Access : Private, virtual +// Description : child birth +//////////////////////////////////////////////////////////////////// + +void PointParticleRenderer:: +birth_particle(int) { +} + +//////////////////////////////////////////////////////////////////// +// Function : kill_particle +// Access : Private, virtual +// Description : child kill +//////////////////////////////////////////////////////////////////// + +void PointParticleRenderer:: +kill_particle(int) { +} + +//////////////////////////////////////////////////////////////////// +// Function : create_color +// Access : Private +// Description : Generates the point color based on the render_type +//////////////////////////////////////////////////////////////////// + +Colorf PointParticleRenderer:: +create_color(const BaseParticle *p) { + Colorf color; + float life_t, vel_t, alpha_linear_t; + bool have_alpha_t = false; + + switch (_blend_type) { + + //// Constant solid color + + case PP_ONE_COLOR: + color = _color1; + break; + + //// Blending colors based on life + + case PP_BLEND_LIFE: + life_t = p->get_parameterized_age(); + alpha_linear_t = life_t; + have_alpha_t = true; + + if (_blend_method == PP_BLEND_CUBIC) + life_t = cubic_smooth(life_t); + + color = color_lerp(life_t, _color1, _color2); + + break; + + //// Blending colors based on vel + + case PP_BLEND_VEL: + vel_t = p->get_parameterized_vel(); + + if (_blend_method == PP_BLEND_CUBIC) + vel_t = cubic_smooth(vel_t); + + color = color_lerp(vel_t, _color1, _color2); + + break; + } + + // handle alpha channel + + if (!((_alpha_decay == PR_NO_ALPHA) || (_alpha_decay == PR_ALPHA_USER) || + (_alpha_decay == PR_ALPHA_INVALID))) { + if (have_alpha_t == false) + alpha_linear_t = p->get_parameterized_age(); + + if (_alpha_decay == PR_ALPHA_OUT) + color[3] = 1.0f - alpha_linear_t; + else + color[3] = alpha_linear_t; + } + + return color; +} + +//////////////////////////////////////////////////////////////////// +// Function : render +// Access : Public +// Description : renders the particle system out to a GeomNode +//////////////////////////////////////////////////////////////////// + +void PointParticleRenderer:: +render(vector< PT(PhysicsObject) >& po_vector, int ttl_particles) { + + BaseParticle *cur_particle; + + int cur_index = 0; + int remaining_particles = ttl_particles; + int i; + + Vertexf *cur_vert = &_vertex_array[0]; + Colorf *cur_color = &_color_array[0]; + + // init the aabb + + _aabb_min.set(99999.0f, 99999.0f, 99999.0f); + _aabb_max.set(-99999.0f, -99999.0f, -99999.0f); + + // run through every filled slot + + for (i = 0; i < po_vector.size(); i++) { + cur_particle = (BaseParticle *) po_vector[i].p(); + + if (cur_particle->get_alive() == false) + continue; + + // x aabb adjust + + if (cur_particle->get_position().get_x() > _aabb_max.get_x()) + _aabb_max[0] = cur_particle->get_position().get_x(); + else if (cur_particle->get_position().get_x() < _aabb_min.get_x()) + _aabb_min[0] = cur_particle->get_position().get_x(); + + // y aabb adjust + + if (cur_particle->get_position().get_y() > _aabb_max.get_y()) + _aabb_max[1] = cur_particle->get_position().get_y(); + else if (cur_particle->get_position().get_y() < _aabb_min.get_y()) + _aabb_min[1] = cur_particle->get_position().get_y(); + + // z aabb adjust + + if (cur_particle->get_position().get_z() > _aabb_max.get_z()) + _aabb_max[2] = cur_particle->get_position().get_z(); + else if (cur_particle->get_position().get_z() < _aabb_min.get_z()) + _aabb_min[2] = cur_particle->get_position().get_z(); + + // stuff it into the arrays + + *cur_vert++ = cur_particle->get_position(); + *cur_color++ = create_color(cur_particle); + + // maybe jump out early? + + remaining_particles--; + if (remaining_particles == 0) + break; + } + + _point_primitive->set_num_prims(ttl_particles); + + // done filling geompoint node, now do the bb stuff + + LPoint3f aabb_center = _aabb_min + ((_aabb_max - _aabb_min) * 0.5f); + float radius = (aabb_center - _aabb_min).length(); + + _interface_node->set_bound(BoundingSphere(aabb_center, radius)); +} diff --git a/panda/src/particlesystem/pointParticleRenderer.h b/panda/src/particlesystem/pointParticleRenderer.h new file mode 100644 index 0000000000..14c798c00f --- /dev/null +++ b/panda/src/particlesystem/pointParticleRenderer.h @@ -0,0 +1,87 @@ +// Filename: pointParticleRenderer.h +// Created by: charles (20Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef POINTPARTICLERENDERER_H +#define POINTPARTICLERENDERER_H + +#include "baseParticleRenderer.h" +#include "baseParticle.h" + +#include +#include +#include +#include +#include + +enum PointParticleBlendType { + PP_ONE_COLOR, + PP_BLEND_LIFE, + PP_BLEND_VEL +}; + +//////////////////////////////////////////////////////////////////// +// Class : PointParticleRenderer +// Description : Simple point/point particle renderer. Does NOT +// handle billboards- use BillboardParticleRenderer +// for that. +//////////////////////////////////////////////////////////////////// + +class EXPCL_PANDAPHYSICS PointParticleRenderer : public BaseParticleRenderer { +private: + + Colorf _color1, _color2; + float _point_size; + + PT(GeomPoint) _point_primitive; + + PTA_Vertexf _vertex_array; + PTA_Colorf _color_array; + + int _max_pool_size; + + PointParticleBlendType _blend_type; + ParticleRendererBlendMethod _blend_method; + + LPoint3f _aabb_min, _aabb_max; + + Colorf create_color(const BaseParticle *p); + + virtual void birth_particle(int index); + virtual void kill_particle(int index); + virtual void init_geoms(void); + virtual void render(vector< PT(PhysicsObject) >& po_vector, + int ttl_particles); + virtual void resize_pool(int new_size); + +public: + + PointParticleRenderer(const PointParticleRenderer& copy); + PointParticleRenderer(ParticleRendererAlphaDecay ad = PR_NO_ALPHA, + float point_size = 1.0f, + PointParticleBlendType bt = PP_ONE_COLOR, + ParticleRendererBlendMethod bm = PP_NO_BLEND, + const Colorf& c1 = Colorf(1.0f, 1.0f, 1.0f, 1.0f), + const Colorf& c2 = Colorf(1.0f, 1.0f, 1.0f, 1.0f)); + + virtual ~PointParticleRenderer(void); + + virtual BaseParticleRenderer *make_copy(void); + + INLINE void set_point_size(float point_size); + INLINE void set_color1(const Colorf& c1); + INLINE void set_color2(const Colorf& c2); + INLINE void set_blend_type(PointParticleBlendType bt); + INLINE void set_blend_method(ParticleRendererBlendMethod bm); + + INLINE float get_point_size(void) const; + INLINE const Colorf& get_color1(void) const; + INLINE const Colorf& get_color2(void) const; + INLINE PointParticleBlendType get_blend_type(void) const; + INLINE ParticleRendererBlendMethod get_blend_method(void) const; +}; + +#include "pointParticleRenderer.I" + +#endif // POINTPARTICLERENDERER_H diff --git a/panda/src/particlesystem/rectangleEmitter.I b/panda/src/particlesystem/rectangleEmitter.I new file mode 100644 index 0000000000..d9bb830bc8 --- /dev/null +++ b/panda/src/particlesystem/rectangleEmitter.I @@ -0,0 +1,26 @@ +// Filename: rectangleEmitter.I +// Created by: charles (26Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_boundaries +// Access : public +// Description : boundary set +//////////////////////////////////////////////////////////////////// + +INLINE void RectangleEmitter:: +set_boundaries(const LPoint2f& vmin, const LPoint2f& vmax) { + _vmin = vmin; _vmax = vmax; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_launch_vec +// Access : public +// Description : boundary set +//////////////////////////////////////////////////////////////////// + +INLINE void RectangleEmitter:: +set_launch_vec(const LVector3f& lv) { + _launch_vec = lv; +} diff --git a/panda/src/particlesystem/rectangleEmitter.cxx b/panda/src/particlesystem/rectangleEmitter.cxx new file mode 100644 index 0000000000..dbd9a794b2 --- /dev/null +++ b/panda/src/particlesystem/rectangleEmitter.cxx @@ -0,0 +1,71 @@ +// Filename: rectangleEmitter.cxx +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "rectangleEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Function : RectangleEmitter +// Access : Public +// Description : constructor +//////////////////////////////////////////////////////////////////// +RectangleEmitter:: +RectangleEmitter(void) : + BaseParticleEmitter() { + _vmin.set(0.0f, 0.0f); + _vmax.set(0.0f, 0.0f); + _launch_vec.set(0.0f, 0.0f, 0.0f); +} + +//////////////////////////////////////////////////////////////////// +// Function : RectangleEmitter +// Access : Public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +RectangleEmitter:: +RectangleEmitter(const RectangleEmitter ©) : + BaseParticleEmitter(copy) { + _vmin = copy._vmin; + _vmax = copy._vmax; + _launch_vec = copy._launch_vec; +} + +//////////////////////////////////////////////////////////////////// +// Function : RectangleEmitter +// Access : Public +// Description : destructor +//////////////////////////////////////////////////////////////////// +RectangleEmitter:: +~RectangleEmitter(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : Public +// Description : copier +//////////////////////////////////////////////////////////////////// +BaseParticleEmitter *RectangleEmitter:: +make_copy(void) { + return new RectangleEmitter(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : RectangleEmitter::create_particle_location +// Access : Public +// Description : Generates a location on the rectangle +//////////////////////////////////////////////////////////////////// +void RectangleEmitter:: +assign_initial_values(LPoint3f& pos, LVector3f& vel) +{ + float t_x = bounded_rand(); + float t_y = bounded_rand(); + + LVector2f v_diff = _vmax - _vmin; + + float lerp_x = _vmin[0] + t_x * v_diff[0]; + float lerp_y = _vmin[1] + t_y * v_diff[1]; + + pos.set(lerp_x, lerp_y, 0.0f); + vel = _launch_vec; +} diff --git a/panda/src/particlesystem/rectangleEmitter.h b/panda/src/particlesystem/rectangleEmitter.h new file mode 100644 index 0000000000..c772e46a74 --- /dev/null +++ b/panda/src/particlesystem/rectangleEmitter.h @@ -0,0 +1,36 @@ +// Filename: rectangleEmitter.h +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef RECTANGLEEMITTER_H +#define RECTANGLEEMITTER_H + +#include "baseParticleEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Class : RectangleEmitter +// Description : Describes a planar square region in which +// particles are generated. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS RectangleEmitter : public BaseParticleEmitter { +private: + LPoint2f _vmin, _vmax; + LVector3f _launch_vec; + + virtual void assign_initial_values(LPoint3f& pos, LVector3f& vel); + +public: + RectangleEmitter(void); + RectangleEmitter(const RectangleEmitter ©); + virtual ~RectangleEmitter(void); + + virtual BaseParticleEmitter *make_copy(void); + + INLINE void set_boundaries(const LPoint2f& vmin, const LPoint2f& vmax); + INLINE void set_launch_vec(const LVector3f& lv); +}; + +#include "rectangleEmitter.I" + +#endif // RECTANGLEEMITTER_H diff --git a/panda/src/particlesystem/ringEmitter.I b/panda/src/particlesystem/ringEmitter.I new file mode 100644 index 0000000000..ccb0139b48 --- /dev/null +++ b/panda/src/particlesystem/ringEmitter.I @@ -0,0 +1,37 @@ +// Filename: ringEmitter.I +// Created by: charles (26Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_radius +// Access : public +// Description : radius set +//////////////////////////////////////////////////////////////////// + +INLINE void RingEmitter:: +set_radius(float r) { + _radius = r; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_aoe +// Access : public +// Description : angle of elevation set +//////////////////////////////////////////////////////////////////// + +INLINE void RingEmitter:: +set_aoe(float aoe) { + _aoe = aoe; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_magnitude +// Access : public +// Description : magnitude set +//////////////////////////////////////////////////////////////////// + +INLINE void RingEmitter:: +set_magnitude(float m) { + _mag = m; +} diff --git a/panda/src/particlesystem/ringEmitter.cxx b/panda/src/particlesystem/ringEmitter.cxx new file mode 100644 index 0000000000..b4a76edd2c --- /dev/null +++ b/panda/src/particlesystem/ringEmitter.cxx @@ -0,0 +1,75 @@ +// Filename: ringEmitter.cxx +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "ringEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Function : RingEmitter +// Access : Public +// Description : constructor +//////////////////////////////////////////////////////////////////// +RingEmitter:: +RingEmitter(void) : + _radius(0.0f), _aoe(0.0f), _mag(0.0f) { +} + +//////////////////////////////////////////////////////////////////// +// Function : RingEmitter +// Access : Public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +RingEmitter:: +RingEmitter(const RingEmitter ©) : + BaseParticleEmitter(copy) { + _radius = copy._radius; + _aoe = copy._aoe; + _mag = copy._mag; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~RingEmitter +// Access : Public +// Description : destructor +//////////////////////////////////////////////////////////////////// +RingEmitter:: +~RingEmitter(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : Public +// Description : copier +//////////////////////////////////////////////////////////////////// +BaseParticleEmitter *RingEmitter:: +make_copy(void) { + return new RingEmitter(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : RingEmitter::create_particle_location +// Access : Public +// Description : Generates a location on the ring +//////////////////////////////////////////////////////////////////// +void RingEmitter:: +assign_initial_values(LPoint3f& pos, LVector3f& vel) +{ + float theta = bounded_rand() * 2.0f * MathNumbers::pi; + float cos_theta = cosf(theta); + float sin_theta = sinf(theta); + + float new_x = cos_theta * _radius; + float new_y = sin_theta * _radius; + + pos.set(new_x, new_y, 0.0f); + + float vel_z = _mag * sinf(_aoe * (MathNumbers::pi / 180.0f)); + float abs_diff = fabs((_mag *_mag) - (vel_z * vel_z)); + float root_mag_minus_z_squared = sqrtf(abs_diff); + + float vel_x = cos_theta * root_mag_minus_z_squared; + float vel_y = sin_theta * root_mag_minus_z_squared; + + vel.set(vel_x, vel_y, vel_z); +} diff --git a/panda/src/particlesystem/ringEmitter.h b/panda/src/particlesystem/ringEmitter.h new file mode 100644 index 0000000000..5af3eec64c --- /dev/null +++ b/panda/src/particlesystem/ringEmitter.h @@ -0,0 +1,38 @@ +// Filename: ringEmitter.h +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef RINGEMITTER_H +#define RINGEMITTER_H + +#include "baseParticleEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Class : RingEmitter +// Description : Describes a planar ring region in which +// particles are generated. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS RingEmitter : public BaseParticleEmitter { +private: + float _radius; + float _aoe; // angle of elevation + float _mag; + + virtual void assign_initial_values(LPoint3f& pos, LVector3f& vel); + +public: + RingEmitter(void); + RingEmitter(const RingEmitter ©); + virtual ~RingEmitter(void); + + virtual BaseParticleEmitter *make_copy(void); + + INLINE void set_radius(float r); + INLINE void set_aoe(float aoe); + INLINE void set_magnitude(float m); +}; + +#include "ringEmitter.I" + +#endif // RINGEMITTER_H diff --git a/panda/src/particlesystem/sparkleParticleRenderer.I b/panda/src/particlesystem/sparkleParticleRenderer.I new file mode 100644 index 0000000000..ad5af90093 --- /dev/null +++ b/panda/src/particlesystem/sparkleParticleRenderer.I @@ -0,0 +1,108 @@ +// Filename: sparkleParticleRenderer.I +// Created by: charles (27Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_center_color +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void SparkleParticleRenderer:: +set_center_color(const Colorf& c) { + _center_color = c; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_edge_color +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void SparkleParticleRenderer:: +set_edge_color(const Colorf& c) { + _edge_color = c; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_life_scale +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void SparkleParticleRenderer:: +set_life_scale(SparkleParticleLifeScale ls) { + _life_scale = ls; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_birth_mag +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void SparkleParticleRenderer:: +set_birth_mag(float mag) { + _birth_mag = mag; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_death_mag +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void SparkleParticleRenderer:: +set_death_mag(float mag) { + _death_mag = mag; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_center_color +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE const Colorf& SparkleParticleRenderer:: +get_center_color(void) const { + return _center_color; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_edge_color +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE const Colorf& SparkleParticleRenderer:: +get_edge_color(void) const { + return _edge_color; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_life_scale +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE SparkleParticleLifeScale SparkleParticleRenderer:: +get_life_scale(void) const { + return _life_scale; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_birth_mag +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE float SparkleParticleRenderer:: +get_birth_mag(void) const { + return _birth_mag; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_death_mag +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE float SparkleParticleRenderer:: +get_death_mag(void) const { + return _death_mag; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_magnitude +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE float SparkleParticleRenderer:: +get_magnitude(BaseParticle *bp) { + if (_life_scale == SP_NO_SCALE) + return _birth_mag; + else { + float s_x = cubic_smooth(bp->get_parameterized_age()); + return lerp(s_x, _birth_mag, _death_mag); + } +} diff --git a/panda/src/particlesystem/sparkleParticleRenderer.cxx b/panda/src/particlesystem/sparkleParticleRenderer.cxx new file mode 100644 index 0000000000..2b0dede881 --- /dev/null +++ b/panda/src/particlesystem/sparkleParticleRenderer.cxx @@ -0,0 +1,250 @@ +// Filename: sparkleParticleRenderer.cxx +// Created by: charles (27Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "sparkleParticleRenderer.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function : SparkleParticleRenderer +// Access : Public +// Description : Default Constructor +//////////////////////////////////////////////////////////////////// + +SparkleParticleRenderer:: +SparkleParticleRenderer(void) : + _center_color(Colorf(1.0f, 1.0f, 1.0f, 1.0f)), + _edge_color(Colorf(1.0f, 1.0f, 1.0f, 1.0f)), + _birth_mag(0.1f), _death_mag(0.1f), + BaseParticleRenderer(PR_NO_ALPHA) { + _line_primitive = new GeomLine; + init_geoms(); +} + +//////////////////////////////////////////////////////////////////// +// Function : SparkleParticleRenderer +// Access : Public +// Description : Constructor +//////////////////////////////////////////////////////////////////// + +SparkleParticleRenderer:: +SparkleParticleRenderer(const Colorf& center, const Colorf& edge, + float birth_mag, float death_mag, + SparkleParticleLifeScale life_scale, + ParticleRendererAlphaDecay alpha_decay) : + _center_color(center), _edge_color(edge), _birth_mag(birth_mag), + _death_mag(death_mag), _life_scale(life_scale), + BaseParticleRenderer(alpha_decay) { + _line_primitive = new GeomLine; + init_geoms(); +} + +//////////////////////////////////////////////////////////////////// +// Function : SparkleParticleRenderer +// Access : Public +// Description : Copy Constructor +//////////////////////////////////////////////////////////////////// + +SparkleParticleRenderer:: +SparkleParticleRenderer(const SparkleParticleRenderer& copy) : + BaseParticleRenderer(copy) { + _center_color = copy._center_color; + _edge_color = copy._edge_color; + _birth_mag = copy._birth_mag; + _death_mag = copy._death_mag; + _life_scale = copy._life_scale; + + _line_primitive = new GeomLine; + init_geoms(); +} + +//////////////////////////////////////////////////////////////////// +// Function : ~SparkleParticleRenderer +// Access : Public +// Description : Destructor +//////////////////////////////////////////////////////////////////// + +SparkleParticleRenderer:: +~SparkleParticleRenderer(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make copy +// Access : Public +// Description : child virtual for spawning systems +//////////////////////////////////////////////////////////////////// + +BaseParticleRenderer *SparkleParticleRenderer:: +make_copy(void) { + return new SparkleParticleRenderer(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : birth_particle +// Access : Private, virtual +// Description : child birth +//////////////////////////////////////////////////////////////////// + +void SparkleParticleRenderer:: +birth_particle(int) { +} + +//////////////////////////////////////////////////////////////////// +// Function : kill_particle +// Access : Private, virtual +// Description : child kill +//////////////////////////////////////////////////////////////////// + +void SparkleParticleRenderer:: +kill_particle(int) { +} + +//////////////////////////////////////////////////////////////////// +// Function : resize_pool +// Access : private +// Description : resizes the render pool. Reference counting +// makes this easy. +//////////////////////////////////////////////////////////////////// + +void SparkleParticleRenderer:: +resize_pool(int new_size) { + _vertex_array = PTA_Vertexf(new_size * 12); + _color_array = PTA_Colorf(new_size * 12); + + _line_primitive->set_coords(_vertex_array, G_PER_VERTEX); + _line_primitive->set_colors(_color_array, G_PER_VERTEX); + + _max_pool_size = new_size; +} + +//////////////////////////////////////////////////////////////////// +// Function : init_geoms +// Access : private +// Description : initializes the geomnodes +//////////////////////////////////////////////////////////////////// + +void SparkleParticleRenderer:: +init_geoms(void) { + _line_primitive->set_num_prims(0); + + _interface_node->clear(); + _interface_node->add_geom(_line_primitive); +} + +//////////////////////////////////////////////////////////////////// +// Function : render +// Access : private +// Description : populates the GeomLine +//////////////////////////////////////////////////////////////////// + +void SparkleParticleRenderer:: +render(vector< PT(PhysicsObject) >& po_vector, int ttl_particles) { + + if (!ttl_particles) + return; + + BaseParticle *cur_particle; + + int cur_index = 0; + int remaining_particles = ttl_particles; + int i; + + Vertexf *cur_vert = &_vertex_array[0]; + Colorf *cur_color = &_color_array[0]; + + // init the aabb + + _aabb_min.set(99999.0f, 99999.0f, 99999.0f); + _aabb_max.set(-99999.0f, -99999.0f, -99999.0f); + + // run through the array + + for (i = 0; i < po_vector.size(); i++) { + cur_particle = (BaseParticle *) po_vector[i].p(); + + if (cur_particle->get_alive() == false) + continue; + + // adjust the aabb + + if (cur_particle->get_position().get_x() > _aabb_max.get_x()) + _aabb_max[0] = cur_particle->get_position().get_x(); + else if (cur_particle->get_position().get_x() < _aabb_min.get_x()) + _aabb_min[0] = cur_particle->get_position().get_x(); + + if (cur_particle->get_position().get_y() > _aabb_max.get_y()) + _aabb_max[1] = cur_particle->get_position().get_y(); + else if (cur_particle->get_position().get_y() < _aabb_min.get_y()) + _aabb_min[1] = cur_particle->get_position().get_y(); + + if (cur_particle->get_position().get_z() > _aabb_max.get_z()) + _aabb_max[2] = cur_particle->get_position().get_z(); + else if (cur_particle->get_position().get_z() < _aabb_min.get_z()) + _aabb_min[2] = cur_particle->get_position().get_z(); + + // draw the particle. + + float mag = get_magnitude(cur_particle); + float neg_mag = -mag; + float alpha; + + LPoint3f pos = cur_particle->get_position(); + Colorf center_color = _center_color; + Colorf edge_color = _edge_color; + + // handle alpha + + if (_alpha_decay != PR_NO_ALPHA) { + alpha = cur_particle->get_parameterized_age(); + + if (_alpha_decay == PR_ALPHA_OUT) + alpha = 1.0f - alpha; + + center_color[3] = alpha; + edge_color[3] = alpha; + } + + // 6 lines coming from the center point. + + *cur_vert++ = pos; + *cur_vert++ = pos + Vertexf(mag, 0.0f, 0.0f); + *cur_vert++ = pos; + *cur_vert++ = pos + Vertexf(neg_mag, 0.0f, 0.0f); + *cur_vert++ = pos; + *cur_vert++ = pos + Vertexf(0.0f, mag, 0.0f); + *cur_vert++ = pos; + *cur_vert++ = pos + Vertexf(0.0f, neg_mag, 0.0f); + *cur_vert++ = pos; + *cur_vert++ = pos + Vertexf(0.0f, 0.0f, mag); + *cur_vert++ = pos; + *cur_vert++ = pos + Vertexf(0.0f, 0.0f, neg_mag); + + *cur_color++ = center_color; + *cur_color++ = edge_color; + *cur_color++ = center_color; + *cur_color++ = edge_color; + *cur_color++ = center_color; + *cur_color++ = edge_color; + *cur_color++ = center_color; + *cur_color++ = edge_color; + *cur_color++ = center_color; + *cur_color++ = edge_color; + *cur_color++ = center_color; + *cur_color++ = edge_color; + + remaining_particles--; + if (remaining_particles == 0) + break; + } + + _line_primitive->set_num_prims(6 * ttl_particles); + + // done filling geomline node, now do the bb stuff + + LPoint3f aabb_center = _aabb_min + ((_aabb_max - _aabb_min) * 0.5f); + float radius = (aabb_center - _aabb_min).length(); + + _interface_node->set_bound(BoundingSphere(aabb_center, radius)); +} diff --git a/panda/src/particlesystem/sparkleParticleRenderer.h b/panda/src/particlesystem/sparkleParticleRenderer.h new file mode 100644 index 0000000000..8db5ed611a --- /dev/null +++ b/panda/src/particlesystem/sparkleParticleRenderer.h @@ -0,0 +1,85 @@ +// Filename: sparkleParticleRenderer.h +// Created by: charles (27Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SPARKLEPARTICLERENDERER_H +#define SPARKLEPARTICLERENDERER_H + +#include "baseParticle.h" +#include "baseParticleRenderer.h" + +#include +#include +#include +#include + +enum SparkleParticleLifeScale { + SP_NO_SCALE, + SP_SCALE +}; + +//////////////////////////////////////////////////////////////////// +// Class : SparkleParticleRenderer +// Description : pretty sparkly things. +//////////////////////////////////////////////////////////////////// + +class EXPCL_PANDAPHYSICS SparkleParticleRenderer : public BaseParticleRenderer { +private: + + Colorf _center_color; + Colorf _edge_color; + + float _birth_mag; + float _death_mag; + + PT(GeomLine) _line_primitive; + + PTA_Vertexf _vertex_array; + PTA_Colorf _color_array; + + int _max_pool_size; + + SparkleParticleLifeScale _life_scale; + LPoint3f _aabb_min, _aabb_max; + + INLINE float get_magnitude(BaseParticle *bp); + + virtual void birth_particle(int index); + virtual void kill_particle(int index); + virtual void init_geoms(void); + virtual void render(vector< PT(PhysicsObject) >& po_vector, + int ttl_particles); + virtual void resize_pool(int new_size); + +public: + + SparkleParticleRenderer(void); + SparkleParticleRenderer(const SparkleParticleRenderer& copy); + SparkleParticleRenderer(const Colorf& center, + const Colorf& edge, + float birth_mag, + float death_mag, + SparkleParticleLifeScale life_scale, + ParticleRendererAlphaDecay alpha_decay); + + virtual ~SparkleParticleRenderer(void); + + virtual BaseParticleRenderer *make_copy(void); + + INLINE void set_center_color(const Colorf& c); + INLINE void set_edge_color(const Colorf& c); + INLINE void set_life_scale(SparkleParticleLifeScale); + INLINE void set_birth_mag(float mag); + INLINE void set_death_mag(float mag); + + INLINE const Colorf& get_center_color(void) const; + INLINE const Colorf& get_edge_color(void) const; + INLINE SparkleParticleLifeScale get_life_scale(void) const; + INLINE float get_birth_mag(void) const; + INLINE float get_death_mag(void) const; +}; + +#include "sparkleParticleRenderer.I" + +#endif // SPARKLEPARTICLERENDERER_H diff --git a/panda/src/particlesystem/sphereSurfaceEmitter.I b/panda/src/particlesystem/sphereSurfaceEmitter.I new file mode 100644 index 0000000000..202ffd4f90 --- /dev/null +++ b/panda/src/particlesystem/sphereSurfaceEmitter.I @@ -0,0 +1,21 @@ +// Filename: sphereSurfaceEmitter.I +// Created by: charles (26Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_particlesystem.h" + +//////////////////////////////////////////////////////////////////// +// Function : set_radius +// Access : public +// Description : radius set +//////////////////////////////////////////////////////////////////// + +INLINE void SphereSurfaceEmitter:: +set_radius(float r) { + if (r == 0.0f) + particlesystem_cat.error() << "SphereSurfaceEmitter cannot have " + << "radius of 0." << endl; + else + _radius = r; +} diff --git a/panda/src/particlesystem/sphereSurfaceEmitter.cxx b/panda/src/particlesystem/sphereSurfaceEmitter.cxx new file mode 100644 index 0000000000..b4495732c0 --- /dev/null +++ b/panda/src/particlesystem/sphereSurfaceEmitter.cxx @@ -0,0 +1,65 @@ +// Filename: sphereSurfaceEmitter.cxx +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "sphereSurfaceEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Function : SphereSurfaceEmitter +// Access : Public +// Description : constructor +//////////////////////////////////////////////////////////////////// +SphereSurfaceEmitter:: +SphereSurfaceEmitter(void) { + _radius = 0.0f; +} + +//////////////////////////////////////////////////////////////////// +// Function : SphereSurfaceEmitter +// Access : Public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +SphereSurfaceEmitter:: +SphereSurfaceEmitter(const SphereSurfaceEmitter ©) : + BaseParticleEmitter(copy) { + _radius = copy._radius; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~SphereSurfaceEmitter +// Access : Public +// Description : destructor +//////////////////////////////////////////////////////////////////// +SphereSurfaceEmitter:: +~SphereSurfaceEmitter(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : Public +// Description : copier +//////////////////////////////////////////////////////////////////// +BaseParticleEmitter *SphereSurfaceEmitter:: +make_copy(void) { + return new SphereSurfaceEmitter(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : SphereSurfaceEmitter::create_particle_location +// Access : Public +// Description : Generates a location on the sphere +//////////////////////////////////////////////////////////////////// +void SphereSurfaceEmitter:: +assign_initial_values(LPoint3f& pos, LVector3f& vel) +{ + float z, theta, r; + float t; + + z = _radius - (2.0f * _radius * bounded_rand()); + r = sqrtf((_radius * _radius) - (z * z)); + theta = bounded_rand() * 2.0f * MathNumbers::pi; + + pos.set(r * cosf(theta), r * sinf(theta), z); + vel = pos / _radius; +} diff --git a/panda/src/particlesystem/sphereSurfaceEmitter.h b/panda/src/particlesystem/sphereSurfaceEmitter.h new file mode 100644 index 0000000000..17bb326791 --- /dev/null +++ b/panda/src/particlesystem/sphereSurfaceEmitter.h @@ -0,0 +1,34 @@ +// Filename: sphereSurfaceEmitter.h +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SPHERESURFACEEMITTER_H +#define SPHERESURFACEEMITTER_H + +#include "baseParticleEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Class : SphereSurfaceEmitter +// Description : Describes a curved space in which +// particles are generated. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS SphereSurfaceEmitter : public BaseParticleEmitter { +private: + float _radius; + + virtual void assign_initial_values(LPoint3f& pos, LVector3f& vel); + +public: + SphereSurfaceEmitter(void); + SphereSurfaceEmitter(const SphereSurfaceEmitter ©); + virtual ~SphereSurfaceEmitter(void); + + virtual BaseParticleEmitter *make_copy(void); + + INLINE void set_radius(float r); +}; + +#include "sphereSurfaceEmitter.I" + +#endif // SPHERESURFACEEMITTER_H diff --git a/panda/src/particlesystem/sphereVolumeEmitter.I b/panda/src/particlesystem/sphereVolumeEmitter.I new file mode 100644 index 0000000000..d4e9915670 --- /dev/null +++ b/panda/src/particlesystem/sphereVolumeEmitter.I @@ -0,0 +1,21 @@ +// Filename: sphereVolumeEmitter.I +// Created by: charles (26Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_particlesystem.h" + +//////////////////////////////////////////////////////////////////// +// Function : set_radius +// Access : public +// Description : radius set +//////////////////////////////////////////////////////////////////// + +INLINE void SphereVolumeEmitter:: +set_radius(float r) { + if (r == 0.0f) + particlesystem_cat.error() << "SphereVolumeEmitter can not have " + << "radius of 0." << endl; + else + _radius = r; +} diff --git a/panda/src/particlesystem/sphereVolumeEmitter.cxx b/panda/src/particlesystem/sphereVolumeEmitter.cxx new file mode 100644 index 0000000000..eadce01ce3 --- /dev/null +++ b/panda/src/particlesystem/sphereVolumeEmitter.cxx @@ -0,0 +1,74 @@ +// Filename: sphereVolumeEmitter.cxx +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "sphereVolumeEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Function : SphereVolumeEmitter +// Access : Public +// Description : constructor +//////////////////////////////////////////////////////////////////// +SphereVolumeEmitter:: +SphereVolumeEmitter(void) { + _radius = 0.0f; +} + +//////////////////////////////////////////////////////////////////// +// Function : SphereVolumeEmitter +// Access : Public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +SphereVolumeEmitter:: +SphereVolumeEmitter(const SphereVolumeEmitter ©) : + BaseParticleEmitter(copy) { + _radius = copy._radius; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~SphereVolumeEmitter +// Access : Public +// Description : destructor +//////////////////////////////////////////////////////////////////// +SphereVolumeEmitter:: +~SphereVolumeEmitter(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : Public +// Description : copier +//////////////////////////////////////////////////////////////////// +BaseParticleEmitter *SphereVolumeEmitter:: +make_copy(void) { + return new SphereVolumeEmitter(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : SphereVolumeEmitter::create_particle_location +// Access : Public +// Description : Generates a location in the sphere +//////////////////////////////////////////////////////////////////// +void SphereVolumeEmitter:: +assign_initial_values(LPoint3f& pos, LVector3f& vel) +{ + float z, theta, r; + float t; + + z = _radius - (2.0f * _radius * bounded_rand()); + r = sqrtf((_radius * _radius) - (z * z)); + theta = bounded_rand() * 2.0f * MathNumbers::pi; + + t = bounded_rand(); + + while (t == 0.0f) + t = bounded_rand(); + + float pos_x = r * cosf(theta) * t; + float pos_y = r * sinf(theta) * t; + float pos_z = z * t; + + pos.set(pos_x, pos_y, pos_z); + vel = pos / _radius; +} diff --git a/panda/src/particlesystem/sphereVolumeEmitter.h b/panda/src/particlesystem/sphereVolumeEmitter.h new file mode 100644 index 0000000000..bcd665e1d4 --- /dev/null +++ b/panda/src/particlesystem/sphereVolumeEmitter.h @@ -0,0 +1,33 @@ +// Filename: sphereVolumeEmitter.h +// Created by: charles (22Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SPHEREVOLUMEEMITTER_H +#define SPHEREVOLUMEEMITTER_H + +#include "baseParticleEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Class : SphereVolumeEmitter +// Description : Describes a voluminous spherical region in which +// particles are generated. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS SphereVolumeEmitter : public BaseParticleEmitter { +private: + float _radius; + + virtual void assign_initial_values(LPoint3f& pos, LVector3f& vel); + +public: + SphereVolumeEmitter(void); + SphereVolumeEmitter(const SphereVolumeEmitter ©); + virtual ~SphereVolumeEmitter(void); + + virtual BaseParticleEmitter *make_copy(void); + INLINE void set_radius(float r); +}; + +#include "sphereVolumeEmitter.I" + +#endif // SPHEREVOLUMEEMITTER_H diff --git a/panda/src/particlesystem/spriteParticleRenderer.I b/panda/src/particlesystem/spriteParticleRenderer.I new file mode 100644 index 0000000000..0636393ded --- /dev/null +++ b/panda/src/particlesystem/spriteParticleRenderer.I @@ -0,0 +1,131 @@ +// Filename: spriteParticleRenderer.I +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_texture +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void SpriteParticleRenderer:: +set_texture(Texture *tex) { + _sprite_primitive->set_texture(tex); +} + +//////////////////////////////////////////////////////////////////// +// Function : get_texture +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE Texture *SpriteParticleRenderer:: +get_texture(void) const { + return _sprite_primitive->get_texture(); +} + +//////////////////////////////////////////////////////////////////// +// Function : set_alpha_disable +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void SpriteParticleRenderer:: +set_alpha_disable(bool ad) { + _sprite_primitive->set_alpha_disable(ad); +} + +//////////////////////////////////////////////////////////////////// +// Function : get_alpha_disable +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE bool SpriteParticleRenderer:: +get_alpha_disable(void) const { + return _sprite_primitive->get_alpha_disable(); +} + +//////////////////////////////////////////////////////////////////// +// Function : set_color +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void SpriteParticleRenderer:: +set_color(const Colorf& color) { + _color = color; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_animation_flags +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void SpriteParticleRenderer:: +set_animation_flags(bool animate_x_ratio, + bool animate_y_ratio, + bool animate_theta) { + if (animate_x_ratio == true && _animate_x_ratio == false) { + _x_texel_array = PTA_float(_pool_size); + _sprite_primitive->set_x_texel_ratio(_x_texel_array, G_PER_PRIM); + } + else if (animate_x_ratio == false && _animate_x_ratio == true) { + _x_texel_array = PTA_float(1); + _sprite_primitive->set_x_texel_ratio(_x_texel_array, G_OVERALL); + } + + _animate_x_ratio = animate_x_ratio; + + if (animate_y_ratio == true && _animate_y_ratio == false) { + _y_texel_array = PTA_float(_pool_size); + _sprite_primitive->set_y_texel_ratio(_y_texel_array, G_PER_PRIM); + } + else if (animate_y_ratio == false && _animate_y_ratio == true) { + _y_texel_array = PTA_float(1); + _sprite_primitive->set_y_texel_ratio(_y_texel_array, G_OVERALL); + } + + _animate_y_ratio = animate_y_ratio; + + if (animate_theta == true && _animate_theta == false) { + _theta_array = PTA_float(_pool_size); + _sprite_primitive->set_thetas(_theta_array, G_PER_PRIM); + } + else if (animate_theta == false && _animate_theta == true) { + _theta_array = PTA_float(_pool_size); + _sprite_primitive->set_thetas(_theta_array, G_OVERALL); + } + + _animate_theta = animate_theta; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_x_ratios +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void SpriteParticleRenderer:: +set_x_ratios(float initial_x_texel_ratio, + float final_x_texel_ratio) { + _initial_x_texel_ratio = initial_x_texel_ratio; + _final_x_texel_ratio = final_x_texel_ratio; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_y_ratios +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void SpriteParticleRenderer:: +set_y_ratios(float initial_y_texel_ratio, + float final_y_texel_ratio) { + _initial_y_texel_ratio = initial_y_texel_ratio; + _final_y_texel_ratio = final_y_texel_ratio; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_nonanimated_theta +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void SpriteParticleRenderer:: +set_nonanimated_theta(float theta) { + _theta = theta; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_blend_type +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void SpriteParticleRenderer:: +set_blend_type(ParticleRendererBlendMethod bm) { + _blend_method = bm; +} diff --git a/panda/src/particlesystem/spriteParticleRenderer.cxx b/panda/src/particlesystem/spriteParticleRenderer.cxx new file mode 100644 index 0000000000..35d4c1b405 --- /dev/null +++ b/panda/src/particlesystem/spriteParticleRenderer.cxx @@ -0,0 +1,280 @@ +// Filename: spriteParticleRenderer.cxx +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include + +#include "spriteParticleRenderer.h" + +//////////////////////////////////////////////////////////////////// +// Function : SpriteParticleRenderer +// Access : public +// Description : constructor +//////////////////////////////////////////////////////////////////// +SpriteParticleRenderer:: +SpriteParticleRenderer(Texture *tex) : + _animate_x_ratio(false), + _animate_y_ratio(false), + _animate_theta(false), + _blend_method(PP_BLEND_LINEAR), + _pool_size(0), + _initial_x_texel_ratio(0.02f), + _initial_y_texel_ratio(0.02f), + _theta(0.0f), + _color(Colorf(1.0f, 1.0f, 1.0f, 1.0f)), + BaseParticleRenderer(PR_NO_ALPHA) { + _sprite_primitive = new GeomSprite(tex); + init_geoms(); +} + +//////////////////////////////////////////////////////////////////// +// Function : SpriteParticleRenderer +// Access : public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +SpriteParticleRenderer:: +SpriteParticleRenderer(const SpriteParticleRenderer& copy) : + _pool_size(0), BaseParticleRenderer(copy) { + _animate_x_ratio = copy._animate_x_ratio; + _animate_y_ratio = copy._animate_y_ratio; + _animate_theta = copy._animate_theta; + _blend_method = copy._blend_method; + _initial_x_texel_ratio = copy._initial_x_texel_ratio; + _final_x_texel_ratio = copy._final_x_texel_ratio; + _initial_y_texel_ratio = copy._initial_y_texel_ratio; + _final_y_texel_ratio = copy._final_y_texel_ratio; + _theta = copy._theta; + _color = copy._color; + _sprite_primitive = new GeomSprite(copy.get_texture()); + init_geoms(); +} + +//////////////////////////////////////////////////////////////////// +// Function : ~SpriteParticleRenderer +// Access : public +// Description : destructor +//////////////////////////////////////////////////////////////////// +SpriteParticleRenderer:: +~SpriteParticleRenderer(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : public +// Description : child dynamic copy +//////////////////////////////////////////////////////////////////// +BaseParticleRenderer *SpriteParticleRenderer:: +make_copy(void) { + return new SpriteParticleRenderer(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : resize_pool +// Access : private +// Description : reallocate the vertex pool. +//////////////////////////////////////////////////////////////////// +void SpriteParticleRenderer:: +resize_pool(int new_size) { + if (new_size == _pool_size) + return; + + _pool_size = new_size; + + GeomBindType _x_bind, _y_bind, _theta_bind; + + // handle the x texel ratio + if (_animate_x_ratio == true) { + _x_texel_array = PTA_float(new_size); + _x_bind = G_PER_PRIM; + } + else { + _x_texel_array = PTA_float(1); + _x_bind = G_OVERALL; + } + + // handle the y texel ratio + if (_animate_y_ratio == true) { + _y_texel_array = PTA_float(new_size); + _y_bind = G_PER_PRIM; + } + else { + _y_texel_array = PTA_float(1); + _y_bind = G_OVERALL; + } + + // handle the theta vector + if (_animate_theta == true) { + _theta_array = PTA_float(new_size); + _theta_bind = G_PER_PRIM; + } + else { + _theta_array = PTA_float(1); + _theta_bind = G_OVERALL; + } + + _vertex_array = PTA_Vertexf(new_size); + _color_array = PTA_Colorf(new_size); + + _sprite_primitive->set_coords(_vertex_array, G_PER_VERTEX); + _sprite_primitive->set_colors(_color_array, G_PER_PRIM); + _sprite_primitive->set_x_texel_ratio(_x_texel_array, _x_bind); + _sprite_primitive->set_y_texel_ratio(_y_texel_array, _y_bind); + _sprite_primitive->set_thetas(_theta_array, _theta_bind); +} + +//////////////////////////////////////////////////////////////////// +// Function : init_geoms +// Access : private +// Description : initializes everything, called on traumatic events +// such as construction and serious particlesystem +// modifications +//////////////////////////////////////////////////////////////////// +void SpriteParticleRenderer:: +init_geoms(void) { + _sprite_primitive->set_num_prims(0); + + _interface_node->clear(); + _interface_node->add_geom(_sprite_primitive); +} + +//////////////////////////////////////////////////////////////////// +// Function : birth_particle +// Access : private +// Description : child birth, one of those 'there-if-we-want-it' +// things. not really too useful here, so it turns +// out we don't really want it. +//////////////////////////////////////////////////////////////////// +void SpriteParticleRenderer:: +birth_particle(int) { +} + +//////////////////////////////////////////////////////////////////// +// Function : kill_particle +// Access : private +// Description : child death +//////////////////////////////////////////////////////////////////// +void SpriteParticleRenderer:: +kill_particle(int) { +} + +//////////////////////////////////////////////////////////////////// +// Function : render +// Access : private +// Description : big child render. populates the geom node. +//////////////////////////////////////////////////////////////////// +void SpriteParticleRenderer:: +render(vector< PT(PhysicsObject) >& po_vector, int ttl_particles) { + BaseParticle *cur_particle; + + int cur_index = 0; + int remaining_particles = ttl_particles; + int i; + + Vertexf *cur_vert = &_vertex_array[0]; + Colorf *cur_color = &_color_array[0]; + float *cur_x_texel = &_x_texel_array[0]; + float *cur_y_texel = &_y_texel_array[0]; + float *cur_theta = &_theta_array[0]; + + if (_animate_x_ratio == false) + *cur_x_texel = _initial_x_texel_ratio; + + if (_animate_y_ratio == false) + *cur_y_texel = _initial_y_texel_ratio; + + if (_animate_theta == false) + *cur_theta = _theta; + + // init the aabb + _aabb_min.set(99999.0f, 99999.0f, 99999.0f); + _aabb_max.set(-99999.0f, -99999.0f, -99999.0f); + + // run through every filled slot + for (i = 0; i < po_vector.size(); i++) { + cur_particle = (BaseParticle *) po_vector[i].p(); + + if (cur_particle->get_alive() == false) + continue; + + // x aabb adjust + if (cur_particle->get_position().get_x() > _aabb_max.get_x()) + _aabb_max[0] = cur_particle->get_position().get_x(); + else if (cur_particle->get_position().get_x() < _aabb_min.get_x()) + _aabb_min[0] = cur_particle->get_position().get_x(); + + // y aabb adjust + if (cur_particle->get_position().get_y() > _aabb_max.get_y()) + _aabb_max[1] = cur_particle->get_position().get_y(); + else if (cur_particle->get_position().get_y() < _aabb_min.get_y()) + _aabb_min[1] = cur_particle->get_position().get_y(); + + // z aabb adjust + if (cur_particle->get_position().get_z() > _aabb_max.get_z()) + _aabb_max[2] = cur_particle->get_position().get_z(); + else if (cur_particle->get_position().get_z() < _aabb_min.get_z()) + _aabb_min[2] = cur_particle->get_position().get_z(); + + // put the current vertex into the array + *cur_vert++ = cur_particle->get_position(); + + // put the current color into the array + Colorf c = _color; + + if (!(get_alpha_decay() == PR_NO_ALPHA || + get_alpha_decay() == PR_ALPHA_INVALID)) { + float t = cur_particle->get_parameterized_age(); + + if (get_alpha_decay() == PR_ALPHA_OUT) + c[3] = 1.0f - t; + else { + if (get_alpha_decay() == PR_ALPHA_IN) + c[3] = t; + } + } + + *cur_color++ = c; + + // handle x scaling + if (_animate_x_ratio == true) { + float t = cur_particle->get_parameterized_age(); + + // this ... isn't pretty. (ghetto cubic) + if (_blend_method == PP_BLEND_CUBIC) + t = t * t * (3 - (2 * t)); + + *cur_x_texel++ = (_initial_x_texel_ratio + + (t * (_final_x_texel_ratio - _initial_x_texel_ratio))); + } + + // handle y scaling + if (_animate_y_ratio == true) { + float t = cur_particle->get_parameterized_age(); + + // this ... isn't pretty either. + if (_blend_method == PP_BLEND_CUBIC) + t = t * t * (3 - (2 * t)); + + *cur_y_texel++ = (_initial_y_texel_ratio + + (t * (_final_y_texel_ratio - _initial_y_texel_ratio))); + } + + // handle theta + if (_animate_theta == true) + *cur_theta++ = cur_particle->get_theta(); + + // maybe jump out early? + remaining_particles--; + if (remaining_particles == 0) + break; + } + + _sprite_primitive->set_num_prims(ttl_particles); + + // done filling geompoint node, now do the bb stuff + LPoint3f aabb_center = _aabb_min + ((_aabb_max - _aabb_min) * 0.5f); + float radius = (aabb_center - _aabb_min).length(); + + _interface_node->set_bound(BoundingSphere(aabb_center, radius)); +} diff --git a/panda/src/particlesystem/spriteParticleRenderer.h b/panda/src/particlesystem/spriteParticleRenderer.h new file mode 100644 index 0000000000..56779d31d9 --- /dev/null +++ b/panda/src/particlesystem/spriteParticleRenderer.h @@ -0,0 +1,89 @@ +// Filename: spriteParticleRenderer.h +// Created by: charles (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SPRITEPARTICLERENDERER_H +#define SPRITEPARTICLERENDERER_H + +#include +#include +#include +#include +#include +#include +#include + +#include "baseParticleRenderer.h" +#include "baseParticle.h" + +//////////////////////////////////////////////////////////////////// +// Class : SpriteParticleRenderer +// Description : Renders a particle system with high-speed nasty +// trick sprites. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS SpriteParticleRenderer : public BaseParticleRenderer { +private: + PT(GeomSprite) _sprite_primitive; + PTA_Vertexf _vertex_array; + PTA_Colorf _color_array; + PTA_float _x_texel_array; + PTA_float _y_texel_array; + PTA_float _theta_array; + + Colorf _color; + + float _initial_x_texel_ratio, _final_x_texel_ratio; + float _initial_y_texel_ratio, _final_y_texel_ratio; + float _theta; + + bool _animate_x_ratio, _animate_y_ratio; + bool _animate_theta; + + ParticleRendererBlendMethod _blend_method; + + Vertexf _aabb_min, _aabb_max; + + int _pool_size; + + virtual void birth_particle(int index); + virtual void kill_particle(int index); + virtual void init_geoms(void); + virtual void render(vector< PT(PhysicsObject) > &po_vector, + int ttl_particles); + virtual void resize_pool(int new_size); + +public: + SpriteParticleRenderer(Texture *tex = (Texture *) NULL); + SpriteParticleRenderer(const SpriteParticleRenderer ©); + virtual ~SpriteParticleRenderer(void); + + virtual BaseParticleRenderer *make_copy(void); + + INLINE void set_texture(Texture *tex); + INLINE Texture *get_texture(void) const; + + INLINE void set_color(const Colorf &color); + + // this is the main interface for whether or not you want the sizes + // to change over the course of the particle's lifetime. + INLINE void set_animation_flags(bool animate_x_ratio, + bool animate_y_ratio, + bool animate_theta); + + INLINE void set_x_ratios(float initial_x_texel_ratio, + float final_x_texel_ratio = 0.0f); + INLINE void set_y_ratios(float initial_y_texel_ratio, + float final_y_texel_ratio = 0.0f); + INLINE void set_nonanimated_theta(float theta); + + // alpha + INLINE void set_blend_type(ParticleRendererBlendMethod bm); + + INLINE void set_alpha_disable(bool ad); + INLINE bool get_alpha_disable(void) const; +}; + +#include "spriteParticleRenderer.I" + +#endif // SPRITEPARTICLERENDERER_H diff --git a/panda/src/particlesystem/tangentRingEmitter.I b/panda/src/particlesystem/tangentRingEmitter.I new file mode 100644 index 0000000000..78c972dc76 --- /dev/null +++ b/panda/src/particlesystem/tangentRingEmitter.I @@ -0,0 +1,22 @@ +// Filename: tangentRingEmitter.I +// Created by: charles (25Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_radius +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void TangentRingEmitter:: +set_radius(float r) { + _radius = r; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_radius +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE float TangentRingEmitter:: +get_radius(void) const { + return _radius; +} diff --git a/panda/src/particlesystem/tangentRingEmitter.cxx b/panda/src/particlesystem/tangentRingEmitter.cxx new file mode 100644 index 0000000000..735342c723 --- /dev/null +++ b/panda/src/particlesystem/tangentRingEmitter.cxx @@ -0,0 +1,62 @@ +// Filename: tangentRingEmitter.cxx +// Created by: charles (25Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "tangentRingEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Function : tangentRingEmitter +// Access : public +// Description : constructor +//////////////////////////////////////////////////////////////////// +TangentRingEmitter:: +TangentRingEmitter(void) { + _radius = 0.0f; +} + +//////////////////////////////////////////////////////////////////// +// Function : tangentRingEmitter +// Access : public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +TangentRingEmitter:: +TangentRingEmitter(const TangentRingEmitter ©) : + BaseParticleEmitter(copy) { + _radius = copy._radius; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~tangentringemitter +// Access : public, virtual +// Description : destructor +//////////////////////////////////////////////////////////////////// +TangentRingEmitter:: +~TangentRingEmitter(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : public, virtual +// Description : child copier +//////////////////////////////////////////////////////////////////// +BaseParticleEmitter *TangentRingEmitter:: +make_copy(void) { + return new TangentRingEmitter(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : assign_initial_values +// Access : private, virtual +// Description : fills in a newly-born particle +//////////////////////////////////////////////////////////////////// +void TangentRingEmitter:: +assign_initial_values(LPoint3f& pos, LVector3f& vel) { + float theta = bounded_rand() * 2.0f * MathNumbers::pi; + + float x = _radius * cosf(theta); + float y = _radius * sinf(theta); + + pos.set(x, y, 0); + vel.set(-y, x, 0); +} diff --git a/panda/src/particlesystem/tangentRingEmitter.h b/panda/src/particlesystem/tangentRingEmitter.h new file mode 100644 index 0000000000..e36253b061 --- /dev/null +++ b/panda/src/particlesystem/tangentRingEmitter.h @@ -0,0 +1,35 @@ +// Filename: tangentRingEmitter.h +// Created by: charles (25Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TANGENTRINGEMITTER_H +#define TANGENTRINGEMITTER_H + +#include "baseParticleEmitter.h" + +//////////////////////////////////////////////////////////////////// +// Class : TangentRingEmitter +// Description : Describes a planar ring region in which +// tangent particles are generated. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS TangentRingEmitter : public BaseParticleEmitter { +private: + float _radius; + + virtual void assign_initial_values(LPoint3f& pos, LVector3f& vel); + +public: + TangentRingEmitter(void); + TangentRingEmitter(const TangentRingEmitter ©); + virtual ~TangentRingEmitter(void); + + virtual BaseParticleEmitter *make_copy(void); + + INLINE void set_radius(float r); + INLINE float get_radius(void) const; +}; + +#include "tangentRingEmitter.I" + +#endif // TANGENTRINGEMITTER_H diff --git a/panda/src/particlesystem/zSpinParticle.I b/panda/src/particlesystem/zSpinParticle.I new file mode 100644 index 0000000000..711945abdd --- /dev/null +++ b/panda/src/particlesystem/zSpinParticle.I @@ -0,0 +1,32 @@ +// Filename: zSpinParticle.I +// Created by: charles (16Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : set_thetas +// Description : public +//////////////////////////////////////////////////////////////////// +INLINE void ZSpinParticle:: +set_thetas(float cur, float target) { + _initial_theta = cur; + _cur_theta = cur; + _target_theta = target; + + float diff = _target_theta - _initial_theta; + + // figure out which way to spin. + if (diff < 0) { + if (fabs(diff) >= 180) + _positive_increment = true; + else + _positive_increment = false; + } + else { + if (fabs(diff) >= 180) + _positive_increment = false; + else + _positive_increment = true; + } +} + diff --git a/panda/src/particlesystem/zSpinParticle.cxx b/panda/src/particlesystem/zSpinParticle.cxx new file mode 100644 index 0000000000..8620be0bca --- /dev/null +++ b/panda/src/particlesystem/zSpinParticle.cxx @@ -0,0 +1,101 @@ +// Filename: zSpinParticle.cxx +// Created by: charles (16Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "zSpinParticle.h" + +//////////////////////////////////////////////////////////////////// +// Function : ZSpinParticle +// Access : public +// Description : constructor +//////////////////////////////////////////////////////////////////// +ZSpinParticle:: +ZSpinParticle(void) : + BaseParticle() { + _cur_theta = 0.0f; + _initial_theta = 0.0f; + _target_theta = 0.0f; + _positive_increment = true; +} + +//////////////////////////////////////////////////////////////////// +// Function : ZSpinParticle +// Access : public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +ZSpinParticle:: +ZSpinParticle(const ZSpinParticle ©) : + BaseParticle(copy) { + _cur_theta = copy._cur_theta; + _initial_theta = copy._initial_theta; + _target_theta = copy._target_theta; + _positive_increment = copy._positive_increment; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~ZSpinParticle +// Access : public, virtual +// Description : destructor +//////////////////////////////////////////////////////////////////// +ZSpinParticle:: +~ZSpinParticle(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : public, virtual +// Description : dynamic copier +//////////////////////////////////////////////////////////////////// +PhysicsObject *ZSpinParticle:: +make_copy(void) const { + return new ZSpinParticle(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : init +// Access : public, virtual +// Description : +//////////////////////////////////////////////////////////////////// +void ZSpinParticle:: +init(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : update +// Access : public, virtual +// Description : +//////////////////////////////////////////////////////////////////// +void ZSpinParticle:: +update(void) { + float t = get_parameterized_age(); + + if (_positive_increment) + _cur_theta = _initial_theta + (t * (fabs(_target_theta - _initial_theta))); + else + _cur_theta = _initial_theta - (t * (fabs(_target_theta - _initial_theta))); + + if (_cur_theta >= 360.0f) + _cur_theta -= 360.0f; + else if (_cur_theta < 0.0f) + _cur_theta += 360.0f; +} + +//////////////////////////////////////////////////////////////////// +// Function : die +// Access : public, virtual +// Description : +//////////////////////////////////////////////////////////////////// +void ZSpinParticle:: +die(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : get_theta +// Access : public, virtual +// Description : +//////////////////////////////////////////////////////////////////// +float ZSpinParticle:: +get_theta(void) const { + return _cur_theta; +} diff --git a/panda/src/particlesystem/zSpinParticle.h b/panda/src/particlesystem/zSpinParticle.h new file mode 100644 index 0000000000..0d01d098ef --- /dev/null +++ b/panda/src/particlesystem/zSpinParticle.h @@ -0,0 +1,47 @@ +// Filename: zSpinParticle.h +// Created by: charles (16Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ZSPINPARTICLE_H +#define ZSPINPARTICLE_H + +#include "baseParticle.h" + +//////////////////////////////////////////////////////////////////// +// Class : ZSpinParticle +// Description : describes a particle that spins along its z axis. +// this is kind of an intermediary class- if you're +// using a SpriteParticleRenderer and you want your +// sprites to spin without having them be full-blown +// oriented (i.e. angry quat math), use this. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS ZSpinParticle : public BaseParticle { +private: + float _initial_theta; + float _cur_theta; + float _target_theta; + float _dtheta; + bool _positive_increment; + +public: + ZSpinParticle(void); + ZSpinParticle(const ZSpinParticle ©); + virtual ~ZSpinParticle(void); + + virtual PhysicsObject *make_copy(void) const; + + virtual void init(void); + virtual void update(void); + virtual void die(void); + + virtual float get_theta(void) const; + + // these are dumped into one function so that the direction + // of rotation can be calculated at this time. + INLINE void set_thetas(float cur, float target); +}; + +#include "zSpinParticle.I" + +#endif // ZSPINPARTICLE_H diff --git a/panda/src/particlesystem/zSpinParticleFactory.I b/panda/src/particlesystem/zSpinParticleFactory.I new file mode 100644 index 0000000000..48eb5e0789 --- /dev/null +++ b/panda/src/particlesystem/zSpinParticleFactory.I @@ -0,0 +1,59 @@ +// Filename: zSpinParticleFactory.I +// Created by: charles (16Aug00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function : set_initial_theta +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void ZSpinParticleFactory:: +set_initial_theta(float theta) { + _initial_theta = theta; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_final_theta +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void ZSpinParticleFactory:: +set_final_theta(float theta) { + _final_theta = theta; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_theta_delta +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void ZSpinParticleFactory:: +set_theta_delta(float delta) { + _theta_delta = delta; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_initial_theta +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE float ZSpinParticleFactory:: +get_initial_theta(void) const { + return _initial_theta; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_final_theta +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE float ZSpinParticleFactory:: +get_final_theta(void) const { + return _final_theta; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_theta_delta +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE float ZSpinParticleFactory:: +get_theta_delta(void) const { + return _theta_delta; +} diff --git a/panda/src/particlesystem/zSpinParticleFactory.cxx b/panda/src/particlesystem/zSpinParticleFactory.cxx new file mode 100644 index 0000000000..980b8b0d75 --- /dev/null +++ b/panda/src/particlesystem/zSpinParticleFactory.cxx @@ -0,0 +1,68 @@ +// Filename: zSpinParticleFactory.cxx +// Created by: charles (16Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "zSpinParticleFactory.h" +#include "zSpinParticle.h" + +//////////////////////////////////////////////////////////////////// +// Function : ZSpinParticleFactory +// Access : public +// Description : constructor +//////////////////////////////////////////////////////////////////// +ZSpinParticleFactory:: +ZSpinParticleFactory(void) : + BaseParticleFactory() { + _initial_theta = 0.0f; + _final_theta = 0.0f; + _theta_delta = 0.0f; +} + +//////////////////////////////////////////////////////////////////// +// Function : ZSpinParticleFactory +// Access : public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +ZSpinParticleFactory:: +ZSpinParticleFactory(const ZSpinParticleFactory ©) : + BaseParticleFactory(copy) { + _initial_theta = copy._initial_theta; + _final_theta = copy._final_theta; + _theta_delta = copy._theta_delta; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~ZSpinParticleFactory +// Access : virtual, public +// Description : destructor +//////////////////////////////////////////////////////////////////// +ZSpinParticleFactory:: +~ZSpinParticleFactory(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : alloc_particle +// Access : private, virtual +// Description : factory method +//////////////////////////////////////////////////////////////////// +BaseParticle *ZSpinParticleFactory:: +alloc_particle(void) const { + return new ZSpinParticle; +} + +//////////////////////////////////////////////////////////////////// +// Function : populate_child_particle +// Access : private, virtual +// Description : factory populator +//////////////////////////////////////////////////////////////////// +void ZSpinParticleFactory:: +populate_child_particle(BaseParticle *bp) const { + ZSpinParticle *zsp = (ZSpinParticle *) bp; + + float final_theta = _final_theta; + if (_theta_delta != 0.0f) + final_theta += _theta_delta - (bounded_rand() * 2.0f * _theta_delta); + + zsp->set_thetas(_initial_theta, final_theta); +} diff --git a/panda/src/particlesystem/zSpinParticleFactory.h b/panda/src/particlesystem/zSpinParticleFactory.h new file mode 100644 index 0000000000..eca9d6158d --- /dev/null +++ b/panda/src/particlesystem/zSpinParticleFactory.h @@ -0,0 +1,41 @@ +// Filename: zSpinParticleFactory.h +// Created by: charles (16Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ZSPINPARTICLEFACTORY_H +#define ZSPINPARTICLEFACTORY_H + +#include "baseParticleFactory.h" + +//////////////////////////////////////////////////////////////////// +// Class : ZSpinParticleFactory +// Description : see filename +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS ZSpinParticleFactory : + public BaseParticleFactory { +private: + virtual void populate_child_particle(BaseParticle *bp) const; + virtual BaseParticle *alloc_particle(void) const; + + float _initial_theta; + float _final_theta; + float _theta_delta; + +public: + ZSpinParticleFactory(void); + ZSpinParticleFactory(const ZSpinParticleFactory ©); + virtual ~ZSpinParticleFactory(void); + + INLINE void set_initial_theta(float theta); + INLINE void set_final_theta(float theta); + INLINE void set_theta_delta(float delta); + + INLINE float get_initial_theta(void) const; + INLINE float get_final_theta(void) const; + INLINE float get_theta_delta(void) const; +}; + +#include "zSpinParticleFactory.I" + +#endif // ZSPINPARTICLEFACTORY_H diff --git a/panda/src/physics/Sources.pp b/panda/src/physics/Sources.pp new file mode 100644 index 0000000000..e7aa2cd8dc --- /dev/null +++ b/panda/src/physics/Sources.pp @@ -0,0 +1,61 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET physics + #define LOCAL_LIBS \ + linmath sgraph sgattrib sgraphutil + + #define SOURCES \ + actorNode.I actorNode.cxx actorNode.h angularEulerIntegrator.cxx \ + angularEulerIntegrator.h angularForce.cxx angularForce.h \ + angularIntegrator.cxx angularIntegrator.h angularVectorForce.I \ + angularVectorForce.cxx angularVectorForce.h baseForce.I \ + baseForce.cxx baseForce.h baseIntegrator.I baseIntegrator.cxx \ + baseIntegrator.h config_physics.cxx config_physics.h forceNode.I \ + forceNode.cxx forceNode.h linearCylinderVortexForce.I \ + linearCylinderVortexForce.cxx linearCylinderVortexForce.h \ + linearDistanceForce.I linearDistanceForce.cxx linearDistanceForce.h \ + linearEulerIntegrator.cxx linearEulerIntegrator.h linearForce.I \ + linearForce.cxx linearForce.h linearFrictionForce.I \ + linearFrictionForce.cxx linearFrictionForce.h linearIntegrator.cxx \ + linearIntegrator.h linearJitterForce.cxx linearJitterForce.h \ + linearNoiseForce.I linearNoiseForce.cxx linearNoiseForce.h \ + linearRandomForce.I linearRandomForce.cxx linearRandomForce.h \ + linearSinkForce.cxx linearSinkForce.h linearSourceForce.cxx \ + linearSourceForce.h linearUserDefinedForce.I \ + linearUserDefinedForce.cxx linearUserDefinedForce.h \ + linearVectorForce.I linearVectorForce.cxx linearVectorForce.h \ + physical.I physical.cxx physical.h physicalNode.I physicalNode.cxx \ + physicalNode.h physicsManager.I physicsManager.cxx physicsManager.h \ + physicsObject.I physicsObject.cxx physicsObject.h + + #define INSTALL_HEADERS \ + actorNode.I actorNode.h angularEulerIntegrator.h angularForce.h \ + angularIntegrator.h angularVectorForce.I angularVectorForce.h \ + baseForce.I baseForce.h baseIntegrator.I baseIntegrator.h \ + config_physics.h forceNode.I forceNode.h forces.h \ + linearCylinderVortexForce.I linearCylinderVortexForce.h \ + linearDistanceForce.I linearDistanceForce.h linearEulerIntegrator.h \ + linearForce.I linearForce.h linearFrictionForce.I \ + linearFrictionForce.h linearIntegrator.h linearJitterForce.h \ + linearNoiseForce.I linearNoiseForce.h linearRandomForce.I \ + linearRandomForce.h linearSinkForce.h linearSourceForce.h \ + linearUserDefinedForce.I linearUserDefinedForce.h \ + linearVectorForce.I linearVectorForce.h physical.I physical.h \ + physicalNode.I physicalNode.h physicsManager.I physicsManager.h \ + physicsObject.I physicsObject.h + + #define IGATESCAN all + +#end lib_target + +#begin test_bin_target + #define TARGET test_physics + #define LOCAL_LIBS \ + linmath physics + + #define SOURCES \ + test_physics.cxx + +#end test_bin_target + diff --git a/panda/src/physics/actorNode.I b/panda/src/physics/actorNode.I new file mode 100644 index 0000000000..73829e35d2 --- /dev/null +++ b/panda/src/physics/actorNode.I @@ -0,0 +1,22 @@ +// Filename: actorNode.I +// Created by: charles (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_parent_arc +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void ActorNode:: +set_parent_arc(RenderRelation *arc) { + _parent_arc = arc; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_parent_arc +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE RenderRelation *ActorNode:: +get_parent_arc(void) const { + return _parent_arc; +} diff --git a/panda/src/physics/actorNode.cxx b/panda/src/physics/actorNode.cxx new file mode 100644 index 0000000000..caf1195be1 --- /dev/null +++ b/panda/src/physics/actorNode.cxx @@ -0,0 +1,102 @@ +// Filename: actorNode.cxx +// Created by: charles (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "actorNode.h" +#include "config_physics.h" +#include "physicsObject.h" + +#include + +TypeHandle ActorNode::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : ActorNode +// Access : public +// Description : Constructor +//////////////////////////////////////////////////////////////////// +ActorNode:: +ActorNode(const string &name) : + PhysicalNode(name), _parent_arc(NULL) { + add_physical(new Physical(1, true)); + _mass_center = get_physical(0)->get_phys_body(); + _mass_center->set_active(true); + _ok_to_callback = true; +} + +//////////////////////////////////////////////////////////////////// +// Function : ActorNode +// Access : public +// Description : Copy Constructor. This does NOT copy the parent +// arc, but does deep copy the physical/physicsObject +//////////////////////////////////////////////////////////////////// +ActorNode:: +ActorNode(const ActorNode ©) : + PhysicalNode(copy) { + _parent_arc = (RenderRelation *) NULL; + _ok_to_callback = true; + _mass_center = get_physical(0)->get_phys_body(); +} + +//////////////////////////////////////////////////////////////////// +// Function : ~ActorNode +// Access : public +// Description : destructor +//////////////////////////////////////////////////////////////////// +ActorNode:: +~ActorNode(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : update_arc +// Access : public +// Description : this populates the parent arc with the transform +// generated by the contained Physical, moving the +// node and subsequent geometry. +//////////////////////////////////////////////////////////////////// +void ActorNode:: +update_arc(void) { + nassertv(_parent_arc != (RenderRelation *) NULL); + + LMatrix4f lcs = _mass_center->get_lcs(); + + // lock the callback so that this doesn't call transform_changed. + _ok_to_callback = false; + _parent_arc->set_transition(new TransformTransition(lcs)); + _ok_to_callback = true; +} + +//////////////////////////////////////////////////////////////////// +// Function : transform_changed +// Access : private, virtual +// Description : node hook. This function handles outside +// (non-physics) actions on the actor +// and updates the internal representation of the node. +//////////////////////////////////////////////////////////////////// +void ActorNode:: +transform_changed(NodeRelation *arc) { + // this callback could be triggered by update_arc, BAD. + if (_ok_to_callback == false) + return; + + TransformTransition *tt; + + // get the transition + tt = (TransformTransition *) + arc->get_transition(TransformTransition::get_class_type()); + + // extract the position + LPoint3f pos = tt->get_matrix().get_row3(3); + + // extract the orientation + if (_mass_center->get_oriented() == true) { + LOrientationf orientation; + orientation.set(tt->get_matrix()); + + _mass_center->set_orientation(orientation); + } + + // apply + _mass_center->set_position(pos); +} diff --git a/panda/src/physics/actorNode.h b/panda/src/physics/actorNode.h new file mode 100644 index 0000000000..c5a1d5c478 --- /dev/null +++ b/panda/src/physics/actorNode.h @@ -0,0 +1,65 @@ +// Filename: actorNode.h +// Created by: charles (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ACTORNODE_H +#define ACTORNODE_H + +#include +#include "physicalNode.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : ActorNode +// Description : Like a physical node, but with a little more. The +// actornode assumes responsibility for its parent arc, +// and changes in its own PhysicsObject will be +// reflected as transforms in the arc. This relation +// goes both ways; changes in the arc will update the +// object's position (shoves). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS ActorNode : public PhysicalNode { +private: + // node hook if the client changes the parent arc. + virtual void transform_changed(NodeRelation *arc); + + RenderRelation *_parent_arc; + PhysicsObject *_mass_center; + + bool _ok_to_callback; + +public: + ActorNode(const string &name = ""); + ActorNode(const ActorNode ©); + virtual ~ActorNode(void); + + // update the parent arc with PhysicsObject information + void update_arc(void); + + INLINE void set_parent_arc(RenderRelation *arc); + INLINE RenderRelation *get_parent_arc(void) const; + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + PhysicalNode::init_type(); + register_type(_type_handle, "ActorNode", + PhysicalNode::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + +}; + +#include "actorNode.I" + +#endif // ACTORNODE_H diff --git a/panda/src/physics/angularEulerIntegrator.cxx b/panda/src/physics/angularEulerIntegrator.cxx new file mode 100644 index 0000000000..b4d65f94c7 --- /dev/null +++ b/panda/src/physics/angularEulerIntegrator.cxx @@ -0,0 +1,146 @@ +// Filename: angularEulerIntergator.cxx +// Created by: charles (09Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "angularEulerIntegrator.h" +#include "forceNode.h" +#include "physicalNode.h" +#include "config_physics.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function : AngularEulerIntegrator +// Access : Public +// Description : constructor +//////////////////////////////////////////////////////////////////// +AngularEulerIntegrator:: +AngularEulerIntegrator(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : AngularEulerIntegrator +// Access : Public +// Description : destructor +//////////////////////////////////////////////////////////////////// +AngularEulerIntegrator:: +~AngularEulerIntegrator(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : Integrate +// Access : Public +// Description : Integrate a step of motion (based on dt) by +// applying every force in force_vec to every object +// in obj_vec. +//////////////////////////////////////////////////////////////////// +void AngularEulerIntegrator:: +child_integrate(Physical *physical, + vector< PT(AngularForce) >& forces, + float dt) { + // perform the precomputation. Note that the vector returned by + // get_precomputed_matrices() has the matrices loaded in order of force + // type: first global, then local. If you're using this as a guide to write + // another integrator, be sure to process your forces global, then local. + // otherwise your transforms will be VERY bad. No good. + precompute_angular_matrices(physical, forces); + const vector< LMatrix4f > &matrices = get_precomputed_angular_matrices(); + + // Loop through each object in the set. This processing occurs in O(pf) time, + // where p is the number of physical objects and f is the number of + // forces. Unfortunately, no precomputation of forces can occur, as + // each force is possibly contingent on such things as the position and + // velocity of each physicsobject in the set. Accordingly, we have + // to grunt our way through each one. wrt caching of the xform matrix + // should help. + vector< PT(PhysicsObject) >::const_iterator current_object_iter; + current_object_iter = physical->get_object_vector().begin(); + + for (; current_object_iter != physical->get_object_vector().end(); + current_object_iter++) { + + PhysicsObject *current_object = *current_object_iter; + + // bail out if this object doesn't exist or doesn't want to be + // processed. + if (current_object == (PhysicsObject *) NULL) + continue; + + if (current_object->get_active() == false) + continue; + + LVector3f accum_vec(0, 0, 0); + + // set up the traversal stuff. + ForceNode *force_node; + vector< PT(AngularForce) >::const_iterator f_cur; + + LVector3f f; + + // global forces + f_cur = forces.begin(); + int index = 0; + for (; f_cur != forces.end(); f_cur++) { + AngularForce *cur_force = *f_cur; + + // make sure the force is turned on. + if (cur_force->get_active() == false) + continue; + + force_node = cur_force->get_force_node(); + + // now we go from force space to our object's space. + f = matrices[index++] * cur_force->get_vector(current_object); + + // tally it into the accum vector, applying the inertial tensor. + accum_vec += f; + } + + // local forces + f_cur = physical->get_angular_forces().begin(); + for (; f_cur != physical->get_angular_forces().end(); f_cur++) { + AngularForce *cur_force = *f_cur; + + // make sure the force is turned on. + if (cur_force->get_active() == false) + continue; + + force_node = cur_force->get_force_node(); + + // go from force space to object space + f = matrices[index++] * cur_force->get_vector(current_object); + + // tally it into the accum vectors + accum_vec += f; + } + + // apply the accumulated torque vector to the object's inertial tensor. + // this matrix represents how much force the object 'wants' applied to it + // in any direction, among other things. + accum_vec = current_object->get_inertial_tensor() * accum_vec; + + // derive this into the angular velocity vector. + LVector3f rot_vec = current_object->get_rotation(); + rot_vec += accum_vec * dt; + + // here's the trick. we've been accumulating these forces as vectors + // and treating them as vectors, but now we're going to treat them as pure + // imaginary quaternions where r = 0. This vector NOW represents the + // imaginary vector formed by (i, j, k). + + LVector3f normalized_rot_vec = rot_vec; + float len = rot_vec.length(); + + normalized_rot_vec *= 1.0f / len; + LRotationf rot_quat = LRotationf(normalized_rot_vec, len); + + LOrientationf old_orientation = current_object->get_orientation(); + LOrientationf new_orientation = old_orientation * rot_quat; + new_orientation.normalize(); + + // and write the results back. + current_object->set_orientation(new_orientation); + current_object->set_rotation(rot_vec); + } +} diff --git a/panda/src/physics/angularEulerIntegrator.h b/panda/src/physics/angularEulerIntegrator.h new file mode 100644 index 0000000000..949a5cd4e3 --- /dev/null +++ b/panda/src/physics/angularEulerIntegrator.h @@ -0,0 +1,27 @@ +// Filename: angularEulerIntergator.h +// Created by: charles (09Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANGULAREULERINTEGRATOR_H +#define ANGULAREULERINTEGRATOR_H + +#include "angularIntegrator.h" + +//////////////////////////////////////////////////////////////////// +// Class : AngularEulerIntegrator +// Description : Performs Euler integration on a vector of +// physically modelable objects given a quantum dt. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS AngularEulerIntegrator : public AngularIntegrator { +private: + virtual void child_integrate(Physical *physical, + vector< PT(AngularForce) >& forces, + float dt); + +public: + AngularEulerIntegrator(void); + virtual ~AngularEulerIntegrator(void); +}; + +#endif // EULERINTEGRATOR_H diff --git a/panda/src/physics/angularForce.cxx b/panda/src/physics/angularForce.cxx new file mode 100644 index 0000000000..8d066c8725 --- /dev/null +++ b/panda/src/physics/angularForce.cxx @@ -0,0 +1,48 @@ +// Filename: angularForce.cxx +// Created by: charles (08Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "angularForce.h" + +TypeHandle AngularForce::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : AngularForce +// Access : protected +// Description : constructor +//////////////////////////////////////////////////////////////////// +AngularForce:: +AngularForce(void) : + BaseForce() { +} + +//////////////////////////////////////////////////////////////////// +// Function : AngularForce +// Access : protected +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +AngularForce:: +AngularForce(const AngularForce ©) : + BaseForce(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function : ~AngularForce +// Access : public, virtual +// Description : destructor +//////////////////////////////////////////////////////////////////// +AngularForce:: +~AngularForce(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : get_vector +// Access : public +// Description : access query +//////////////////////////////////////////////////////////////////// +LVector3f AngularForce:: +get_vector(const PhysicsObject *po) { + LVector3f v = get_child_vector(po); + return v; +} diff --git a/panda/src/physics/angularForce.h b/panda/src/physics/angularForce.h new file mode 100644 index 0000000000..303fe0b913 --- /dev/null +++ b/panda/src/physics/angularForce.h @@ -0,0 +1,47 @@ +// Filename: angularForce.h +// Created by: charles (08Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANGULARFORCE_H +#define ANGULARFORCE_H + +#include "baseForce.h" + +//////////////////////////////////////////////////////////////////// +// Class : AngularForce +// Description : pure virtual parent of all quat-based forces. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS AngularForce : public BaseForce { +private: + virtual LVector3f get_child_vector(const PhysicsObject *po) = 0; + +protected: + AngularForce(void); + AngularForce(const AngularForce ©); + +public: + virtual ~AngularForce(void); + + virtual AngularForce *make_copy(void) const = 0; + LVector3f get_vector(const PhysicsObject *po); + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + BaseForce::init_type(); + register_type(_type_handle, "AngularForce", + BaseForce::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif // ANGULARFORCE_H diff --git a/panda/src/physics/angularIntegrator.cxx b/panda/src/physics/angularIntegrator.cxx new file mode 100644 index 0000000000..8ca5622513 --- /dev/null +++ b/panda/src/physics/angularIntegrator.cxx @@ -0,0 +1,40 @@ +// Filename: AngularIntegrator.cxx +// Created by: charles (09Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "angularIntegrator.h" + +//////////////////////////////////////////////////////////////////// +// Function : AngularIntegrator +// Access : protected +// Description : constructor +//////////////////////////////////////////////////////////////////// +AngularIntegrator:: +AngularIntegrator(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : ~AngularIntegrator +// Access : public, virtual +// Description : destructor +//////////////////////////////////////////////////////////////////// +AngularIntegrator:: +~AngularIntegrator(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : Integrate +// Access : public +// Description : high-level integration. API. +//////////////////////////////////////////////////////////////////// +void AngularIntegrator:: +integrate(Physical *physical, vector< PT(AngularForce) >& forces, + float dt) { + // intercept in case we want to censor/adjust values + if (dt > _max_angular_dt) + dt = _max_angular_dt; + + // this actually does the integration. + child_integrate(physical, forces, dt); +} diff --git a/panda/src/physics/angularIntegrator.h b/panda/src/physics/angularIntegrator.h new file mode 100644 index 0000000000..b46fabb9c8 --- /dev/null +++ b/panda/src/physics/angularIntegrator.h @@ -0,0 +1,37 @@ +// Filename: AngularIntegrator.h +// Created by: charles (09Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANGULARINTEGRATOR_H +#define ANGULARINTEGRATOR_H + +#include "baseIntegrator.h" +#include "angularForce.h" + +//////////////////////////////////////////////////////////////////// +// Class : BaseAngularIntegrator +// Description : Pure virtual base class for physical modeling. +// Takes physically modelable objects and applies +// forces to them. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS AngularIntegrator : public BaseIntegrator { +private: + static const float _max_angular_dt; + + // this allows baseAngularIntegrator to censor/modify data that the + // actual integration function receives. + virtual void child_integrate(Physical *physical, vector< PT(AngularForce) > &forces, + float dt) = 0; + +protected: + AngularIntegrator(void); + +public: + virtual ~AngularIntegrator(void); + + void integrate(Physical *physical, vector< PT(AngularForce) > &forces, + float dt); +}; + +#endif // ANGULARINTEGRATOR_H diff --git a/panda/src/physics/angularVectorForce.I b/panda/src/physics/angularVectorForce.I new file mode 100644 index 0000000000..4351887293 --- /dev/null +++ b/panda/src/physics/angularVectorForce.I @@ -0,0 +1,22 @@ +// Filename: angularVectorForce.I +// Created by: charles (09Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_vector +// Access : public +//////////////////////////////////////////////////////////////////// +void AngularVectorForce:: +set_vector(const LVector3f &v) { + _fvec = v; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_vector +// Access : public +//////////////////////////////////////////////////////////////////// +void AngularVectorForce:: +set_vector(float x, float y, float z) { + _fvec.set(x, y, z); +} diff --git a/panda/src/physics/angularVectorForce.cxx b/panda/src/physics/angularVectorForce.cxx new file mode 100644 index 0000000000..e91ea87105 --- /dev/null +++ b/panda/src/physics/angularVectorForce.cxx @@ -0,0 +1,69 @@ +// Filename: angularVectorForce.cxx +// Created by: charles (09Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "angularVectorForce.h" + +TypeHandle AngularVectorForce::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : AngularVectorForce +// Access : public +// Description : constructor +//////////////////////////////////////////////////////////////////// +AngularVectorForce:: +AngularVectorForce(const LVector3f &vec) : + AngularForce(), _fvec(vec) { +} + +//////////////////////////////////////////////////////////////////// +// Function : AngularVectorForce +// Access : public +// Description : constructor +//////////////////////////////////////////////////////////////////// +AngularVectorForce:: +AngularVectorForce(float x, float y, float z) : + AngularForce() { + _fvec.set(x, y, z); +} + +//////////////////////////////////////////////////////////////////// +// Function : AngularVectorForce +// Access : public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +AngularVectorForce:: +AngularVectorForce(const AngularVectorForce ©) : + AngularForce(copy) { + _fvec = copy._fvec; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~AngularVectorForce +// Access : public, virtual +// Description : destructor +//////////////////////////////////////////////////////////////////// +AngularVectorForce:: +~AngularVectorForce(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : private, virtual +// Description : dynamic copier +//////////////////////////////////////////////////////////////////// +AngularForce *AngularVectorForce:: +make_copy(void) const { + return new AngularVectorForce(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : get_child_vector +// Access : private, virtual +// Description : query +//////////////////////////////////////////////////////////////////// +LVector3f AngularVectorForce:: +get_child_vector(const PhysicsObject *) { + return _fvec; +} diff --git a/panda/src/physics/angularVectorForce.h b/panda/src/physics/angularVectorForce.h new file mode 100644 index 0000000000..051db2955d --- /dev/null +++ b/panda/src/physics/angularVectorForce.h @@ -0,0 +1,52 @@ +// Filename: angularVectorForce.h +// Created by: charles (09Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ANGULARVECTORFORCE_H +#define ANGULARVECTORFORCE_H + +#include "angularForce.h" + +//////////////////////////////////////////////////////////////////// +// Class : AngularVectorForce +// Description : a simple directed torque force, the angular +// equivalent of simple vector force. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS AngularVectorForce : public AngularForce { +private: + LVector3f _fvec; + + virtual AngularForce *make_copy(void) const; + virtual LVector3f get_child_vector(const PhysicsObject *po); + +public: + AngularVectorForce(const LVector3f& vec); + AngularVectorForce(float x = 0.0f, float y = 0.0f, float z = 0.0f); + AngularVectorForce(const AngularVectorForce ©); + virtual ~AngularVectorForce(void); + + INLINE void set_vector(const LVector3f& v); + INLINE void set_vector(float x, float y, float z); + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + AngularForce::init_type(); + register_type(_type_handle, "AngularVectorForce", + AngularForce::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "angularVectorForce.I" + +#endif // ANGULARVECTORFORCE_H diff --git a/panda/src/physics/baseForce.I b/panda/src/physics/baseForce.I new file mode 100644 index 0000000000..53a5c96799 --- /dev/null +++ b/panda/src/physics/baseForce.I @@ -0,0 +1,31 @@ +// Filename: baseForce.I +// Created by: charles (08Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : get_force_node +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE ForceNode *BaseForce:: +get_force_node(void) const { + return _force_node; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_active +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE void BaseForce:: +set_active(bool active) { + _active = active; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_active +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE bool BaseForce:: +get_active(void) const { + return _active; +} diff --git a/panda/src/physics/baseForce.cxx b/panda/src/physics/baseForce.cxx new file mode 100644 index 0000000000..8a04c01171 --- /dev/null +++ b/panda/src/physics/baseForce.cxx @@ -0,0 +1,39 @@ +// Filename: baseForce.cxx +// Created by: charles (08Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "baseForce.h" + +TypeHandle BaseForce::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : BaseForce +// Access : protected +// Description : constructor +//////////////////////////////////////////////////////////////////// +BaseForce:: +BaseForce(bool active) : + _force_node(NULL), _active(active) { +} + +//////////////////////////////////////////////////////////////////// +// Function : BaseForce +// Access : protected +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +BaseForce:: +BaseForce(const BaseForce ©) : + TypedReferenceCount(copy) { + _active = copy._active; + _force_node = (ForceNode *) NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~BaseForce +// Access : public, virtual +// Description : destructor +//////////////////////////////////////////////////////////////////// +BaseForce:: +~BaseForce(void) { +} diff --git a/panda/src/physics/baseForce.h b/panda/src/physics/baseForce.h new file mode 100644 index 0000000000..484b4fe40b --- /dev/null +++ b/panda/src/physics/baseForce.h @@ -0,0 +1,65 @@ +// Filename: baseForce.h +// Created by: charles (08Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BASEFORCE_H +#define BASEFORCE_H + +#include +#include +#include + +#include "physicsObject.h" + +class ForceNode; + +//////////////////////////////////////////////////////////////////// +// Class : BaseForce +// Description : pure virtual base class for all forces that could +// POSSIBLY exist. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS BaseForce : public TypedReferenceCount { +private: + ForceNode *_force_node; + bool _active; + + virtual LVector3f get_child_vector(const PhysicsObject *po) = 0; + +protected: + BaseForce(bool active = true); + BaseForce(const BaseForce ©); + +public: + virtual ~BaseForce(void); + + INLINE bool get_active(void) const; + INLINE void set_active(bool active); + + INLINE ForceNode *get_force_node(void) const; + + virtual LVector3f get_vector(const PhysicsObject *po) = 0; + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + TypedReferenceCount::init_type(); + register_type(_type_handle, "BaseForce", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend class ForceNode; +}; + +#include "baseForce.I" + +#endif // BASEFORCE_H diff --git a/panda/src/physics/baseIntegrator.I b/panda/src/physics/baseIntegrator.I new file mode 100644 index 0000000000..4151a7a4bd --- /dev/null +++ b/panda/src/physics/baseIntegrator.I @@ -0,0 +1,22 @@ +// Filename: baseIntegrator.I +// Created by: charles (11Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : get_precomputed_linear_matrices +// Access : protected +//////////////////////////////////////////////////////////////////// +INLINE const vector< LMatrix4f > &BaseIntegrator:: +get_precomputed_linear_matrices(void) const { + return _precomputed_linear_matrices; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_precomputed_angular_matrices +// Access : protected +//////////////////////////////////////////////////////////////////// +INLINE const vector< LMatrix4f > &BaseIntegrator:: +get_precomputed_angular_matrices(void) const { + return _precomputed_angular_matrices; +} diff --git a/panda/src/physics/baseIntegrator.cxx b/panda/src/physics/baseIntegrator.cxx new file mode 100644 index 0000000000..58b6ffd469 --- /dev/null +++ b/panda/src/physics/baseIntegrator.cxx @@ -0,0 +1,130 @@ +// Filename: baseIntegrator.cxx +// Created by: charles (11Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "baseIntegrator.h" +#include "physicalNode.h" +#include "forceNode.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function : BaseIntegrator +// Access : protected +// Description : constructor +//////////////////////////////////////////////////////////////////// +BaseIntegrator:: +BaseIntegrator(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : ~BaseIntegrator +// Access : public, virtual +// Description : destructor +//////////////////////////////////////////////////////////////////// +BaseIntegrator:: +~BaseIntegrator(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : precompute_linear_matrices +// Access : protected +// Description : effectively caches the xform matrices between +// the physical's node and every force acting on it +// so that each PhysicsObject in the set held by the +// Physical doesn't have to wrt. +//////////////////////////////////////////////////////////////////// +void BaseIntegrator:: +precompute_linear_matrices(Physical *physical, + const vector< PT(LinearForce) > &forces) { + // make sure the physical's in the scene graph, somewhere. + PhysicalNode *physical_node = physical->get_physical_node(); + nassertv(physical_node != NULL); + + // by global forces, we mean forces not contained in the physical + int global_force_vec_size = forces.size(); + + // by local forces, we mean members of the physical's force set. + int local_force_vec_size = physical->get_linear_forces().size(); + int index = 0, i; + + LMatrix4f current_xform; + ForceNode *force_node; + + // prepare the vector + _precomputed_linear_matrices.clear(); + _precomputed_linear_matrices.reserve(global_force_vec_size + local_force_vec_size); + + // tally the global xforms + for (i = 0; i < global_force_vec_size; i++) { + force_node = forces[i]->get_force_node(); + nassertv(force_node != (ForceNode *) NULL); + + get_rel_mat(physical_node, force_node, current_xform); + _precomputed_linear_matrices[index++] = current_xform; + } + + const vector< PT(LinearForce) > &force_vector = + physical->get_linear_forces(); + + // tally the local xforms + for (i = 0; i < local_force_vec_size; i++) { + force_node = force_vector[i]->get_force_node(); + nassertv(force_node != (ForceNode *) NULL); + + get_rel_mat(physical_node, force_node, current_xform); + _precomputed_linear_matrices[index++] = current_xform; + } +} + +//////////////////////////////////////////////////////////////////// +// Function : precompute_angular_matrices +// Access : protected +// Description : effectively caches the xform matrices between +// the physical's node and every force acting on it +// so that each PhysicsObject in the set held by the +// Physical doesn't have to wrt. +//////////////////////////////////////////////////////////////////// +void BaseIntegrator:: +precompute_angular_matrices(Physical *physical, + const vector< PT(AngularForce) > &forces) { + // make sure the physical's in the scene graph, somewhere. + PhysicalNode *physical_node = physical->get_physical_node(); + nassertv(physical_node != NULL); + + // by global forces, we mean forces not contained in the physical + int global_force_vec_size = forces.size(); + + // by local forces, we mean members of the physical's force set. + int local_force_vec_size = physical->get_angular_forces().size(); + int index = 0, i; + + LMatrix4f current_xform; + ForceNode *force_node; + + // prepare the vector + _precomputed_angular_matrices.clear(); + _precomputed_angular_matrices.reserve(global_force_vec_size + local_force_vec_size); + + // tally the global xforms + for (i = 0; i < global_force_vec_size; i++) { + force_node = forces[i]->get_force_node(); + nassertv(force_node != (ForceNode *) NULL); + + get_rel_mat(physical_node, force_node, current_xform); + _precomputed_angular_matrices[index++] = current_xform; + } + + const vector< PT(AngularForce) > &force_vector = + physical->get_angular_forces(); + + // tally the local xforms + for (i = 0; i < local_force_vec_size; i++) { + force_node = force_vector[i]->get_force_node(); + nassertv(force_node != (ForceNode *) NULL); + + get_rel_mat(physical_node, force_node, current_xform); + _precomputed_angular_matrices[index++] = current_xform; + } +} diff --git a/panda/src/physics/baseIntegrator.h b/panda/src/physics/baseIntegrator.h new file mode 100644 index 0000000000..71725c0623 --- /dev/null +++ b/panda/src/physics/baseIntegrator.h @@ -0,0 +1,53 @@ +// Filename: baseIntegrator.h +// Created by: charles (11Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BASEINTEGRATOR_H +#define BASEINTEGRATOR_H + +#include +#include +#include +#include + +#include "linearForce.h" +#include "angularForce.h" + +#include + +class Physical; + +//////////////////////////////////////////////////////////////////// +// Class : BaseIntegrator +// Description : pure virtual integrator class that holds cached +// matrix information that really should be common to +// any possible child implementation. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS BaseIntegrator : public ReferenceCount { +private: + // since the wrt for each physicsobject between its physicalnode + // and however many forces will be the same among one physical, + // the transformation matrices can be pulled out of the inner loop + // and precomputed. + vector< LMatrix4f > _precomputed_linear_matrices; + vector< LMatrix4f > _precomputed_angular_matrices; + +protected: + BaseIntegrator(void); + + INLINE const vector< LMatrix4f > &get_precomputed_linear_matrices(void) const; + INLINE const vector< LMatrix4f > &get_precomputed_angular_matrices(void) const; + + void precompute_linear_matrices(Physical *physical, + const vector< PT(LinearForce) > &forces); + void precompute_angular_matrices(Physical *physical, + const vector< PT(AngularForce) > &forces); + +public: + virtual ~BaseIntegrator(void); +}; + +#include "baseIntegrator.I" + +#endif // BASEINTEGRATOR_H diff --git a/panda/src/physics/config_physics.cxx b/panda/src/physics/config_physics.cxx new file mode 100644 index 0000000000..9d5cdf377c --- /dev/null +++ b/panda/src/physics/config_physics.cxx @@ -0,0 +1,47 @@ +// Filename: config_physics.cxx +// Created by: charles (17Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_physics.h" +#include "physicsObject.h" +#include "physicalNode.h" +#include "linearIntegrator.h" +#include "angularIntegrator.h" +#include "forceNode.h" +#include "forces.h" +#include + +ConfigureDef(config_physics); +NotifyCategoryDef(physics, ""); + +ConfigureFn(config_physics) { + PhysicsObject::init_type(); + Physical::init_type(); + PhysicalNode::init_type(); + ForceNode::init_type(); + BaseForce::init_type(); + LinearForce::init_type(); + LinearVectorForce::init_type(); + LinearRandomForce::init_type(); + LinearJitterForce::init_type(); + LinearNoiseForce::init_type(); + LinearDistanceForce::init_type(); + LinearSinkForce::init_type(); + LinearSourceForce::init_type(); + LinearFrictionForce::init_type(); + LinearUserDefinedForce::init_type(); + LinearCylinderVortexForce::init_type(); +} + +const float LinearIntegrator::_max_linear_dt = + config_physics.GetFloat("default_max_linear_dt", 1.0f / 30.0f); + +const float AngularIntegrator::_max_angular_dt = + config_physics.GetFloat("default_max_angular_dt", 1.0f / 30.0f); + +int LinearNoiseForce::_random_seed = + config_physics.GetInt("default_noise_force_seed", 665); + +const float PhysicsObject::_default_terminal_velocity = + config_physics.GetFloat("default_terminal_velocity", 400.0f); diff --git a/panda/src/physics/config_physics.h b/panda/src/physics/config_physics.h new file mode 100644 index 0000000000..93bef55e56 --- /dev/null +++ b/panda/src/physics/config_physics.h @@ -0,0 +1,16 @@ +// Filename: config_physics.h +// Created by: charles (17Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_PHYSICS_H +#define CONFIG_PHYSICS_H + +#include +#include +#include + +ConfigureDecl(config_physics, EXPCL_PANDAPHYSICS, EXPTP_PANDAPHYSICS); +NotifyCategoryDecl(physics, EXPCL_PANDAPHYSICS, EXPTP_PANDAPHYSICS); + +#endif // CONFIG_PHYSICS_H diff --git a/panda/src/physics/forceNode.I b/panda/src/physics/forceNode.I new file mode 100644 index 0000000000..af6f4084b3 --- /dev/null +++ b/panda/src/physics/forceNode.I @@ -0,0 +1,43 @@ +// Filename: forceNode.I +// Created by: charles (02Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : clear +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void ForceNode:: +clear(void) { + _forces.erase(_forces.begin(), _forces.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function : get_force +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE BaseForce *ForceNode:: +get_force(int index) const { + nassertr(index >= 0 && index < _forces.size(), + (BaseForce *) NULL); + return _forces[index]; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_num_forces +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE int ForceNode:: +get_num_forces(void) const { + return _forces.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function : add_force +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void ForceNode:: +add_force(BaseForce *force) { + _forces.push_back(force); + force->_force_node = this; +} diff --git a/panda/src/physics/forceNode.cxx b/panda/src/physics/forceNode.cxx new file mode 100644 index 0000000000..14621eedde --- /dev/null +++ b/panda/src/physics/forceNode.cxx @@ -0,0 +1,92 @@ +// Filename: forceNode.cxx +// Created by: charles (02Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "forceNode.h" +#include "config_physics.h" + +TypeHandle ForceNode::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : ForceNode +// Access : public +// Description : default constructor +//////////////////////////////////////////////////////////////////// +ForceNode:: +ForceNode(const string &name) : + NamedNode(name) { +} + +//////////////////////////////////////////////////////////////////// +// Function : ForceNode +// Access : public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +ForceNode:: +ForceNode(const ForceNode ©) : + NamedNode(copy), _forces(copy._forces) { +} + +//////////////////////////////////////////////////////////////////// +// Function : ~ForceNode +// Access : public, virtual +// Description : destructor +//////////////////////////////////////////////////////////////////// +ForceNode:: +~ForceNode(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : operator = +// Access : public +// Description : assignment operator +//////////////////////////////////////////////////////////////////// +ForceNode &ForceNode:: +operator =(const ForceNode ©) { + NamedNode::operator =(copy); + _forces = copy._forces; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : public, virtual +// Description : dynamic child copy +//////////////////////////////////////////////////////////////////// +Node *ForceNode:: +make_copy(void) const { + return new ForceNode(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : add_forces_from +// Access : public +// Description : append operation +//////////////////////////////////////////////////////////////////// +void ForceNode:: +add_forces_from(const ForceNode &other) { + vector< PT(BaseForce) >::iterator last = _forces.end() - 1; + + _forces.insert(_forces.end(), + other._forces.begin(), other._forces.end()); + + for (; last != _forces.end(); last++) + (*last)->_force_node = this; +} + +//////////////////////////////////////////////////////////////////// +// Function : remove_force +// Access : public +// Description : remove operation +//////////////////////////////////////////////////////////////////// +void ForceNode:: +remove_force(int index) { + nassertv(index >= 0 && index <= _forces.size()); + + vector< PT(BaseForce) >::iterator remove; + remove = _forces.begin() + index; + (*remove)->_force_node = (ForceNode *) NULL; + + _forces.erase(remove); +} diff --git a/panda/src/physics/forceNode.h b/panda/src/physics/forceNode.h new file mode 100644 index 0000000000..6cae25c3c8 --- /dev/null +++ b/panda/src/physics/forceNode.h @@ -0,0 +1,63 @@ +// Filename: forceNode.h +// Created by: charles (02Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FORCENODE_H +#define FORCENODE_H + +#include +#include + +#include "baseForce.h" + +//////////////////////////////////////////////////////////////////// +// Class : ForceNode +// Description : A force that lives in the scene graph and is +// therefore subject to local coordinate systems. +// An example of this would be simulating gravity +// in a rotating space station. or something. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS ForceNode : public NamedNode { +private: + vector< PT(BaseForce) > _forces; + +public: + ForceNode(const string &name = ""); + ForceNode(const ForceNode ©); + virtual ~ForceNode(void); + + ForceNode &operator =(const ForceNode ©); + + virtual bool safe_to_flatten(void) const { return false; } + virtual Node *make_copy(void) const; + + INLINE void clear(void); + INLINE BaseForce *get_force(int index) const; + INLINE int get_num_forces(void) const; + INLINE void add_force(BaseForce *force); + + void add_forces_from(const ForceNode &other); + void remove_force(int index); + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + NamedNode::init_type(); + register_type(_type_handle, "ForceNode", + NamedNode::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "forceNode.I" + +#endif // FORCENODE_H diff --git a/panda/src/physics/forces.h b/panda/src/physics/forces.h new file mode 100644 index 0000000000..d6f82c3860 --- /dev/null +++ b/panda/src/physics/forces.h @@ -0,0 +1,20 @@ +// Filename: forces.h +// Created by: charles (20Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FORCES_H +#define FORCES_H + +#include "linearVectorForce.h" +#include "linearNoiseForce.h" +#include "linearJitterForce.h" +#include "linearSourceForce.h" +#include "linearSinkForce.h" +#include "linearFrictionForce.h" +#include "linearCylinderVortexForce.h" +#include "linearUserDefinedForce.h" + +#include "angularVectorForce.h" + +#endif // FORCES_H diff --git a/panda/src/physics/linearCylinderVortexForce.I b/panda/src/physics/linearCylinderVortexForce.I new file mode 100644 index 0000000000..0526811ca5 --- /dev/null +++ b/panda/src/physics/linearCylinderVortexForce.I @@ -0,0 +1,58 @@ +// Filename: LinearCylinderVortexForce.I +// Created by: charles (24Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_radius +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void LinearCylinderVortexForce:: +set_radius(float radius) { + _radius = radius; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_length +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void LinearCylinderVortexForce:: +set_length(float length) { + _length = length; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_coef +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void LinearCylinderVortexForce:: +set_coef(float coef) { + _coef = coef; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_radius +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE float LinearCylinderVortexForce:: +get_radius(void) const { + return _radius; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_length +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE float LinearCylinderVortexForce:: +get_length(void) const { + return _length; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_coef +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE float LinearCylinderVortexForce:: +get_coef(void) const { + return _coef; +} diff --git a/panda/src/physics/linearCylinderVortexForce.cxx b/panda/src/physics/linearCylinderVortexForce.cxx new file mode 100644 index 0000000000..3c7adee6ff --- /dev/null +++ b/panda/src/physics/linearCylinderVortexForce.cxx @@ -0,0 +1,100 @@ +// Filename: LinearCylinderVortexForce.cxx +// Created by: charles (24Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_physics.h" +#include "linearCylinderVortexForce.h" + +TypeHandle LinearCylinderVortexForce::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : LinearCylinderVortexForce +// Access : public +// Description : Simple Constructor +//////////////////////////////////////////////////////////////////// +LinearCylinderVortexForce:: +LinearCylinderVortexForce(float radius, float length, float coef, + float a, bool md) : + _radius(radius), _length(length), _coef(coef), + LinearForce(a, md) { +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearCylinderVortexForce +// Access : public +// Description : copy Constructor +//////////////////////////////////////////////////////////////////// +LinearCylinderVortexForce:: +LinearCylinderVortexForce(const LinearCylinderVortexForce ©) : + LinearForce(copy) { + _radius = copy._radius; + _length = copy._length; + _coef = copy._coef; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~LinearCylinderVortexForce +// Access : public +// Description : Destructor +//////////////////////////////////////////////////////////////////// +LinearCylinderVortexForce:: +~LinearCylinderVortexForce(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : public, virtual +// Description : child copier +//////////////////////////////////////////////////////////////////// +LinearForce *LinearCylinderVortexForce:: +make_copy(void) { + return new LinearCylinderVortexForce(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : get_child_vector +// Access : private, virtual +// Description : returns the centripetal force vector for the +// passed-in object +//////////////////////////////////////////////////////////////////// +LVector3f LinearCylinderVortexForce:: +get_child_vector(const PhysicsObject *po) { + // get the force-space transform- this MUST be the relative matrix + // from the point's local coordinate system to the attached node's + // local system. + LMatrix4f force_space_xform = LMatrix4f::ident_mat(); + LVector3f force_vec(0.0f, 0.0f, 0.0f); + + // project the point into force_space + LPoint3f point = po->get_position(); + + // clip along length + if (point[2] < 0.0f || point[2] > _length) + return force_vec; + + // clip to radius + float x_squared = point[0] * point[0]; + float y_squared = point[1] * point[1]; + float dist_squared = x_squared + y_squared; + float radius_squared = _radius * _radius; + + // squared space increases monotonically wrt linear space, + // so there's no need to sqrt to check inside/outside this disc. + if (dist_squared > radius_squared) + return force_vec; + + float r = sqrtf(dist_squared); + + LVector3f tangential = po->get_velocity(); + tangential[2] = 0.0f; + + LVector3f centripetal = -point; + centripetal[2] = 0.0f; + centripetal.normalize(); + + // a = v^2 / r + centripetal = centripetal * _coef * (tangential.length_squared() / r); + + return centripetal; +} diff --git a/panda/src/physics/linearCylinderVortexForce.h b/panda/src/physics/linearCylinderVortexForce.h new file mode 100644 index 0000000000..762fcc94e2 --- /dev/null +++ b/panda/src/physics/linearCylinderVortexForce.h @@ -0,0 +1,68 @@ +// Filename: LinearCylinderVortexForce.h +// Created by: charles (24Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINEARCYLINDERVORTEXFORCE_H +#define LINEARCYLINDERVORTEXFORCE_H + +#include "linearForce.h" + +//////////////////////////////////////////////////////////////////// +// Class : LinearCylinderVortexForce +// Description : Defines a cylinder inside of which all forces are +// tangential to the theta of the particle wrt the +// z-axis in local coord. space. This happens by +// assigning the force a node by which the cylinder is +// transformed. Be warned- this will suck anything +// that it can reach directly into orbit and will NOT +// let go. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS LinearCylinderVortexForce : public LinearForce { +private: + float _radius; + float _length; + float _coef; + + virtual LinearForce *make_copy(void); + virtual LVector3f get_child_vector(const PhysicsObject *po); + +public: + LinearCylinderVortexForce(float radius = 1.0f, + float length = 0.0f, + float coef = 1.0f, + float a = 1.0f, + bool md = false); + LinearCylinderVortexForce(const LinearCylinderVortexForce ©); + virtual ~LinearCylinderVortexForce(void); + + INLINE void set_coef(float coef); + INLINE float get_coef(void) const; + + INLINE void set_radius(float radius); + INLINE float get_radius(void) const; + + INLINE void set_length(float length); + INLINE float get_length(void) const; + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LinearForce::init_type(); + register_type(_type_handle, "LinearCylinderVortexForce", + LinearForce::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "linearCylinderVortexForce.I" + +#endif // LINEARCYLINDERVORTEXFORCE_H diff --git a/panda/src/physics/linearDistanceForce.I b/panda/src/physics/linearDistanceForce.I new file mode 100644 index 0000000000..72c22a3d6b --- /dev/null +++ b/panda/src/physics/linearDistanceForce.I @@ -0,0 +1,80 @@ +// Filename: LinearDistanceForce.I +// Created by: charles (21Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_falloff_type +// Access : Public +// Description : falloff_type encapsulating wrap +//////////////////////////////////////////////////////////////////// +INLINE void LinearDistanceForce:: +set_falloff_type(FalloffType ft) { + _falloff = ft; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_radius +// Access : Public +// Description : set the radius +//////////////////////////////////////////////////////////////////// +INLINE void LinearDistanceForce:: +set_radius(float r) { + _radius = r; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_force_center +// Access : Public +// Description : set the force center +//////////////////////////////////////////////////////////////////// +INLINE void LinearDistanceForce:: +set_force_center(const LPoint3f& p) { + _force_center = p; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_falloff_type +// Access : public +// Description : falloff_type query +//////////////////////////////////////////////////////////////////// +INLINE FalloffType LinearDistanceForce:: +get_falloff_type(void) const { + return _falloff; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_radius +// Access : public +// Description : radius query +//////////////////////////////////////////////////////////////////// +INLINE float LinearDistanceForce:: +get_radius(void) const { + return _radius; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_force_center +// Access : public +// Description : force_center query +//////////////////////////////////////////////////////////////////// +INLINE LPoint3f LinearDistanceForce:: +get_force_center(void) const { + return _force_center; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_scalar_term +// Access : private +// Description : calculate the term based on falloff +//////////////////////////////////////////////////////////////////// +INLINE float LinearDistanceForce:: +get_scalar_term(void) const { + float r = _radius; + if (_falloff == FT_ONE_OVER_R_SQUARED) + r = r * r; + else if (_falloff == FT_ONE_OVER_R_CUBED) + r = r * r * r; + + return (1.0f / r); +} diff --git a/panda/src/physics/linearDistanceForce.cxx b/panda/src/physics/linearDistanceForce.cxx new file mode 100644 index 0000000000..56785dcf28 --- /dev/null +++ b/panda/src/physics/linearDistanceForce.cxx @@ -0,0 +1,41 @@ +// Filename: LinearDistanceForce.cxx +// Created by: charles (21Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "linearDistanceForce.h" + +TypeHandle LinearDistanceForce::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : LinearDistanceForce +// Access : Protected +// Description : Simple constructor +//////////////////////////////////////////////////////////////////// +LinearDistanceForce:: +LinearDistanceForce(const LPoint3f& p, FalloffType ft, float r, float a, bool m) : + _falloff(ft), _radius(r), _force_center(p), + LinearForce(a, m) { +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearDistanceForce +// Access : Protected +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +LinearDistanceForce:: +LinearDistanceForce(const LinearDistanceForce ©) : + LinearForce(copy) { + _falloff = copy._falloff; + _radius = copy._radius; + _force_center = copy._force_center; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~LinearDistanceForce +// Access : Protected +// Description : destructor +//////////////////////////////////////////////////////////////////// +LinearDistanceForce:: +~LinearDistanceForce(void) { +} diff --git a/panda/src/physics/linearDistanceForce.h b/panda/src/physics/linearDistanceForce.h new file mode 100644 index 0000000000..8ed96c41e5 --- /dev/null +++ b/panda/src/physics/linearDistanceForce.h @@ -0,0 +1,70 @@ +// Filename: LinearDistanceForce.h +// Created by: charles (21Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINEARDISTANCEFORCE_H +#define LINEARDISTANCEFORCE_H + +#include "linearForce.h" + +enum FalloffType { + FT_ONE_OVER_R, + FT_ONE_OVER_R_SQUARED, + FT_ONE_OVER_R_CUBED +}; + +class BamReader; + +//////////////////////////////////////////////////////////////////// +// Class : LinearDistanceForce +// Description : Pure virtual class for sinks and sources +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS LinearDistanceForce : public LinearForce { +public: + INLINE void set_radius(float r); + INLINE void set_falloff_type(FalloffType ft); + INLINE void set_force_center(const LPoint3f& p); + + INLINE float get_radius(void) const; + INLINE FalloffType get_falloff_type(void) const; + INLINE LPoint3f get_force_center(void) const; + + INLINE float get_scalar_term(void) const; + +private: + LPoint3f _force_center; + + FalloffType _falloff; + float _radius; + + virtual LinearForce *make_copy(void) = 0; + virtual LVector3f get_child_vector(const PhysicsObject *po) = 0; + +protected: + LinearDistanceForce(const LPoint3f& p, FalloffType ft, float r, float a, + bool m); + LinearDistanceForce(const LinearDistanceForce ©); + virtual ~LinearDistanceForce(void); + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LinearForce::init_type(); + register_type(_type_handle, "LinearDistanceForce", + LinearForce::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "linearDistanceForce.I" + +#endif // LINEARDISTANCEFORCE_H diff --git a/panda/src/physics/linearEulerIntegrator.cxx b/panda/src/physics/linearEulerIntegrator.cxx new file mode 100644 index 0000000000..163bb5bd44 --- /dev/null +++ b/panda/src/physics/linearEulerIntegrator.cxx @@ -0,0 +1,168 @@ +// Filename: LinearEulerIntegrator.cxx +// Created by: charles (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "linearEulerIntegrator.h" +#include "forceNode.h" +#include "physicalNode.h" +#include "config_physics.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function : LinearEulerIntegrator +// Access : Public +// Description : constructor +//////////////////////////////////////////////////////////////////// +LinearEulerIntegrator:: +LinearEulerIntegrator(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearEulerIntegrator +// Access : Public +// Description : destructor +//////////////////////////////////////////////////////////////////// +LinearEulerIntegrator:: +~LinearEulerIntegrator(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : Integrate +// Access : Public +// Description : Integrate a step of motion (based on dt) by +// applying every force in force_vec to every object +// in obj_vec. +//////////////////////////////////////////////////////////////////// +void LinearEulerIntegrator:: +child_integrate(Physical *physical, + vector< PT(LinearForce) >& forces, + float dt) { + vector< PT(PhysicsObject) >::const_iterator current_object_iter; + + // perform the precomputation. Note that the vector returned by + // get_precomputed_matrices() has the matrices loaded in order of force + // type: first global, then local. If you're using this as a guide to write + // another integrator, be sure to process your forces global, then local. + // otherwise your transforms will be VERY bad. + precompute_linear_matrices(physical, forces); + const vector< LMatrix4f > &matrices = get_precomputed_linear_matrices(); + + // Loop through each object in the set. This processing occurs in O(pf) time, + // where p is the number of physical objects and f is the number of + // forces. Unfortunately, no precomputation of forces can occur, as + // each force is possibly contingent on such things as the position and + // velocity of each physicsobject in the set. Accordingly, we have + // to grunt our way through each one. wrt caching of the xform matrix + // should help. + + current_object_iter = physical->get_object_vector().begin(); + for (; current_object_iter != physical->get_object_vector().end(); + current_object_iter++) { + LVector3f md_accum_vec, non_md_accum_vec, accel_vec, vel_vec; + LPoint3f pos; + float mass; + + PhysicsObject *current_object = *current_object_iter; + + // bail out if this object doesn't exist or doesn't want to be + // processed. + if (current_object == (PhysicsObject *) NULL) + continue; + + if (current_object->get_active() == false) + continue; + + // reset the accumulation vectors for this object + md_accum_vec.set(0.0f, 0.0f, 0.0f); + non_md_accum_vec.set(0.0f, 0.0f, 0.0f); + + // run through each acting force and sum it + LVector3f f; + LMatrix4f force_to_object_xform; + + ForceNode *force_node; + vector< PT(LinearForce) >::const_iterator f_cur; + + // global forces + f_cur = forces.begin(); + int index = 0; + for (; f_cur != forces.end(); f_cur++) { + LinearForce *cur_force = *f_cur; + + // make sure the force is turned on. + if (cur_force->get_active() == false) + continue; + + force_node = cur_force->get_force_node(); + + // now we go from force space to our object's space. + f = matrices[index++] * cur_force->get_vector(current_object); + + // tally it into the accum vectors. + if (cur_force->get_mass_dependent() == true) + md_accum_vec += f; + else + non_md_accum_vec += f; + } + + // local forces + f_cur = physical->get_linear_forces().begin(); + for (; f_cur != physical->get_linear_forces().end(); f_cur++) { + LinearForce *cur_force = *f_cur; + + // make sure the force is turned on. + if (cur_force->get_active() == false) + continue; + + force_node = cur_force->get_force_node(); + + // go from force space to object space + f = matrices[index++] * cur_force->get_vector(current_object); + + // tally it into the accum vectors + if (cur_force->get_mass_dependent() == true) + md_accum_vec += f; + else + non_md_accum_vec += f; + } + + // get this object's physical info + pos = current_object->get_position(); + vel_vec = current_object->get_velocity(); + mass = current_object->get_mass(); + + // we want 'a' in F = ma + // get it by computing F / m + nassertv(mass != 0.0f); + + accel_vec = md_accum_vec / mass; + accel_vec += non_md_accum_vec; + + // step the position and velocity + vel_vec += accel_vec * dt; + + // cap terminal velocity + float len = vel_vec.length(); + + if (len > current_object->get_terminal_velocity()) { + cout << "Capping terminal velocity at: " << current_object->get_terminal_velocity() << endl; + vel_vec *= current_object->get_terminal_velocity() / len; + } + + pos += vel_vec * dt; + + // and store them back. + current_object->set_position(pos); + current_object->set_velocity(vel_vec); + } +} + + + + + + + + diff --git a/panda/src/physics/linearEulerIntegrator.h b/panda/src/physics/linearEulerIntegrator.h new file mode 100644 index 0000000000..a06792cb3d --- /dev/null +++ b/panda/src/physics/linearEulerIntegrator.h @@ -0,0 +1,27 @@ +// Filename: lineareulerintegrator.h +// Created by: charles (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINEAREULERINTEGRATOR_H +#define LINEAREULERINTEGRATOR_H + +#include "linearIntegrator.h" + +//////////////////////////////////////////////////////////////////// +// Class : LINEAREulerIntegrator +// Description : Performs Euler integration on a vector of +// physically modelable objects given a quantum dt. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS LinearEulerIntegrator : public LinearIntegrator { +private: + virtual void child_integrate(Physical *physical, + vector< PT(LinearForce) >& forces, + float dt); + +public: + LinearEulerIntegrator(void); + virtual ~LinearEulerIntegrator(void); +}; + +#endif // EULERINTEGRATOR_H diff --git a/panda/src/physics/linearForce.I b/panda/src/physics/linearForce.I new file mode 100644 index 0000000000..810b35f072 --- /dev/null +++ b/panda/src/physics/linearForce.I @@ -0,0 +1,51 @@ +// Filename: linearForce.I +// Created by: charles (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_amplitude +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE void LinearForce:: +set_amplitude(float a) { + _amplitude = a; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_amplitude +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE float LinearForce:: +get_amplitude(void) const { + return _amplitude; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_mass_dependent +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE bool LinearForce:: +get_mass_dependent(void) const { + return _mass_dependent; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_mass_Dependent +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE void LinearForce:: +set_mass_dependent(bool m) { + _mass_dependent = m; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_vector_masks +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE void LinearForce:: +set_vector_masks(bool x, bool y, bool z) { + _x_mask = x; + _y_mask = y; + _z_mask = z; +} diff --git a/panda/src/physics/linearForce.cxx b/panda/src/physics/linearForce.cxx new file mode 100644 index 0000000000..151a6213a7 --- /dev/null +++ b/panda/src/physics/linearForce.cxx @@ -0,0 +1,69 @@ +// Filename: LinearForce.cxx +// Created by: charles (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +#include "linearForce.h" + +TypeHandle LinearForce::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : LinearForce +// Access : Protected +// Description : Default/component-based constructor +//////////////////////////////////////////////////////////////////// +LinearForce:: +LinearForce(float a, bool mass) : + BaseForce(true), + _amplitude(a), _mass_dependent(mass), + _x_mask(true), _y_mask(true), _z_mask(true) { +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearForce +// Access : Protected +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +LinearForce:: +LinearForce(const LinearForce& copy) : + BaseForce(copy) { + _amplitude = copy._amplitude; + _mass_dependent = copy._mass_dependent; + _x_mask = copy._x_mask; + _y_mask = copy._y_mask; + _z_mask = copy._z_mask; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~LinearForce +// Access : Public +// Description : Destructor +//////////////////////////////////////////////////////////////////// +LinearForce:: +~LinearForce(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : get_vector +// Access : Public +//////////////////////////////////////////////////////////////////// +LVector3f LinearForce:: +get_vector(const PhysicsObject *po) { + LVector3f child_vector = get_child_vector(po) * _amplitude; + + if (_x_mask == false) + child_vector[0] = 0.0f; + + if (_y_mask == false) + child_vector[1] = 0.0f; + + if (_z_mask == false) + child_vector[2] = 0.0f; + + return child_vector; +} diff --git a/panda/src/physics/linearForce.h b/panda/src/physics/linearForce.h new file mode 100644 index 0000000000..6125604334 --- /dev/null +++ b/panda/src/physics/linearForce.h @@ -0,0 +1,66 @@ +// Filename: LinearForce.h +// Created by: charles (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINEARFORCE_H +#define LINEARFORCE_H + +#include "baseForce.h" + +/////////////////////////////////////////////////////////////////// +// Class : LinearForce +// Description : A force that acts on a PhysicsObject by way of an +// Integrator. This is a pure virtual base class. +/////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS LinearForce : public BaseForce { +private: + float _amplitude; + bool _mass_dependent; + + bool _x_mask; + bool _y_mask; + bool _z_mask; + + virtual LVector3f get_child_vector(const PhysicsObject *po) = 0; + +protected: + LinearForce(float a, bool mass); + LinearForce(const LinearForce& copy); + +public: + ~LinearForce(void); + + INLINE void set_amplitude(const float a); + INLINE void set_mass_dependent(bool m); + + INLINE float get_amplitude(void) const; + INLINE bool get_mass_dependent(void) const; + + INLINE void set_vector_masks(bool x, bool y, bool z); + + virtual LVector3f get_vector(const PhysicsObject *po); + + virtual LinearForce *make_copy(void) = 0; + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + BaseForce::init_type(); + register_type(_type_handle, "LinearForce", + BaseForce::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "linearForce.I" + +#endif // BASELINEARFORCE_H diff --git a/panda/src/physics/linearFrictionForce.I b/panda/src/physics/linearFrictionForce.I new file mode 100644 index 0000000000..8822d694fd --- /dev/null +++ b/panda/src/physics/linearFrictionForce.I @@ -0,0 +1,22 @@ +// Filename: LinearFrictionForce.I +// Created by: charles (31Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_coef +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void LinearFrictionForce:: +set_coef(float coef) { + _coef = coef; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_coef +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE float LinearFrictionForce:: +get_coef(void) const { + return _coef; +} diff --git a/panda/src/physics/linearFrictionForce.cxx b/panda/src/physics/linearFrictionForce.cxx new file mode 100644 index 0000000000..287709a074 --- /dev/null +++ b/panda/src/physics/linearFrictionForce.cxx @@ -0,0 +1,73 @@ +// Filename: LinearFrictionForce.cxx +// Created by: charles (23Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "linearFrictionForce.h" + +TypeHandle LinearFrictionForce::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : LinearFrictionForce +// Access : Public +// Description : Constructor +//////////////////////////////////////////////////////////////////// +LinearFrictionForce:: +LinearFrictionForce(float coef, float a, bool m) : + LinearForce(a, m) { + + // friction REALLY shouldn't be outside of [0, 1] + if (coef < 0.0f) + coef = 0.0f; + else if (coef > 1.0f) + coef = 1.0f; + + _coef = coef; +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearFrictionForce +// Access : Public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +LinearFrictionForce:: +LinearFrictionForce(const LinearFrictionForce ©) : + LinearForce(copy) { + _coef = copy._coef; +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearFrictionForce +// Access : Public +// Description : destructor +//////////////////////////////////////////////////////////////////// +LinearFrictionForce:: +~LinearFrictionForce(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : Public +// Description : copier +//////////////////////////////////////////////////////////////////// +LinearForce *LinearFrictionForce:: +make_copy(void) { + return new LinearFrictionForce(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearFrictionForce +// Access : Public +// Description : Constructor +//////////////////////////////////////////////////////////////////// +LVector3f LinearFrictionForce:: +get_child_vector(const PhysicsObject* po) { + LVector3f v = po->get_velocity(); + LVector3f friction = -v * (1.0f - _coef); + + // cary said to cap this at zero so that friction can't reverse + // your direction, but it seems to me that if you're computing: + // v + (-v * _coef), _coef in [0, 1] + // that this will always be greater than or equal to zero. + return friction; +} diff --git a/panda/src/physics/linearFrictionForce.h b/panda/src/physics/linearFrictionForce.h new file mode 100644 index 0000000000..820b7cd5cb --- /dev/null +++ b/panda/src/physics/linearFrictionForce.h @@ -0,0 +1,50 @@ +// Filename: LinearFrictionForce.h +// Created by: charles (23Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINEARFRICTIONFORCE_H +#define LINEARFRICTIONFORCE_H + +#include "linearForce.h" + +//////////////////////////////////////////////////////////////////// +// Class : LinearFrictionForce +// Description : Friction-based drag force +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS LinearFrictionForce : public LinearForce { +private: + float _coef; + + virtual LinearForce *make_copy(void); + virtual LVector3f get_child_vector(const PhysicsObject *); + +public: + LinearFrictionForce(float coef = 1.0f, float a = 1.0f, bool m = false); + LinearFrictionForce(const LinearFrictionForce ©); + virtual ~LinearFrictionForce(void); + + INLINE void set_coef(float coef); + INLINE float get_coef(void) const; + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LinearForce::init_type(); + register_type(_type_handle, "LinearFrictionForce", + LinearForce::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "linearFrictionForce.I" + +#endif // LINEARFRICTIONFORCE_H diff --git a/panda/src/physics/linearIntegrator.cxx b/panda/src/physics/linearIntegrator.cxx new file mode 100644 index 0000000000..58912e85d7 --- /dev/null +++ b/panda/src/physics/linearIntegrator.cxx @@ -0,0 +1,45 @@ +// Filename: LinearIntegrator.cxx +// Created by: charles (02Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "linearIntegrator.h" +#include "config_physics.h" +#include "physicalNode.h" +#include "forceNode.h" + +//////////////////////////////////////////////////////////////////// +// Function : BaseLinearIntegrator +// Access : Protected +// Description : constructor +//////////////////////////////////////////////////////////////////// +LinearIntegrator:: +LinearIntegrator(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : ~LinearIntegrator +// Access : public, virtual +// Description : destructor +//////////////////////////////////////////////////////////////////// +LinearIntegrator:: +~LinearIntegrator(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : integrate +// Access : public +// Description : parent integration routine, hands off to child +// virtual. +//////////////////////////////////////////////////////////////////// +void LinearIntegrator:: +integrate(Physical *physical, vector< PT(LinearForce) > &forces, + float dt) { + // cap dt so physics don't go flying off on lags + if (dt > _max_linear_dt) + dt = _max_linear_dt; + + child_integrate(physical, forces, dt); +} diff --git a/panda/src/physics/linearIntegrator.h b/panda/src/physics/linearIntegrator.h new file mode 100644 index 0000000000..604b736bb9 --- /dev/null +++ b/panda/src/physics/linearIntegrator.h @@ -0,0 +1,38 @@ +// Filename: LinearIntegrator.h +// Created by: charles (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINEARINTEGRATOR_H +#define LINEARINTEGRATOR_H + +#include "physicsObject.h" +#include "baseIntegrator.h" +#include "linearForce.h" + +//////////////////////////////////////////////////////////////////// +// Class : LinearIntegrator +// Description : Pure virtual base class for physical modeling. +// Takes physically modelable objects and applies +// forces to them. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS LinearIntegrator : public BaseIntegrator { +private: + static const float _max_linear_dt; + + // this allows baseLinearIntegrator to censor/modify data that the + // actual integration function receives. + virtual void child_integrate(Physical *physical, vector< PT(LinearForce) > &forces, + float dt) = 0; + +protected: + LinearIntegrator(void); + +public: + virtual ~LinearIntegrator(void); + + void integrate(Physical *physical, vector< PT(LinearForce) > &forces, + float dt); +}; + +#endif // LINEARINTEGRATOR_H diff --git a/panda/src/physics/linearJitterForce.cxx b/panda/src/physics/linearJitterForce.cxx new file mode 100644 index 0000000000..5cddca7f35 --- /dev/null +++ b/panda/src/physics/linearJitterForce.cxx @@ -0,0 +1,57 @@ +// Filename: LinearJitterForce.cxx +// Created by: charles (16Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "linearJitterForce.h" + +TypeHandle LinearJitterForce::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : LinearJitterForce +// Access : Public +// Description : constructor +//////////////////////////////////////////////////////////////////// +LinearJitterForce:: +LinearJitterForce(float a, bool mass) : + LinearRandomForce(a, mass) { +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearJitterForce +// Access : Public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +LinearJitterForce:: +LinearJitterForce(const LinearJitterForce ©) : + LinearRandomForce(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearJitterForce +// Access : Public +// Description : constructor +//////////////////////////////////////////////////////////////////// +LinearJitterForce:: +~LinearJitterForce(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : Public +// Description : copier +//////////////////////////////////////////////////////////////////// +LinearForce *LinearJitterForce:: +make_copy(void) { + return new LinearJitterForce(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : get_child_vector +// Access : Public +// Description : random value +//////////////////////////////////////////////////////////////////// +LVector3f LinearJitterForce:: +get_child_vector(const PhysicsObject *) { + return random_unit_vector(); +} diff --git a/panda/src/physics/linearJitterForce.h b/panda/src/physics/linearJitterForce.h new file mode 100644 index 0000000000..d7a677b48a --- /dev/null +++ b/panda/src/physics/linearJitterForce.h @@ -0,0 +1,44 @@ +// Filename: LinearJitterForce.h +// Created by: charles (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINEARJITTERFORCE_H +#define LINEARJITTERFORCE_H + +#include "linearRandomForce.h" + +//////////////////////////////////////////////////////////////////// +// Class : LinearJitterForce +// Description : Completely random noise force vector. Not +// repeatable, reliable, or predictable. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS LinearJitterForce : public LinearRandomForce { +private: + virtual LVector3f get_child_vector(const PhysicsObject *po); + virtual LinearForce *make_copy(void); + +public: + LinearJitterForce(float a = 1.0f, bool m = false); + LinearJitterForce(const LinearJitterForce ©); + virtual ~LinearJitterForce(void); + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LinearRandomForce::init_type(); + register_type(_type_handle, "LinearJitterForce", + LinearRandomForce::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif // LINEARJITTERFORCE_H diff --git a/panda/src/physics/linearNoiseForce.I b/panda/src/physics/linearNoiseForce.I new file mode 100644 index 0000000000..efec0a5b22 --- /dev/null +++ b/panda/src/physics/linearNoiseForce.I @@ -0,0 +1,75 @@ +// Filename: LinearNoiseForce.I +// Created by: charles (19Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : prn_lookup +// Access : Private +// Description : Returns a valid entry in the prn table +//////////////////////////////////////////////////////////////////// +INLINE unsigned char LinearNoiseForce:: +prn_lookup(int index) const { + return _prn_table[index & 255]; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_prn_entry +// Access : Private +// Description : Hashes a point, returns a prn +//////////////////////////////////////////////////////////////////// +INLINE unsigned char LinearNoiseForce:: +get_prn_entry(const LPoint3f& point) const { + return prn_lookup(point[0] + prn_lookup(point[1] + prn_lookup(point[2]))); +} + +//////////////////////////////////////////////////////////////////// +// Function : get_prn_entry +// Access : Private +// Description : Hashes a point, returns a prn (piecewise) +//////////////////////////////////////////////////////////////////// +INLINE unsigned char LinearNoiseForce:: +get_prn_entry(const float x, const float y, const float z) const { + return prn_lookup(x + prn_lookup(y + prn_lookup(z))); +} + +//////////////////////////////////////////////////////////////////// +// Function : get_lattice_entry +// Access : Private +// Description : Hashes a point, returns a gradient vector +//////////////////////////////////////////////////////////////////// +INLINE LVector3f& LinearNoiseForce:: +get_lattice_entry(const LPoint3f& point) { + return _gradient_table[get_prn_entry(point)]; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_lattice_entry +// Access : Private +// Description : Hashes a point, returns a gradient vector (piecewise) +//////////////////////////////////////////////////////////////////// +INLINE LVector3f& LinearNoiseForce:: +get_lattice_entry(const float x, const float y, const float z) { + return _gradient_table[get_prn_entry(x, y, z)]; +} + +//////////////////////////////////////////////////////////////////// +// Function : cubic_step +// Access : Private +// Description : Smooths a parameterized interpolation using +// 2x^3 - 3x^2 +//////////////////////////////////////////////////////////////////// +INLINE float LinearNoiseForce:: +cubic_step(const float x) const { + return x * x * ((2 * x) - 3); +} + +//////////////////////////////////////////////////////////////////// +// Function : vlerp +// Access : Private +// Description : Vector linear interpolation +//////////////////////////////////////////////////////////////////// +INLINE LVector3f LinearNoiseForce:: +vlerp(const float t, const LVector3f& v0, const LVector3f& v1) const { + return v0 + ((v1 - v0) * t); +} diff --git a/panda/src/physics/linearNoiseForce.cxx b/panda/src/physics/linearNoiseForce.cxx new file mode 100644 index 0000000000..4cafd51b37 --- /dev/null +++ b/panda/src/physics/linearNoiseForce.cxx @@ -0,0 +1,138 @@ +// Filename: linearNoiseForce.cxx +// Created by: charles (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include + +#include "linearNoiseForce.h" + +// declare the statics + +bool LinearNoiseForce::_initialized = false; +unsigned char LinearNoiseForce::_prn_table[256]; +LVector3f LinearNoiseForce::_gradient_table[256]; + +TypeHandle LinearNoiseForce::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : InitNoiseTables +// Access : Public +// Description : One-time config function, sets up the PRN +// lattice. +//////////////////////////////////////////////////////////////////// +void LinearNoiseForce:: +init_noise_tables(void) { + int i; + LVector3f *gtable = _gradient_table; + + // since this is a repeatable noise function, we always want + // to init with the same seed. + srand(_random_seed); + + for (i = 0; i < 256; i++) { + // fill the 1d array + _prn_table[i] = rand() & 255; + + // now fill the gradient array + *gtable++ = random_unit_vector(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearNoiseForce +// Access : Public +// Description : constructor +//////////////////////////////////////////////////////////////////// +LinearNoiseForce:: +LinearNoiseForce(float a, bool mass) : + LinearRandomForce(a, mass) { + if (_initialized == false) { + init_noise_tables(); + _initialized = true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearNoiseForce +// Access : Public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +LinearNoiseForce:: +LinearNoiseForce(const LinearNoiseForce ©) : + LinearRandomForce(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function : ~LinearNoiseForce +// Access : Public +// Description : destructor +//////////////////////////////////////////////////////////////////// +LinearNoiseForce:: +~LinearNoiseForce(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : Public +// Description : copier +//////////////////////////////////////////////////////////////////// +LinearForce *LinearNoiseForce:: +make_copy(void) { + return new LinearNoiseForce(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : get_child_vector +// Access : Public +// Description : Returns the noise value based on the object's +// position. +//////////////////////////////////////////////////////////////////// +LVector3f LinearNoiseForce:: +get_child_vector(const PhysicsObject *po) { + LPoint3f p = po->get_position(); + + // get all of the components + int int_x, int_y, int_z; + float frac_x, frac_y, frac_z; + + int_x = (int) p[0]; + frac_x = p[0] - int_x; + + int_y = (int) p[1]; + frac_y = p[1] - int_y; + + int_z = (int) p[2]; + frac_z = p[2] - int_z; + + // apply the cubic smoother to the fractional values + float cubic_x, cubic_y, cubic_z; + + cubic_x = cubic_step(frac_x); + cubic_y = cubic_step(frac_y); + cubic_z = cubic_step(frac_z); + + // trilinear interpolation into the cube (over, in, down) + LVector3f temp0, temp1, temp2, temp3; + + // x direction + temp0 = vlerp(cubic_x, get_lattice_entry(p), + get_lattice_entry(p[0] + 1.0f, p[1], p[2])); + + temp1 = vlerp(cubic_x, get_lattice_entry(p[0], p[1], p[2] + 1.0f), + get_lattice_entry(p[0] + 1.0f, p[1], p[2] + 1.0f)); + + temp2 = vlerp(cubic_x, get_lattice_entry(p[0], p[1] + 1.0f, p[2]), + get_lattice_entry(p[0] + 1.0f, p[1] + 1.0f, p[2])); + + temp3 = vlerp(cubic_x, get_lattice_entry(p[0], p[1] + 1.0f, p[2] + 1.0f), + get_lattice_entry(p[0] + 1.0f, p[1] + 1.0f, p[2] + 1.0f)); + + // y direction + temp0 = vlerp(cubic_z, temp0, temp1); + temp1 = vlerp(cubic_z, temp2, temp3); + + // z direction + return vlerp(cubic_y, temp0, temp1); +} diff --git a/panda/src/physics/linearNoiseForce.h b/panda/src/physics/linearNoiseForce.h new file mode 100644 index 0000000000..60959a6576 --- /dev/null +++ b/panda/src/physics/linearNoiseForce.h @@ -0,0 +1,63 @@ +// Filename: LinearNoiseForce.h +// Created by: charles (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINEARNOISEFORCE_H +#define LINEARNOISEFORCE_H + +#include "linearRandomForce.h" + +//////////////////////////////////////////////////////////////////// +// Class : LinearNoiseForce +// Description : Repeating noise force vector. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS LinearNoiseForce : public LinearRandomForce { +private: + static unsigned char _prn_table[256]; + static LVector3f _gradient_table[256]; + static bool _initialized; + + INLINE float cubic_step(const float x) const; + INLINE LVector3f vlerp(const float t, const LVector3f& v0, const LVector3f& v1) const; + + INLINE unsigned char get_prn_entry(const LPoint3f& point) const; + INLINE unsigned char get_prn_entry(const float x, const float y, const float z) const; + + INLINE LVector3f& get_lattice_entry(const LPoint3f& point); + INLINE LVector3f& get_lattice_entry(const float x, const float y, const float z); + + INLINE unsigned char prn_lookup(int index) const; + + virtual LVector3f get_child_vector(const PhysicsObject *po); + virtual LinearForce *make_copy(void); + +public: + static int _random_seed; + static void init_noise_tables(void); + + LinearNoiseForce(float a = 1.0f, bool m = false); + LinearNoiseForce(const LinearNoiseForce ©); + virtual ~LinearNoiseForce(void); + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LinearRandomForce::init_type(); + register_type(_type_handle, "LinearNoiseForce", + LinearRandomForce::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "linearNoiseForce.I" + +#endif // LINEARNOISEFORCE_H diff --git a/panda/src/physics/linearRandomForce.I b/panda/src/physics/linearRandomForce.I new file mode 100644 index 0000000000..de5a986a94 --- /dev/null +++ b/panda/src/physics/linearRandomForce.I @@ -0,0 +1,21 @@ +// Filename: LinearRandomForce.I +// Created by: charles (19Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : random_unit_vector +// Access : Protected +// Description : generates a random unit vector +//////////////////////////////////////////////////////////////////// +INLINE LVector3f LinearRandomForce:: +random_unit_vector(void) { + float z, r, theta; + + z = 1.0f - (2.0f * bounded_rand()); + r = sqrtf(1.0f - (z * z)); + theta = 2.0f * 3.1415926f * bounded_rand(); + + return LVector3f(r * cosf(theta), r * sinf(theta), z); +} + diff --git a/panda/src/physics/linearRandomForce.cxx b/panda/src/physics/linearRandomForce.cxx new file mode 100644 index 0000000000..7f46d9bd1c --- /dev/null +++ b/panda/src/physics/linearRandomForce.cxx @@ -0,0 +1,50 @@ +// Filename: LinearRandomForce.cxx +// Created by: charles (19Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "linearRandomForce.h" + +TypeHandle LinearRandomForce::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : LinearRandomForce +// Access : Protected +// Description : vector constructor +//////////////////////////////////////////////////////////////////// +LinearRandomForce:: +LinearRandomForce(float a, bool mass) : + LinearForce(a, mass) { +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearRandomForce +// Access : Protected +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +LinearRandomForce:: +LinearRandomForce(const LinearRandomForce ©) : + LinearForce(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function : ~LinearRandomForce +// Access : public +// Description : destructor +//////////////////////////////////////////////////////////////////// +LinearRandomForce:: +~LinearRandomForce(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : bounded_rand +// Access : Protected +// Description : Returns a float in [0, 1] +//////////////////////////////////////////////////////////////////// +float LinearRandomForce:: +bounded_rand(void) { + int val = rand() & 0x7fffffff; + float f_val = (float) val / (float) 0x7fffffff; + + return f_val; +} diff --git a/panda/src/physics/linearRandomForce.h b/panda/src/physics/linearRandomForce.h new file mode 100644 index 0000000000..9d3123c8ba --- /dev/null +++ b/panda/src/physics/linearRandomForce.h @@ -0,0 +1,52 @@ +// Filename: LinearRandomForce.h +// Created by: charles (19Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINEARRANDOMFORCE_H +#define LINEARRANDOMFORCE_H + +#include +#include + +#include "linearForce.h" + +//////////////////////////////////////////////////////////////////// +// Class : LinearRandomForce +// Description : Pure virtual, parent to noiseForce and jitterForce +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS LinearRandomForce : public LinearForce { +protected: + static float bounded_rand(void); + static LVector3f random_unit_vector(void); + + LinearRandomForce(float a = 1.0f, bool m = false); + LinearRandomForce(const LinearRandomForce ©); + + virtual LVector3f get_child_vector(const PhysicsObject *po) = 0; + virtual LinearForce *make_copy(void) = 0; + +public: + virtual ~LinearRandomForce(void); + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LinearForce::init_type(); + register_type(_type_handle, "LinearRandomForce", + LinearForce::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "linearRandomForce.I" + +#endif // LINEARRANDOMFORCE_H diff --git a/panda/src/physics/linearSinkForce.cxx b/panda/src/physics/linearSinkForce.cxx new file mode 100644 index 0000000000..517975a81c --- /dev/null +++ b/panda/src/physics/linearSinkForce.cxx @@ -0,0 +1,69 @@ +// Filename: LinearSinkForce.cxx +// Created by: charles (21Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "linearSinkForce.h" + +TypeHandle LinearSinkForce::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : LinearSinkForce +// Access : Public +// Description : Simple constructor +//////////////////////////////////////////////////////////////////// +LinearSinkForce:: +LinearSinkForce(const LPoint3f& p, FalloffType f, float r, float a, + bool mass) : + LinearDistanceForce(p, f, r, a, mass) { +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearSinkForce +// Access : Public +// Description : Simple constructor +//////////////////////////////////////////////////////////////////// +LinearSinkForce:: +LinearSinkForce(void) : + LinearDistanceForce(LPoint3f(0.0f, 0.0f, 0.0f), FT_ONE_OVER_R_SQUARED, + 1.0f, 1.0f, true) { +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearSinkForce +// Access : Public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +LinearSinkForce:: +LinearSinkForce(const LinearSinkForce ©) : + LinearDistanceForce(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function : ~LinearSinkForce +// Access : Public +// Description : Simple destructor +//////////////////////////////////////////////////////////////////// +LinearSinkForce:: +~LinearSinkForce(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : Public +// Description : copier +//////////////////////////////////////////////////////////////////// +LinearForce *LinearSinkForce:: +make_copy(void) { + return new LinearSinkForce(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : get_child_vector +// Access : Public +// Description : virtual force query +//////////////////////////////////////////////////////////////////// +LVector3f LinearSinkForce:: +get_child_vector(const PhysicsObject *po) { + return (get_force_center() - po->get_position()) * get_scalar_term(); +} diff --git a/panda/src/physics/linearSinkForce.h b/panda/src/physics/linearSinkForce.h new file mode 100644 index 0000000000..394393e6be --- /dev/null +++ b/panda/src/physics/linearSinkForce.h @@ -0,0 +1,45 @@ +// Filename: LinearSinkForce.h +// Created by: charles (21Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINEARSINKFORCE_H +#define LINEARSINKFORCE_H + +#include "linearDistanceForce.h" + +//////////////////////////////////////////////////////////////////// +// Class : LinearSinkForce +// Description : Attractor force. Think black hole. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS LinearSinkForce : public LinearDistanceForce { +private: + virtual LVector3f get_child_vector(const PhysicsObject *po); + virtual LinearForce *make_copy(void); + +public: + LinearSinkForce(const LPoint3f& p, FalloffType f, float r, float a = 1.0f, + bool m = true); + LinearSinkForce(void); + LinearSinkForce(const LinearSinkForce ©); + virtual ~LinearSinkForce(void); + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LinearDistanceForce::init_type(); + register_type(_type_handle, "LinearSinkForce", + LinearDistanceForce::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif // LINEARSINKFORCE_H diff --git a/panda/src/physics/linearSourceForce.cxx b/panda/src/physics/linearSourceForce.cxx new file mode 100644 index 0000000000..8b9455ec44 --- /dev/null +++ b/panda/src/physics/linearSourceForce.cxx @@ -0,0 +1,69 @@ +// Filename: LinearSourceForce.cxx +// Created by: charles (21Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "linearSourceForce.h" + +TypeHandle LinearSourceForce::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : LinearSourceForce +// Access : Public +// Description : Simple constructor +//////////////////////////////////////////////////////////////////// +LinearSourceForce:: +LinearSourceForce(const LPoint3f& p, FalloffType f, float r, float a, + bool mass) : + LinearDistanceForce(p, f, r, a, mass) { +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearSourceForce +// Access : Public +// Description : Simple constructor +//////////////////////////////////////////////////////////////////// +LinearSourceForce:: +LinearSourceForce(void) : + LinearDistanceForce(LPoint3f(0.0f, 0.0f, 0.0f), FT_ONE_OVER_R_SQUARED, + 1.0f, 1.0f, true) { +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearSourceForce +// Access : Public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +LinearSourceForce:: +LinearSourceForce(const LinearSourceForce ©) : + LinearDistanceForce(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function : ~LinearSourceForce +// Access : Public +// Description : Simple destructor +//////////////////////////////////////////////////////////////////// +LinearSourceForce:: +~LinearSourceForce(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : Public +// Description : copier +//////////////////////////////////////////////////////////////////// +LinearForce *LinearSourceForce:: +make_copy(void) { + return new LinearSourceForce(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : get_child_vector +// Access : Public +// Description : virtual force query +//////////////////////////////////////////////////////////////////// +LVector3f LinearSourceForce:: +get_child_vector(const PhysicsObject *po) { + return (po->get_position() - get_force_center()) * get_scalar_term(); +} diff --git a/panda/src/physics/linearSourceForce.h b/panda/src/physics/linearSourceForce.h new file mode 100644 index 0000000000..4f0861dd30 --- /dev/null +++ b/panda/src/physics/linearSourceForce.h @@ -0,0 +1,45 @@ +// Filename: LinearSourceForce.h +// Created by: charles (21Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINEARSOURCEFORCE_H +#define LINEARSOURCEFORCE_H + +#include "linearDistanceForce.h" + +//////////////////////////////////////////////////////////////////// +// Class : LinearSourceForce +// Description : Repellant force. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS LinearSourceForce : public LinearDistanceForce { +private: + virtual LVector3f get_child_vector(const PhysicsObject *po); + virtual LinearForce *make_copy(void); + +public: + LinearSourceForce(const LPoint3f& p, FalloffType f, float r, float a = 1.0f, + bool mass = true); + LinearSourceForce(void); + LinearSourceForce(const LinearSourceForce ©); + virtual ~LinearSourceForce(void); + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LinearDistanceForce::init_type(); + register_type(_type_handle, "LinearSourceForce", + LinearDistanceForce::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif // LINEARSOURCEFORCE_H diff --git a/panda/src/physics/linearUserDefinedForce.I b/panda/src/physics/linearUserDefinedForce.I new file mode 100644 index 0000000000..12db53abae --- /dev/null +++ b/panda/src/physics/linearUserDefinedForce.I @@ -0,0 +1,13 @@ +// Filename: LinearUserDefinedForce.I +// Created by: charles (31Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_proc +// Access : public +//////////////////////////////////////////////////////////////////// +void LinearUserDefinedForce:: +set_proc(LVector3f (*proc)(const PhysicsObject *)) { + _proc = proc; +} diff --git a/panda/src/physics/linearUserDefinedForce.cxx b/panda/src/physics/linearUserDefinedForce.cxx new file mode 100644 index 0000000000..ef9e336278 --- /dev/null +++ b/panda/src/physics/linearUserDefinedForce.cxx @@ -0,0 +1,59 @@ +// Filename: LinearUserDefinedForce.cxx +// Created by: charles (31Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "linearUserDefinedForce.h" + +TypeHandle LinearUserDefinedForce::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : LinearUserDefinedForce +// Access : public +// Description : constructor +//////////////////////////////////////////////////////////////////// +LinearUserDefinedForce:: +LinearUserDefinedForce(LVector3f (*proc)(const PhysicsObject *), + float a, bool md) : + _proc(proc), LinearForce(a, md) { +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearUserDefinedForce +// Access : public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +LinearUserDefinedForce:: +LinearUserDefinedForce(const LinearUserDefinedForce ©) : + LinearForce(copy) { + _proc = copy._proc; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~LinearUserDefinedForce +// Access : public +// Description : destructor +//////////////////////////////////////////////////////////////////// +LinearUserDefinedForce:: +~LinearUserDefinedForce(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : private, virtual +// Description : child copier +//////////////////////////////////////////////////////////////////// +LinearForce *LinearUserDefinedForce:: +make_copy(void) { + return new LinearUserDefinedForce(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : get_child_vector +// Access : private, virtual +// Description : force builder +//////////////////////////////////////////////////////////////////// +LVector3f LinearUserDefinedForce:: +get_child_vector(const PhysicsObject *po) { + return _proc(po); +} diff --git a/panda/src/physics/linearUserDefinedForce.h b/panda/src/physics/linearUserDefinedForce.h new file mode 100644 index 0000000000..d247c20594 --- /dev/null +++ b/panda/src/physics/linearUserDefinedForce.h @@ -0,0 +1,57 @@ +// Filename: LinearUserDefinedForce.h +// Created by: charles (31Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINEARUSERDEFINEDFORCE_H +#define LINEARUSERDEFINEDFORCE_H + +#include "linearForce.h" + +//////////////////////////////////////////////////////////////////// +// Class : LinearUserDefinedForce +// Description : a programmable force that takes an evaluator fn. +// +// NOTE : AS OF Interrogate => Squeak, this class does NOT +// get FFI'd due to the function pointer bug, and is +// currently NOT getting interrogated. Change this +// in the makefile when the time is right or this class +// becomes needed... +//////////////////////////////////////////////////////////////////// +class LinearUserDefinedForce : public LinearForce { +private: + LVector3f (*_proc)(const PhysicsObject *po); + + virtual LVector3f get_child_vector(const PhysicsObject *po); + virtual LinearForce *make_copy(void); + +public: + LinearUserDefinedForce(LVector3f (*proc)(const PhysicsObject *) = NULL, + float a = 1.0f, + bool md = false); + LinearUserDefinedForce(const LinearUserDefinedForce ©); + virtual ~LinearUserDefinedForce(void); + + INLINE void set_proc(LVector3f (*proc)(const PhysicsObject *)); + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LinearForce::init_type(); + register_type(_type_handle, "LinearUserDefinedForce", + LinearForce::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "linearUserDefinedForce.I" + +#endif // LINEARUSERDEFINEDFORCE_H diff --git a/panda/src/physics/linearVectorForce.I b/panda/src/physics/linearVectorForce.I new file mode 100644 index 0000000000..342603ce7b --- /dev/null +++ b/panda/src/physics/linearVectorForce.I @@ -0,0 +1,24 @@ +// Filename: LinearVectorForce.I +// Created by: charles (21Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_vector +// Access : Public +// Description : encapsulating wrapper +//////////////////////////////////////////////////////////////////// +INLINE void LinearVectorForce:: +set_vector(const LVector3f& v) { + _fvec = v; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_vector +// Access : Public +// Description : piecewise encapsulating wrapper +//////////////////////////////////////////////////////////////////// +INLINE void LinearVectorForce:: +set_vector(float x, float y, float z) { + _fvec.set(x, y, z); +} diff --git a/panda/src/physics/linearVectorForce.cxx b/panda/src/physics/linearVectorForce.cxx new file mode 100644 index 0000000000..d6bd8f4de4 --- /dev/null +++ b/panda/src/physics/linearVectorForce.cxx @@ -0,0 +1,75 @@ +// Filename: LinearVectorForce.cxx +// Created by: charles (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +#include "linearVectorForce.h" + +TypeHandle LinearVectorForce::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : LinearVectorForce +// Access : Public +// Description : Vector Constructor +//////////////////////////////////////////////////////////////////// +LinearVectorForce:: +LinearVectorForce(const LVector3f& vec, float a, bool mass) : + _fvec(vec), LinearForce(a, mass) { +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearVectorForce +// Access : Public +// Description : Default/Piecewise constructor +//////////////////////////////////////////////////////////////////// +LinearVectorForce:: +LinearVectorForce(float x, float y, float z, float a, bool mass) : + LinearForce(a, mass) { + _fvec.set(x, y, z); +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearVectorForce +// Access : Public +// Description : Copy Constructor +//////////////////////////////////////////////////////////////////// +LinearVectorForce:: +LinearVectorForce(const LinearVectorForce ©) : + LinearForce(copy) { + _fvec = copy._fvec; +} + +//////////////////////////////////////////////////////////////////// +// Function : LinearVectorForce +// Access : Public +// Description : Destructor +//////////////////////////////////////////////////////////////////// +LinearVectorForce:: +~LinearVectorForce(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : Public, virtual +// Description : copier +//////////////////////////////////////////////////////////////////// +LinearForce *LinearVectorForce:: +make_copy(void) { + return new LinearVectorForce(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : get_child_vector +// Access : Public +// Description : vector access +//////////////////////////////////////////////////////////////////// +LVector3f LinearVectorForce:: +get_child_vector(const PhysicsObject *) { + return _fvec; +} + diff --git a/panda/src/physics/linearVectorForce.h b/panda/src/physics/linearVectorForce.h new file mode 100644 index 0000000000..0c293e59d4 --- /dev/null +++ b/panda/src/physics/linearVectorForce.h @@ -0,0 +1,53 @@ +// Filename: linearVectorForce.h +// Created by: charles (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINEARVECTORFORCE_H +#define LINEARVECTORFORCE_H + +#include "linearForce.h" + +//////////////////////////////////////////////////////////////// +// Class : LinearVectorForce +// Description : Simple directed vector force. Suitable for +// gravity, non-turbulent wind, etc... +//////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS LinearVectorForce : public LinearForce { +private: + LVector3f _fvec; + + virtual LinearForce *make_copy(void); + virtual LVector3f get_child_vector(const PhysicsObject *po); + +public: + LinearVectorForce(const LVector3f& vec, float a = 1.0f, bool mass = false); + LinearVectorForce(const LinearVectorForce ©); + LinearVectorForce(float x = 0.0f, float y = 0.0f, float z = 0.0f, + float a = 1.0f, bool mass = false); + virtual ~LinearVectorForce(void); + + INLINE void set_vector(const LVector3f& v); + INLINE void set_vector(float x, float y, float z); + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LinearForce::init_type(); + register_type(_type_handle, "LinearVectorForce", + LinearForce::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "linearVectorForce.I" + +#endif // LINEARVECTORFORCE_H diff --git a/panda/src/physics/physical.I b/panda/src/physics/physical.I new file mode 100644 index 0000000000..0f06d26a67 --- /dev/null +++ b/panda/src/physics/physical.I @@ -0,0 +1,161 @@ +// Filename: physical.I +// Created by: charles (16Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////// +// Function : clear_linear_forces +// Access : Public +// Description : Erases the linear force list +//////////////////////////////////////////////////////////////////// +INLINE void Physical:: +clear_linear_forces(void) { + _linear_forces.erase(_linear_forces.begin(), + _linear_forces.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function : clear_angular_forces +// Access : Public +// Description : Erases the angular force list +//////////////////////////////////////////////////////////////////// +INLINE void Physical:: +clear_angular_forces(void) { + _angular_forces.erase(_angular_forces.begin(), + _angular_forces.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function : clear_physics_objects +// Access : Public +// Description : Erases the object list +//////////////////////////////////////////////////////////////////// +INLINE void Physical:: +clear_physics_objects(void) { + _physics_objects.erase(_physics_objects.begin(), + _physics_objects.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function : add_linear_force +// Access : Public +// Description : Adds a linear force to the force list +//////////////////////////////////////////////////////////////////// +INLINE void Physical:: +add_linear_force(LinearForce *f) { + _linear_forces.push_back(f); +} + +//////////////////////////////////////////////////////////////////// +// Function : add_angular_force +// Access : Public +// Description : Adds an angular force to the force list +//////////////////////////////////////////////////////////////////// +INLINE void Physical:: +add_angular_force(AngularForce *f) { + _angular_forces.push_back(f); +} + +//////////////////////////////////////////////////////////////////// +// Function : remove_linear_force +// Access : Public +// Description : removes a linear force from the force list +//////////////////////////////////////////////////////////////////// +INLINE void Physical:: +remove_linear_force(LinearForce *f) { + vector< PT(LinearForce) >::iterator found; + + // this is a PT because the templates don't like what should be + // perfectly allowable, which is to search for bf directly. + PT(LinearForce) pt_lf = f; + found = find(_linear_forces.begin(), _linear_forces.end(), pt_lf); + + if (found == _linear_forces.end()) + return; + + _linear_forces.erase(found); +} + +//////////////////////////////////////////////////////////////////// +// Function : remove_angular_force +// Access : Public +// Description : removes an angular force from the force list +//////////////////////////////////////////////////////////////////// +INLINE void Physical:: +remove_angular_force(AngularForce *f) { + vector< PT(AngularForce) >::iterator found; + + PT(AngularForce) pt_af = f; + found = find(_angular_forces.begin(), _angular_forces.end(), pt_af); + + if (found == _angular_forces.end()) + return; + + _angular_forces.erase(found); +} + +//////////////////////////////////////////////////////////////////// +// Function : add_physics_object +// Access : Public +// Description : Adds an object to the physics object vector +//////////////////////////////////////////////////////////////////// +INLINE void Physical:: +add_physics_object(PhysicsObject *po) { + _physics_objects.push_back(po); +} + +//////////////////////////////////////////////////////////////////// +// Function : get_physics_manager +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE PhysicsManager *Physical:: +get_physics_manager(void) const { + return _physics_manager; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_phys_body +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE PhysicsObject *Physical:: +get_phys_body(void) const { + return _phys_body; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_physical_node +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE PhysicalNode *Physical:: +get_physical_node(void) const { + return _physical_node; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_object_vector +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE const vector< PT(PhysicsObject) > &Physical:: +get_object_vector(void) const { + return _physics_objects; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_linear_forces +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE const vector< PT(LinearForce) > &Physical:: +get_linear_forces(void) const { + return _linear_forces; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_angular_forces +// Access : Public +//////////////////////////////////////////////////////////////////// +INLINE const vector< PT(AngularForce) > &Physical:: +get_angular_forces(void) const { + return _angular_forces; +} diff --git a/panda/src/physics/physical.cxx b/panda/src/physics/physical.cxx new file mode 100644 index 0000000000..fad8b68cef --- /dev/null +++ b/panda/src/physics/physical.cxx @@ -0,0 +1,112 @@ +// Filename: physical.cxx +// Created by: charles (16Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "physical.h" +#include "physicsManager.h" + +TypeHandle Physical::_type_handle; + +// the idea here is that most physicals will NOT be collections of +// sets (i.e. particle systems and whatever else). Because of this, +// the default constructor, unless otherwise specified, will +// automatically allocate and initialize one PhysicalObject. +// this makes it easier for high-level work. + +// pre-alloc is ONLY for multiple-object physicals, and if true, +// fills the physics_object vector with dead nodes, pre-allocating +// for the speed end of the speed-vs-overhead deal. + +//////////////////////////////////////////////////////////////////// +// Function : Physical +// Access : Public +// Description : Default Constructor +//////////////////////////////////////////////////////////////////// +Physical:: +Physical(int ttl_objects, bool pre_alloc) { + _physical_node = (PhysicalNode *) NULL; + _physics_manager = (PhysicsManager *) NULL; + + if (ttl_objects == 1) { + _phys_body = new PhysicsObject; + add_physics_object(_phys_body); + } + else { + int i; + _phys_body = (PhysicsObject *) NULL; + + // allocate each object. + if (pre_alloc == true) { + PhysicsObject *po; + + for (i = 0; i < ttl_objects; i++) { + po = new PhysicsObject; + add_physics_object(po); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function : Physical +// Access : Public +// Description : copy constructor (note- does deep copy of pn's) +// but does NOT attach itself to its template's +// physicsmanager. +//////////////////////////////////////////////////////////////////// +Physical:: +Physical(const Physical& copy) { + _physics_manager = (PhysicsManager *) NULL; + + // copy the forces. + vector< PT(LinearForce) >::const_iterator lf_cur; + vector< PT(LinearForce) >::const_iterator lf_end = copy._linear_forces.end(); + + for (lf_cur = copy._linear_forces.begin(); lf_cur != lf_end; lf_cur++) { + _linear_forces.push_back((*lf_cur)->make_copy()); + } + + vector< PT(AngularForce) >::const_iterator af_cur; + vector< PT(AngularForce) >::const_iterator af_end = copy._angular_forces.end(); + + for (af_cur = copy._angular_forces.begin(); af_cur != af_end; af_cur++) { + _angular_forces.push_back((*af_cur)->make_copy()); + } + + // copy the physics objects + vector< PT(PhysicsObject) >::const_iterator p_cur; + vector< PT(PhysicsObject) >::const_iterator p_end = copy._physics_objects.end(); + + for (p_cur = copy._physics_objects.begin(); p_cur != p_end; p_cur++) { + // oooh so polymorphic. + _physics_objects.push_back((*p_cur)->make_copy()); + } + + // now set the one-element-quick-access pointer + if (_physics_objects.size() == 1) + _phys_body = _physics_objects[0]; + else + _phys_body = (PhysicsObject *) NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function : ~Physical +// Access : Public +// Description : destructor +//////////////////////////////////////////////////////////////////// +Physical:: +~Physical(void) { + // note that this removes a physical from a physics manager. + // this is safe because the physics manager doesn't keep PT's to + // physicals, simply *'s, and also means that we don't have to tell + // the physics manager ourselves when one of our physicals is dead. + if (_physics_manager != (PhysicsManager *) NULL) { + _physics_manager->remove_physical(this); + } +} + + + diff --git a/panda/src/physics/physical.h b/panda/src/physics/physical.h new file mode 100644 index 0000000000..feb704554f --- /dev/null +++ b/panda/src/physics/physical.h @@ -0,0 +1,93 @@ +// Filename: physical.h +// Created by: charles (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PHYSICAL_H +#define PHYSICAL_H + +#include +#include +#include + +#include +#include + +#include "physicsObject.h" +#include "linearForce.h" +#include "angularForce.h" + +class PhysicalNode; +class PhysicsManager; + +//////////////////////////////////////////////////////////////////// +// Class : Physical +// Description : Defines a set of physically modeled attributes. +// If you want physics applied to your class, derive +// it from this. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS Physical : public TypedReferenceCount { +private: + PhysicsManager *_physics_manager; + PhysicalNode *_physical_node; + +protected: + // containers + vector< PT(PhysicsObject) > _physics_objects; + vector< PT(LinearForce) > _linear_forces; + vector< PT(AngularForce) > _angular_forces; + + // this pointer exists to make life easy. If a physical exists + // with only one element (i.e. NOT a particle system or set-physical), + // then this pointer points at the only physicsobject. The object + // is still of course contained in the _physics_objects vector, but + // this is kind of a quicker way there. + PhysicsObject *_phys_body; +public: + Physical(int ttl_objects = 1, bool pre_alloc = false); + Physical(const Physical& copy); + + virtual ~Physical(void); + + // helpers + INLINE PhysicsManager *get_physics_manager(void) const; + INLINE PhysicalNode *get_physical_node(void) const; + INLINE PhysicsObject *get_phys_body(void) const; + + INLINE const vector< PT(PhysicsObject) > &get_object_vector(void) const; + INLINE const vector< PT(LinearForce) > &get_linear_forces(void) const; + INLINE const vector< PT(AngularForce) > &get_angular_forces(void) const; + + INLINE void clear_linear_forces(void); + INLINE void clear_angular_forces(void); + INLINE void clear_physics_objects(void); + INLINE void add_linear_force(LinearForce *f); + INLINE void add_angular_force(AngularForce *f); + INLINE void add_physics_object(PhysicsObject *po); + INLINE void remove_linear_force(LinearForce *f); + INLINE void remove_angular_force(AngularForce *f); + + friend class PhysicsManager; + friend class PhysicalNode; + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + TypedReferenceCount::init_type(); + register_type(_type_handle, "Physical", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "physical.I" + +#endif // __PHYSICAL_H__ diff --git a/panda/src/physics/physicalNode.I b/panda/src/physics/physicalNode.I new file mode 100644 index 0000000000..a3328205a5 --- /dev/null +++ b/panda/src/physics/physicalNode.I @@ -0,0 +1,43 @@ +// Filename: physicalNode.I +// Created by: charles (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : clear +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void PhysicalNode:: +clear(void) { + _physicals.erase(_physicals.begin(), _physicals.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function : get_physical +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE Physical *PhysicalNode:: +get_physical(int index) const { + nassertr(index >= 0 && index < _physicals.size(), + (Physical *) NULL); + return _physicals[index]; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_num_physicals +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE int PhysicalNode:: +get_num_physicals(void) const { + return _physicals.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function : add_physical +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void PhysicalNode:: +add_physical(Physical *physical) { + _physicals.push_back(physical); + physical->_physical_node = this; +} diff --git a/panda/src/physics/physicalNode.cxx b/panda/src/physics/physicalNode.cxx new file mode 100644 index 0000000000..5cc10eb4d8 --- /dev/null +++ b/panda/src/physics/physicalNode.cxx @@ -0,0 +1,92 @@ +// Filename: physicalNode.cxx +// Created by: charles (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "physicalNode.h" + +// static stuff. +TypeHandle PhysicalNode::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : PhysicalNode +// Access : public +// Description : default constructor +//////////////////////////////////////////////////////////////////// +PhysicalNode:: +PhysicalNode(const string &name) : + NamedNode(name) { +} + +//////////////////////////////////////////////////////////////////// +// Function : PhysicalNode +// Access : public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +PhysicalNode:: +PhysicalNode(const PhysicalNode ©) : + NamedNode(copy), _physicals(copy._physicals) { +} + +//////////////////////////////////////////////////////////////////// +// Function : ~PhysicalNode +// Access : public, virtual +// Description : destructor +//////////////////////////////////////////////////////////////////// +PhysicalNode:: +~PhysicalNode(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : operator = +// Access : public +// Description : assignment operator +//////////////////////////////////////////////////////////////////// +PhysicalNode &PhysicalNode:: +operator =(const PhysicalNode ©) { + NamedNode::operator =(copy); + _physicals = copy._physicals; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : public, virtual +// Description : dynamic child copy +//////////////////////////////////////////////////////////////////// +Node *PhysicalNode:: +make_copy(void) const { + return new PhysicalNode(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : add_physicals_from +// Access : public +// Description : append operation +//////////////////////////////////////////////////////////////////// +void PhysicalNode:: +add_physicals_from(const PhysicalNode &other) { + vector< PT(Physical) >::iterator last = _physicals.end() - 1; + + _physicals.insert(_physicals.end(), + other._physicals.begin(), other._physicals.end()); + + for (; last != _physicals.end(); last++) + (*last)->_physical_node = this; +} + +//////////////////////////////////////////////////////////////////// +// Function : remove_physical +// Access : public +// Description : remove operation +//////////////////////////////////////////////////////////////////// +void PhysicalNode:: +remove_physical(int index) { + nassertv(index >= 0 && index <= _physicals.size()); + + vector< PT(Physical) >::iterator remove; + remove = _physicals.begin() + index; + (*remove)->_physical_node = (PhysicalNode *) NULL; + + _physicals.erase(remove); +} diff --git a/panda/src/physics/physicalNode.h b/panda/src/physics/physicalNode.h new file mode 100644 index 0000000000..30286e4050 --- /dev/null +++ b/panda/src/physics/physicalNode.h @@ -0,0 +1,64 @@ +// Filename: physicalNode.h +// Created by: charles (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PHYSICALNODE_H +#define PHYSICALNODE_H + +#include +#include + +#include + +#include "physical.h" +#include "config_physics.h" + +//////////////////////////////////////////////////////////////////// +// Class : PhysicalNode +// Description : Graph node that encapsulated a series of physical +// objects +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS PhysicalNode : public NamedNode { +private: + vector< PT(Physical) > _physicals; + +public: + PhysicalNode(const string &name = ""); + PhysicalNode(const PhysicalNode ©); + virtual ~PhysicalNode(void); + + PhysicalNode &operator =(const PhysicalNode ©); + + virtual bool safe_to_flatten(void) const { return false; } + virtual Node *make_copy(void) const; + + INLINE void clear(void); + INLINE Physical *get_physical(int index) const; + INLINE int get_num_physicals(void) const; + INLINE void add_physical(Physical *physical); + + void add_physicals_from(const PhysicalNode &other); + void remove_physical(int index); + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + NamedNode::init_type(); + register_type(_type_handle, "PhysicalNode", + NamedNode::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "physicalNode.I" + +#endif // PHYSICALNODE_H diff --git a/panda/src/physics/physics.txt b/panda/src/physics/physics.txt new file mode 100644 index 0000000000..96cfc1a5b0 --- /dev/null +++ b/panda/src/physics/physics.txt @@ -0,0 +1,42 @@ +// Panda Physics Documentation +// Charles Nicholson, Intern-at-large. 7/17/00 + +How to make physics go: +----------------------- + +Physics lives mainly in the scene graph. To put a physical object +into the scene graph, use either an ActorNode or a PhysicalNode. +If you want this node to react to outside input (i.e. Jesse banging +on the arc above it), use an ActorNode. ActorNodes will also update +their own arcs according to their internal physics information. +PhysicalNodes are pretty much the same, but they don't have the automatic +graph response/update routines built-in, so for most cases, you'll probably +want to use an ActorNode. + +Forces (gravity, noise, jitter, friction) can act on these objects, and they +live in the graph as well. Forces come in two flavors: linear and angular, +and ForceNodes can hold both. When a force acts upon an object, the relative +matrix between the ForceNode and the ActorNode is calculated and applied to the +force vectors, which in turn get applied to the objects via integration. + +Integration occurs in the PhysicsManager, which has references to all of the +forces and objects to be acted on. Like forces, there are two types of integration: +linear and angular. Respective integrators must be plugged in to each for anything +to happen. + +What to do if nothing's moving/happening: +----------------------------------------- + +1. Make sure you have a PhysicsManager. + Make sure that an integrator (LinearEulerIntegrator and/or AngularEulerIntegrator) + is plugged in to the manager. + +2. Make sure you have your Physicals (live inside the Actor/Physical Nodes) attached + to the manager. + +3. Make sure that your forces live in ForceNodes and are attached to either the + physical or the manager. + +4. Make sure you're calling PhysicsManager::do_physics() once per frame. + +If none of these work, try slapping yourself. diff --git a/panda/src/physics/physicsManager.I b/panda/src/physics/physicsManager.I new file mode 100644 index 0000000000..40217d106f --- /dev/null +++ b/panda/src/physics/physicsManager.I @@ -0,0 +1,96 @@ +// Filename: physics_manager.I +// Created by: charles (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : attach_physical +// Access : Public +// Description : Registers a Physical class with the manager +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsManager:: +attach_physical(Physical *p) { + p->_physics_manager = this; + _physicals.push_back(p); +} + +//////////////////////////////////////////////////////////////////// +// Function : attach_linear_force +// Access : Public +// Description : Adds a global linear force to the physics manager +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsManager:: +add_linear_force(LinearForce *f) { + _linear_forces.push_back(f); +} + +//////////////////////////////////////////////////////////////////// +// Function : attach_physicalnode +// Access : Public +// Description : Registers an physicalnode with the manager +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsManager:: +attach_physicalnode(PhysicalNode *p) { + for (int i = 0; i < p->get_num_physicals(); i++) + attach_physical(p->get_physical(i)); +} + +//////////////////////////////////////////////////////////////////// +// Function : clear_linear_forces +// Access : Public +// Description : Resets the physics manager force vector +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsManager:: +clear_linear_forces(void) { + _linear_forces.erase(_linear_forces.begin(), _linear_forces.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function : attach_angular_force +// Access : Public +// Description : Adds a global angular force to the physics manager +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsManager:: +add_angular_force(AngularForce *f) { + _angular_forces.push_back(f); +} + +//////////////////////////////////////////////////////////////////// +// Function : clear_angular_forces +// Access : Public +// Description : Resets the physics manager force vector +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsManager:: +clear_angular_forces(void) { + _angular_forces.erase(_angular_forces.begin(), _angular_forces.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function : clear_physicals +// Access : Public +// Description : Resets the physics manager objects vector +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsManager:: +clear_physicals(void) { + _physicals.erase(_physicals.begin(), _physicals.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function : attach_linear_integrator +// Access : Public +// Description : Hooks a linear integrator into the manager +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsManager:: +attach_linear_integrator(LinearIntegrator *i) { + _linear_integrator = i; +} + +//////////////////////////////////////////////////////////////////// +// Function : attach_angular_integrator +// Access : Public +// Description : Hooks an angular integrator into the manager +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsManager:: +attach_angular_integrator(AngularIntegrator *i) { + _angular_integrator = i; +} diff --git a/panda/src/physics/physicsManager.cxx b/panda/src/physics/physicsManager.cxx new file mode 100644 index 0000000000..518e1b7cb0 --- /dev/null +++ b/panda/src/physics/physicsManager.cxx @@ -0,0 +1,119 @@ +// Filename: physics_manager.cxx +// Created by: charles (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include + +#include "physicsManager.h" +#include "actorNode.h" + +//////////////////////////////////////////////////////////////////// +// Function : PhysicsManager +// Access : Public +// Description : Default Constructor. NOTE: EulerIntegrator is +// the standard default. +//////////////////////////////////////////////////////////////////// +PhysicsManager:: +PhysicsManager(void) { + _linear_integrator.clear(); + _angular_integrator.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function : ~PhysicsManager +// Access : Public +// Description : Simple Destructor +//////////////////////////////////////////////////////////////////// +PhysicsManager:: +~PhysicsManager(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : remove_linear_force +// Access : Public +// Description : takes a linear force out of the physics list +//////////////////////////////////////////////////////////////////// +void PhysicsManager:: +remove_linear_force(LinearForce *f) { + vector< PT(LinearForce) >::iterator found; + + PT(LinearForce) ptbf = f; + found = find(_linear_forces.begin(), _linear_forces.end(), ptbf); + + if (found == _linear_forces.end()) + return; + + _linear_forces.erase(found); +} + +//////////////////////////////////////////////////////////////////// +// Function : remove_angular_force +// Access : Public +// Description : takes an angular force out of the physics list +//////////////////////////////////////////////////////////////////// +void PhysicsManager:: +remove_angular_force(AngularForce *f) { + vector< PT(AngularForce) >::iterator found; + + PT(BaseForce) ptbf = f; + found = find(_angular_forces.begin(), _angular_forces.end(), ptbf); + + if (found == _angular_forces.end()) + return; + + _angular_forces.erase(found); +} + +//////////////////////////////////////////////////////////////////// +// Function : remove_physical +// Access : Public +// Description : takes a physical out of the object list +//////////////////////////////////////////////////////////////////// +void PhysicsManager:: +remove_physical(Physical *p) { + vector< Physical * >::iterator found; + + found = find(_physicals.begin(), _physicals.end(), p); + if (found == _physicals.end()) + return; + + p->_physics_manager = (PhysicsManager *) NULL; + _physicals.erase(found); +} + +//////////////////////////////////////////////////////////////////// +// Function : DoPhysics +// Access : Public +// Description : This is the main high-level API call. Performs +// integration on every attached Physical. +//////////////////////////////////////////////////////////////////// +void PhysicsManager:: +do_physics(float dt) { + vector< Physical * >::iterator p_cur; + + // now, run through each physics object in the set. + p_cur = _physicals.begin(); + + for (; p_cur != _physicals.end(); p_cur++) { + Physical *physical = *p_cur; + + // do linear + if (_linear_integrator.is_null() == false) { + _linear_integrator->integrate(physical, _linear_forces, dt); + } + + // do angular + if (_angular_integrator.is_null() == false) { + _angular_integrator->integrate(physical, _angular_forces, dt); + } + + // if it's an actor node, tell it to update itself. + PhysicalNode *pn = physical->get_physical_node(); + if (pn->is_of_type(ActorNode::get_class_type())) { + ActorNode *an = (ActorNode *) pn; + an->update_arc(); + } + } +} diff --git a/panda/src/physics/physicsManager.h b/panda/src/physics/physicsManager.h new file mode 100644 index 0000000000..978a6fe7e0 --- /dev/null +++ b/panda/src/physics/physicsManager.h @@ -0,0 +1,68 @@ +// Filename: physicsManager.h +// Created by: charles (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PHYSICSMANAGER_H +#define PHYSICSMANAGER_H + +#include +#include + +#include "physical.h" +#include "linearForce.h" +#include "angularForce.h" +#include "linearIntegrator.h" +#include "angularIntegrator.h" +#include "physicalNode.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : PhysicsManager +// Description : Physics don't get much higher-level than this. +// Attach as many Physicals (particle systems, etc..) +// as you want, pick an integrator and go. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS PhysicsManager { +private: + // NOTE that the physicals container is NOT reference counted. + // this does indeed mean that you are NOT supposed to use this + // as a primary storage container for the physicals. This is so + // because physicals, on their death, ask to be removed from their + // current physicsmanager, if one exists, relieving the client from + // the task and also allowing for dynamically created and destroyed + // physicals. + vector< Physical * > _physicals; + vector< PT(LinearForce) > _linear_forces; + vector< PT(AngularForce) > _angular_forces; + + PT(LinearIntegrator) _linear_integrator; + PT(AngularIntegrator) _angular_integrator; + +public: + PhysicsManager(void); + virtual ~PhysicsManager(void); + + INLINE void attach_linear_integrator(LinearIntegrator *i); + INLINE void attach_angular_integrator(AngularIntegrator *i); + INLINE void attach_physical(Physical *p); + INLINE void attach_physicalnode(PhysicalNode *p); + INLINE void add_linear_force(LinearForce *f); + INLINE void add_angular_force(AngularForce *f); + INLINE void clear_linear_forces(void); + INLINE void clear_angular_forces(void); + INLINE void clear_physicals(void); + + void remove_physical(Physical *p); + void remove_linear_force(LinearForce *f); + void remove_angular_force(AngularForce *f); + void do_physics(float dt); + + friend class Physical; +}; + +#include "physicsManager.I" + +#endif // PHYSICSMANAGER_H diff --git a/panda/src/physics/physicsObject.I b/panda/src/physics/physicsObject.I new file mode 100644 index 0000000000..99580612a1 --- /dev/null +++ b/panda/src/physics/physicsObject.I @@ -0,0 +1,184 @@ +// Filename: physics_object.I +// Created by: charles (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : set_mass +// Access : Public +// Description : Specify the mass of the object +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsObject:: +set_mass(float m) { + _mass = m; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_position +// Access : Public +// Description : Vector position assignment +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsObject:: +set_position(const LPoint3f& pos) { + _position = pos; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_position +// Access : Public +// Description : Piecewise position assignment +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsObject:: +set_position(float x, float y, float z) { + _position.set(x, y, z); +} + +//////////////////////////////////////////////////////////////////// +// Function : set_velocity +// Access : Public +// Description : Vector velocity assignment +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsObject:: +set_velocity(const LVector3f& vel) { + _velocity = vel; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_velocity +// Access : Public +// Description : Piecewise velocity assignment +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsObject:: +set_velocity(float x, float y, float z) { + _velocity.set(x, y, z); +} + +//////////////////////////////////////////////////////////////////// +// Function : set_active +// Access : Public +// Description : Process Flag assignment +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsObject:: +set_active(bool flag) { + _process_me = flag; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_terminal_velocity +// Access : Public +// Description : tv assignment +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsObject:: +set_terminal_velocity(float tv) { + _terminal_velocity = tv; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_mass +// Access : Public +// Description : Mass Query +//////////////////////////////////////////////////////////////////// +INLINE float PhysicsObject:: +get_mass(void) const { + return _mass; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_position +// Access : Public +// Description : Position Query +//////////////////////////////////////////////////////////////////// +INLINE LPoint3f PhysicsObject:: +get_position(void) const { + return _position; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_velocity +// Access : Public +// Description : Velocity Query +//////////////////////////////////////////////////////////////////// +INLINE LVector3f PhysicsObject:: +get_velocity(void) const { + return _velocity; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_active +// Access : Public +// Description : Process Flag Query +//////////////////////////////////////////////////////////////////// +INLINE bool PhysicsObject:: +get_active(void) const { + return _process_me; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_terminal_velocity +// Access : Public +// Description : tv query +//////////////////////////////////////////////////////////////////// +INLINE float PhysicsObject:: +get_terminal_velocity(void) const { + return _terminal_velocity; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_orientation +// Access : Public +// Description : +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsObject:: +set_orientation(const LOrientationf &orientation) { + _orientation = orientation; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_rotation +// Access : Public +// Description : +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsObject:: +set_rotation(const LVector3f &rotation) { + _rotation = rotation; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_orientation +// Access : Public +// Description : +//////////////////////////////////////////////////////////////////// +INLINE LOrientationf PhysicsObject:: +get_orientation(void) const { + return _orientation; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_rotation +// Access : Public +// Description : +//////////////////////////////////////////////////////////////////// +INLINE LVector3f PhysicsObject:: +get_rotation(void) const { + return _rotation; +} + +//////////////////////////////////////////////////////////////////// +// Function : set_oriented +// Access : Public +// Description : +//////////////////////////////////////////////////////////////////// +INLINE void PhysicsObject:: +set_oriented(bool flag) { + _oriented = flag; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_oriented +// Access : Public +// Description : +//////////////////////////////////////////////////////////////////// +INLINE bool PhysicsObject:: +get_oriented(void) const { + return _oriented; +} diff --git a/panda/src/physics/physicsObject.cxx b/panda/src/physics/physicsObject.cxx new file mode 100644 index 0000000000..d2c74003de --- /dev/null +++ b/panda/src/physics/physicsObject.cxx @@ -0,0 +1,98 @@ +// Filename: physics_object.cxx +// Created by: charles (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "physicsObject.h" + +TypeHandle PhysicsObject::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function : PhysicsObject +// Access : Public +// Description : Default Constructor +//////////////////////////////////////////////////////////////////// +PhysicsObject:: +PhysicsObject(void) : + _process_me(false), _mass(1.0f), _oriented(true), + _terminal_velocity(_default_terminal_velocity) { + _position.set(0, 0, 0); + _velocity.set(0, 0, 0); + _orientation.set(1, 0, 0, 0); + _rotation.set(0, 0, 0); +} + +//////////////////////////////////////////////////////////////////// +// Function : PhysicsObject +// Access : Public +// Description : copy constructor +//////////////////////////////////////////////////////////////////// +PhysicsObject:: +PhysicsObject(const PhysicsObject& copy) { + operator=(copy); +} + +//////////////////////////////////////////////////////////////////// +// Function : ~PhysicsObject +// Access : Public +// Description : Destructor +//////////////////////////////////////////////////////////////////// +PhysicsObject:: +~PhysicsObject(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function : Assignment operator +// Access : Public +// Description : +//////////////////////////////////////////////////////////////////// +const PhysicsObject &PhysicsObject:: +operator =(const PhysicsObject &other) { + _process_me = other._process_me; + _mass = other._mass; + _position = other._position; + _velocity = other._velocity; + _orientation = other._orientation; + _rotation = other._rotation; + _terminal_velocity = other._terminal_velocity; + _oriented = other._oriented; + + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function : make_copy +// Access : Public, virtual +// Description : dynamic copy. +//////////////////////////////////////////////////////////////////// +PhysicsObject *PhysicsObject:: +make_copy(void) const { + return new PhysicsObject(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function : get_lcs +// Access : Public +// Description : returns a transform matrix to this object's +// local coordinate system. +//////////////////////////////////////////////////////////////////// +LMatrix4f PhysicsObject:: +get_lcs(void) const { + LMatrix4f m = LMatrix4f::translate_mat(_position); + + if (_oriented == true) + _orientation.extract_to_matrix(m); + + return m; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_inertial_tensor +// Access : Public +// Description : returns a transform matrix that represents the +// object's willingness to be forced. +//////////////////////////////////////////////////////////////////// +LMatrix4f PhysicsObject:: +get_inertial_tensor(void) const { + return LMatrix4f::ident_mat(); +} diff --git a/panda/src/physics/physicsObject.h b/panda/src/physics/physicsObject.h new file mode 100644 index 0000000000..457f83a272 --- /dev/null +++ b/panda/src/physics/physicsObject.h @@ -0,0 +1,93 @@ +// Filename: physics_object.h +// Created by: charles (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PHYSICS_OBJECT_H +#define PHYSICS_OBJECT_H + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : PhysicsObject +// Description : A body on which physics will be applied. If you're +// looking to add physical motion to your class, do +// NOT derive from this. Derive from Physical instead. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAPHYSICS PhysicsObject : public TypedReferenceCount { +private: + // physical + LPoint3f _position; + LVector3f _velocity; + + // angular + LOrientationf _orientation; + LVector3f _rotation; + + float _terminal_velocity; + float _mass; + + bool _process_me; + bool _oriented; + +public: + PhysicsObject(void); + PhysicsObject(const PhysicsObject ©); + virtual ~PhysicsObject(void); + const PhysicsObject &operator =(const PhysicsObject &other); + + static const float _default_terminal_velocity; + + INLINE void set_mass(float); + INLINE float get_mass(void) const; + + INLINE void set_position(const LPoint3f &pos); + INLINE void set_position(float x, float y, float z); + INLINE LPoint3f get_position(void) const; + + INLINE void set_velocity(const LVector3f &vel); + INLINE void set_velocity(float x, float y, float z); + INLINE LVector3f get_velocity(void) const; + + INLINE void set_active(bool flag); + INLINE bool get_active(void) const; + + INLINE void set_oriented(bool flag); + INLINE bool get_oriented(void) const; + + INLINE void set_terminal_velocity(float tv); + INLINE float get_terminal_velocity(void) const; + + INLINE void set_orientation(const LOrientationf &orientation); + INLINE LOrientationf get_orientation(void) const; + + INLINE void set_rotation(const LVector3f &rotation); + INLINE LVector3f get_rotation(void) const; + + virtual LMatrix4f get_inertial_tensor(void) const; + virtual LMatrix4f get_lcs(void) const; + virtual PhysicsObject *make_copy(void) const; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "PhysicsObject", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "physicsObject.I" + +#endif // __PHYSICS_OBJECT_H__ diff --git a/panda/src/physics/test_physics.cxx b/panda/src/physics/test_physics.cxx new file mode 100644 index 0000000000..0587632fae --- /dev/null +++ b/panda/src/physics/test_physics.cxx @@ -0,0 +1,83 @@ +// Filename: test_physics.cxx +// Created by: charles (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include "physical.h" +#include "physicsManager.h" +#include "forces.h" + +class Baseball : public Physical +{ + public: + + int ttl_balls; + int color; + + Baseball(int tb = 1) : ttl_balls(tb), Physical(tb, true) {} +}; + +int main(int, char **) +{ + PhysicsManager physics_manager; + Baseball b(8); + + int i = 0; + + // test the noise force + + Baseball nf_b; + nf_b._phys_body->set_position(0.0f, 0.0f, 0.0f); + nf_b._phys_body->set_velocity(1.0f / 16.0f, 0.0f, 0.0f); + nf_b._phys_body->set_processflag(true); + nf_b._phys_body->set_mass(1.0f); + + NoiseForce nf(1.0, false); + + physics_manager.attach_physical(&nf_b); + + for (int monkey = 0; monkey < 16; monkey++) + { + cout << "ball: " << nf_b._phys_body->get_position() << endl; + cout << "nf: " << nf.get_vector(nf_b._phys_body) << endl; + + physics_manager.do_physics(1.0f / 16.0f); + } + + physics_manager.remove_physical(&nf_b); + + // get on with life + + b.add_force(new JitterForce(0.1f)); + + for (i = 0; i < b.ttl_balls; i++) + { + b._physics_objects[i]->set_position(i * 2.0f, float(i), 0.0f); + b._physics_objects[i]->set_velocity(5.0f, 0.0f, 30.0f); + b._physics_objects[i]->set_processflag(true); + b._physics_objects[i]->set_mass(1.0f); + } + + physics_manager.attach_physical(&b); + physics_manager.add_force(new VectorForce(0.0f, 0.0f, -9.8f, 1.0f, false)); + + cout << "Object vector:" << endl; + + for (i = 0; i < b.ttl_balls; i++) + { + cout << "vel: " << b._physics_objects[i]->get_velocity() << " "; + cout << "pos: " << b._physics_objects[i]->get_position() << endl; + } + + physics_manager.do_physics(1.0f); + + cout << "Physics have been applied." << endl; + + for (i = 0; i < b.ttl_balls; i++) + { + cout << "vel: " << b._physics_objects[i]->get_velocity() << " "; + cout << "pos: " << b._physics_objects[i]->get_position() << endl; + } +} + diff --git a/panda/src/pnm/Sources.pp b/panda/src/pnm/Sources.pp new file mode 100644 index 0000000000..ca557787fb --- /dev/null +++ b/panda/src/pnm/Sources.pp @@ -0,0 +1,17 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET pnm + + #define SOURCES \ + bitio.c bitio.h compile.h libpbm.h libpbm1.c libpbm2.c libpbm3.c \ + libpbm4.c libpbm5.c libpgm.h libpgm1.c libpgm2.c libpnm1.c \ + libpnm2.c libpnm3.c libpnm4.c libppm.h libppm1.c libppm2.c \ + libppm3.c libppm4.c libppm5.c pbm.h pbmfont.h pbmplus.h pgm.h \ + pnm.h ppm.h ppmcmap.h ppmdraw.h rast.h version.h + + #define INSTALL_HEADERS \ + pbm.h pbmplus.h pgm.h pnm.h ppm.h + +#end lib_target + diff --git a/panda/src/pnm/bitio.c b/panda/src/pnm/bitio.c new file mode 100644 index 0000000000..fb7e8e640f --- /dev/null +++ b/panda/src/pnm/bitio.c @@ -0,0 +1,202 @@ +/*\ + * $Id$ + * + * bitio.c - bitstream I/O + * + * Works for (sizeof(unsigned long)-1)*8 bits. + * + * Copyright (C) 1992 by David W. Sanderson. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. This software is provided "as is" + * without express or implied warranty. + * + * $Log$ + * Revision 1.1 2000/10/04 01:14:42 drose + * Initial revision + * + * Revision 1.5 1992/11/24 19:36:46 dws + * Added copyright. + * + * Revision 1.4 1992/11/17 03:37:50 dws + * updated comment + * + * Revision 1.3 1992/11/10 23:15:16 dws + * Removed superfluous code. + * + * Revision 1.2 1992/11/10 23:11:22 dws + * Generalized to handle more than one bitstream at once. + * + * Revision 1.1 1992/11/10 18:33:21 dws + * Initial revision + * +\*/ + +#include "bitio.h" + +struct bitstream +{ + FILE * + f; /* bytestream */ + unsigned long + bitbuf; /* bit buffer */ + int + nbitbuf; /* number of bits in 'bitbuf' */ + char + mode; +}; + +#define Mask(n) ((1<<(n))-1) + +#define BitPut(b,ul,n) ((b)->bitbuf = (((b)->bitbuf<<(n)) \ + |((ul)&Mask(n))), \ + (b)->nbitbuf += (n)) + +#define BitGet(b,n) (((b)->bitbuf>>((b)->nbitbuf-=(n))) & Mask(n)) + +/* + * pm_bitinit() - allocate and return a struct bitstream * for the + * given FILE*. + * + * mode must be one of "r" or "w", according to whether you will be + * reading from or writing to the struct bitstream *. + * + * Returns 0 on error. + */ + +EXPCL_PANDA struct bitstream * +pm_bitinit(FILE *f, char *mode) +{ + struct bitstream *ans = (struct bitstream *)0; + + if(!f || !mode || !*mode) + return ans; + if(strcmp(mode, "r") && strcmp(mode, "w")) + return ans; + + ans = (struct bitstream *)calloc(1, sizeof(struct bitstream)); + if(ans) + { + ans->f = f; + ans->mode = *mode; + } + + return ans; +} + +/* + * pm_bitfini() - deallocate the given struct bitstream *. + * + * You must call this after you are done with the struct bitstream *. + * + * It may flush some bits left in the buffer. + * + * Returns the number of bytes written, -1 on error. + */ + +EXPCL_PANDA int +pm_bitfini(struct bitstream *b) +{ + int nbyte = 0; + + if(!b) + return -1; + + /* flush the output */ + if(b->mode == 'w') + { + /* flush the bits */ + if (b->nbitbuf < 0 || b->nbitbuf >= 8) + { + /* pm_bitwrite() didn't work */ + return -1; + } + + /* + * If we get to here, nbitbuf is 0..7 + */ + if(b->nbitbuf) + { + char c; + + BitPut(b, 0, (long)8-(b->nbitbuf)); + c = (char) BitGet(b, (long)8); + if(putc(c, b->f) == EOF) + { + return -1; + } + nbyte++; + } + } + + free(b); + return nbyte; +} + +/* + * pm_bitread() - read the next nbits into *val from the given file. + * + * The last pm_bitread() must be followed by a call to pm_bitfini(). + * + * Returns the number of bytes read, -1 on error. + */ + +EXPCL_PANDA int +pm_bitread(struct bitstream *b, unsigned long nbits, unsigned long *val) +{ + int nbyte = 0; + int c; + + if(!b) + return -1; + + while (b->nbitbuf < nbits) + { + if((c = getc(b->f)) == EOF) + { + return -1; + } + nbyte++; + + BitPut(b, c, (long) 8); + } + + *val = BitGet(b, nbits); + return nbyte; +} + +/* + * pm_bitwrite() - write the low nbits of val to the given file. + * + * The last pm_bitwrite() must be followed by a call to pm_bitfini(). + * + * Returns the number of bytes written, -1 on error. + */ + +EXPCL_PANDA int +pm_bitwrite(struct bitstream *b, unsigned long nbits, unsigned long val) +{ + int nbyte = 0; + char c; + + if(!b) + return -1; + + BitPut(b, val, nbits); + + while (b->nbitbuf >= 8) + { + c = (char) BitGet(b, (long)8); + + if(putc(c, b->f) == EOF) + { + return -1; + } + nbyte++; + } + + return nbyte; +} diff --git a/panda/src/pnm/bitio.h b/panda/src/pnm/bitio.h new file mode 100644 index 0000000000..4137fe2d67 --- /dev/null +++ b/panda/src/pnm/bitio.h @@ -0,0 +1,85 @@ +/*\ + * $Id$ + * + * bitio.h - bitstream I/O + * + * Works for (sizeof(unsigned long)-1)*8 bits. + * + * Copyright (C) 1992 by David W. Sanderson. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. This software is provided "as is" + * without express or implied warranty. + * + * $Log$ + * Revision 1.1 2000/10/04 01:14:42 drose + * Initial revision + * + * Revision 1.4 1992/11/24 19:37:02 dws + * Added copyright + * + * Revision 1.3 1992/11/17 03:37:59 dws + * updated comment + * + * Revision 1.2 1992/11/10 23:10:22 dws + * Generalized to handle more than one bitstream at a time. + * + * Revision 1.1 1992/11/10 18:33:51 dws + * Initial revision + * +\*/ + +#ifndef _BITIO_H_ +#define _BITIO_H_ + +#include + +#include "pbmplus.h" + +typedef struct bitstream *BITSTREAM; + +/* + * pm_bitinit() - allocate and return a BITSTREAM for the given FILE*. + * + * mode must be one of "r" or "w", according to whether you will be + * reading from or writing to the BITSTREAM. + * + * Returns 0 on error. + */ + +extern EXPCL_PANDA BITSTREAM pm_bitinit(FILE *f, char *mode); + +/* + * pm_bitfini() - deallocate the given BITSTREAM. + * + * You must call this after you are done with the BITSTREAM. + * + * It may flush some bits left in the buffer. + * + * Returns the number of bytes written, -1 on error. + */ + +extern EXPCL_PANDA int pm_bitfini(BITSTREAM b); + +/* + * pm_bitread() - read the next nbits into *val from the given file. + * + * Returns the number of bytes read, -1 on error. + */ + +extern EXPCL_PANDA int pm_bitread(BITSTREAM b, unsigned long nbits, unsigned long *val); + +/* + * pm_bitwrite() - write the low nbits of val to the given file. + * + * The last pm_bitwrite() must be followed by a call to pm_bitflush(). + * + * Returns the number of bytes written, -1 on error. + */ + +extern EXPCL_PANDA int pm_bitwrite(BITSTREAM b, unsigned long nbits, unsigned long val); + +#endif /* _BITIO_H_ */ diff --git a/panda/src/pnm/compile.h b/panda/src/pnm/compile.h new file mode 100644 index 0000000000..9b3d47b599 --- /dev/null +++ b/panda/src/pnm/compile.h @@ -0,0 +1,4 @@ +/* compile_time.h This file tells the package when it was compiled */ +/* DO NOT EDIT - THIS FILE IS MAINTAINED AUTOMATICALLY */ +#define COMPILE_TIME "Tue Mar 19 19:03:20 PST 1996" +#define COMPILED_BY "drose" diff --git a/panda/src/pnm/libpbm.h b/panda/src/pnm/libpbm.h new file mode 100644 index 0000000000..a11c888f8c --- /dev/null +++ b/panda/src/pnm/libpbm.h @@ -0,0 +1,17 @@ +/* libpbm.h - internal header file for libpbm portable bitmap library +*/ + +#ifndef _LIBPBM_H_ +#define _LIBPBM_H_ + +/* Here are some routines internal to the pbm library. */ + +char pbm_getc ARGS(( FILE* file )); +unsigned char pbm_getrawbyte ARGS(( FILE* file )); +int pbm_getint ARGS(( FILE* file )); + +int pbm_readmagicnumber ARGS(( FILE* file )); + +EXPCL_PANDA void pbm_readpbminitrest( FILE* file, int* colsP, int* rowsP ); + +#endif /*_LIBPBM_H_*/ diff --git a/panda/src/pnm/libpbm1.c b/panda/src/pnm/libpbm1.c new file mode 100644 index 0000000000..e49d2e2913 --- /dev/null +++ b/panda/src/pnm/libpbm1.c @@ -0,0 +1,1549 @@ +/* libpbm1.c - pbm utility library part 1 +** +** Copyright (C) 1988 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include + +#include "pbm.h" +#include "version.h" +#include "compile.h" +#include "libpbm.h" +#if __STDC__ +#include +#else /*__STDC__*/ +#include +#endif /*__STDC__*/ + +/* Assume we have strerror() available. */ +#define A_STRERROR + + +/* Variable-sized arrays. */ + +char* +pm_allocrow( cols, size ) + int cols; + int size; + { + register char* itrow; + + itrow = (char*) malloc( cols * size ); + if ( itrow == (char*) 0 ) + pm_error( "out of memory allocating a row" ); + return itrow; + } + +void +pm_freerow( itrow ) + char* itrow; + { + free( itrow ); + } + + +#ifndef A_FRAGARRAY +char** +pm_allocarray( cols, rows, size ) + int cols, rows; + int size; + { + char** its; + int i; + + its = (char**) malloc( rows * sizeof(char*) ); + if ( its == (char**) 0 ) + pm_error( "out of memory allocating an array" ); + its[0] = (char*) malloc( rows * cols * size ); + if ( its[0] == (char*) 0 ) + pm_error( "out of memory allocating an array" ); + for ( i = 1; i < rows; ++i ) + its[i] = &(its[0][i * cols * size]); + return its; + } + +void +pm_freearray( its, rows ) + char** its; + int rows; + { + free( its[0] ); + free( its ); + } +#else /* A_FRAGARRAY */ +char** +pm_allocarray( cols, rows, size ) + int cols, rows; + int size; + { + char** its; + int i; + its = (char**) malloc( (rows + 1) * sizeof(char*) ); + if ( its == (char**) 0 ) + pm_error( "out of memory allocating an array" ); + its[rows] = its[0] = (char*) malloc( rows * cols * size ); + if ( its[0] != (char*) 0 ) + for ( i = 1; i < rows; ++i ) + its[i] = &(its[0][i * cols * size]); + else + for( i = 0; i < rows; ++i ) + its[i] = pm_allocrow( cols, size ); + return its; + } +void +pm_freearray( its, rows ) + char** its; + int rows; + { + int i; + if( its[rows] != (char*) 0 ) + free( its[rows] ); + else + for( i = 0; i < rows; ++i ) + pm_freerow( its[i] ); + free( its ); + } +#endif /* A_FRAGARRAY */ + + +/* Case-insensitive keyword matcher. */ + +int +pm_keymatch( str, keyword, minchars ) + char* str; + char* keyword; + int minchars; + { + register int len; + + len = strlen( str ); + if ( len < minchars ) + return 0; + while ( --len >= 0 ) + { + register char c1, c2; + + c1 = *str++; + c2 = *keyword++; + if ( c2 == '\0' ) + return 0; + if ( isupper( c1 ) ) + c1 = tolower( c1 ); + if ( isupper( c2 ) ) + c1 = tolower( c2 ); + if ( c1 != c2 ) + return 0; + } + return 1; + } + + +/* Log base two hacks. */ + +int +pm_maxvaltobits( maxval ) + int maxval; + { + if ( maxval <= 1 ) + return 1; + else if ( maxval <= 3 ) + return 2; + else if ( maxval <= 7 ) + return 3; + else if ( maxval <= 15 ) + return 4; + else if ( maxval <= 31 ) + return 5; + else if ( maxval <= 63 ) + return 6; + else if ( maxval <= 127 ) + return 7; + else if ( maxval <= 255 ) + return 8; + else if ( maxval <= 511 ) + return 9; + else if ( maxval <= 1023 ) + return 10; + else if ( maxval <= 2047 ) + return 11; + else if ( maxval <= 4095 ) + return 12; + else if ( maxval <= 8191 ) + return 13; + else if ( maxval <= 16383 ) + return 14; + else if ( maxval <= 32767 ) + return 15; + else if ( (long) maxval <= 65535L ) + return 16; + else + pm_error( "maxval of %d is too large!", maxval ); + return 0; /* just to make compiler happy; can't get here. */ + } + +int +pm_bitstomaxval( bits ) + int bits; + { + return ( 1 << bits ) - 1; + } + + +/* Initialization. */ + +static char* progname; +static int showmessages; + +void +pm_init( argcP, argv ) + int* argcP; + char* argv[]; + { + int argn, i; +#ifdef A_RGBENV + char *rgbdef; +#endif /* A_RGBENV */ + +#ifndef VMS + /* Extract program name. */ + progname = rindex( argv[0], '/'); +#else +{ char **temp_argv = argv; + int old_argc = *argcP; + int i; + getredirection( argcP, &temp_argv ); + if (*argcP > old_argc) { + /* Number of command line arguments has increased */ + fprintf( stderr, "Sorry!! getredirection() for VMS has changed the argument list!!!\n"); + fprintf( stderr, "This is intolerable at the present time, so we must stop!!!\n"); + exit(1); + } + for (i=0; i<*argcP; i++) + argv[i] = temp_argv[i]; + } + if ( progname == NULL ) progname = rindex( argv[0], ']'); + if ( progname == NULL ) progname = rindex( argv[0], '>'); +#endif + if ( progname == NULL ) + progname = argv[0]; + else + ++progname; + + /* Check for any global args. */ + showmessages = 1; + for ( argn = 1; argn < *argcP; ++argn ) + { + if ( pm_keymatch( argv[argn], "-quiet", 6 ) ) + { + showmessages = 0; + } + else if ( pm_keymatch( argv[argn], "-version", 7 ) ) + { + pm_message( "Version: %s", PBMPLUS_VERSION ); +#if defined(COMPILE_TIME) && defined(COMPILED_BY) + pm_message( "Compiled %s by user \"%s\"", + COMPILE_TIME, COMPILED_BY ); +#endif +#ifdef BSD + pm_message( "BSD defined" ); +#endif /*BSD*/ +#ifdef SYSV +#ifdef VMS + pm_message( "VMS & SYSV defined" ); +#else + pm_message( "SYSV defined" ); +#endif +#endif /*SYSV*/ +#ifdef MSDOS + pm_message( "MSDOS defined" ); +#endif /*MSDOS*/ +#ifdef AMIGA + pm_message( "AMIGA defined" ); +#endif /* AMIGA */ +#ifdef PBMPLUS_RAWBITS + pm_message( "PBMPLUS_RAWBITS defined" ); +#endif /*PBMPLUS_RAWBITS*/ +#ifdef PBMPLUS_BROKENPUTC1 + pm_message( "PBMPLUS_BROKENPUTC1 defined" ); +#endif /*PBMPLUS_BROKENPUTC1*/ +#ifdef PBMPLUS_BROKENPUTC2 + pm_message( "PBMPLUS_BROKENPUTC2 defined" ); +#endif /*PBMPLUS_BROKENPUTC2*/ +#ifdef PGM_BIGGRAYS + pm_message( "PGM_BIGGRAYS defined" ); +#endif /*PGM_BIGGRAYS*/ +#ifdef PPM_PACKCOLORS + pm_message( "PPM_PACKCOLORS defined" ); +#endif /*PPM_PACKCOLORS*/ +#ifdef DEBUG + pm_message( "DEBUG defined" ); +#endif /*DEBUG*/ +#ifdef NEED_VFPRINTF1 + pm_message( "NEED_VFPRINTF1 defined" ); +#endif /*NEED_VFPRINTF1*/ +#ifdef NEED_VFPRINTF2 + pm_message( "NEED_VFPRINTF2 defined" ); +#endif /*NEED_VFPRINTF2*/ +#ifdef RGB_DB +#ifndef A_RGBENV + pm_message( "RGB_DB=\"%s\"", RGB_DB ); +#else /* A_RGBENV */ + if( rgbdef = getenv(RGB_DB) ) + pm_message( "RGB_DB= Env-var %s (set to \"%s\")", RGB_DB, rgbdef ); + else + pm_message( "RGB_DB= Env-var %s (unset)", RGB_DB ); +#endif /* A_RGBENV */ +#endif /*RGB_DB*/ +#ifdef LIBTIFF + pm_message( "LIBTIFF defined" ); +#endif /*LIBTIFF*/ + exit( 0 ); + } + else + continue; + for ( i = argn + 1; i <= *argcP; ++i ) + argv[i - 1] = argv[i]; + --(*argcP); + } + } + +void +pbm_init( argcP, argv ) + int* argcP; + char* argv[]; + { + pm_init( argcP, argv ); + } + + +/* Error handling. */ + +void +pm_usage( usage ) + char* usage; + { + fprintf( stderr, "usage: %s %s\n", progname, usage ); + exit( 1 ); + } + +void +pm_perror( reason ) + char* reason; + { +#ifndef A_STRERROR + extern char* sys_errlist[]; +#endif /* A_STRERROR */ + extern int errno; + char* e; + +#ifdef A_STRERROR + e = strerror(errno); +#else /* A_STRERROR */ + e = sys_errlist[errno]; +#endif /* A_STRERROR */ + + if ( reason != 0 && reason[0] != '\0' ) + pm_error( "%s - %s", reason, e ); + else + pm_error( "%s", e ); + } + +#if __STDC__ +void +pm_message( char* format, ... ) + { + va_list args; + + va_start( args, format ); +#else /*__STDC__*/ +/*VARARGS1*/ +void +pm_message( va_alist ) + va_dcl + { /*}*/ + va_list args; + char* format; + + va_start( args ); + format = va_arg( args, char* ); +#endif /*__STDC__*/ + + if ( showmessages ) + { + fprintf( stderr, "%s: ", progname ); + (void) vfprintf( stderr, format, args ); + fputc( '\n', stderr ); + } + va_end( args ); + } + +#if __STDC__ +void +pm_error( char* format, ... ) + { + va_list args; + + va_start( args, format ); +#else /*__STDC__*/ +/*VARARGS1*/ +void +pm_error( va_alist ) + va_dcl + { /*}*/ + va_list args; + char* format; + + va_start( args ); + format = va_arg( args, char* ); +#endif /*__STDC__*/ + + fprintf( stderr, "%s: ", progname ); + (void) vfprintf( stderr, format, args ); + fputc( '\n', stderr ); + va_end( args ); + exit( 1 ); + } + +#ifdef NEED_VFPRINTF1 + +/* Micro-vfprintf, for systems that don't have vfprintf but do have _doprnt. +*/ + +int +vfprintf( stream, format, args ) + FILE* stream; + char* format; + va_list args; + { + return _doprnt( format, args, stream ); + } +#endif /*NEED_VFPRINTF1*/ + +#ifdef NEED_VFPRINTF2 + +/* Portable mini-vfprintf, for systems that don't have either vfprintf or +** _doprnt. This depends only on fprintf. If you don't have fprintf, +** you might consider getting a new stdio library. +*/ + +int +vfprintf( stream, format, args ) + FILE* stream; + char* format; + va_list args; + { + int n; + char* ep; + char fchar; + char tformat[512]; + int do_long; + int i; + long l; + unsigned u; + unsigned long ul; + char* s; + double d; + + n = 0; + while ( *format != '\0' ) + { + if ( *format != '%' ) + { /* Not special, just write out the char. */ + (void) putc( *format, stream ); + ++n; + ++format; + } + else + { + do_long = 0; + ep = format + 1; + + /* Skip over all the field width and precision junk. */ + if ( *ep == '-' ) + ++ep; + if ( *ep == '0' ) + ++ep; + while ( isdigit( *ep ) ) + ++ep; + if ( *ep == '.' ) + { + ++ep; + while ( isdigit( *ep ) ) + ++ep; + } + if ( *ep == '#' ) + ++ep; + if ( *ep == 'l' ) + { + do_long = 1; + ++ep; + } + + /* Here's the field type. Extract it, and copy this format + ** specifier to a temp string so we can add an end-of-string. + */ + fchar = *ep; + (void) strncpy( tformat, format, ep - format + 1 ); + tformat[ep - format + 1] = '\0'; + + /* Now do a one-argument fprintf with the format string we have + ** isolated. + */ + switch ( fchar ) + { + case 'd': + if ( do_long ) + { + l = va_arg( args, long ); + n += fprintf( stream, tformat, l ); + } + else + { + i = va_arg( args, int ); + n += fprintf( stream, tformat, i ); + } + break; + + case 'o': + case 'x': + case 'X': + case 'u': + if ( do_long ) + { + ul = va_arg( args, unsigned long ); + n += fprintf( stream, tformat, ul ); + } + else + { + u = va_arg( args, unsigned ); + n += fprintf( stream, tformat, u ); + } + break; + + case 'c': + i = (char) va_arg( args, int ); + n += fprintf( stream, tformat, i ); + break; + + case 's': + s = va_arg( args, char* ); + n += fprintf( stream, tformat, s ); + break; + + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + d = va_arg( args, double ); + n += fprintf( stream, tformat, d ); + break; + + case '%': + (void) putc( '%', stream ); + ++n; + break; + + default: + return -1; + } + + /* Resume formatting on the next character. */ + format = ep + 1; + } + } + return nc; + } +#endif /*NEED_VFPRINTF2*/ + +#ifdef NEED_STRSTR +/* for systems which do not provide strstr */ +char* +strstr(s1, s2) + char *s1, *s2; +{ + int ls2 = strlen(s2); + + if (ls2 == 0) + return (s1); + while (strlen(s1) >= ls2) { + if (strncmp(s1, s2, ls2) == 0) + return (s1); + s1++; + } + return (0); +} + +#endif /*NEED_STRSTR*/ + + +/* File open/close that handles "-" as stdin and checks errors. */ + +FILE* +pm_openr( name ) + char* name; + { + FILE* f; + + if ( strcmp( name, "-" ) == 0 ) + f = stdin; + else + { +#ifndef VMS + f = fopen( name, "rb" ); +#else + f = fopen ( name, "r", "ctx=stm" ); +#endif + if ( f == NULL ) + { + pm_perror( name ); + exit( 1 ); + } + } + return f; + } + +FILE* +pm_openw( name ) + char* name; + { + FILE* f; + +#ifndef VMS + f = fopen( name, "wb" ); +#else + f = fopen ( name, "w", "mbc=32", "mbf=2" ); /* set buffer factors */ +#endif + if ( f == NULL ) + { + pm_perror( name ); + exit( 1 ); + } + return f; + } + +void +pm_close( f ) + FILE* f; + { + fflush( f ); + if ( ferror( f ) ) + pm_message( "a file read or write error occurred at some point" ); + if ( f != stdin ) + if ( fclose( f ) != 0 ) + pm_perror( "fclose" ); + } + +/* Endian I/O. +*/ + +int +pm_readbigshort( in, sP ) + FILE* in; + short* sP; + { + int c; + + if ( (c = getc( in )) == EOF ) + return -1; + *sP = ( c & 0xff ) << 8; + if ( (c = getc( in )) == EOF ) + return -1; + *sP |= c & 0xff; + return 0; + } + +#if __STDC__ +int +pm_writebigshort( FILE* out, short s ) +#else /*__STDC__*/ +int +pm_writebigshort( out, s ) + FILE* out; + short s; +#endif /*__STDC__*/ + { + (void) putc( ( s >> 8 ) & 0xff, out ); + (void) putc( s & 0xff, out ); + return 0; + } + +int +pm_readbiglong( in, lP ) + FILE* in; + long* lP; + { + int c; + + if ( (c = getc( in )) == EOF ) + return -1; + *lP = ( c & 0xff ) << 24; + if ( (c = getc( in )) == EOF ) + return -1; + *lP |= ( c & 0xff ) << 16; + if ( (c = getc( in )) == EOF ) + return -1; + *lP |= ( c & 0xff ) << 8; + if ( (c = getc( in )) == EOF ) + return -1; + *lP |= c & 0xff; + return 0; + } + +int +pm_writebiglong( out, l ) + FILE* out; + long l; + { + (void) putc( ( l >> 24 ) & 0xff, out ); + (void) putc( ( l >> 16 ) & 0xff, out ); + (void) putc( ( l >> 8 ) & 0xff, out ); + (void) putc( l & 0xff, out ); + return 0; + } + +int +pm_readlittleshort( in, sP ) + FILE* in; + short* sP; + { + int c; + + if ( (c = getc( in )) == EOF ) + return -1; + *sP = c & 0xff; + if ( (c = getc( in )) == EOF ) + return -1; + *sP |= ( c & 0xff ) << 8; + return 0; + } + +#if __STDC__ +int +pm_writelittleshort( FILE* out, short s ) +#else /*__STDC__*/ +int +pm_writelittleshort( out, s ) + FILE* out; + short s; +#endif /*__STDC__*/ + { + (void) putc( s & 0xff, out ); + (void) putc( ( s >> 8 ) & 0xff, out ); + return 0; + } + +int +pm_readlittlelong( in, lP ) + FILE* in; + long* lP; + { + int c; + + if ( (c = getc( in )) == EOF ) + return -1; + *lP = c & 0xff; + if ( (c = getc( in )) == EOF ) + return -1; + *lP |= ( c & 0xff ) << 8; + if ( (c = getc( in )) == EOF ) + return -1; + *lP |= ( c & 0xff ) << 16; + if ( (c = getc( in )) == EOF ) + return -1; + *lP |= ( c & 0xff ) << 24; + return 0; + } + +int +pm_writelittlelong( out, l ) + FILE* out; + long l; + { + (void) putc( l & 0xff, out ); + (void) putc( ( l >> 8 ) & 0xff, out ); + (void) putc( ( l >> 16 ) & 0xff, out ); + (void) putc( ( l >> 24 ) & 0xff, out ); + return 0; + } + + +/* Read a file of unknown size to a buffer. Return the number of bytes + read. Allocate more memory as we need it. The calling routine has + to free() the buffer. + + Oliver Trepte, oliver@fysik4.kth.se, 930613 */ + +#define PM_BUF_SIZE 16384 /* First try this size of the buffer, then + double this until we reach PM_MAX_BUF_INC */ +#define PM_MAX_BUF_INC 65536 /* Don't allocate more memory in larger blocks + than this. */ + +char *pm_read_unknown_size( file, nread ) + FILE* file; + long* nread; +{ + long nalloc; + register int val; + char* buf; + + *nread = 0; + if ((buf=malloc(PM_BUF_SIZE)) == NULL) + pm_error("Cannot allocate memory"); + nalloc = PM_BUF_SIZE; + + while(1) { + if (*nread >= nalloc) { /* We need a larger buffer */ + if (nalloc > PM_MAX_BUF_INC) + nalloc += PM_MAX_BUF_INC; + else + nalloc += nalloc; + if ((buf=realloc(buf, nalloc)) == NULL) + pm_error("Cannot allocate %d bytes of memory", nalloc); + } + + if ((val = getc(file)) == EOF) + return (buf); + + buf[(*nread)++] = val; + } +} + +/*****************************************************************************/ + +#ifdef VMS +/* + * @(#)argproc.c 1.0 89/02/01 Mark Pizzolato (mark@infopiz.uucp) + */ + +#ifndef lint +char argproc_version[] = "@(#)argproc.c VMS uucp Version infopiz-1.0"; +#endif + +#include "includes.h" /* System include files, system dependent */ + + +/* + * getredirection() is intended to aid in porting C programs + * to VMS (Vax-11 C) which does not support '>' and '<' + * I/O redirection, along with a command line pipe mechanism + * using the '|' AND background command execution '&'. + * The piping mechanism will probably work with almost any 'filter' type + * of program. With suitable modification, it may useful for other + * portability problems as well. + * + * Author: Mark Pizzolato mark@infopiz.UUCP + */ +struct list_item + { + struct list_item *next; + char *value; + }; + +int +getredirection(ac, av) +int *ac; +char ***av; +/* + * Process vms redirection arg's. Exit if any error is seen. + * If getredirection() processes an argument, it is erased + * from the vector. getredirection() returns a new argc and argv value. + * In the event that a background command is requested (by a trailing "&"), + * this routine creates a background subprocess, and simply exits the program. + * + * Warning: do not try to simplify the code for vms. The code + * presupposes that getredirection() is called before any data is + * read from stdin or written to stdout. + * + * Normal usage is as follows: + * + * main(argc, argv) + * int argc; + * char *argv[]; + * { + * getredirection(&argc, &argv); + * } + */ +{ + int argc = *ac; /* Argument Count */ + char **argv = *av; /* Argument Vector */ + char *ap; /* Argument pointer */ + int j; /* argv[] index */ + extern int errno; /* Last vms i/o error */ + int item_count = 0; /* Count of Items in List */ + int new_file; /* flag, true if '>' used */ + struct list_item *list_head = 0; /* First Item in List */ + struct list_item *list_tail; /* Last Item in List */ + char *in = NULL; /* Input File Name */ + char *out = NULL; /* Output File Name */ + char *outmode = "w"; /* Mode to Open Output File */ + int cmargc = 0; /* Piped Command Arg Count */ + char **cmargv = NULL;/* Piped Command Arg Vector */ + stat_t statbuf; /* fstat buffer */ + + /* + * First handle the case where the last thing on the line ends with + * a '&'. This indicates the desire for the command to be run in a + * subprocess, so we satisfy that desire. + */ + ap = argv[argc-1]; + if (0 == strcmp("&", ap)) + exit(background_process(--argc, argv)); + if ('&' == ap[strlen(ap)-1]) + { + ap[strlen(ap)-1] = '\0'; + exit(background_process(argc, argv)); + } + /* + * Now we handle the general redirection cases that involve '>', '>>', + * '<', and pipes '|'. + */ + for (new_file = 0, j = 0; j < argc; ++j) + { + if (0 == strcmp("<", argv[j])) + { + if (j+1 >= argc) + { + errno = EINVAL; + perror("No input file"); + exit(EXIT_ERR); + } + in = argv[++j]; + continue; + } + if ('<' == *(ap = argv[j])) + { + in = 1 + ap; + continue; + } + if (0 == strcmp(">", ap)) + { + if (j+1 >= argc) + { + errno = EINVAL; + perror("No output file"); + exit(EXIT_ERR); + } + out = argv[++j]; + new_file = 1; + continue; + } + if ('>' == *ap) + { + if ('>' == ap[1]) + { + outmode = "a"; + if ('\0' == ap[2]) + out = argv[++j]; + else + out = 2 + ap; + } + else + { out = 1 + ap; new_file = 1; } + continue; + } + if (0 == strcmp("|", argv[j])) + { + if (j+1 >= argc) + { + errno = EPIPE; + perror("No command to Pipe to"); + exit(EXIT_ERR); + } + cmargc = argc-(j+1); + cmargv = &argv[j+1]; + argc = j; + outmode = "wb"; /* pipes are binary mode devices */ + continue; + } + if ('|' == *(ap = argv[j])) + { + ++argv[j]; + cmargc = argc-j; + cmargv = &argv[j]; + argc = j; + outmode = "wb"; /* pipes are binary mode devices */ + continue; + } + expand_wild_cards(ap, &list_head, &list_tail, &item_count); + } + /* + * Allocate and fill in the new argument vector, Some Unix's terminate + * the list with an extra null pointer. + */ + argv = *av = calloc(item_count+1, sizeof(char *)); + for (j = 0; j < item_count; ++j, list_head = list_head->next) + argv[j] = list_head->value; + *ac = item_count; + if (cmargv != NULL) + { + char subcmd[1024]; + static char *pipe_and_fork(); + + if (out != NULL) + { + errno = EINVAL; + perror("Invalid '|' and '>' specified"); + exit(EXIT_ERR); + } + strcpy(subcmd, cmargv[0]); + for (j = 1; j < cmargc; ++j) + { + strcat(subcmd, " \""); + strcat(subcmd, cmargv[j]); + strcat(subcmd, "\""); + } + out = pipe_and_fork(subcmd); + outmode = "wb"; + } + + /* Check for input from a pipe (mailbox) */ + + if(fstat(0, &statbuf) == 0){ + if(strncmp(statbuf.st_dev, "MB", 2) == 0 || + strncmp(statbuf.st_dev, "_MB", 3) == 0){ + + /* Input from a pipe, reopen it in binary mode to disable */ + /* carriage control processing. */ + + if (in != NULL){ + errno = EINVAL; + perror("Invalid '|' and '<' specified"); + exit(EXIT_ERR); + } + freopen(statbuf.st_dev, "rb", stdin); + } + } + else { + perror("fstat failed"); + exit(EXIT_ERR); + } + +#ifdef __ALPHA + /*, "mbc=32", "mbf=2"))) blows up on the ALPHA cbm 11/08/92 */ + if ((in != NULL) && (NULL == freopen(in, "r", stdin))) + { +#else + if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2"))) + { +#endif + perror(in); /* Can't find file */ + exit(EXIT_ERR); /* Is a fatal error */ + } +#ifdef __ALPHA + if ((out != NULL) && (NULL == freopen(out, outmode, stdout))) + { +#else + if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2"))) + { +#endif + perror(ap); /* Error, can't write or append */ + exit(EXIT_ERR); /* Is a fatal error */ + } + + if ( new_file ) { + /* + * We are making an explicit output file, fstat the file and + * declare exit handler to be able change the file to fixed length + * records if necessary. + */ + char fname[256]; + static int outfile_rundown(), check_outfile_filetype(); + static stat_t ofile_stat; + static struct exit_control_block { + struct exit_control_block *flink; + int (*exit_routine)(); + int arg_count; + int *status_address; /* arg 1 */ + stat_t *stat; /* arg 2 */ + int exit_status; + int skew[128]; + } exit_block = + { 0, outfile_rundown, 2, &exit_block.exit_status, &ofile_stat, 0 }; + + if ( fstat ( fileno(stdout), &ofile_stat ) == 0 ) + sys$dclexh ( &exit_block ); + else fprintf(stderr,"Error fstating stdout - %s\n", + strerror(errno,vaxc$errno) ); + + if ( fgetname ( stdout, fname, 0 ) ) check_outfile_filetype ( fname ); + } +#ifdef DEBUG + fprintf(stderr, "Arglist:\n"); + for (j = 0; j < *ac; ++j) + fprintf(stderr, "argv[%d] = '%s'\n", j, argv[j]); +#endif +} + +static int binary_outfile = 0; +void set_outfile_binary() { binary_outfile = 1; return; } + +/* + * Check if output file should be set to binary (fixed 512) on exit based + * upon the filetype. + */ +static int check_outfile_filetype ( name ) + char *name; +{ + char *binary_filetypes, *ext, *p, *t; + binary_filetypes = (char *) getenv ( "PBM_BINARY_FILETYPES" ); + if ( binary_filetypes == NULL ) return 0; + + ext = strchr ( name, '.' ); if ( ext == NULL ) return 0; + ext++; + t = strrchr ( name, '.' ); if ( t != NULL ) *t = '\0'; + + for ( p = binary_filetypes; *p != '\0'; p++ ) { + for ( t = p; + (*p != '\0' ) && (strchr ( ", ", *p ) == NULL); + p++ ) *p = toupper(*p); + *p = '\0'; + + if ( strcmp ( t, ext ) == 0 ) { + binary_outfile = 1; + break; + } + } + return binary_outfile; +} + + +/* + * Exit handler to set output file to binary on image exit. + */ +static int outfile_rundown ( reason, statbuf ) + int *reason; + stat_t *statbuf; +{ + int code, channel, status, LIB$GETDVI(), sys$assign(), sys$qiow(); + long int fib_desc[2], devchar; + short int iosb[4]; + $DESCRIPTOR(device, statbuf->st_dev); + struct fibdef fib; /* File information block (XQP) */ + struct atrdef atr[2]; + struct fat { + unsigned char rtype, ratattrib; + unsigned short int rsize, hiblk[2], efblk[2], ffbyte, maxrec, defext, gbc; + unsigned short int reserved[4], versions; + } rat; + + if ( !binary_outfile ) return 1; /* leave file alone */ + /* + * Assign channel to device listed in stat block and test if it is + * a directory structured device, returning if not. + */ + device.dsc$w_length = strlen ( statbuf->st_dev ); + status = sys$assign ( &device, &channel, 0, 0 ); + if ((status & 1) == 0) { fprintf(stderr, + "assign error to %s (%d)\n", device.dsc$a_pointer, status); + return status; } + code = DVI$_DEVCHAR; + status = LIB$GETDVI ( &code, &channel, 0, &devchar ); + if ((status & 1) == 0) { fprintf(stderr, "getdvi error: %d\n", status); + return status; } + if ( (devchar & DEV$M_DIR) == 0 ) return 1; + /* + * Build File Information Block and Atrribute block. + */ +#ifdef __ALPHA + fib.fib$w_fid[0] = statbuf->st_ino[0]; + fib.fib$w_fid[1] = statbuf->st_ino[1]; + fib.fib$w_fid[2] = statbuf->st_ino[2]; +#else + fib.fib$r_fid_overlay.fib$w_fid[0] = statbuf->st_ino[0]; + fib.fib$r_fid_overlay.fib$w_fid[1] = statbuf->st_ino[1]; + fib.fib$r_fid_overlay.fib$w_fid[2] = statbuf->st_ino[2]; +#endif + + atr[0].atr$w_size = sizeof(rat); + atr[0].atr$w_type = ATR$C_RECATTR; + atr[0].atr$l_addr = &rat; + atr[1].atr$w_size = 0; atr[1].atr$w_type = 0; + /* + * Perform access function with read_attributes sub-function. + */ + freopen ( "SYS$OUTPUT:", "a", stdout ); + fib_desc[0] = 10; fib_desc[1] = (long int) &fib; +#ifdef __ALPHA + fib.fib$l_acctl = FIB$M_WRITE; +#else + fib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_WRITE; +#endif + status = sys$qiow ( 0, channel, IO$_ACCESS|IO$M_ACCESS, + &iosb, 0, 0, &fib_desc, 0, 0, 0, &atr, 0 ); + /* + * If status successful, update record byte and perform a MODIFY. + */ + if ( (status&1) == 1 ) status = iosb[0]; + if ( (status&1) == 1 ) { + rat.rtype = 1; /* fixed length records */ + rat.rsize = 512; /* 512 byte block recrods */ + rat.ratattrib = 0; /* Record attributes: none */ + + status = sys$qiow + ( 0, channel, IO$_MODIFY, &iosb, 0, 0, &fib_desc, 0, 0, 0, &atr, 0 ); + if ( (status&1) == 1 ) status = iosb[0]; + } + sys$dassgn ( channel ); + if ( (status & 1) == 0 ) fprintf ( stderr, + "Failed to convert output file to binary format, status: %d\n", status); + return status; +} + + +static add_item(head, tail, value, count) +struct list_item **head; +struct list_item **tail; +char *value; +int *count; +{ + if (*head == 0) + { + if (NULL == (*head = calloc(1, sizeof(**head)))) + { + errno = ENOMEM; + perror(""); + exit(EXIT_ERR); + } + *tail = *head; + } + else + if (NULL == ((*tail)->next = calloc(1, sizeof(**head)))) + { + errno = ENOMEM; + perror(""); + exit(EXIT_ERR); + } + else + *tail = (*tail)->next; + (*tail)->value = value; + ++(*count); +} + +static expand_wild_cards(item, head, tail, count) +char *item; +struct ltem_list **head; +struct ltem_list **tail; +int *count; +{ +int expcount = 0; +int context = 0; +int status; +int status_value; +int had_version; +$DESCRIPTOR(filespec, item); +$DESCRIPTOR(defaultspec, "SYS$DISK:[]*.*;"); +$DESCRIPTOR(resultspec, ""); + + if (strcspn(item, "*%") == strlen(item)) + { + add_item(head, tail, item, count); + return; + } + resultspec.dsc$b_dtype = DSC$K_DTYPE_T; + resultspec.dsc$b_class = DSC$K_CLASS_D; + resultspec.dsc$a_pointer = NULL; + filespec.dsc$w_length = strlen(item); + /* + * Only return version specs, if the caller specified a version + */ + had_version = strchr(item, ';'); + while (1 == (1&lib$find_file(&filespec, &resultspec, &context, + &defaultspec, 0, &status_value, &0))) + { + char *string; + char *c; + + if (NULL == (string = calloc(1, resultspec.dsc$w_length+1))) + { + errno = ENOMEM; + perror(""); + exit(EXIT_ERR); + } + strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length); + string[resultspec.dsc$w_length] = '\0'; + if (NULL == had_version) + *((char *)strrchr(string, ';')) = '\0'; + /* + * Be consistent with what the C RTL has already done to the rest of + * the argv items and lowercase all of these names. + */ + for (c = string; *c; ++c) + if (isupper(*c)) + *c = tolower(*c); + add_item(head, tail, string, count); + ++expcount; + } + if (expcount == 0) + add_item(head, tail, item, count); + lib$sfree1_dd(&resultspec); + lib$find_file_end(&context); +} + +static int child_st[2]; /* Event Flag set when child process completes */ + +static short child_chan;/* I/O Channel for Pipe Mailbox */ + +static exit_handler(status) +int *status; +{ +short iosb[4]; + + if (0 == child_st[0]) + { +#ifdef DEBUG + fprintf(stderr, "Waiting for Child Process to Finnish . . .\n"); +#endif + fflush(stdout); /* Have to flush pipe for binary data to */ + /* terminate properly -- */ +#ifdef DEBUG + fprintf(stderr, " stdout flushed. . .\n"); +#endif + sys$qio(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0); +#ifdef DEBUG + fprintf(stderr, " Pipe terminated. . .\n"); +#endif + fclose(stdout); +#ifdef DEBUG + fprintf(stderr, " stdout closed. . .\n"); +#endif + sys$synch(0, child_st); + sys$dassgn(child_chan); + } +#ifdef DEBUG + fprintf(stderr, " sync done. . .\n"); +#endif +} + +#include /* System Information Definitions */ + +static sig_child(chan) +int chan; +{ +#ifdef DEBUG + fprintf(stderr, "Child Completion AST, st: %x\n", child_st[0] ); +#endif + if (child_st[0] == 0) + { child_st[0] = 1; } + sys$setef ( 0 ); +} + +static struct exit_control_block + { + struct exit_control_block *flink; + int (*exit_routine)(); + int arg_count; + int *status_address; + int exit_status; + } exit_block = + { + 0, + exit_handler, + 1, + &exit_block.exit_status, + 0 + }; + +static char *pipe_and_fork(cmd) +char *cmd; +{ + $DESCRIPTOR(cmddsc, cmd); + static char mbxname[64], ef = 0; + $DESCRIPTOR(mbxdsc, mbxname); + short iosb[4]; + int status; + int pid; + struct + { + short dna_buflen; + short dna_itmcod; + char *dna_buffer; + short *dna_retlen; + int listend; + } itmlst = + { + sizeof(mbxname), + DVI$_DEVNAM, + mbxname, + &mbxdsc.dsc$w_length, + 0 + }; + int mbxsize; + struct + { + short mbf_buflen; + short mbf_itmcod; + int *mbf_maxbuf; + short *mbf_retlen; + int listend; + } syiitmlst = + { + sizeof(mbxsize), + SYI$_MAXBUF, + &mbxsize, + 0, + 0 + }; + + cmddsc.dsc$w_length = strlen(cmd); + /* + * Get the SYSGEN parameter MAXBUF, and the smaller of it and 2048 as + * the size of the 'pipe' mailbox. + */ + if (1 == (1&(vaxc$errno = sys$getsyiw(0, 0, 0, &syiitmlst, iosb, 0, 0, 0)))) + vaxc$errno = iosb[0]; + if (0 == (1&vaxc$errno)) + { + errno = EVMSERR; + perror("Can't get SYSGEN parameter value for MAXBUF"); + exit(EXIT_ERR); + } + if (mbxsize > 2048) + mbxsize = 2048; + if (0 == (1&(vaxc$errno = sys$crembx(0, &child_chan, mbxsize, mbxsize, 0, 0, 0)))) + { + errno = EVMSERR; + perror("Can't create pipe mailbox"); + exit(EXIT_ERR); + } + if (1 == (1&(vaxc$errno = sys$getdviw(0, child_chan, 0, &itmlst, iosb, + 0, 0, 0)))) + vaxc$errno = iosb[0]; + if (0 == (1&vaxc$errno)) + { + errno = EVMSERR; + perror("Can't get pipe mailbox device name"); + exit(EXIT_ERR); + } + mbxname[mbxdsc.dsc$w_length] = '\0'; +#ifdef DEBUG + fprintf(stderr, "Pipe Mailbox Name = '%s'\n", mbxname); +#endif + if (0 == (1&(vaxc$errno = lib$spawn(&cmddsc, &mbxdsc, 0, &1, + 0, &pid, child_st, &ef, sig_child, + &child_chan)))) + { + errno = EVMSERR; + perror("Can't spawn subprocess"); + exit(EXIT_ERR); + } +#ifdef DEBUG + fprintf(stderr, "Subprocess's Pid = %08X\n", pid); +#endif + sys$dclexh(&exit_block); + return(mbxname); +} + +background_process(argc, argv) +int argc; +char **argv; +{ +char command[2048] = "$"; +$DESCRIPTOR(value, command); +$DESCRIPTOR(cmd, "BACKGROUND$COMMAND"); +$DESCRIPTOR(null, "NLA0:"); +int pid; + + strcat(command, argv[0]); + while (--argc) + { + strcat(command, " \""); + strcat(command, *(++argv)); + strcat(command, "\""); + } + value.dsc$w_length = strlen(command); + if (0 == (1&(vaxc$errno = lib$set_symbol(&cmd, &value)))) + { + errno = EVMSERR; + perror("Can't create symbol for subprocess command"); + exit(EXIT_ERR); + } + if (0 == (1&(vaxc$errno = lib$spawn(&cmd, &null, 0, &17, 0, &pid)))) + { + errno = EVMSERR; + perror("Can't spawn subprocess"); + exit(EXIT_ERR); + } +#ifdef DEBUG + fprintf(stderr, "%s\n", command); +#endif + fprintf(stderr, "%08X\n", pid); + return(EXIT_OK); +} + +/* got this off net.sources */ + +#ifdef VMS +#define index strchr +#endif /*VMS*/ + +/* + * get option letter from argument vector + */ +int opterr = 1, /* useless, never set or used */ + optind = 1, /* index into parent argv vector */ + optopt; /* character checked for validity */ +char *optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define EMSG "" +#define tell(s) fputs(progname,stderr);fputs(s,stderr); \ + fputc(optopt,stderr);fputc('\n',stderr);return(BADCH); + +getopt(nargc,nargv,ostr) +int nargc; +char **nargv, + *ostr; +{ + static char *place = EMSG; /* option letter processing */ + register char *oli; /* option letter list index */ + char *index(); + char *progname; + + if(!*place) { /* update scanning pointer */ + if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF); + if (*place == '-') { /* found "--" */ + ++optind; + return(EOF); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) { + if(!*place) ++optind; +#ifdef VMS + progname = strrchr(nargv[0],']'); +#else + progname = rindex(nargv[0],'/'); +#endif + if (!progname) progname = nargv[0]; else progname++; + tell(": illegal option -- "); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) ++optind; + } + else { /* need an argument */ + if (*place) optarg = place; /* no white space */ + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; +#ifdef VMS + progname = strrchr(nargv[0],']'); +#else + progname = rindex(nargv[0],'/'); +#endif + if (!progname) progname = nargv[0]; else progname++; + tell(": option requires an argument -- "); + } + else optarg = nargv[optind]; /* white space */ + place = EMSG; + ++optind; + } + return(optopt); /* dump back option letter */ +} +#endif /* VMS */ diff --git a/panda/src/pnm/libpbm2.c b/panda/src/pnm/libpbm2.c new file mode 100644 index 0000000000..54a52f957c --- /dev/null +++ b/panda/src/pnm/libpbm2.c @@ -0,0 +1,134 @@ +/* libpbm2.c - pbm utility library part 2 +** +** Copyright (C) 1988 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "pbm.h" +#include "libpbm.h" + +static bit pbm_getbit ARGS((FILE *)); +static bit +pbm_getbit( file ) + FILE* file; + { + register char ch; + + do + { + ch = pbm_getc( file ); + } + while ( ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' ); + + if ( ch != '0' && ch != '1' ) + pm_error( "junk in file where bits should be" ); + + return ( ch == '1' ) ? 1 : 0; + } + +int +pbm_readmagicnumber( file ) + FILE* file; + { + int ich1, ich2; + + ich1 = getc( file ); + if ( ich1 == EOF ) + pm_error( "EOF / read error reading magic number" ); + ich2 = getc( file ); + if ( ich2 == EOF ) + pm_error( "EOF / read error reading magic number" ); + return ich1 * 256 + ich2; + } + +void +pbm_readpbminitrest( file, colsP, rowsP ) + FILE* file; + int* colsP; + int* rowsP; + { + /* Read size. */ + *colsP = pbm_getint( file ); + *rowsP = pbm_getint( file ); + } + +void +pbm_readpbminit( file, colsP, rowsP, formatP ) + FILE* file; + int* colsP; + int* rowsP; + int* formatP; + { + /* Check magic number. */ + *formatP = pbm_readmagicnumber( file ); + switch ( PBM_FORMAT_TYPE(*formatP) ) + { + case PBM_TYPE: + pbm_readpbminitrest( file, colsP, rowsP ); + break; + + default: + pm_error( "bad magic number - not a pbm file" ); + } + } + +void +pbm_readpbmrow( file, bitrow, cols, format ) + FILE* file; + bit* bitrow; + int cols, format; + { + register int col, bitshift; + register unsigned char item; + register bit* bP; + + switch ( format ) + { + case PBM_FORMAT: + for ( col = 0, bP = bitrow; col < cols; ++col, ++bP ) + *bP = pbm_getbit( file ); + break; + + case RPBM_FORMAT: + bitshift = -1; + for ( col = 0, bP = bitrow; col < cols; ++col, ++bP ) + { + if ( bitshift == -1 ) + { + item = pbm_getrawbyte( file ); + bitshift = 7; + } + *bP = ( item >> bitshift ) & 1; + --bitshift; + } + break; + + default: + pm_error( "can't happen" ); + } + } + +bit** +pbm_readpbm( file, colsP, rowsP ) + FILE* file; + int* colsP; + int* rowsP; + { + register bit** bits; + int format, row; + + pbm_readpbminit( file, colsP, rowsP, &format ); + + bits = pbm_allocarray( *colsP, *rowsP ); + + for ( row = 0; row < *rowsP; ++row ) + pbm_readpbmrow( file, bits[row], *colsP, format ); + + return bits; + } diff --git a/panda/src/pnm/libpbm3.c b/panda/src/pnm/libpbm3.c new file mode 100644 index 0000000000..04c13dfe64 --- /dev/null +++ b/panda/src/pnm/libpbm3.c @@ -0,0 +1,123 @@ +/* libpbm3.c - pbm utility library part 3 +** +** Copyright (C) 1988 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "pbm.h" +#include "libpbm.h" + +static void pbm_writepbmrowplain ARGS((FILE* file, bit* bitrow, int cols)); +#ifdef PBMPLUS_RAWBITS +static void pbm_writepbmrowraw ARGS((FILE* file, bit* bitrow, int cols)); +#endif /* PBMPLUS_RAWBITS */ +void +pbm_writepbminit( file, cols, rows, forceplain ) + FILE* file; + int cols, rows; + int forceplain; + { +#ifdef PBMPLUS_RAWBITS + if ( ! forceplain ) { + fprintf( file, "%c%c\n%d %d\n", PBM_MAGIC1, RPBM_MAGIC2, cols, rows ); +#ifdef VMS + set_outfile_binary(); +#endif + } + else + fprintf( file, "%c%c\n%d %d\n", PBM_MAGIC1, PBM_MAGIC2, cols, rows ); +#else /*PBMPLUS_RAWBITS*/ + fprintf( file, "%c%c\n%d %d\n", PBM_MAGIC1, PBM_MAGIC2, cols, rows ); +#endif /*PBMPLUS_RAWBITS*/ + } + +#ifdef PBMPLUS_RAWBITS +static void +pbm_writepbmrowraw( file, bitrow, cols ) + FILE* file; + bit* bitrow; + int cols; + { + register int col, bitshift; + register unsigned char item; + register bit* bP; + + bitshift = 7; + item = 0; + for ( col = 0, bP = bitrow; col < cols; ++col, ++bP ) + { + if ( *bP ) + item += 1 << bitshift; + --bitshift; + if ( bitshift == -1 ) + { + (void) putc( item, file ); + bitshift = 7; + item = 0; + } + } + if ( bitshift != 7 ) + (void) putc( item, file ); + } +#endif /*PBMPLUS_RAWBITS*/ + +static void +pbm_writepbmrowplain( file, bitrow, cols ) + FILE* file; + bit* bitrow; + int cols; + { + register int col, charcount; + register bit* bP; + + charcount = 0; + for ( col = 0, bP = bitrow; col < cols; ++col, ++bP ) + { + if ( charcount >= 70 ) + { + (void) putc( '\n', file ); + charcount = 0; + } + (void) putc( *bP ? '1' : '0', file ); + ++charcount; + } + (void) putc( '\n', file ); + } + +void +pbm_writepbmrow( file, bitrow, cols, forceplain ) + FILE* file; + bit* bitrow; + int cols; + int forceplain; + { +#ifdef PBMPLUS_RAWBITS + if ( ! forceplain ) + pbm_writepbmrowraw( file, bitrow, cols ); + else + pbm_writepbmrowplain( file, bitrow, cols ); +#else /*PBMPLUS_RAWBITS*/ + pbm_writepbmrowplain( file, bitrow, cols ); +#endif /*PBMPLUS_RAWBITS*/ + } + +void +pbm_writepbm( file, bits, cols, rows, forceplain ) + FILE* file; + bit** bits; + int cols, rows; + int forceplain; + { + int row; + + pbm_writepbminit( file, cols, rows, forceplain ); + + for ( row = 0; row < rows; ++row ) + pbm_writepbmrow( file, bits[row], cols, forceplain ); + } diff --git a/panda/src/pnm/libpbm4.c b/panda/src/pnm/libpbm4.c new file mode 100644 index 0000000000..96e5bcd61a --- /dev/null +++ b/panda/src/pnm/libpbm4.c @@ -0,0 +1,80 @@ +/* libpbm4.c - pbm utility library part 4 +** +** Copyright (C) 1988 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "pbm.h" +#include "libpbm.h" + +char +pbm_getc( file ) + FILE* file; + { + register int ich; + register char ch; + + ich = getc( file ); + if ( ich == EOF ) + pm_error( "EOF / read error" ); + ch = (char) ich; + + if ( ch == '#' ) + { + do + { + ich = getc( file ); + if ( ich == EOF ) + pm_error( "EOF / read error" ); + ch = (char) ich; + } + while ( ch != '\n' && ch != '\r' ); + } + + return ch; + } + +unsigned char +pbm_getrawbyte( file ) + FILE* file; + { + register int iby; + + iby = getc( file ); + if ( iby == EOF ) + pm_error( "EOF / read error" ); + return (unsigned char) iby; + } + +int +pbm_getint( file ) + FILE* file; + { + register char ch; + register int i; + + do + { + ch = pbm_getc( file ); + } + while ( ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' ); + + if ( ch < '0' || ch > '9' ) + pm_error( "junk in file where an integer should be" ); + + i = 0; + do + { + i = i * 10 + ch - '0'; + ch = pbm_getc( file ); + } + while ( ch >= '0' && ch <= '9' ); + + return i; + } diff --git a/panda/src/pnm/libpbm5.c b/panda/src/pnm/libpbm5.c new file mode 100644 index 0000000000..51c70523d5 --- /dev/null +++ b/panda/src/pnm/libpbm5.c @@ -0,0 +1,1090 @@ +/* libpbm5.c - pbm utility library part 5 +** +** Font routines. +** +** Support for BDF fonts Copyright 1993 by George Phillips. +** +** Copyright (C) 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "pbm.h" +#include "pbmfont.h" + +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* The default font, packed in hex so this source file doesn't get huge. +** You can replace this with your own font using pbm_dumpfont(). +*/ +#define DEFAULTFONT_ROWS 155 +#define DEFAULTFONT_COLS 112 +static unsigned long defaultfont_bits[DEFAULTFONT_ROWS][(DEFAULTFONT_COLS+31)/32] = { + {0x00000000L,0x20000c00L,0x10000000L,0x00000000L}, + {0xc600a000L,0x42000810L,0x00000002L,0x00000063L}, + {0x6c00a000L,0x45000810L,0x00000002L,0x00000036L}, + {0x6c00a000L,0x88800808L,0xf2e1dee2L,0x00000036L}, + {0x54000000L,0x80000800L,0x11122442L,0x0000002aL}, + {0x54000001L,0x00000800L,0x11122442L,0x0000002aL}, + {0x54000001L,0x00000800L,0x11122282L,0x0000002aL}, + {0x44000102L,0x00000800L,0x11122382L,0x00000022L}, + {0xee000102L,0x00000800L,0x11e1e102L,0x00000077L}, + {0x00000204L,0x00000800L,0x11002102L,0x00000000L}, + {0x00000000L,0x00000c00L,0x11002102L,0x00000000L}, + {0x00000000L,0x003f8000L,0xe3807600L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x02000080L,0x00040000L,0x00120000L,0x00000001L}, + {0x04000082L,0x828e1838L,0x20210100L,0x00000002L}, + {0x04000082L,0x82912448L,0x20210100L,0x00000002L}, + {0x08000082L,0x8fd01940L,0x404087c2L,0x00000004L}, + {0x08000080L,0x050c0622L,0x00408102L,0x00000004L}, + {0x10000080L,0x05061874L,0x0040828fL,0x00008008L}, + {0x10000080L,0x1f912688L,0x00408002L,0x00000008L}, + {0x20000000L,0x0a11098cL,0x00408002L,0x00000010L}, + {0x20000080L,0x0a0e0672L,0x00210000L,0x00000010L}, + {0x40000000L,0x00040000L,0x00210000L,0x00000020L}, + {0x00000000L,0x00000000L,0x00120000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x004e0838L,0x7023e1cfL,0x00008000L}, + {0x00000000L,0x00913844L,0x88620208L,0x00008000L}, + {0x08000000L,0x00910844L,0x08a20401L,0x00000004L}, + {0x10000000L,0x01110844L,0x08a20401L,0x00000008L}, + {0x20000000L,0x01110808L,0x3123c781L,0x00000010L}, + {0x400003e0L,0x02110810L,0x0a202441L,0x00000020L}, + {0x20000000L,0x02110820L,0x0bf02442L,0x00000010L}, + {0x10008000L,0x04110844L,0x88242442L,0x00000008L}, + {0x08008002L,0x040e3e7cL,0x7073c382L,0x00000004L}, + {0x00010000L,0x08000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x0000e1c0L,0x00000000L,0x00000000L,0x00000000L}, + {0x00011220L,0x00000000L,0x70e38f87L,0x00000000L}, + {0x20011220L,0x00020020L,0x89108448L,0x00008010L}, + {0x10011220L,0x00040010L,0x09314448L,0x00008008L}, + {0x0800e221L,0x02083e08L,0x11514788L,0x00000004L}, + {0x040111e0L,0x00100004L,0x2153e448L,0x00000002L}, + {0x08011020L,0x00083e08L,0x213a2448L,0x00008004L}, + {0x10011040L,0x02040010L,0x01022448L,0x00008008L}, + {0x2000e381L,0x02020020L,0x20e77f87L,0x00000010L}, + {0x00000000L,0x04000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x3803e7efL,0xc73bbe3dL,0xdb863ce7L,0x0000001cL}, + {0x44011224L,0x48910808L,0x91036648L,0x00008022L}, + {0x4c011285L,0x48910808L,0xa1036648L,0x00008026L}, + {0x54011387L,0x081f0808L,0xc102a548L,0x0000802aL}, + {0x54011285L,0x09910808L,0xe102a548L,0x0000802aL}, + {0x4e011204L,0x08910848L,0x9112a4c8L,0x00008027L}, + {0x40011224L,0x08910848L,0x891224c8L,0x00008020L}, + {0x3803e7efL,0x073bbe31L,0xcff77e47L,0x0000001cL}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000003L,0x00000000L}, + {0x0003e1cfL,0x87bff7efL,0xdfbf77c2L,0x00000000L}, + {0x00013224L,0x48a4a244L,0x89122442L,0x00000000L}, + {0x00011224L,0x4824a244L,0xa8a14482L,0x00000000L}, + {0x00013227L,0x8e04226cL,0xa8414102L,0x00000000L}, + {0x0001e224L,0x83842228L,0xa8a08102L,0x00000000L}, + {0x00010224L,0x40842228L,0xd8a08242L,0x00000000L}, + {0x00010224L,0x48843638L,0x51108442L,0x00000000L}, + {0x0003c1ceL,0x6f1f1c10L,0x53b9c7c2L,0x00000000L}, + {0x00000060L,0x00000000L,0x00000002L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000003L,0x00000000L}, + {0xfe000000L,0x00000000L,0x00000000L,0x0000007fL}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00010180L,0x000000c0L,0x003001c0L,0x00000000L}, + {0x08008081L,0x00040040L,0x00100200L,0x00000004L}, + {0x10008082L,0x80040040L,0x00100200L,0x00000008L}, + {0x10004084L,0x40023c78L,0x70f1c7c7L,0x00004008L}, + {0x10004080L,0x00000244L,0x89122208L,0x00008008L}, + {0x20002080L,0x00001e44L,0x8113e208L,0x00008010L}, + {0x10002080L,0x00002244L,0x81120208L,0x00008008L}, + {0x10001080L,0x00002244L,0x89122208L,0x00008008L}, + {0x10001080L,0x00001db8L,0x70e9c787L,0x00008008L}, + {0x10000880L,0x00000000L,0x00000000L,0x00008008L}, + {0x08000180L,0x00000000L,0x00000000L,0x00008004L}, + {0x00000000L,0x1fc00000L,0x00000007L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00030080L,0x981c0000L,0x00000000L,0x00000000L}, + {0x20010000L,0x08040000L,0x00000000L,0x00000010L}, + {0x10010000L,0x08040000L,0x00000000L,0x00000008L}, + {0x10016387L,0x898474b8L,0x72e1d5c7L,0x00000008L}, + {0x10019080L,0x8a042a64L,0x89122208L,0x00008008L}, + {0x08011080L,0x8c042a44L,0x89122207L,0x00000004L}, + {0x10011080L,0x8a042a44L,0x89122200L,0x00008008L}, + {0x10011080L,0x89042a44L,0x89122208L,0x00008008L}, + {0x1003bbe0L,0x98dfebe6L,0x71e1e787L,0x00000008L}, + {0x10000000L,0x80000000L,0x01002000L,0x00000008L}, + {0x20000000L,0x80000000L,0x01002000L,0x00000010L}, + {0x00000007L,0x00000000L,0x03807000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00008000L,0x00000000L,0x10410000L,0x00000000L}, + {0x00008000L,0x00000000L,0x20408000L,0x00000000L}, + {0x0001f66eL,0xfdfbf77cL,0x20408000L,0x00000000L}, + {0x24008224L,0x488a2248L,0x20408240L,0x00000012L}, + {0x54008224L,0x4a842210L,0x40404540L,0x0000002aL}, + {0x48008222L,0x8a8a1420L,0x20408480L,0x00000024L}, + {0x00008a23L,0x85111c44L,0x20408000L,0x00000000L}, + {0x000071d1L,0x0531887cL,0x20408000L,0x00000000L}, + {0x00000000L,0x00000800L,0x20408000L,0x00000000L}, + {0x00000000L,0x00000800L,0x10410000L,0x00000000L}, + {0x00000000L,0x00003000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x00000000L,0x00000000L,0x00000000L}, + {0x00000000L,0x20000c00L,0x10000000L,0x00000000L}, + {0xc600a000L,0x42000810L,0x00000002L,0x00000063L}, + {0x6c00a000L,0x45000810L,0x00000002L,0x00000036L}, + {0x6c00a000L,0x88800808L,0xf2e1dee2L,0x00000036L}, + {0x54000000L,0x80000800L,0x11122442L,0x0000002aL}, + {0x54000001L,0x00000800L,0x11122442L,0x0000002aL}, + {0x54000001L,0x00000800L,0x11122282L,0x0000002aL}, + {0x44000102L,0x00000800L,0x11122382L,0x00000022L}, + {0xee000102L,0x00000800L,0x11e1e102L,0x00000077L}, + {0x00000204L,0x00000800L,0x11002102L,0x00000000L}, + {0x00000000L,0x00000c00L,0x11002102L,0x00000000L}, + {0x00000000L,0x003f8000L,0xe3807600L,0x00000000L} + }; + +/* A default BDF font */ +/* Not as nicely compacted as the PBM font, oh well. */ + +static struct glyph _g[190] = { + { 1, 1, 0, 0, 3, "\0" }, + { 1, 9, 1, 0, 3, "\1\1\1\1\1\1\1\0\1" }, + { 3, 3, 1, 6, 5, "\1\0\1\1\0\1\1\0\1" }, + { 5, 8, 0, 0, 6, "\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\1\0\1\0\0\1\0\1\0" }, + { 5, 11, 0, -1, 6, "\0\0\1\0\0\0\1\1\1\0\1\0\1\0\1\1\0\1\0\0\0\1\1\0\0\0\0\1\1\0\0\0\1\0\1\0\0\1\0\1\1\0\1\0\1\0\1\1\1\0\0\0\1\0\0" }, + { 8, 9, 0, 0, 9, "\0\1\1\0\0\0\1\1\1\0\0\1\1\1\1\0\1\0\0\1\0\1\0\0\0\1\1\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\1\0\1\1\0\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\0\1\1\0" }, + { 9, 9, 0, 0, 10, "\0\0\0\1\1\0\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\0\1\1\0\1\1\1\0\1\1\1\1\0\0\1\0\1\1\0\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\0\0\1\1\1\0\1\0\1\1\1\1\0\1\1\0" }, + { 2, 3, 1, 6, 4, "\1\1\0\1\1\0" }, + { 3, 12, 1, -3, 5, "\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1" }, + { 3, 12, 0, -3, 5, "\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0" }, + { 5, 5, 0, 4, 6, "\0\0\1\0\0\1\0\1\0\1\0\1\1\1\0\1\0\1\0\1\0\0\1\0\0" }, + { 5, 5, 1, 1, 7, "\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0" }, + { 2, 3, 0, -2, 3, "\0\1\0\1\1\0" }, + { 5, 1, 1, 3, 8, "\1\1\1\1\1" }, + { 1, 1, 1, 0, 3, "\1" }, + { 3, 9, 0, 0, 3, "\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0" }, + { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\1\0\1\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\1\0\1\1\0\1\1\1\0" }, + { 4, 9, 0, 0, 6, "\0\0\1\0\0\1\1\0\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\1\1" }, + { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\1\1\1\1\1" }, + { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\1\1\1\0\0\0\0\0\1\0\0\0\0\1\1\0\0\0\1\0\1\1\1\0" }, + { 5, 9, 0, 0, 6, "\0\0\0\1\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\1\1\1\1\1\0\0\0\1\0\0\0\0\1\0" }, + { 5, 9, 0, 0, 6, "\0\0\1\1\1\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\1\1\0\0\1\1\0\1\1\1\0" }, + { 5, 9, 0, 0, 6, "\0\0\0\1\1\0\1\1\0\0\0\1\0\0\0\1\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\0" }, + { 5, 9, 0, 0, 6, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\0\1\0\0\0" }, + { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\0\1\1\1\0" }, + { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\1\0\0\0\1\0\0\0\1\1\0\1\1\0\0\0" }, + { 1, 6, 1, 0, 3, "\1\0\0\0\0\1" }, + { 2, 8, 0, -2, 3, "\0\1\0\0\0\0\0\0\0\0\0\1\0\1\1\0" }, + { 6, 5, 0, 1, 8, "\0\0\0\0\1\1\0\0\1\1\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1" }, + { 5, 3, 1, 2, 7, "\1\1\1\1\1\0\0\0\0\0\1\1\1\1\1" }, + { 6, 5, 1, 1, 8, "\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\1\1\0\0\1\1\0\0\0\0" }, + { 4, 9, 0, 0, 5, "\0\1\1\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\1\0\0" }, + { 10, 11, 1, -2, 11, "\0\0\0\0\1\1\1\1\0\0\0\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\1\1\0\1\0\1\1\0\0\1\0\0\1\0\0\1\1\0\1\0\0\0\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\1\0\1\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0" }, + { 9, 9, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" }, + { 7, 9, 0, 0, 8, "\1\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\1\1\1\1\1\1\0" }, + { 7, 9, 0, 0, 8, "\0\0\1\1\1\0\1\0\1\1\0\0\1\1\0\1\0\0\0\0\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\0\1\1\0\0\1\1\0\0\1\1\1\1\0" }, + { 8, 9, 0, 0, 9, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\1\1\1\1\1\1\0\0" }, + { 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" }, + { 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0" }, + { 8, 9, 0, 0, 9, "\0\0\1\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\0\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 8, 9, 0, 0, 9, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\1\1\1\0\0\1\1\1" }, + { 3, 9, 0, 0, 4, "\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 4, 9, 0, 0, 4, "\0\1\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\1\0\1\0\1\1\0\0" }, + { 8, 9, 0, 0, 8, "\1\1\1\0\1\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\1\1\1\0\0\1\1\1" }, + { 6, 9, 0, 0, 7, "\1\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\1\1\1\1\1\1\1" }, + { 11, 9, 0, 0, 11, "\1\1\0\0\0\0\0\0\0\1\1\0\1\1\0\0\0\0\0\1\1\0\0\1\1\0\0\0\0\0\1\1\0\0\1\0\1\0\0\0\1\0\1\0\0\1\0\1\0\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\1\1\1\0\0\1\0\0\1\1\1" }, + { 9, 9, 0, 0, 9, "\1\1\0\0\0\0\1\1\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\0\0\0\1\0" }, + { 8, 9, 0, 0, 9, "\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 7, 9, 0, 0, 7, "\1\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0" }, + { 8, 11, 0, -2, 9, "\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\0\0\1\1" }, + { 8, 9, 0, 0, 8, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\0\1\1\1\1\1\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\1\1\1\0\0\0\1\1" }, + { 6, 9, 0, 0, 7, "\0\1\1\1\0\1\1\0\0\0\1\1\1\0\0\0\0\1\0\1\1\0\0\0\0\0\1\1\1\0\0\0\0\0\1\1\1\0\0\0\0\1\1\1\0\0\1\1\1\0\1\1\1\0" }, + { 7, 9, 0, 0, 7, "\1\1\1\1\1\1\1\1\0\0\1\0\0\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0" }, + { 8, 9, 0, 0, 8, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0" }, + { 12, 9, 0, 0, 12, "\1\1\1\0\1\1\1\0\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\1\0\0\0\0\0\1\0\1\0\1\0\1\0\0\0\0\0\1\1\0\0\1\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0" }, + { 8, 9, 0, 0, 8, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\1\0\1\1\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\0\1\0\1\1\1\0\0\1\1\1" }, + { 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\0\0\0" }, + { 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\1\1\1\1" }, + { 3, 12, 1, -3, 5, "\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1" }, + { 3, 9, 0, 0, 3, "\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1" }, + { 3, 12, 0, -3, 5, "\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1" }, + { 5, 5, 0, 4, 6, "\0\0\1\0\0\0\1\0\1\0\0\1\0\1\0\1\0\0\0\1\1\0\0\0\1" }, + { 6, 1, 0, -3, 6, "\1\1\1\1\1\1" }, + { 2, 3, 1, 6, 4, "\0\1\1\0\1\1" }, + { 5, 6, 1, 0, 6, "\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" }, + { 5, 9, 0, 0, 6, "\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0" }, + { 4, 6, 1, 0, 5, "\0\1\1\0\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\1\0\1\1\0" }, + { 5, 9, 1, 0, 6, "\0\0\1\1\0\0\0\0\1\0\0\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" }, + { 5, 6, 1, 0, 6, "\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" }, + { 3, 9, 0, 0, 3, "\0\0\1\0\1\0\0\1\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0" }, + { 5, 9, 1, -3, 6, "\0\1\1\1\1\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\0\1\0\0\0\0\1\1\1\0\1\0\0\0\1\1\0\0\0\1\0\1\1\1\0" }, + { 6, 9, 0, 0, 6, "\1\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" }, + { 3, 9, 0, 0, 3, "\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 2, 12, 0, -3, 3, "\0\1\0\0\0\0\1\1\0\1\0\1\0\1\0\1\0\1\0\1\0\1\1\0" }, + { 6, 9, 0, 0, 6, "\1\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\1\0\1\0\0\0\1\1\0\0\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\1" }, + { 3, 9, 0, 0, 3, "\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 9, 6, 0, 0, 9, "\1\0\1\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1\0\1\1" }, + { 6, 6, 0, 0, 6, "\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" }, + { 4, 6, 1, 0, 6, "\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" }, + { 5, 9, 0, -3, 6, "\1\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\0" }, + { 5, 9, 1, -3, 6, "\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\1\1\1" }, + { 4, 6, 0, 0, 4, "\1\0\1\1\0\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\1\1\1\0" }, + { 4, 6, 1, 0, 6, "\0\1\1\1\1\0\0\1\1\1\0\0\0\0\1\1\1\0\0\1\1\1\1\0" }, + { 4, 7, 0, 0, 4, "\0\1\0\0\1\1\1\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\1" }, + { 6, 6, 0, 0, 6, "\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" }, + { 6, 6, 0, 0, 6, "\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0" }, + { 9, 6, 0, 0, 9, "\1\1\1\0\1\1\0\1\1\0\1\0\0\1\0\0\1\0\0\1\1\0\1\0\1\1\0\0\0\1\0\1\0\1\0\0\0\0\1\1\0\1\0\0\0\0\0\1\0\0\1\0\0\0" }, + { 5, 6, 1, 0, 6, "\1\1\0\1\1\0\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\1\0\1\1\0\1\1" }, + { 6, 9, 0, -3, 6, "\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" }, + { 4, 6, 1, 0, 6, "\1\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\1" }, + { 4, 12, 1, -3, 6, "\0\0\1\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\1" }, + { 1, 9, 1, 0, 3, "\1\1\1\1\1\1\1\1\1" }, + { 4, 12, 0, -3, 6, "\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\1\1\0\0" }, + { 6, 2, 0, 3, 7, "\0\1\1\0\0\1\1\0\0\1\1\0" }, + { 1, 9, 1, -3, 4, "\1\0\1\1\1\1\1\1\1" }, + { 5, 8, 1, -1, 6, "\0\0\0\0\1\0\1\1\1\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\1\0\0\1\0\1\1\1\0\1\0\0\0\0" }, + { 5, 9, 0, 0, 6, "\0\0\1\1\0\0\1\0\0\1\0\1\0\0\0\0\1\0\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\1\1\1\0\1\1" }, + { 6, 7, 1, 1, 7, "\1\0\0\0\0\1\0\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\0\1\0\0\0\0\1" }, + { 5, 9, 0, 0, 6, "\1\0\0\0\1\1\0\0\0\1\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\1\1\0" }, + { 1, 9, 1, 0, 3, "\1\1\1\0\0\1\1\1\1" }, + { 4, 12, 1, -3, 6, "\0\1\1\1\1\0\0\1\1\1\0\0\0\1\1\0\1\0\1\1\1\0\0\1\1\0\0\1\1\1\0\1\0\1\1\0\0\0\1\1\1\0\0\1\1\1\1\0" }, + { 3, 1, 0, 7, 3, "\1\0\1" }, + { 9, 9, 1, 0, 11, "\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\1\1\0\1\0\1\0\1\0\0\1\0\0\1\1\0\1\0\0\0\0\0\1\1\0\1\0\0\1\0\0\1\0\1\0\1\1\1\0\1\0\0\1\1\0\0\0\1\1\0\0\0\0\1\1\1\0\0\0" }, + { 3, 6, 1, 3, 5, "\1\1\0\0\0\1\1\1\1\1\0\1\0\0\0\1\1\1" }, + { 5, 5, 1, 0, 7, "\0\0\1\0\1\0\1\0\1\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1" }, + { 6, 4, 1, 1, 8, "\1\1\1\1\1\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1" }, + { 4, 1, 1, 3, 6, "\1\1\1\1" }, + { 9, 9, 1, 0, 11, "\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\1\1\0\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\1\1\0\0\1\1\0\0\1\0\1\0\0\1\1\1\0\1\0\1\0\1\0\0\1\1\0\0\0\1\1\0\0\0\1\1\1\1\0\0\0" }, + { 4, 1, 0, 7, 4, "\1\1\1\1" }, + { 4, 4, 0, 5, 5, "\0\1\1\0\1\0\0\1\1\0\0\1\0\1\1\0" }, + { 5, 7, 1, 0, 7, "\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\1\1" }, + { 4, 5, 0, 4, 4, "\0\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1" }, + { 3, 5, 0, 4, 4, "\1\1\1\0\0\1\0\1\0\0\0\1\1\1\0" }, + { 2, 2, 1, 7, 4, "\0\1\1\0" }, + { 6, 9, 0, -3, 6, "\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\1\0\0\0\0\0\1\0\0\0\0\0\1\1\0\0\0" }, + { 6, 12, 0, -3, 7, "\0\1\1\1\1\1\1\1\1\0\1\0\1\1\1\0\1\0\1\1\1\0\1\0\1\1\1\0\1\0\0\1\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0" }, + { 1, 1, 1, 3, 3, "\1" }, + { 3, 3, 0, -3, 3, "\0\1\0\0\0\1\1\1\1" }, + { 3, 5, 0, 4, 4, "\0\1\0\1\1\0\0\1\0\0\1\0\1\1\1" }, + { 3, 6, 1, 3, 5, "\0\1\0\1\0\1\1\0\1\0\1\0\0\0\0\1\1\1" }, + { 5, 5, 0, 0, 7, "\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\1\0\1\0\1\0\1\0\0" }, + { 9, 9, 0, 0, 9, "\0\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\1\0\1\0\0\0\1\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0" }, + { 9, 9, 0, 0, 9, "\0\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\1\0\1\1\0\0\0\0\1\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\1\1\1" }, + { 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\1\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\1\0\1\0\0\0\1\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0" }, + { 4, 9, 0, -3, 5, "\0\0\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\1\1\0" }, + { 9, 12, 0, 0, 9, "\0\0\0\1\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" }, + { 9, 12, 0, 0, 9, "\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" }, + { 9, 12, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" }, + { 9, 12, 0, 0, 9, "\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" }, + { 9, 11, 0, 0, 9, "\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" }, + { 9, 12, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" }, + { 10, 9, 0, 0, 11, "\0\0\1\1\1\1\1\1\1\1\0\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\1\1\1\1\0\0\1\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\0\0\1\1\1\1\1\1" }, + { 7, 12, 0, -3, 8, "\0\0\1\1\1\0\1\0\1\1\0\0\1\1\0\1\0\0\0\0\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\0\1\1\0\0\1\1\0\0\1\1\1\1\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\1\1\0\0" }, + { 7, 12, 0, 0, 8, "\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" }, + { 7, 12, 0, 0, 8, "\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" }, + { 7, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" }, + { 7, 11, 0, 0, 8, "\0\0\1\0\1\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" }, + { 3, 12, 0, 0, 4, "\1\0\0\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 3, 12, 0, 0, 4, "\0\0\1\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 3, 12, 0, 0, 4, "\0\1\0\1\0\1\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 3, 11, 0, 0, 4, "\1\0\1\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 8, 9, 0, 0, 9, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\1\1\1\1\1\1\0\0" }, + { 9, 12, 0, 0, 9, "\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\1\1\0\0\0\0\1\1\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\0\0\0\1\0" }, + { 8, 12, 0, 0, 9, "\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 8, 12, 0, 0, 9, "\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 8, 12, 0, 0, 9, "\0\0\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 8, 12, 0, 0, 9, "\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 8, 11, 0, 0, 9, "\0\0\1\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 5, 5, 1, 1, 7, "\1\0\0\0\1\0\1\0\1\0\0\0\1\0\0\0\1\0\1\0\1\0\0\0\1" }, + { 9, 10, 0, 0, 9, "\0\0\0\0\0\0\0\0\1\0\0\1\1\1\1\0\1\0\0\1\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\1\0\0\1\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\0\1\1\0\0\1\0\1\1\1\1\0\0\0" }, + { 8, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 8, 12, 0, 0, 8, "\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 8, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 8, 11, 0, 0, 8, "\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" }, + { 9, 12, 0, 0, 9, "\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\0\0\0" }, + { 7, 9, 0, 0, 7, "\1\1\1\0\0\0\0\0\1\0\0\0\0\0\0\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0" }, + { 6, 9, 0, 0, 6, "\0\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\0\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\0\1\1\1\0\1\1\0" }, + { 5, 9, 1, 0, 6, "\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" }, + { 5, 9, 1, 0, 6, "\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" }, + { 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" }, + { 5, 9, 1, 0, 6, "\0\1\0\1\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" }, + { 5, 8, 1, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" }, + { 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" }, + { 8, 6, 1, 0, 9, "\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\1\1\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\0\1\1\0\1\1\1\0" }, + { 4, 9, 1, -3, 5, "\0\1\1\0\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\1\0\1\1\0\0\1\0\0\0\0\1\0\1\1\1\0" }, + { 5, 9, 1, 0, 6, "\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" }, + { 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" }, + { 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" }, + { 5, 8, 1, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" }, + { 3, 9, 0, 0, 3, "\1\0\0\0\1\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 3, 9, 0, 0, 3, "\0\1\0\1\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 3, 9, 0, 0, 3, "\0\1\0\1\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 3, 8, 0, 0, 3, "\1\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" }, + { 4, 9, 1, 0, 6, "\0\1\0\0\0\1\1\1\1\0\1\0\0\1\1\1\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" }, + { 6, 9, 0, 0, 6, "\0\0\1\0\1\0\0\1\0\1\0\0\0\0\0\0\0\0\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" }, + { 4, 9, 1, 0, 6, "\0\1\0\0\0\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" }, + { 4, 9, 1, 0, 6, "\0\0\1\0\0\1\0\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" }, + { 4, 9, 1, 0, 6, "\0\0\1\0\0\1\0\1\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" }, + { 4, 9, 1, 0, 6, "\0\1\0\1\1\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" }, + { 4, 8, 1, 0, 6, "\1\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" }, + { 5, 5, 1, 1, 7, "\0\0\1\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\1\0\0" }, + { 6, 7, 0, -1, 6, "\0\0\1\1\0\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\0\0\1\0\0\0\0\0" }, + { 6, 9, 0, 0, 6, "\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" }, + { 6, 9, 0, 0, 6, "\0\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" }, + { 6, 9, 0, 0, 6, "\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" }, + { 6, 8, 0, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" }, + { 6, 12, 0, -3, 6, "\0\0\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" }, + { 5, 12, 0, -3, 6, "\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\0" }, + { 6, 11, 0, -3, 6, "\0\1\0\0\1\0\0\0\0\0\0\0\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" } +}; + +static struct font default_bdffont = { 14, 15, -1, -3, { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + _g + 0, + _g + 1, + _g + 2, + _g + 3, + _g + 4, + _g + 5, + _g + 6, + _g + 7, + _g + 8, + _g + 9, + _g + 10, + _g + 11, + _g + 12, + _g + 13, + _g + 14, + _g + 15, + _g + 16, + _g + 17, + _g + 18, + _g + 19, + _g + 20, + _g + 21, + _g + 22, + _g + 23, + _g + 24, + _g + 25, + _g + 26, + _g + 27, + _g + 28, + _g + 29, + _g + 30, + _g + 31, + _g + 32, + _g + 33, + _g + 34, + _g + 35, + _g + 36, + _g + 37, + _g + 38, + _g + 39, + _g + 40, + _g + 41, + _g + 42, + _g + 43, + _g + 44, + _g + 45, + _g + 46, + _g + 47, + _g + 48, + _g + 49, + _g + 50, + _g + 51, + _g + 52, + _g + 53, + _g + 54, + _g + 55, + _g + 56, + _g + 57, + _g + 58, + _g + 59, + _g + 60, + _g + 61, + _g + 62, + _g + 63, + _g + 64, + _g + 65, + _g + 66, + _g + 67, + _g + 68, + _g + 69, + _g + 70, + _g + 71, + _g + 72, + _g + 73, + _g + 74, + _g + 75, + _g + 76, + _g + 77, + _g + 78, + _g + 79, + _g + 80, + _g + 81, + _g + 82, + _g + 83, + _g + 84, + _g + 85, + _g + 86, + _g + 87, + _g + 88, + _g + 89, + _g + 90, + _g + 91, + _g + 92, + _g + 93, + _g + 94, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + _g + 95, + _g + 96, + _g + 97, + _g + 98, + _g + 99, + _g + 100, + _g + 101, + _g + 102, + _g + 103, + _g + 104, + _g + 105, + _g + 106, + _g + 107, + _g + 108, + _g + 109, + _g + 110, + _g + 111, + _g + 112, + _g + 113, + _g + 114, + _g + 115, + _g + 116, + _g + 117, + _g + 118, + _g + 119, + _g + 120, + _g + 121, + _g + 122, + _g + 123, + _g + 124, + _g + 125, + _g + 126, + _g + 127, + _g + 128, + _g + 129, + _g + 130, + _g + 131, + _g + 132, + _g + 133, + _g + 134, + _g + 135, + _g + 136, + _g + 137, + _g + 138, + _g + 139, + _g + 140, + _g + 141, + _g + 142, + _g + 143, + _g + 144, + _g + 145, + _g + 146, + _g + 147, + _g + 148, + _g + 149, + _g + 150, + _g + 151, + _g + 152, + _g + 153, + _g + 154, + _g + 155, + _g + 156, + _g + 157, + _g + 158, + _g + 159, + _g + 160, + _g + 161, + _g + 162, + _g + 163, + _g + 164, + _g + 165, + _g + 166, + _g + 167, + _g + 168, + _g + 169, + _g + 170, + _g + 171, + _g + 172, + _g + 173, + _g + 174, + _g + 175, + _g + 176, + _g + 177, + _g + 178, + _g + 179, + _g + 180, + _g + 181, + _g + 182, + _g + 183, + _g + 184, + _g + 185, + _g + 186, + _g + 187, + _g + 188, + _g + 189 + } +}; + +struct font* +pbm_defaultfont( name ) + char* name; + { + bit** defaultfont; + int row, col, scol; + unsigned long l; + + if (!strcmp(name, "bdf")) + return &default_bdffont; + + if (strcmp(name, "fixed")) + pm_error( "built-in font name unknown, try 'bdf' or 'fixed'" ); + + defaultfont = pbm_allocarray( DEFAULTFONT_COLS, DEFAULTFONT_ROWS ); + for ( row = 0; row < DEFAULTFONT_ROWS; ++row ) + { + for ( col = 0; col < DEFAULTFONT_COLS; col += 32 ) + { + l = defaultfont_bits[row][col / 32]; + for ( scol = min( col + 32, DEFAULTFONT_COLS ) - 1; + scol >= col; --scol ) + { + if ( l & 1 ) + defaultfont[row][scol] = 1; + else + defaultfont[row][scol] = 0; + l >>= 1; + } + } + } + + return pbm_dissectfont( defaultfont, DEFAULTFONT_ROWS, DEFAULTFONT_COLS ); + } + +struct font* +pbm_dissectfont( font, frows, fcols ) + bit** font; + int frows; + int fcols; + { + /* + ** This routine expects a font bitmap representing the following text: + ** + ** (0,0) + ** M ",/^_[`jpqy| M + ** + ** / !"#$%&'()*+ / + ** < ,-./01234567 < + ** > 89:;<=>?@ABC > + ** @ DEFGHIJKLMNO @ + ** _ PQRSTUVWXYZ[ _ + ** { \]^_`abcdefg { + ** } hijklmnopqrs } + ** ~ tuvwxyz{|}~ ~ + ** + ** M ",/^_[`jpqy| M + ** + ** The bitmap must be cropped exactly to the edges. + ** + ** The dissection works by finding the first blank row and column; that + ** gives the height and width of the maximum-sized character, which is + ** not too useful. But the distance from there to the opposite side is + ** an integral multiple of the cell size, and that's what we need. Then + ** it's just a matter of filling in all the coordinates. + ** + ** The difference between char_height, char_width and char_aheight, + ** char_awidth is that the first is the size of the cell including + ** spacing, while the second is just the actual maximum-size character. + */ + int char_width, char_height, char_awidth, char_aheight; + int brow, bcol, row, col, d, ch, r, c, i; + struct font* fn; + struct glyph* glyph; + char* bmap; + bit b; + + /* Find first blank row. */ + for ( brow = 0; brow < frows / 6; ++brow ) + { + b = font[brow][0]; + for ( col = 1; col < fcols; ++col ) + if ( font[brow][col] != b ) + goto nextrow; + goto gotblankrow; + nextrow: ; + } + pm_error( "couldn't find blank row in font" ); + +gotblankrow: + /* Find first blank col. */ + for ( bcol = 0; bcol < fcols / 8; ++bcol ) + { + b = font[0][bcol]; + for ( row = 1; row < frows; ++row ) + if ( font[row][bcol] != b ) + goto nextcol; + goto gotblankcol; + nextcol: ; + } + pm_error( "couldn't find blank col in font" ); + +gotblankcol: + /* Now compute character cell size. */ + d = frows - brow; + char_height = d / 11; + if ( char_height * 11 != d ) + pm_error( "problem computing character cell height" ); + d = fcols - bcol; + char_width = d / 15; + if ( char_width * 15 != d ) + pm_error( "problem computing character cell width" ); + char_aheight = brow; + char_awidth = bcol; + + /* Now convert to a general font */ + + fn = (struct font*) malloc( sizeof(struct font) ); + if ( fn == (struct font*) 0 ) + pm_error( "out of memory allocating font structure" ); + + fn->maxwidth = char_awidth; + fn->maxheight = char_height; + fn->x = fn->y = 0; + for (i = 0; i < 256; i++) + fn->glyph[i] = 0; + fn->oldfont = font; + fn->frows = frows; + fn->fcols = fcols; + + glyph = (struct glyph*) malloc( sizeof(struct glyph) * 95 ); + if ( glyph == (struct glyph*) 0 ) + pm_error( "out of memory allocating glyphs" ); + + bmap = (char*) malloc( fn->maxwidth * fn->maxheight * 95 ); + if ( bmap == (char*) 0) + pm_error( "out of memory allocating glyph data" ); + + /* Now fill in the 0,0 coords. */ + row = char_height * 2; + col = char_width * 2; + for ( ch = 0; ch < 95; ++ch ) + { + glyph[ch].width = fn->maxwidth; + glyph[ch].height = fn->maxheight; + glyph[ch].x = glyph[ch].y = 0; + glyph[ch].xadd = char_width; + + for ( r = 0; r < glyph[ch].height; ++r ) + for ( c = 0; c < glyph[ch].width; ++c ) + bmap[r * glyph[ch].width + c] = font[row + r][col + c]; + + glyph[ch].bmap = bmap; + bmap += glyph[ch].width * glyph[ch].height; + + fn->glyph[ch + 32] = glyph + ch; + + col += char_width; + if ( col >= char_width * 14 ) + { + col = char_width * 2; + row += char_height; + } + } + + return fn; + } + +struct font* +pbm_loadfont( filename ) +char* filename; +{ + FILE* fp; + struct font* fn; + char line[256]; + + fp = pm_openr( filename ); + fgets(line, 256, fp); + pm_close( fp ); + + if (line[0] == PBM_MAGIC1 && + (line[1] == PBM_MAGIC2 || line[1] == RPBM_MAGIC2)) + { + return pbm_loadpbmfont( filename ); + } + else if (!strncmp(line, "STARTFONT", 9)) { + if (!(fn = pbm_loadbdffont( filename ))) + pm_error( "could not load BDF font file" ); + return fn; + } + else + pm_error( "font file not in a recognized format "); + return NULL; /* to make compiler happy; can't get here. */ +} + +struct font* pbm_loadpbmfont( filename ) +char* filename; +{ + FILE* ifp; + bit** font; + int fcols, frows; + + ifp = pm_openr( filename ); + font = pbm_readpbm( ifp, &fcols, &frows ); + pm_close( ifp ); + return pbm_dissectfont( font, frows, fcols ); +} + +void +pbm_dumpfont( fn ) + struct font* fn; +{ + /* Dump out font as C source code. */ + int row, col, scol, lperrow; + unsigned long l; + + if (fn->oldfont) { + printf( "#define DEFAULTFONT_ROWS %d\n", fn->frows ); + printf( "#define DEFAULTFONT_COLS %d\n", fn->fcols ); + printf( "static unsigned long defaultfont_bits[DEFAULTFONT_ROWS][(DEFAULTFONT_COLS+31)/32] = {\n" ); + for ( row = 0; row < fn->frows; ++row ) + { + lperrow = 0; + for ( col = 0; col < fn->fcols; col += 32 ) + { + if ( lperrow == 0 ) + printf( " {" ); + else if ( lperrow % 6 == 0 ) + { + printf( ",\n " ); + lperrow = 0; + } + else + printf( "," ); + l = 0; + for ( scol = col; scol < min( col + 32, fn->fcols ); ++scol ) + { + l <<= 1; + if ( fn->oldfont[row][scol] ) + l |= 1; + } + printf( "0x%08lxL", l ); + ++lperrow; + } + printf( "}%s\n", row == fn->frows - 1 ? "" : "," ); + } + printf( " };\n" ); + } + else { + struct glyph* glyph; + int i, j, ng; + + ng = 0; + for (i = 0; i < 256; i++) + if (fn->glyph[i]) + ng++; + + printf("static struct glyph _g[%d] = {\n", ng); + for (i = 0; i < 256; i++) { + if (!(glyph = fn->glyph[i])) + continue; + + printf(" { %d, %d, %d, %d, %d, \"", glyph->width, glyph->height, + glyph->x, glyph->y, glyph->xadd); + + for (j = 0; j < glyph->width * glyph->height; j++) + if (glyph->bmap[j]) + printf("\\1"); + else + printf("\\0"); + + ng--; + printf("\" }%s\n", ng ? "," : ""); + } + printf("};\n"); + + printf("static struct font default_bdffont = { %d, %d, %d, %d, {\n", + fn->maxwidth, fn->maxheight, fn->x, fn->y); + + for (i = 0; i < 256; i++) { + if (fn->glyph[i]) + printf(" _g + %d", ng++); + else + printf(" 0"); + + if (i != 255) printf(","); + printf("\n"); + } + + printf(" }\n};\n"); + exit(0); + + } + +} + + +/* Routines for loading a BDF font file */ + +static int readline ARGS((FILE* fp, char* buf, char* arg[])); + +#define expect(str) if (readline(fp, line, arg) < 0 || strcmp(arg[0], (str))) \ + { fclose(fp); return 0; } + +struct font* pbm_loadbdffont(name) +char* name; +{ + FILE* fp; + char line[1024], *arg[32], *b, *hex; + int i, n, numchar, hdig, encoding; + struct font* font; + struct glyph* glyph; + + if (!(fp = fopen(name, "r"))) + return 0; + + expect("STARTFONT"); + + if (!(font = (struct font*)malloc(sizeof(struct font)))) + pm_error("no memory for font"); + font->oldfont = 0; + for (i = 0; i < 256; i++) + font->glyph[i] = 0; + + while (readline(fp, line, arg) >= 0) { + if (!strcmp(arg[0], "COMMENT")) + continue; + if (!strcmp(arg[0], "SIZE")) + continue; + + if (!strcmp(arg[0], "STARTPROPERTIES")) { + n = atoi(arg[1]); + for (; n > 0 && readline(fp, line, arg) >= 0; n--) + ; + } + else if (!strcmp(arg[0], "FONTBOUNDINGBOX")) { + font->maxwidth = atoi(arg[1]); + font->maxheight = atoi(arg[2]); + font->x = atoi(arg[3]); + font->y = atoi(arg[4]); + } + else if (!strcmp(arg[0], "ENDFONT")) { + fclose(fp); + return font; + } + else if (!strcmp(arg[0], "CHARS")) { + numchar = atoi(arg[1]); + while (numchar > 0) { + if (readline(fp, line, arg) < 0) { fclose(fp); return 0; } + if (!strcmp(arg[0], "COMMENT")) + continue; + if (strcmp(arg[0], "STARTCHAR")) { fclose(fp); return 0; } + if (!(glyph = (struct glyph*)malloc(sizeof(struct glyph)))) + pm_error("no memory for font glyph"); + + expect("ENCODING"); + if ((encoding = atoi(arg[1])) < 0) { + if (arg[2]) + encoding = atoi(arg[2]); + else { + while (readline(fp, line, arg) >= 0) + if (!strcmp(arg[0], "ENDCHAR")) + break; + + numchar--; + continue; + } + } + expect("SWIDTH"); + expect("DWIDTH"); + glyph->xadd = atoi(arg[1]); + expect("BBX"); + glyph->width = atoi(arg[1]); + glyph->height = atoi(arg[2]); + glyph->x = atoi(arg[3]); + glyph->y = atoi(arg[4]); + + if (!(glyph->bmap = (char*)malloc(glyph->width * glyph->height))) + pm_error("no memory for font glyph byte map"); + + if (readline(fp, line, arg) < 0) { fclose(fp); return 0; } + if (!strcmp(arg[0], "ATTRIBUTES")) + if (readline(fp, line, arg) < 0) { fclose(fp); return 0; } + + b = glyph->bmap; + for (n = glyph->height; n > 0; n--) { + if (readline(fp, line, arg) < 0) { fclose(fp); return 0; } + hex = line; + for (i = glyph->width; i > 0; i -= 4) { + hdig = *hex++; + if (hdig >= '0' && hdig <= '9') + hdig -= '0'; + else if (hdig >= 'a' && hdig <= 'f') + hdig -= 'a' - 10; + else if (hdig >= 'A' && hdig <= 'F') + hdig -= 'A' - 10; + + *b++ = hdig & 8 ? 1 : 0; + if (i > 1) *b++ = hdig & 4 ? 1 : 0; + if (i > 2) *b++ = hdig & 2 ? 1 : 0; + if (i > 3) *b++ = hdig & 1; + } + } + + expect("ENDCHAR"); + + font->glyph[encoding] = glyph; + + numchar--; + } + } + } + return font; +} + +static int readline(fp, buf, arg) +FILE* fp; +char* buf; +char* arg[]; +{ + if (!fgets(buf, 1024, fp)) + return -1; + + return mk_argvn(buf, arg, 32); +} + +int mk_argvn(s, vec, mk_max) +char* s; +char* vec[]; +int mk_max; +{ + int n; + + n = 0; + while (*s) { + if (isspace(*s)) { + *s++ = '\0'; + continue; + } + vec[n++] = s; + if (n >= mk_max) + break; + while (*s && !isspace(*s)) + s++; + } + vec[n] = 0; + return n; +} diff --git a/panda/src/pnm/libpgm.h b/panda/src/pnm/libpgm.h new file mode 100644 index 0000000000..1da62bbaa8 --- /dev/null +++ b/panda/src/pnm/libpgm.h @@ -0,0 +1,11 @@ +/* libpgm.h - internal header file for libpgm portable graymap library +*/ + +#ifndef _LIBPGM_H_ +#define _LIBPGM_H_ + +/* Here are some routines internal to the pgm library. */ + +EXPCL_PANDA void pgm_readpgminitrest( FILE* file, int* colsP, int* rowsP, gray* maxvalP ); + +#endif /*_LIBPGM_H_*/ diff --git a/panda/src/pnm/libpgm1.c b/panda/src/pnm/libpgm1.c new file mode 100644 index 0000000000..62dc9dff2c --- /dev/null +++ b/panda/src/pnm/libpgm1.c @@ -0,0 +1,149 @@ +/* libpgm1.c - pgm utility library part 1 +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "pgm.h" +#include "libpgm.h" +#include "pbm.h" +#include "libpbm.h" + +void +pgm_init( argcP, argv ) + int* argcP; + char* argv[]; + { + pbm_init( argcP, argv ); + } + +void +pgm_readpgminitrest( file, colsP, rowsP, maxvalP ) + FILE* file; + int* colsP; + int* rowsP; + gray* maxvalP; + { + int maxval; + + /* Read size. */ + *colsP = pbm_getint( file ); + *rowsP = pbm_getint( file ); + + /* Read maxval. */ + maxval = pbm_getint( file ); + if ( maxval > PGM_MAXMAXVAL ) + pm_error( "maxval is too large - try reconfiguring with PGM_BIGGRAYS" ); + *maxvalP = maxval; + } + +gray pgm_pbmmaxval = 1; + +void +pgm_readpgminit( file, colsP, rowsP, maxvalP, formatP ) + FILE* file; + int* colsP; + int* rowsP; + int* formatP; + gray* maxvalP; + { + /* Check magic number. */ + *formatP = pbm_readmagicnumber( file ); + switch ( PGM_FORMAT_TYPE(*formatP) ) + { + case PGM_TYPE: + pgm_readpgminitrest( file, colsP, rowsP, maxvalP ); + break; + + case PBM_TYPE: + pbm_readpbminitrest( file, colsP, rowsP ); + *maxvalP = pgm_pbmmaxval; + break; + + default: + pm_error( "bad magic number - not a pgm or pbm file" ); + } + } + +#if __STDC__ +void +pgm_readpgmrow( FILE* file, gray* grayrow, int cols, gray maxval, int format ) +#else /*__STDC__*/ +void +pgm_readpgmrow( file, grayrow, cols, maxval, format ) + FILE* file; + gray* grayrow; + int cols; + gray maxval; + int format; +#endif /*__STDC__*/ + { + register int col; + register gray* gP; + bit* bitrow; + register bit* bP; + + switch ( format ) + { + case PGM_FORMAT: + for ( col = 0, gP = grayrow; col < cols; ++col, ++gP ) + { + *gP = pbm_getint( file ); +#ifdef DEBUG + if ( *gP > maxval ) + pm_error( "value out of bounds (%u > %u)", *gP, maxval ); +#endif /*DEBUG*/ + } + break; + + case RPGM_FORMAT: + for ( col = 0, gP = grayrow; col < cols; ++col, ++gP ) + { + *gP = pbm_getrawbyte( file ); +#ifdef DEBUG + if ( *gP > maxval ) + pm_error( "value out of bounds (%u > %u)", *gP, maxval ); +#endif /*DEBUG*/ + } + break; + + case PBM_FORMAT: + case RPBM_FORMAT: + bitrow = pbm_allocrow( cols ); + pbm_readpbmrow( file, bitrow, cols, format ); + for ( col = 0, gP = grayrow, bP = bitrow; col < cols; ++col, ++gP, ++bP ) + *gP = ( *bP == PBM_WHITE ) ? maxval : 0; + pbm_freerow( bitrow ); + break; + + default: + pm_error( "can't happen" ); + } + } + +gray** +pgm_readpgm( file, colsP, rowsP, maxvalP ) + FILE* file; + int* colsP; + int* rowsP; + gray* maxvalP; + { + gray** grays; + int row; + int format; + + pgm_readpgminit( file, colsP, rowsP, maxvalP, &format ); + + grays = pgm_allocarray( *colsP, *rowsP ); + + for ( row = 0; row < *rowsP; ++row ) + pgm_readpgmrow( file, grays[row], *colsP, *maxvalP, format ); + + return grays; + } diff --git a/panda/src/pnm/libpgm2.c b/panda/src/pnm/libpgm2.c new file mode 100644 index 0000000000..3b60578698 --- /dev/null +++ b/panda/src/pnm/libpgm2.c @@ -0,0 +1,161 @@ +/* libpgm2.c - pgm utility library part 2 +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "pgm.h" +#include "libpgm.h" + +static void putus ARGS((unsigned short n, FILE* file)); +static void pgm_writepgmrowplain ARGS((FILE* file, gray* grayrow, int cols, gray maxval)); +#ifdef PBMPLUS_RAWBITS +static void pgm_writepgmrowraw ARGS((FILE* file, gray* grayrow, int cols, gray maxval)); +#endif /* PBMPLUS_RAWBITS */ +#if __STDC__ +void +pgm_writepgminit( FILE* file, int cols, int rows, gray maxval, int forceplain ) +#else /*__STDC__*/ +void +pgm_writepgminit( file, cols, rows, maxval, forceplain ) + FILE* file; + int cols, rows; + gray maxval; + int forceplain; +#endif /*__STDC__*/ + { +#ifdef PBMPLUS_RAWBITS + if ( maxval <= 255 && ! forceplain ) { + fprintf( + file, "%c%c\n%d %d\n%d\n", PGM_MAGIC1, RPGM_MAGIC2, + cols, rows, maxval ); +#ifdef VMS + set_outfile_binary(); +#endif + } + else + fprintf( + file, "%c%c\n%d %d\n%d\n", PGM_MAGIC1, PGM_MAGIC2, + cols, rows, maxval ); +#else /*PBMPLUS_RAWBITS*/ + fprintf( + file, "%c%c\n%d %d\n%d\n", PGM_MAGIC1, PGM_MAGIC2, + cols, rows, maxval ); +#endif /*PBMPLUS_RAWBITS*/ + } + +static void +putus( n, file ) + unsigned short n; + FILE* file; + { + if ( n >= 10 ) + putus( n / 10, file ); + (void) putc( n % 10 + '0', file ); + } + +#ifdef PBMPLUS_RAWBITS +static void +pgm_writepgmrowraw( file, grayrow, cols, maxval ) + FILE* file; + gray* grayrow; + int cols; + gray maxval; + { + register int col; + register gray* gP; + + for ( col = 0, gP = grayrow; col < cols; ++col, ++gP ) + { +#ifdef DEBUG + if ( *gP > maxval ) + pm_error( "value out of bounds (%u > %u)", *gP, maxval ); +#endif /*DEBUG*/ + (void) putc( *gP, file ); + } + } +#endif /*PBMPLUS_RAWBITS*/ + +static void +pgm_writepgmrowplain( file, grayrow, cols, maxval ) + FILE* file; + gray* grayrow; + int cols; + gray maxval; + { + register int col, charcount; + register gray* gP; + + charcount = 0; + for ( col = 0, gP = grayrow; col < cols; ++col, ++gP ) + { + if ( charcount >= 65 ) + { + (void) putc( '\n', file ); + charcount = 0; + } + else if ( charcount > 0 ) + { + (void) putc( ' ', file ); + ++charcount; + } +#ifdef DEBUG + if ( *gP > maxval ) + pm_error( "value out of bounds (%u > %u)", *gP, maxval ); +#endif /*DEBUG*/ + putus( (unsigned long) *gP, file ); + charcount += 3; + } + if ( charcount > 0 ) + (void) putc( '\n', file ); + } + +#if __STDC__ +void +pgm_writepgmrow( FILE* file, gray* grayrow, int cols, gray maxval, int forceplain ) +#else /*__STDC__*/ +void +pgm_writepgmrow( file, grayrow, cols, maxval, forceplain ) + FILE* file; + gray* grayrow; + int cols; + gray maxval; + int forceplain; +#endif /*__STDC__*/ + { +#ifdef PBMPLUS_RAWBITS + if ( maxval <= 255 && ! forceplain ) + pgm_writepgmrowraw( file, grayrow, cols, maxval ); + else + pgm_writepgmrowplain( file, grayrow, cols, maxval ); +#else /*PBMPLUS_RAWBITS*/ + pgm_writepgmrowplain( file, grayrow, cols, maxval ); +#endif /*PBMPLUS_RAWBITS*/ + } + +#if __STDC__ +void +pgm_writepgm( FILE* file, gray** grays, int cols, int rows, gray maxval, int forceplain ) +#else /*__STDC__*/ +void +pgm_writepgm( file, grays, cols, rows, maxval, forceplain ) + FILE* file; + gray** grays; + int cols, rows; + gray maxval; + int forceplain; +#endif /*__STDC__*/ + { + int row; + + pgm_writepgminit( file, cols, rows, maxval, forceplain ); + + for ( row = 0; row < rows; ++row ) + pgm_writepgmrow( file, grays[row], cols, maxval, forceplain ); + } diff --git a/panda/src/pnm/libpnm1.c b/panda/src/pnm/libpnm1.c new file mode 100644 index 0000000000..6f4c2afe01 --- /dev/null +++ b/panda/src/pnm/libpnm1.c @@ -0,0 +1,132 @@ +/* libpnm1.c - pnm utility library part 1 +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "pnm.h" + +#include "ppm.h" +#include "libppm.h" + +#include "pgm.h" +#include "libpgm.h" + +#include "pbm.h" +#include "libpbm.h" + +void +pnm_init( argcP, argv ) + int* argcP; + char* argv[]; + { + ppm_init( argcP, argv ); + } + +EXPCL_PANDA xelval pnm_pbmmaxval = 1; + +void +pnm_readpnminit( file, colsP, rowsP, maxvalP, formatP ) + FILE* file; + int* colsP; + int* rowsP; + int* formatP; + xelval* maxvalP; + { + gray gmaxval; + + /* Check magic number. */ + *formatP = pbm_readmagicnumber( file ); + switch ( PNM_FORMAT_TYPE(*formatP) ) + { + case PPM_TYPE: + ppm_readppminitrest( file, colsP, rowsP, (pixval*) maxvalP ); + break; + + case PGM_TYPE: + pgm_readpgminitrest( file, colsP, rowsP, &gmaxval ); + *maxvalP = (xelval) gmaxval; + break; + + case PBM_TYPE: + pbm_readpbminitrest( file, colsP, rowsP ); + *maxvalP = pnm_pbmmaxval; + break; + + default: + pm_error( "bad magic number - not a ppm, pgm, or pbm file" ); + } + } + +#if __STDC__ +void +pnm_readpnmrow( FILE* file, xel* xelrow, int cols, xelval maxval, int format ) +#else /*__STDC__*/ +void +pnm_readpnmrow( file, xelrow, cols, maxval, format ) + FILE* file; + xel* xelrow; + xelval maxval; + int cols, format; +#endif /*__STDC__*/ + { + register int col; + register xel* xP; + gray* grayrow; + register gray* gP; + bit* bitrow; + register bit* bP; + + switch ( PNM_FORMAT_TYPE(format) ) + { + case PPM_TYPE: + ppm_readppmrow( file, (pixel*) xelrow, cols, (pixval) maxval, format ); + break; + + case PGM_TYPE: + grayrow = pgm_allocrow( cols ); + pgm_readpgmrow( file, grayrow, cols, (gray) maxval, format ); + for ( col = 0, xP = xelrow, gP = grayrow; col < cols; ++col, ++xP, ++gP ) + PNM_ASSIGN1( *xP, *gP ); + pgm_freerow( grayrow ); + break; + + case PBM_TYPE: + bitrow = pbm_allocrow( cols ); + pbm_readpbmrow( file, bitrow, cols, format ); + for ( col = 0, xP = xelrow, bP = bitrow; col < cols; ++col, ++xP, ++bP ) + PNM_ASSIGN1( *xP, *bP == PBM_BLACK ? 0: pnm_pbmmaxval ); + pbm_freerow( bitrow ); + break; + + default: + pm_error( "can't happen" ); + } + } + +xel** +pnm_readpnm( file, colsP, rowsP, maxvalP, formatP ) + FILE* file; + int* colsP; + int* rowsP; + int* formatP; + xelval* maxvalP; + { + xel** xels; + int row; + + pnm_readpnminit( file, colsP, rowsP, maxvalP, formatP ); + + xels = pnm_allocarray( *colsP, *rowsP ); + + for ( row = 0; row < *rowsP; ++row ) + pnm_readpnmrow( file, xels[row], *colsP, *maxvalP, *formatP ); + + return xels; + } diff --git a/panda/src/pnm/libpnm2.c b/panda/src/pnm/libpnm2.c new file mode 100644 index 0000000000..3e3c1f4733 --- /dev/null +++ b/panda/src/pnm/libpnm2.c @@ -0,0 +1,121 @@ +/* libpnm2.c - pnm utility library part 2 +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "pnm.h" + +#include "ppm.h" +#include "libppm.h" + +#include "pgm.h" +#include "libpgm.h" + +#include "pbm.h" +#include "libpbm.h" + +#if __STDC__ +void +pnm_writepnminit( FILE* file, int cols, int rows, xelval maxval, int format, int forceplain ) +#else /*__STDC__*/ +void +pnm_writepnminit( file, cols, rows, maxval, format, forceplain ) + FILE* file; + int cols, rows, format; + xelval maxval; + int forceplain; +#endif /*__STDC__*/ + { + switch ( PNM_FORMAT_TYPE(format) ) + { + case PPM_TYPE: + ppm_writeppminit( file, cols, rows, (pixval) maxval, forceplain ); + break; + + case PGM_TYPE: + pgm_writepgminit( file, cols, rows, (gray) maxval, forceplain ); + break; + + case PBM_TYPE: + pbm_writepbminit( file, cols, rows, forceplain ); + break; + + default: + pm_error( "can't happen" ); + } + } + +#if __STDC__ +void +pnm_writepnmrow( FILE* file, xel* xelrow, int cols, xelval maxval, int format, int forceplain ) +#else /*__STDC__*/ +void +pnm_writepnmrow( file, xelrow, cols, maxval, format, forceplain ) + FILE* file; + xel* xelrow; + int cols, format; + xelval maxval; + int forceplain; +#endif /*__STDC__*/ + { + register int col; + register xel* xP; + gray* grayrow; + register gray* gP; + bit* bitrow; + register bit* bP; + + switch ( PNM_FORMAT_TYPE(format) ) + { + case PPM_TYPE: + ppm_writeppmrow( file, (pixel*) xelrow, cols, (pixval) maxval, forceplain ); + break; + + case PGM_TYPE: + grayrow = pgm_allocrow( cols ); + for ( col = 0, gP = grayrow, xP = xelrow; col < cols; ++col, ++gP, ++xP ) + *gP = PNM_GET1( *xP ); + pgm_writepgmrow( file, grayrow, cols, (gray) maxval, forceplain ); + pgm_freerow( grayrow ); + break; + + case PBM_TYPE: + bitrow = pbm_allocrow( cols ); + for ( col = 0, bP = bitrow, xP = xelrow; col < cols; ++col, ++bP, ++xP ) + *bP = PNM_GET1( *xP ) == 0 ? PBM_BLACK : PBM_WHITE; + pbm_writepbmrow( file, bitrow, cols, forceplain ); + pbm_freerow( bitrow ); + break; + + default: + pm_error( "can't happen" ); + } + } + +#if __STDC__ +void +pnm_writepnm( FILE* file, xel** xels, int cols, int rows, xelval maxval, int format, int forceplain ) +#else /*__STDC__*/ +void +pnm_writepnm( file, xels, cols, rows, maxval, format, forceplain ) + FILE* file; + xel** xels; + xelval maxval; + int cols, rows, format; + int forceplain; +#endif /*__STDC__*/ + { + int row; + + pnm_writepnminit( file, cols, rows, maxval, format, forceplain ); + + for ( row = 0; row < rows; ++row ) + pnm_writepnmrow( file, xels[row], cols, maxval, format, forceplain ); + } diff --git a/panda/src/pnm/libpnm3.c b/panda/src/pnm/libpnm3.c new file mode 100644 index 0000000000..0299576cd9 --- /dev/null +++ b/panda/src/pnm/libpnm3.c @@ -0,0 +1,386 @@ +/* libpnm3.c - pnm utility library part 3 +** +** Copyright (C) 1989, 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "pnm.h" + +#include "ppm.h" +#include "libppm.h" + +#include "pgm.h" +#include "libpgm.h" + +#include "pbm.h" +#include "libpbm.h" + +#if __STDC__ +xel +pnm_backgroundxel( xel** xels, int cols, int rows, xelval maxval, int format ) +#else /*__STDC__*/ +xel +pnm_backgroundxel( xels, cols, rows, maxval, format ) + xel** xels; + int cols, rows, format; + xelval maxval; +#endif /*__STDC__*/ + { + xel bgxel, ul, ur, ll, lr; + + /* Guess a good background value. */ + ul = xels[0][0]; + ur = xels[0][cols-1]; + ll = xels[rows-1][0]; + lr = xels[rows-1][cols-1]; + + /* First check for three corners equal. */ + if ( PNM_EQUAL( ul, ur ) && PNM_EQUAL( ur, ll ) ) + bgxel = ul; + else if ( PNM_EQUAL( ul, ur ) && PNM_EQUAL( ur, lr ) ) + bgxel = ul; + else if ( PNM_EQUAL( ul, ll ) && PNM_EQUAL( ll, lr ) ) + bgxel = ul; + else if ( PNM_EQUAL( ur, ll ) && PNM_EQUAL( ll, lr ) ) + bgxel = ur; + /* Nope, check for two corners equal. */ + else if ( PNM_EQUAL( ul, ur ) || PNM_EQUAL( ul, ll ) || + PNM_EQUAL( ul, lr ) ) + bgxel = ul; + else if ( PNM_EQUAL( ur, ll ) || PNM_EQUAL( ur, lr ) ) + bgxel = ur; + else if ( PNM_EQUAL( ll, lr ) ) + bgxel = ll; + else + { + /* Nope, we have to average the four corners. This breaks the + ** rules of pnm, but oh well. Let's try to do it portably. */ + switch ( PNM_FORMAT_TYPE(format) ) + { + case PPM_TYPE: + PPM_ASSIGN( bgxel, + PPM_GETR(ul) + PPM_GETR(ur) + PPM_GETR(ll) + PPM_GETR(lr) / 4, + PPM_GETG(ul) + PPM_GETG(ur) + PPM_GETG(ll) + PPM_GETG(lr) / 4, + PPM_GETB(ul) + PPM_GETB(ur) + PPM_GETB(ll) + PPM_GETB(lr) / 4 ); + break; + + case PGM_TYPE: + { + gray gul, gur, gll, glr; + gul = (gray) PNM_GET1( ul ); + gur = (gray) PNM_GET1( ur ); + gll = (gray) PNM_GET1( ll ); + glr = (gray) PNM_GET1( lr ); + PNM_ASSIGN1( bgxel, ( ( gul + gur + gll + glr ) / 4 ) ); + break; + } + + case PBM_TYPE: + pm_error( + "pnm_backgroundxel: four bits no two of which equal each other??" ); + + default: + pm_error( "can't happen" ); + } + } + + return bgxel; + } + +#if __STDC__ +xel +pnm_backgroundxelrow( xel* xelrow, int cols, xelval maxval, int format ) +#else /*__STDC__*/ +xel +pnm_backgroundxelrow( xelrow, cols, maxval, format ) + xel* xelrow; + int cols, format; + xelval maxval; +#endif /*__STDC__*/ + { + xel bgxel, l, r; + + /* Guess a good background value. */ + l = xelrow[0]; + r = xelrow[cols-1]; + + /* First check for both corners equal. */ + if ( PNM_EQUAL( l, r ) ) + bgxel = l; + else + { + /* Nope, we have to average the two corners. This breaks the + ** rules of pnm, but oh well. Let's try to do it portably. */ + switch ( PNM_FORMAT_TYPE(format) ) + { + case PPM_TYPE: + PPM_ASSIGN( bgxel, PPM_GETR(l) + PPM_GETR(r) / 2, + PPM_GETG(l) + PPM_GETG(r) / 2, PPM_GETB(l) + PPM_GETB(r) / 2 ); + break; + + case PGM_TYPE: + { + gray gl, gr; + gl = (gray) PNM_GET1( l ); + gr = (gray) PNM_GET1( r ); + PNM_ASSIGN1( bgxel, ( ( gl + gr ) / 2 ) ); + break; + } + + case PBM_TYPE: + { + int col, blacks; + + /* One black, one white. Gotta count. */ + for ( col = 0, blacks = 0; col < cols; ++col ) + { + if ( PNM_GET1( xelrow[col] ) == 0 ) + ++blacks; + } + if ( blacks >= cols / 2 ) + PNM_ASSIGN1( bgxel, 0 ); + else + PNM_ASSIGN1( bgxel, pnm_pbmmaxval ); + break; + } + + default: + pm_error( "can't happen" ); + } + } + + return bgxel; + } + +#if __STDC__ +xel +pnm_whitexel( xelval maxval, int format ) +#else /*__STDC__*/ +xel +pnm_whitexel( maxval, format ) + xelval maxval; + int format; +#endif /*__STDC__*/ + { + xel x; + + switch ( PNM_FORMAT_TYPE(format) ) + { + case PPM_TYPE: + PPM_ASSIGN( x, maxval, maxval, maxval ); + break; + + case PGM_TYPE: + PNM_ASSIGN1( x, maxval ); + break; + + case PBM_TYPE: + PNM_ASSIGN1( x, pnm_pbmmaxval ); + break; + + default: + pm_error( "can't happen" ); + } + + return x; + } + +#if __STDC__ +xel +pnm_blackxel( xelval maxval, int format ) +#else /*__STDC__*/ +xel +pnm_blackxel( maxval, format ) + xelval maxval; + int format; +#endif /*__STDC__*/ + { + xel x; + + switch ( PNM_FORMAT_TYPE(format) ) + { + case PPM_TYPE: + PPM_ASSIGN( x, 0, 0, 0 ); + break; + + case PGM_TYPE: + PNM_ASSIGN1( x, (xelval) 0 ); + break; + + case PBM_TYPE: + PNM_ASSIGN1( x, (xelval) 0 ); + break; + + default: + pm_error( "can't happen" ); + } + + return x; + } + +#if __STDC__ +void +pnm_invertxel( xel* xP, xelval maxval, int format ) +#else /*__STDC__*/ +void +pnm_invertxel( xP, maxval, format ) + xel* xP; + xelval maxval; + int format; +#endif /*__STDC__*/ + { + switch ( PNM_FORMAT_TYPE(format) ) + { + case PPM_TYPE: + PPM_ASSIGN( + *xP, maxval - PPM_GETR( *xP ), + maxval - PPM_GETG( *xP ), maxval - PPM_GETB( *xP ) ); + break; + + case PGM_TYPE: + PNM_ASSIGN1( *xP, (gray) maxval - (gray) PNM_GET1( *xP ) ); + break; + + case PBM_TYPE: + PNM_ASSIGN1( *xP, ( PNM_GET1( *xP ) == 0 ) ? pnm_pbmmaxval : 0 ); + break; + + default: + pm_error( "can't happen" ); + } + } + +#if __STDC__ +void +pnm_promoteformat( xel** xels, int cols, int rows, xelval maxval, int format, xelval newmaxval, int newformat ) +#else /*__STDC__*/ +void +pnm_promoteformat( xels, cols, rows, maxval, format, newmaxval, newformat ) + xel** xels; + xelval maxval, newmaxval; + int cols, rows, format, newformat; +#endif /*__STDC__*/ + { + int row; + + for ( row = 0; row < rows; ++row ) + pnm_promoteformatrow( + xels[row], cols, maxval, format, newmaxval, newformat ); + } + +#if __STDC__ +void +pnm_promoteformatrow( xel* xelrow, int cols, xelval maxval, int format, xelval newmaxval, int newformat ) +#else /*__STDC__*/ +void +pnm_promoteformatrow( xelrow, cols, maxval, format, newmaxval, newformat ) + xel* xelrow; + xelval maxval, newmaxval; + int cols, format, newformat; +#endif /*__STDC__*/ + { + register int col; + register xel* xP; + + if ( ( PNM_FORMAT_TYPE(format) == PPM_TYPE && + ( PNM_FORMAT_TYPE(newformat) == PGM_TYPE || + PNM_FORMAT_TYPE(newformat) == PBM_TYPE ) ) || + ( PNM_FORMAT_TYPE(format) == PGM_TYPE && + PNM_FORMAT_TYPE(newformat) == PBM_TYPE ) ) + pm_error( "pnm_promoteformatrow: can't promote downwards!" ); + + /* Are we promoting to the same type? */ + if ( PNM_FORMAT_TYPE(format) == PNM_FORMAT_TYPE(newformat) ) + { + if ( PNM_FORMAT_TYPE(format) == PBM_TYPE ) + return; + if ( newmaxval < maxval ) + pm_error( + "pnm_promoteformatrow: can't decrease maxval - try using pnmdepth" ); + if ( newmaxval == maxval ) + return; + /* Increase maxval. */ + switch ( PNM_FORMAT_TYPE(format) ) + { + case PGM_TYPE: + for ( col = 0, xP = xelrow; col < cols; ++col, ++xP ) + PNM_ASSIGN1( + *xP, (int) PNM_GET1(*xP) * newmaxval / maxval ); + break; + + case PPM_TYPE: + for ( col = 0, xP = xelrow; col < cols; ++col, ++xP ) + PPM_DEPTH( *xP, *xP, maxval, newmaxval ); + break; + + default: + pm_error( "shouldn't happen" ); + } + return; + } + + /* We must be promoting to a higher type. */ + switch ( PNM_FORMAT_TYPE(format) ) + { + case PBM_TYPE: + switch ( PNM_FORMAT_TYPE(newformat) ) + { + case PGM_TYPE: + for ( col = 0, xP = xelrow; col < cols; ++col, ++xP ) + if ( PNM_GET1(*xP) == 0 ) + PNM_ASSIGN1( *xP, 0 ); + else + PNM_ASSIGN1( *xP, newmaxval ); + break; + + case PPM_TYPE: + for ( col = 0, xP = xelrow; col < cols; ++col, ++xP ) + if ( PNM_GET1(*xP) == 0 ) + PPM_ASSIGN( *xP, 0, 0, 0 ); + else + PPM_ASSIGN( *xP, newmaxval, newmaxval, newmaxval ); + break; + + default: + pm_error( "can't happen" ); + } + break; + + case PGM_TYPE: + switch ( PNM_FORMAT_TYPE(newformat) ) + { + case PPM_TYPE: + if ( newmaxval < maxval ) + pm_error( + "pnm_promoteformatrow: can't decrease maxval - try using pnmdepth" ); + if ( newmaxval == maxval ) + { + for ( col = 0, xP = xelrow; col < cols; ++col, ++xP ) + PPM_ASSIGN( + *xP, PNM_GET1(*xP), PNM_GET1(*xP), PNM_GET1(*xP) ); + } + else + { /* Increase maxval. */ + for ( col = 0, xP = xelrow; col < cols; ++col, ++xP ) + PPM_ASSIGN( + *xP, (int) PNM_GET1(*xP) * newmaxval / maxval, + (int) PNM_GET1(*xP) * newmaxval / maxval, + (int) PNM_GET1(*xP) * newmaxval / maxval ); + } + break; + + default: + pm_error( "can't happen" ); + } + break; + + default: + pm_error( "can't happen" ); + } + } diff --git a/panda/src/pnm/libpnm4.c b/panda/src/pnm/libpnm4.c new file mode 100644 index 0000000000..8c53b03560 --- /dev/null +++ b/panda/src/pnm/libpnm4.c @@ -0,0 +1,453 @@ +/* libpnm4.c - pnm utility library part 4 +** +** Copyright (C) 1988 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "pnm.h" +#include "rast.h" + +/* +** Semi-work-alike versions of some Sun pixrect routines. Just enough +** for rasterfile reading and writing to work. +*/ + +struct pixrect* +mem_create( w, h, depth ) + int w, h, depth; + { + struct pixrect* p; + struct mpr_data* m; + + p = (struct pixrect*) malloc( sizeof(struct pixrect) ); + if ( p == NULL ) + return NULL; + p->pr_ops = NULL; + p->pr_size.x = w; + p->pr_size.y = h; + p->pr_depth = depth; + m = p->pr_data = (struct mpr_data*) malloc( sizeof(struct mpr_data) ); + if ( m == NULL ) + { + free( p ); + return NULL; + } + /* According to the documentation, linebytes is supposed to be rounded + ** up to a longword (except on 386 boxes). However, this turns out + ** not to be the case. In reality, all of Sun's code rounds up to + ** a short, not a long. + */ + m->md_linebytes = ( w * depth + 15 ) / 16 * 2; + m->md_offset.x = 0; + m->md_offset.y = 0; + m->md_flags = 0; + m->md_image = (unsigned char*) malloc( m->md_linebytes * h ); + if ( m->md_image == NULL ) + { + free( m ); + free( p ); + return NULL; + } + + return p; + } + +void +mem_free( p ) + struct pixrect* p; + { + free( p->pr_data->md_image ); + free( p->pr_data ); + free( p ); + } + +int +pr_dump( p, out, colormap, type, copy_flag ) + struct pixrect* p; + FILE* out; + colormap_t* colormap; + int type, copy_flag; + { + struct rasterfile h; + int size, besize, count; + unsigned char* beimage; + unsigned char* bp; + unsigned char c, pc; + int i, j; + + h.ras_magic = RAS_MAGIC; + h.ras_width = p->pr_size.x; + h.ras_height = p->pr_size.y; + h.ras_depth = p->pr_depth; + + h.ras_type = type; + switch ( type ) + { + case RT_OLD: + pm_error( "old rasterfile type is not supported" ); + + case RT_FORMAT_TIFF: + pm_error( "tiff rasterfile type is not supported" ); + + case RT_FORMAT_IFF: + pm_error( "iff rasterfile type is not supported" ); + + case RT_EXPERIMENTAL: + pm_error( "experimental rasterfile type is not supported" ); + + case RT_STANDARD: + case RT_FORMAT_RGB: + /* Ignore hP->ras_length. */ + h.ras_length = p->pr_size.y * p->pr_data->md_linebytes; + break; + + case RT_BYTE_ENCODED: + size = p->pr_size.y * p->pr_data->md_linebytes; + bp = p->pr_data->md_image; + beimage = (unsigned char*) malloc( size * 3 / 2 ); /* worst case */ + if ( beimage == NULL ) + return PIX_ERR; + besize = 0; + count = 0; + for ( i = 0; i < size; ++i ) + { + c = *bp++; + if ( count > 0 ) + { + if ( pc != c ) + { + if ( count == 1 && pc == 128 ) + { + beimage[besize++] = 128; + beimage[besize++] = 0; + count = 0; + } + else if ( count > 2 || pc == 128 ) + { + beimage[besize++] = 128; + beimage[besize++] = count - 1; + beimage[besize++] = pc; + count = 0; + } + else + { + for ( j = 0; j < count; ++j ) + beimage[besize++] = pc; + count = 0; + } + } + } + pc = c; + ++count; + if ( count == 256 ) + { + beimage[besize++] = 128; + beimage[besize++] = count - 1; + beimage[besize++] = c; + count = 0; + } + } + if ( count > 0 ) + { + if ( count == 1 && c == 128 ) + { + beimage[besize++] = 128; + beimage[besize++] = 0; + } + if ( count > 2 || c == 128 ) + { + beimage[besize++] = 128; + beimage[besize++] = count - 1; + beimage[besize++] = c; + } + else + { + for ( j = 0; j < count; ++j ) + beimage[besize++] = c; + } + } + h.ras_length = besize; + break; + + default: + pm_error( "unknown rasterfile type" ); + } + + if ( colormap == NULL ) + { + h.ras_maptype = RMT_NONE; + h.ras_maplength = 0; + } + else + { + h.ras_maptype = colormap->type; + switch ( colormap->type ) + { + case RMT_EQUAL_RGB: + h.ras_maplength = colormap->length * 3; + break; + + case RMT_RAW: + h.ras_maplength = colormap->length; + break; + + default: + pm_error( "unknown colormap type" ); + } + } + + if ( pm_writebiglong( out, h.ras_magic ) == -1 ) + return PIX_ERR; + if ( pm_writebiglong( out, h.ras_width ) == -1 ) + return PIX_ERR; + if ( pm_writebiglong( out, h.ras_height ) == -1 ) + return PIX_ERR; + if ( pm_writebiglong( out, h.ras_depth ) == -1 ) + return PIX_ERR; + if ( pm_writebiglong( out, h.ras_length ) == -1 ) + return PIX_ERR; + if ( pm_writebiglong( out, h.ras_type ) == -1 ) + return PIX_ERR; + if ( pm_writebiglong( out, h.ras_maptype ) == -1 ) + return PIX_ERR; + if ( pm_writebiglong( out, h.ras_maplength ) == -1 ) + return PIX_ERR; + + if ( colormap != NULL ) + { + switch ( colormap->type ) + { + case RMT_EQUAL_RGB: + if ( fwrite( colormap->map[0], 1, colormap->length, out ) != + colormap->length ) + return PIX_ERR; + if ( fwrite( colormap->map[1], 1, colormap->length, out ) != + colormap->length ) + return PIX_ERR; + if ( fwrite( colormap->map[2], 1, colormap->length, out ) != + colormap->length ) + return PIX_ERR; + break; + + case RMT_RAW: + if ( fwrite( colormap->map[0], 1, colormap->length, out ) != + colormap->length ) + return PIX_ERR; + break; + } + } + + switch ( type ) + { + case RT_STANDARD: + case RT_FORMAT_RGB: + if ( fwrite( p->pr_data->md_image, 1, h.ras_length, out ) != + h.ras_length ) + return PIX_ERR; + break; + + case RT_BYTE_ENCODED: + if ( fwrite( beimage, 1, besize, out ) != besize ) + { + free( beimage ); + return PIX_ERR; + } + free( beimage ); + break; + } + + return 0; + } + +int +pr_load_header( in, hP ) + FILE* in; + struct rasterfile* hP; + { + if ( pm_readbiglong( in, &(hP->ras_magic) ) == -1 ) + return PIX_ERR; + if ( hP->ras_magic != RAS_MAGIC ) + return PIX_ERR; + if ( pm_readbiglong( in, &(hP->ras_width) ) == -1 ) + return PIX_ERR; + if ( pm_readbiglong( in, &(hP->ras_height) ) == -1 ) + return PIX_ERR; + if ( pm_readbiglong( in, &(hP->ras_depth) ) == -1 ) + return PIX_ERR; + if ( pm_readbiglong( in, &(hP->ras_length) ) == -1 ) + return PIX_ERR; + if ( pm_readbiglong( in, &(hP->ras_type) ) == -1 ) + return PIX_ERR; + if ( pm_readbiglong( in, &(hP->ras_maptype) ) == -1 ) + return PIX_ERR; + if ( pm_readbiglong( in, &(hP->ras_maplength) ) == -1 ) + return PIX_ERR; + return 0; + } + +int +pr_load_colormap( in, hP, colormap ) + FILE* in; + struct rasterfile* hP; + colormap_t* colormap; + { + if ( colormap == NULL || hP->ras_maptype == RMT_NONE ) + { + int i; + + for ( i = 0; i < hP->ras_maplength; ++i ) + if ( getc( in ) == EOF ) + return PIX_ERR; + } + else + { + colormap->type = hP->ras_maptype; + switch ( hP->ras_maptype ) + { + case RMT_EQUAL_RGB: + colormap->length = hP->ras_maplength / 3; + colormap->map[0] = (unsigned char*) malloc( colormap->length ); + if ( colormap->map[0] == NULL ) + return PIX_ERR; + colormap->map[1] = (unsigned char*) malloc( colormap->length ); + if ( colormap->map[1] == NULL ) + { + free( colormap->map[0] ); + return PIX_ERR; + } + colormap->map[2] = (unsigned char*) malloc( colormap->length ); + if ( colormap->map[2] == NULL ) + { + free( colormap->map[0] ); + free( colormap->map[1] ); + return PIX_ERR; + } + if ( fread( colormap->map[0], 1, colormap->length, in ) != colormap->length || + fread( colormap->map[1], 1, colormap->length, in ) != colormap->length || + fread( colormap->map[2], 1, colormap->length, in ) != colormap->length ) + { + free( colormap->map[0] ); + free( colormap->map[1] ); + free( colormap->map[2] ); + return PIX_ERR; + } + break; + + case RMT_RAW: + colormap->length = hP->ras_maplength; + colormap->map[0] = (unsigned char*) malloc( colormap->length ); + if ( colormap->map[0] == NULL ) + return PIX_ERR; + colormap->map[2] = colormap->map[1] = colormap->map[0]; + if ( fread( colormap->map[0], 1, hP->ras_maplength, in ) != hP->ras_maplength ) + { + free( colormap->map[0] ); + return PIX_ERR; + } + break; + + default: + pm_error( "unknown colormap type" ); + } + } + return 0; + } + +struct pixrect* +pr_load_image( in, hP, colormap ) + FILE* in; + struct rasterfile* hP; + colormap_t* colormap; + { + struct pixrect* p; + unsigned char* beimage; + register unsigned char* bep; + register unsigned char* bp; + register unsigned char c; + int i; + register int j, count; + + p = mem_create( hP->ras_width, hP->ras_height, hP->ras_depth ); + if ( p == NULL ) + return NULL; + + switch ( hP->ras_type ) + { + case RT_OLD: + pm_error( "old rasterfile type is not supported" ); + + case RT_FORMAT_TIFF: + pm_error( "tiff rasterfile type is not supported" ); + + case RT_FORMAT_IFF: + pm_error( "iff rasterfile type is not supported" ); + + case RT_EXPERIMENTAL: + pm_error( "experimental rasterfile type is not supported" ); + + case RT_STANDARD: + case RT_FORMAT_RGB: + /* Ignore hP->ras_length. */ + i = p->pr_size.y * p->pr_data->md_linebytes; + if ( fread( p->pr_data->md_image, 1, i, in ) != i ) + { + mem_free( p ); + return NULL; + } + break; + + case RT_BYTE_ENCODED: + beimage = (unsigned char*) malloc( hP->ras_length ); + if ( beimage == NULL ) + { + mem_free( p ); + return NULL; + } + if ( fread( beimage, 1, hP->ras_length, in ) != hP->ras_length ) + { + mem_free( p ); + free( beimage ); + return NULL; + } + bep = beimage; + bp = p->pr_data->md_image; + for ( i = 0; i < hP->ras_length; ) + { + c = *bep++; + if ( c == 128 ) + { + count = ( *bep++ ) + 1; + if ( count == 1 ) + { + *bp++ = 128; + i += 2; + } + else + { + c = *bep++; + for ( j = 0; j < count; ++j ) + *bp++ = c; + i += 3; + } + } + else + { + *bp++ = c; + ++i; + } + } + free( beimage ); + break; + + default: + pm_error( "unknown rasterfile type" ); + } + + return p; + } diff --git a/panda/src/pnm/libppm.h b/panda/src/pnm/libppm.h new file mode 100644 index 0000000000..40d0ca6dc9 --- /dev/null +++ b/panda/src/pnm/libppm.h @@ -0,0 +1,11 @@ +/* libppm.h - internal header file for libppm portable pixmap library +*/ + +#ifndef _LIBPPM_H_ +#define _LIBPPM_H_ + +/* Here are some routines internal to the ppm library. */ + +EXPCL_PANDA void ppm_readppminitrest( FILE* file, int* colsP, int* rowsP, pixval* maxvalP ); + +#endif /*_LIBPPM_H_*/ diff --git a/panda/src/pnm/libppm1.c b/panda/src/pnm/libppm1.c new file mode 100644 index 0000000000..26263b4513 --- /dev/null +++ b/panda/src/pnm/libppm1.c @@ -0,0 +1,195 @@ +/* libppm1.c - ppm utility library part 1 +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" +#include "libppm.h" +#include "pgm.h" +#include "libpgm.h" +#include "pbm.h" +#include "libpbm.h" + +void +ppm_init( argcP, argv ) + int* argcP; + char* argv[]; + { + pgm_init( argcP, argv ); + } + +void +ppm_readppminitrest( file, colsP, rowsP, maxvalP ) + FILE* file; + int* colsP; + int* rowsP; + pixval* maxvalP; + { + int maxval; + + /* Read size. */ + *colsP = pbm_getint( file ); + *rowsP = pbm_getint( file ); + + /* Read maxval. */ + maxval = pbm_getint( file ); + if ( maxval > PPM_MAXMAXVAL ) + pm_error( +"maxval is too large - try reconfiguring with PGM_BIGGRAYS\n or without PPM_PACKCOLORS" ); + *maxvalP = maxval; + } + +pixval ppm_pbmmaxval = 1; + +void +ppm_readppminit( file, colsP, rowsP, maxvalP, formatP ) + FILE* file; + int* colsP; + int* rowsP; + int* formatP; + pixval* maxvalP; + { + /* Check magic number. */ + *formatP = pbm_readmagicnumber( file ); + switch ( PPM_FORMAT_TYPE(*formatP) ) + { + case PPM_TYPE: + ppm_readppminitrest( file, colsP, rowsP, maxvalP ); + break; + + case PGM_TYPE: + pgm_readpgminitrest( file, colsP, rowsP, maxvalP ); + break; + + case PBM_TYPE: + pbm_readpbminitrest( file, colsP, rowsP ); + *maxvalP = ppm_pbmmaxval; + break; + + default: + pm_error( "bad magic number - not a ppm, pgm, or pbm file" ); + } + } + +#if __STDC__ +void +ppm_readppmrow( FILE* file, pixel* pixelrow, int cols, pixval maxval, int format ) +#else /*__STDC__*/ +void +ppm_readppmrow( file, pixelrow, cols, maxval, format ) + FILE* file; + pixel* pixelrow; + int cols, format; + pixval maxval; +#endif /*__STDC__*/ + { + register int col; + register pixel* pP; + register pixval r, g, b; + gray* grayrow; + register gray* gP; + bit* bitrow; + register bit* bP; + + switch ( format ) + { + case PPM_FORMAT: + for ( col = 0, pP = pixelrow; col < cols; ++col, ++pP ) + { + r = pbm_getint( file ); +#ifdef DEBUG + if ( r > maxval ) + pm_error( "r value out of bounds (%u > %u)", r, maxval ); +#endif /*DEBUG*/ + g = pbm_getint( file ); +#ifdef DEBUG + if ( g > maxval ) + pm_error( "g value out of bounds (%u > %u)", g, maxval ); +#endif /*DEBUG*/ + b = pbm_getint( file ); +#ifdef DEBUG + if ( b > maxval ) + pm_error( "b value out of bounds (%u > %u)", b, maxval ); +#endif /*DEBUG*/ + PPM_ASSIGN( *pP, r, g, b ); + } + break; + + case RPPM_FORMAT: + for ( col = 0, pP = pixelrow; col < cols; ++col, ++pP ) + { + r = pbm_getrawbyte( file ); +#ifdef DEBUG + if ( r > maxval ) + pm_error( "r value out of bounds (%u > %u)", r, maxval ); +#endif /*DEBUG*/ + g = pbm_getrawbyte( file ); +#ifdef DEBUG + if ( g > maxval ) + pm_error( "g value out of bounds (%u > %u)", g, maxval ); +#endif /*DEBUG*/ + b = pbm_getrawbyte( file ); +#ifdef DEBUG + if ( b > maxval ) + pm_error( "b value out of bounds (%u > %u)", b, maxval ); +#endif /*DEBUG*/ + PPM_ASSIGN( *pP, r, g, b ); + } + break; + + case PGM_FORMAT: + case RPGM_FORMAT: + grayrow = pgm_allocrow( cols ); + pgm_readpgmrow( file, grayrow, cols, maxval, format ); + for ( col = 0, gP = grayrow, pP = pixelrow; col < cols; ++col, ++gP, ++pP ) + { + r = *gP; + PPM_ASSIGN( *pP, r, r, r ); + } + pgm_freerow( grayrow ); + break; + + case PBM_FORMAT: + case RPBM_FORMAT: + bitrow = pbm_allocrow( cols ); + pbm_readpbmrow( file, bitrow, cols, format ); + for ( col = 0, bP = bitrow, pP = pixelrow; col < cols; ++col, ++bP, ++pP ) + { + r = ( *bP == PBM_WHITE ) ? maxval : 0; + PPM_ASSIGN( *pP, r, r, r ); + } + pbm_freerow( bitrow ); + break; + + default: + pm_error( "can't happen" ); + } + } + +pixel** +ppm_readppm( file, colsP, rowsP, maxvalP ) + FILE* file; + int* colsP; + int* rowsP; + pixval* maxvalP; + { + pixel** pixels; + int row; + int format; + + ppm_readppminit( file, colsP, rowsP, maxvalP, &format ); + + pixels = ppm_allocarray( *colsP, *rowsP ); + + for ( row = 0; row < *rowsP; ++row ) + ppm_readppmrow( file, pixels[row], *colsP, *maxvalP, format ); + + return pixels; + } diff --git a/panda/src/pnm/libppm2.c b/panda/src/pnm/libppm2.c new file mode 100644 index 0000000000..d08602c251 --- /dev/null +++ b/panda/src/pnm/libppm2.c @@ -0,0 +1,192 @@ +/* libppm2.c - ppm utility library part 2 +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" +#include "libppm.h" + +static void putus ARGS((unsigned short n, FILE* file)); +static void ppm_writeppmrowplain ARGS((FILE* file, pixel* pixelrow, int cols, pixval maxval)); +#ifdef PBMPLUS_RAWBITS +static void ppm_writeppmrowraw ARGS((FILE* file, pixel* pixelrow, int cols, pixval maxval)); +#endif /* PBMPLUS_RAWBITS */ +#if __STDC__ +void +ppm_writeppminit( FILE* file, int cols, int rows, pixval maxval, int forceplain ) +#else /*__STDC__*/ +void +ppm_writeppminit( file, cols, rows, maxval, forceplain ) + FILE* file; + int cols, rows; + pixval maxval; + int forceplain; +#endif /*__STDC__*/ + { +#ifdef PBMPLUS_RAWBITS + if ( maxval <= 255 && ! forceplain ) { + fprintf( + file, "%c%c\n%d %d\n%d\n", PPM_MAGIC1, RPPM_MAGIC2, + cols, rows, maxval ); +#ifdef VMS + set_outfile_binary(); +#endif + } + else + fprintf( + file, "%c%c\n%d %d\n%d\n", PPM_MAGIC1, PPM_MAGIC2, + cols, rows, maxval ); +#else /*PBMPLUS_RAWBITS*/ + fprintf( + file, "%c%c\n%d %d\n%d\n", PPM_MAGIC1, PPM_MAGIC2, + cols, rows, maxval ); +#endif /*PBMPLUS_RAWBITS*/ + } + +static void +putus( n, file ) + unsigned short n; + FILE* file; + { + if ( n >= 10 ) + putus( n / 10, file ); + (void) putc( n % 10 + '0', file ); + } + +#ifdef PBMPLUS_RAWBITS +static void +ppm_writeppmrowraw( file, pixelrow, cols, maxval ) + FILE* file; + pixel* pixelrow; + int cols; + pixval maxval; + { + register int col; + register pixel* pP; + register pixval val; + + for ( col = 0, pP = pixelrow; col < cols; ++col, ++pP ) + { + val = PPM_GETR( *pP ); +#ifdef DEBUG + if ( val > maxval ) + pm_error( "r value out of bounds (%u > %u)", val, maxval ); +#endif /*DEBUG*/ + (void) putc( val, file ); + val = PPM_GETG( *pP ); +#ifdef DEBUG + if ( val > maxval ) + pm_error( "g value out of bounds (%u > %u)", val, maxval ); +#endif /*DEBUG*/ + (void) putc( val, file ); + val = PPM_GETB( *pP ); +#ifdef DEBUG + if ( val > maxval ) + pm_error( "b value out of bounds (%u > %u)", val, maxval ); +#endif /*DEBUG*/ + (void) putc( val, file ); + } + } +#endif /*PBMPLUS_RAWBITS*/ + +static void +ppm_writeppmrowplain( file, pixelrow, cols, maxval ) + FILE* file; + pixel* pixelrow; + int cols; + pixval maxval; + { + register int col, charcount; + register pixel* pP; + register pixval val; + + charcount = 0; + for ( col = 0, pP = pixelrow; col < cols; ++col, ++pP ) + { + if ( charcount >= 65 ) + { + (void) putc( '\n', file ); + charcount = 0; + } + else if ( charcount > 0 ) + { + (void) putc( ' ', file ); + (void) putc( ' ', file ); + charcount += 2; + } + val = PPM_GETR( *pP ); +#ifdef DEBUG + if ( val > maxval ) + pm_error( "r value out of bounds (%u > %u)", val, maxval ); +#endif /*DEBUG*/ + putus( val, file ); + (void) putc( ' ', file ); + val = PPM_GETG( *pP ); +#ifdef DEBUG + if ( val > maxval ) + pm_error( "g value out of bounds (%u > %u)", val, maxval ); +#endif /*DEBUG*/ + putus( val, file ); + (void) putc( ' ', file ); + val = PPM_GETB( *pP ); +#ifdef DEBUG + if ( val > maxval ) + pm_error( "b value out of bounds (%u > %u)", val, maxval ); +#endif /*DEBUG*/ + putus( val, file ); + charcount += 11; + } + if ( charcount > 0 ) + (void) putc( '\n', file ); + } + +#if __STDC__ +void +ppm_writeppmrow( FILE* file, pixel* pixelrow, int cols, pixval maxval, int forceplain ) +#else /*__STDC__*/ +void +ppm_writeppmrow( file, pixelrow, cols, maxval, forceplain ) + FILE* file; + pixel* pixelrow; + int cols; + pixval maxval; + int forceplain; +#endif /*__STDC__*/ + { +#ifdef PBMPLUS_RAWBITS + if ( maxval <= 255 && ! forceplain ) + ppm_writeppmrowraw( file, pixelrow, cols, maxval ); + else + ppm_writeppmrowplain( file, pixelrow, cols, maxval ); +#else /*PBMPLUS_RAWBITS*/ + ppm_writeppmrowplain( file, pixelrow, cols, maxval ); +#endif /*PBMPLUS_RAWBITS*/ + } + +#if __STDC__ +void +ppm_writeppm( FILE* file, pixel** pixels, int cols, int rows, pixval maxval, int forceplain ) +#else /*__STDC__*/ +void +ppm_writeppm( file, pixels, cols, rows, maxval, forceplain ) + FILE* file; + pixel** pixels; + int cols, rows; + pixval maxval; + int forceplain; +#endif /*__STDC__*/ + { + int row; + + ppm_writeppminit( file, cols, rows, maxval, forceplain ); + + for ( row = 0; row < rows; ++row ) + ppm_writeppmrow( file, pixels[row], cols, maxval, forceplain ); + } diff --git a/panda/src/pnm/libppm3.c b/panda/src/pnm/libppm3.c new file mode 100644 index 0000000000..f1d5add816 --- /dev/null +++ b/panda/src/pnm/libppm3.c @@ -0,0 +1,262 @@ +/* libppm3.c - ppm utility library part 3 +** +** Colormap routines. +** +** Copyright (C) 1989, 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" +#include "ppmcmap.h" +#include "libppm.h" + +#define HASH_SIZE 20023 + +#ifdef PPM_PACKCOLORS +#define ppm_hashpixel(p) ( ( (int) (p) & 0x7fffffff ) % HASH_SIZE ) +#else /*PPM_PACKCOLORS*/ +#define ppm_hashpixel(p) ( ( ( (long) PPM_GETR(p) * 33023 + (long) PPM_GETG(p) * 30013 + (long) PPM_GETB(p) * 27011 ) & 0x7fffffff ) % HASH_SIZE ) +#endif /*PPM_PACKCOLORS*/ + +colorhist_vector +ppm_computecolorhist( pixels, cols, rows, maxcolors, colorsP ) + pixel** pixels; + int cols, rows, maxcolors; + int* colorsP; + { + colorhash_table cht; + colorhist_vector chv; + + cht = ppm_computecolorhash( pixels, cols, rows, maxcolors, colorsP ); + if ( cht == (colorhash_table) 0 ) + return (colorhist_vector) 0; + chv = ppm_colorhashtocolorhist( cht, maxcolors ); + ppm_freecolorhash( cht ); + return chv; + } + +void +ppm_addtocolorhist( chv, colorsP, maxcolors, colorP, value, position ) + colorhist_vector chv; + pixel* colorP; + int* colorsP; + int maxcolors, value, position; + { + int i, j; + + /* Search colorhist for the color. */ + for ( i = 0; i < *colorsP; ++i ) + if ( PPM_EQUAL( chv[i].color, *colorP ) ) + { + /* Found it - move to new slot. */ + if ( position > i ) + { + for ( j = i; j < position; ++j ) + chv[j] = chv[j + 1]; + } + else if ( position < i ) + { + for ( j = i; j > position; --j ) + chv[j] = chv[j - 1]; + } + chv[position].color = *colorP; + chv[position].value = value; + return; + } + if ( *colorsP < maxcolors ) + { + /* Didn't find it, but there's room to add it; so do so. */ + for ( i = *colorsP; i > position; --i ) + chv[i] = chv[i - 1]; + chv[position].color = *colorP; + chv[position].value = value; + ++(*colorsP); + } + } + +colorhash_table +ppm_computecolorhash( pixels, cols, rows, maxcolors, colorsP ) + pixel** pixels; + int cols, rows, maxcolors; + int* colorsP; + { + colorhash_table cht; + register pixel* pP; + colorhist_list chl; + int col, row, hash; + + cht = ppm_alloccolorhash( ); + *colorsP = 0; + + /* Go through the entire image, building a hash table of colors. */ + for ( row = 0; row < rows; ++row ) + for ( col = 0, pP = pixels[row]; col < cols; ++col, ++pP ) + { + hash = ppm_hashpixel( *pP ); + for ( chl = cht[hash]; chl != (colorhist_list) 0; chl = chl->next ) + if ( PPM_EQUAL( chl->ch.color, *pP ) ) + break; + if ( chl != (colorhist_list) 0 ) + ++(chl->ch.value); + else + { + if ( ++(*colorsP) > maxcolors ) + { + ppm_freecolorhash( cht ); + return (colorhash_table) 0; + } + chl = (colorhist_list) malloc( sizeof(struct colorhist_list_item) ); + if ( chl == 0 ) + pm_error( "out of memory computing hash table" ); + chl->ch.color = *pP; + chl->ch.value = 1; + chl->next = cht[hash]; + cht[hash] = chl; + } + } + + return cht; + } + +colorhash_table +ppm_alloccolorhash( ) + { + colorhash_table cht; + int i; + + cht = (colorhash_table) malloc( HASH_SIZE * sizeof(colorhist_list) ); + if ( cht == 0 ) + pm_error( "out of memory allocating hash table" ); + + for ( i = 0; i < HASH_SIZE; ++i ) + cht[i] = (colorhist_list) 0; + + return cht; + } + +int +ppm_addtocolorhash( cht, colorP, value ) + colorhash_table cht; + pixel* colorP; + int value; + { + register int hash; + register colorhist_list chl; + + chl = (colorhist_list) malloc( sizeof(struct colorhist_list_item) ); + if ( chl == 0 ) + return -1; + hash = ppm_hashpixel( *colorP ); + chl->ch.color = *colorP; + chl->ch.value = value; + chl->next = cht[hash]; + cht[hash] = chl; + return 0; + } + +colorhist_vector +ppm_colorhashtocolorhist( cht, maxcolors ) + colorhash_table cht; + int maxcolors; + { + colorhist_vector chv; + colorhist_list chl; + int i, j; + + /* Now collate the hash table into a simple colorhist array. */ + chv = (colorhist_vector) malloc( maxcolors * sizeof(struct colorhist_item) ); + /* (Leave room for expansion by caller.) */ + if ( chv == (colorhist_vector) 0 ) + pm_error( "out of memory generating histogram" ); + + /* Loop through the hash table. */ + j = 0; + for ( i = 0; i < HASH_SIZE; ++i ) + for ( chl = cht[i]; chl != (colorhist_list) 0; chl = chl->next ) + { + /* Add the new entry. */ + chv[j] = chl->ch; + ++j; + } + + /* All done. */ + return chv; + } + +colorhash_table +ppm_colorhisttocolorhash( chv, colors ) + colorhist_vector chv; + int colors; + { + colorhash_table cht; + int i, hash; + pixel color; + colorhist_list chl; + + cht = ppm_alloccolorhash( ); + + for ( i = 0; i < colors; ++i ) + { + color = chv[i].color; + hash = ppm_hashpixel( color ); + for ( chl = cht[hash]; chl != (colorhist_list) 0; chl = chl->next ) + if ( PPM_EQUAL( chl->ch.color, color ) ) + pm_error( + "same color found twice - %d %d %d", PPM_GETR(color), + PPM_GETG(color), PPM_GETB(color) ); + chl = (colorhist_list) malloc( sizeof(struct colorhist_list_item) ); + if ( chl == (colorhist_list) 0 ) + pm_error( "out of memory" ); + chl->ch.color = color; + chl->ch.value = i; + chl->next = cht[hash]; + cht[hash] = chl; + } + + return cht; + } + +int +ppm_lookupcolor( cht, colorP ) + colorhash_table cht; + pixel* colorP; + { + int hash; + colorhist_list chl; + + hash = ppm_hashpixel( *colorP ); + for ( chl = cht[hash]; chl != (colorhist_list) 0; chl = chl->next ) + if ( PPM_EQUAL( chl->ch.color, *colorP ) ) + return chl->ch.value; + + return -1; + } + +void +ppm_freecolorhist( chv ) + colorhist_vector chv; + { + free( (char*) chv ); + } + +void +ppm_freecolorhash( cht ) + colorhash_table cht; + { + int i; + colorhist_list chl, chlnext; + + for ( i = 0; i < HASH_SIZE; ++i ) + for ( chl = cht[i]; chl != (colorhist_list) 0; chl = chlnext ) + { + chlnext = chl->next; + free( (char*) chl ); + } + free( (char*) cht ); + } diff --git a/panda/src/pnm/libppm4.c b/panda/src/pnm/libppm4.c new file mode 100644 index 0000000000..f596c51409 --- /dev/null +++ b/panda/src/pnm/libppm4.c @@ -0,0 +1,328 @@ +/* libppm4.c - ppm utility library part 4 +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" + +static void canonstr ARGS((char* str)); +static long rgbnorm ARGS((long rgb, long lmaxval, int n, char* colorname)); +static void +canonstr( str ) + char* str; + { + while ( *str != '\0' ) + { + if ( *str == ' ' ) + { + (void) strcpy( str, &(str[1]) ); + continue; + } + if ( isupper( *str ) ) + *str = tolower( *str ); + ++str; + } + } + +static long +rgbnorm( rgb, lmaxval, n, colorname ) + long rgb, lmaxval; + int n; + char* colorname; + { + switch ( n ) + { + case 1: + if ( lmaxval != 15 ) + rgb = rgb * lmaxval / 15; + break; + case 2: + if ( lmaxval != 255 ) + rgb = rgb * lmaxval / 255; + break; + case 3: + if ( lmaxval != 4095 ) + rgb = rgb * lmaxval / 4095; + break; + case 4: + if ( lmaxval != 65535L ) + rgb = rgb * lmaxval / 65535L; + break; + default: + pm_error( "invalid color specifier - \"%s\"", colorname ); + } + return rgb; + } + +#if __STDC__ +pixel +ppm_parsecolor( char* colorname, pixval maxval ) +#else /*__STDC__*/ +pixel +ppm_parsecolor( colorname, maxval ) + char* colorname; + pixval maxval; +#endif /*__STDC__*/ + { + int hexit[256], i; + pixel p; + long lmaxval, r, g, b; + char* inval = "invalid color specifier - \"%s\""; + + for ( i = 0; i < 256; ++i ) + hexit[i] = 1234567890; + hexit['0'] = 0; + hexit['1'] = 1; + hexit['2'] = 2; + hexit['3'] = 3; + hexit['4'] = 4; + hexit['5'] = 5; + hexit['6'] = 6; + hexit['7'] = 7; + hexit['8'] = 8; + hexit['9'] = 9; + hexit['a'] = hexit['A'] = 10; + hexit['b'] = hexit['B'] = 11; + hexit['c'] = hexit['C'] = 12; + hexit['d'] = hexit['D'] = 13; + hexit['e'] = hexit['E'] = 14; + hexit['f'] = hexit['F'] = 15; + + lmaxval = maxval; + if ( strncmp( colorname, "rgb:", 4 ) == 0 ) + { + /* It's a new-X11-style hexadecimal rgb specifier. */ + char* cp; + + cp = colorname + 4; + r = g = b = 0; + for ( i = 0; *cp != '/'; ++i, ++cp ) + r = r * 16 + hexit[*cp]; + r = rgbnorm( r, lmaxval, i, colorname ); + for ( i = 0, ++cp; *cp != '/'; ++i, ++cp ) + g = g * 16 + hexit[*cp]; + g = rgbnorm( g, lmaxval, i, colorname ); + for ( i = 0, ++cp; *cp != '\0'; ++i, ++cp ) + b = b * 16 + hexit[*cp]; + b = rgbnorm( b, lmaxval, i, colorname ); + if ( r < 0 || r > lmaxval || g < 0 || g > lmaxval || b < 0 || b > lmaxval ) + pm_error( inval, colorname ); + } + else if ( strncmp( colorname, "rgbi:", 5 ) == 0 ) + { + /* It's a new-X11-style decimal/float rgb specifier. */ + float fr, fg, fb; + + if ( sscanf( colorname, "rgbi:%f/%f/%f", &fr, &fg, &fb ) != 3 ) + pm_error( inval, colorname ); + if ( fr < 0.0 || fr > 1.0 || fg < 0.0 || fg > 1.0 || fb < 0.0 || fb > 1.0 ) + pm_error( "invalid color specifier - \"%s\" - values must be between 0.0 and 1.0", colorname ); + r = fr * lmaxval; + g = fg * lmaxval; + b = fb * lmaxval; + } + else if ( colorname[0] == '#' ) + { + /* It's an old-X11-style hexadecimal rgb specifier. */ + switch ( strlen( colorname ) ) + { + case 4: + r = hexit[colorname[1]]; + g = hexit[colorname[2]]; + b = hexit[colorname[3]]; + r = rgbnorm( r, lmaxval, 1, colorname ); + g = rgbnorm( g, lmaxval, 1, colorname ); + b = rgbnorm( b, lmaxval, 1, colorname ); + break; + + case 7: + r = ( hexit[colorname[1]] << 4 ) + hexit[colorname[2]]; + g = ( hexit[colorname[3]] << 4 ) + hexit[colorname[4]]; + b = ( hexit[colorname[5]] << 4 ) + hexit[colorname[6]]; + r = rgbnorm( r, lmaxval, 2, colorname ); + g = rgbnorm( g, lmaxval, 2, colorname ); + b = rgbnorm( b, lmaxval, 2, colorname ); + break; + + case 10: + r = ( hexit[colorname[1]] << 8 ) + ( hexit[colorname[2]] << 4 ) + + hexit[colorname[3]]; + g = ( hexit[colorname[4]] << 8 ) + ( hexit[colorname[5]] << 4 ) + + hexit[colorname[6]]; + b = ( hexit[colorname[7]] << 8 ) + ( hexit[colorname[8]] << 4 ) + + hexit[colorname[9]]; + r = rgbnorm( r, lmaxval, 3, colorname ); + g = rgbnorm( g, lmaxval, 3, colorname ); + b = rgbnorm( b, lmaxval, 3, colorname ); + break; + + case 13: + r = ( hexit[colorname[1]] << 12 ) + ( hexit[colorname[2]] << 8 ) + + ( hexit[colorname[3]] << 4 ) + hexit[colorname[4]]; + g = ( hexit[colorname[5]] << 12 ) + ( hexit[colorname[6]] << 8 ) + + ( hexit[colorname[7]] << 4 ) + hexit[colorname[8]]; + b = ( hexit[colorname[9]] << 12 ) + ( hexit[colorname[10]] << 8 ) + + ( hexit[colorname[11]] << 4 ) + hexit[colorname[12]]; + r = rgbnorm( r, lmaxval, 4, colorname ); + g = rgbnorm( g, lmaxval, 4, colorname ); + b = rgbnorm( b, lmaxval, 4, colorname ); + break; + + default: + pm_error( inval, colorname ); + } + if ( r < 0 || r > lmaxval || g < 0 || g > lmaxval || b < 0 || b > lmaxval ) + pm_error( inval, colorname ); + } + else if ( ( colorname[0] >= '0' && colorname[0] <= '9' ) || + colorname[0] == '.' ) + { + /* It's an old-style decimal/float rgb specifier. */ + float fr, fg, fb; + + if ( sscanf( colorname, "%f,%f,%f", &fr, &fg, &fb ) != 3 ) + pm_error( inval, colorname ); + if ( fr < 0.0 || fr > 1.0 || fg < 0.0 || fg > 1.0 || fb < 0.0 || fb > 1.0 ) + pm_error( "invalid color specifier - \"%s\" - values must be between 0.0 and 1.0", colorname ); + r = fr * lmaxval; + g = fg * lmaxval; + b = fb * lmaxval; + } + else + { + /* Must be a name from the X-style rgb file. */ +#ifndef RGB_DB + pm_error( "color names database required - please reconfigure with RGBDEF" ); +#else /*RGB_DB*/ + FILE* f; + char buf1[200], buf2[200]; + +#ifndef A_RGBENV + (void) sprintf( buf1, "%s.txt", RGB_DB ); + if ( ( f = fopen( buf1, "r" ) ) == NULL ) + pm_error( "can't open color names database - reconfigure with correct RGBDEF" ); +#else /* A_RGBENV */ + char *rgbdef; + if( (rgbdef = getenv(RGB_DB))==NULL ) + pm_error( "can't get path to color names database - %s not set", RGB_DB ); + if ( ( f = fopen( rgbdef, "r" ) ) == NULL ) + pm_error( "can't open color names database - set %s to correct path", RGB_DB ); +#endif /* A_RGBENV */ + canonstr( colorname ); + while ( fgets( buf1, sizeof(buf1), f ) != NULL ) + { + if ( sscanf( buf1, "%ld %ld %ld %[^\n]", &r, &g, &b, buf2 ) != 4 ) + { + pm_message( + "can't parse color names database line - \"%s\"", buf1 ); + continue; + } + canonstr( buf2 ); + if ( strcmp( colorname, buf2 ) == 0 ) + goto gotit; + } + (void) fclose( f ); + pm_error( "unknown color - \"%s\"", colorname ); + +gotit: + (void) fclose( f ); + /* Rescale from [0..255] if necessary. */ + if ( lmaxval != 255 ) + { + r = r * lmaxval / 255; + g = g * lmaxval / 255; + b = b * lmaxval / 255; + } +#endif /*RGB_DB*/ + } + + PPM_ASSIGN( p, r, g, b ); + return p; + } + +static char colorname[200]; + +#if __STDC__ +char* +ppm_colorname( pixel* colorP, pixval maxval, int hexok ) +#else /*__STDC__*/ +char* +ppm_colorname( colorP, maxval, hexok ) + pixel* colorP; + pixval maxval; + int hexok; +#endif /*__STDC__*/ + { + int r, g, b; +#ifdef RGB_DB + FILE* f; + char buf[200]; + int this_r, this_g, this_b; + int best_diff, this_diff; + char this_colorname[200]; +#ifdef A_RGBENV + char *rgbdef; +#endif /* A_RGBENV */ +#endif /*RGB_DB*/ + + if ( maxval == 255 ) + { + r = PPM_GETR( *colorP ); + g = PPM_GETG( *colorP ); + b = PPM_GETB( *colorP ); + } + else + { + r = (int) PPM_GETR( *colorP ) * 255 / (int) maxval; + g = (int) PPM_GETG( *colorP ) * 255 / (int) maxval; + b = (int) PPM_GETB( *colorP ) * 255 / (int) maxval; + } + +#ifdef RGB_DB +#ifndef A_RGBENV + (void) sprintf( buf, "%s.txt", RGB_DB ); + if ( ( f = fopen( buf, "r" ) ) == NULL ) + pm_error( "can't open color names database - reconfigure with correct RGBDEF" ); +#else /* A_RGBENV */ + if( (rgbdef = getenv(RGB_DB))==NULL ) + pm_error( "can't get path to color names database - %s not set", RGB_DB ); + if ( ( f = fopen( rgbdef, "r" ) ) == NULL ) + pm_error( "can't open color names database - set %s to correct path", RGB_DB ); +#endif /* A_RGBENV */ + best_diff = 32767; + while ( fgets( buf, sizeof(buf), f ) != NULL ) + { + if ( sscanf( buf, "%d %d %d %[^\n]", &this_r, &this_g, &this_b, + this_colorname ) != 4 ) + { + pm_message( + "can't parse color names database line - \"%s\"", + buf ); + continue; + } + this_diff = abs( r - this_r ) + abs( g - this_g ) + abs( b - this_b ); + if ( this_diff < best_diff ) + { + best_diff = this_diff; + (void) strcpy( colorname, this_colorname ); + } + } + (void) fclose( f ); + if ( best_diff != 32767 && ( best_diff == 0 || ! hexok ) ) + return colorname; +#endif /*RGB_DB*/ + + /* Color lookup failed; generate an X11-style hex specifier. */ + if ( ! hexok ) + pm_error( + "color names database required - please reconfigure with RGBDEF" ); + sprintf( colorname, "#%02x%02x%02x", r, g, b ); + return colorname; + } diff --git a/panda/src/pnm/libppm5.c b/panda/src/pnm/libppm5.c new file mode 100644 index 0000000000..35d49a19fc --- /dev/null +++ b/panda/src/pnm/libppm5.c @@ -0,0 +1,696 @@ +/* libppm5.c - ppm utility library part 5 +** +** This library module contains the ppmdraw routines. +** +** Copyright (C) 1989, 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" +#include "ppmdraw.h" + +#define DDA_SCALE 8192 + +#if __STDC__ +void +ppmd_point_drawproc( pixel** pixels, int cols, int rows, pixval maxval, int x, int y, char* clientdata ) +#else /*__STDC__*/ +void +ppmd_point_drawproc( pixels, cols, rows, maxval, x, y, clientdata ) + pixel** pixels; + int cols, rows, x, y; + pixval maxval; + char* clientdata; +#endif /*__STDC__*/ + { + if ( x >= 0 && x < cols && y >= 0 && y < rows ) + pixels[y][x] = *( (pixel*) clientdata ); + } + + +/* Simple fill routine. */ + +#if __STDC__ +void +ppmd_filledrectangle( pixel** pixels, int cols, int rows, pixval maxval, int x, int y, int width, int height, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata ) +#else /*__STDC__*/ +void +ppmd_filledrectangle( pixels, cols, rows, maxval, x, y, width, height, drawprocP, clientdata ) + pixel** pixels; + int cols, rows, x, y, width, height; + pixval maxval; + void (*drawprocP)(); + char* clientdata; +#endif /*__STDC__*/ + { + register int cx, cy, cwidth, cheight, col, row; + + /* Clip. */ + cx = x; + cy = y; + cwidth = width; + cheight = height; + if ( cx < 0 ) + { + cx = 0; + cwidth += x; + } + if ( cy < 0 ) + { + cy = 0; + cheight += y; + } + if ( cx + cwidth > cols ) + cwidth = cols - cx; + if ( cy + cheight > rows ) + cheight = rows - cy; + + /* Draw. */ + for ( row = cy; row < cy + cheight; ++row ) + for ( col = cx; col < cx + cwidth; ++col ) + if ( drawprocP == PPMD_NULLDRAWPROC ) + pixels[row][col] = *( (pixel*) clientdata ); + else + (*drawprocP)( + pixels, cols, rows, maxval, col, row, clientdata ); + } + + +/* Outline drawing stuff. */ + +static int ppmd_linetype = PPMD_LINETYPE_NORMAL; + +int +ppmd_setlinetype( type ) + int type; + { + int old; + + old = ppmd_linetype; + ppmd_linetype = type; + return old; + } + +static int ppmd_lineclip = 1; + +int +ppmd_setlineclip( clip ) + int clip; + { + int old; + + old = ppmd_lineclip; + ppmd_lineclip = clip; + return old; + } + +#if __STDC__ +void +ppmd_line( pixel** pixels, int cols, int rows, pixval maxval, int x0, int y0, int x1, int y1, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata ) +#else /*__STDC__*/ +void +ppmd_line( pixels, cols, rows, maxval, x0, y0, x1, y1, drawprocP, clientdata ) + pixel** pixels; + int cols, rows, x0, y0, x1, y1; + pixval maxval; + void (*drawprocP)(); + char* clientdata; +#endif /*__STDC__*/ + { + register int cx0, cy0, cx1, cy1; + + /* Special case zero-length lines. */ + if ( x0 == x1 && y0 == y1 ) + { + if ( ( ! ppmd_lineclip ) || + ( x0 >= 0 && x0 < cols && y0 >= 0 && y0 < rows ) ) + if ( drawprocP == PPMD_NULLDRAWPROC ) + ppmd_point_drawproc( + pixels, cols, rows, maxval, x0, y0, clientdata ); + else + (*drawprocP)( pixels, cols, rows, maxval, x0, y0, clientdata ); + return; + } + + /* Clip. */ + cx0 = x0; + cy0 = y0; + cx1 = x1; + cy1 = y1; + if ( ppmd_lineclip ) + { + if ( cx0 < 0 ) + { + if ( cx1 < 0 ) return; + cy0 = cy0 + ( cy1 - cy0 ) * ( -cx0 ) / ( cx1 - cx0 ); + cx0 = 0; + } + else if ( cx0 >= cols ) + { + if ( cx1 >= cols ) return; + cy0 = cy0 + ( cy1 - cy0 ) * ( cols - 1 - cx0 ) / ( cx1 - cx0 ); + cx0 = cols - 1; + } + if ( cy0 < 0 ) + { + if ( cy1 < 0 ) return; + cx0 = cx0 + ( cx1 - cx0 ) * ( -cy0 ) / ( cy1 - cy0 ); + cy0 = 0; + } + else if ( cy0 >= rows ) + { + if ( cy1 >= rows ) return; + cx0 = cx0 + ( cx1 - cx0 ) * ( rows - 1 - cy0 ) / ( cy1 - cy0 ); + cy0 = rows - 1; + } + if ( cx1 < 0 ) + { + cy1 = cy1 + ( cy0 - cy1 ) * ( -cx1 ) / ( cx0 - cx1 ); + cx1 = 0; + } + else if ( cx1 >= cols ) + { + cy1 = cy1 + ( cy0 - cy1 ) * ( cols - 1 - cx1 ) / ( cx0 - cx1 ); + cx1 = cols - 1; + } + if ( cy1 < 0 ) + { + cx1 = cx1 + ( cx0 - cx1 ) * ( -cy1 ) / ( cy0 - cy1 ); + cy1 = 0; + } + else if ( cy1 >= rows ) + { + cx1 = cx1 + ( cx0 - cx1 ) * ( rows - 1 - cy1 ) / ( cy0 - cy1 ); + cy1 = rows - 1; + } + + /* Check again for zero-length lines. */ + if ( cx0 == cx1 && cy0 == cy1 ) + { + if ( drawprocP == PPMD_NULLDRAWPROC ) + ppmd_point_drawproc( + pixels, cols, rows, maxval, cx0, cy0, clientdata ); + else + (*drawprocP)( + pixels, cols, rows, maxval, cx0, cy0, clientdata ); + return; + } + } + + /* Draw, using a simple DDA. */ + if ( abs( cx1 - cx0 ) > abs( cy1 - cy0 ) ) + { /* Loop over X domain. */ + register long dy, srow; + register int dx, col, row, prevrow; + + if ( cx1 > cx0 ) + dx = 1; + else + dx = -1; + dy = ( cy1 - cy0 ) * DDA_SCALE / abs( cx1 - cx0 ); + prevrow = row = cy0; + srow = row * DDA_SCALE + DDA_SCALE / 2; + col = cx0; + for ( ; ; ) + { + if ( ppmd_linetype == PPMD_LINETYPE_NODIAGS && row != prevrow ) + { + if ( drawprocP == PPMD_NULLDRAWPROC ) + pixels[prevrow][col] = *( (pixel*) clientdata ); + else + (*drawprocP)( + pixels, cols, rows, maxval, col, prevrow, clientdata ); + prevrow = row; + } + if ( drawprocP == PPMD_NULLDRAWPROC ) + pixels[row][col] = *( (pixel*) clientdata ); + else + (*drawprocP)( + pixels, cols, rows, maxval, col, row, clientdata ); + if ( col == cx1 ) + break; + srow += dy; + row = srow / DDA_SCALE; + col += dx; + } + } + else + { /* Loop over Y domain. */ + register long dx, scol; + register int dy, col, row, prevcol; + + if ( cy1 > cy0 ) + dy = 1; + else + dy = -1; + dx = ( cx1 - cx0 ) * DDA_SCALE / abs( cy1 - cy0 ); + row = cy0; + prevcol = col = cx0; + scol = col * DDA_SCALE + DDA_SCALE / 2; + for ( ; ; ) + { + if ( ppmd_linetype == PPMD_LINETYPE_NODIAGS && col != prevcol ) + { + if ( drawprocP == PPMD_NULLDRAWPROC ) + pixels[row][prevcol] = *( (pixel*) clientdata ); + else + (*drawprocP)( + pixels, cols, rows, maxval, prevcol, row, clientdata ); + prevcol = col; + } + if ( drawprocP == PPMD_NULLDRAWPROC ) + pixels[row][col] = *( (pixel*) clientdata ); + else + (*drawprocP)( + pixels, cols, rows, maxval, col, row, clientdata ); + if ( row == cy1 ) + break; + row += dy; + scol += dx; + col = scol / DDA_SCALE; + } + } + } + +#define SPLINE_THRESH 3 +#if __STDC__ +void +ppmd_spline3( pixel** pixels, int cols, int rows, pixval maxval, int x0, int y0, int x1, int y1, int x2, int y2, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata ) +#else /*__STDC__*/ +void +ppmd_spline3( pixels, cols, rows, maxval, x0, y0, x1, y1, x2, y2, drawprocP, clientdata ) + pixel** pixels; + int cols, rows, x0, y0, x1, y1, x2, y2; + pixval maxval; + void (*drawprocP)(); + char* clientdata; +#endif /*__STDC__*/ + { + register int xa, ya, xb, yb, xc, yc, xp, yp; + + xa = ( x0 + x1 ) / 2; + ya = ( y0 + y1 ) / 2; + xc = ( x1 + x2 ) / 2; + yc = ( y1 + y2 ) / 2; + xb = ( xa + xc ) / 2; + yb = ( ya + yc ) / 2; + + xp = ( x0 + xb ) / 2; + yp = ( y0 + yb ) / 2; + if ( abs( xa - xp ) + abs( ya - yp ) > SPLINE_THRESH ) + ppmd_spline3( + pixels, cols, rows, maxval, x0, y0, xa, ya, xb, yb, drawprocP, + clientdata ); + else + ppmd_line( + pixels, cols, rows, maxval, x0, y0, xb, yb, drawprocP, clientdata ); + + xp = ( x2 + xb ) / 2; + yp = ( y2 + yb ) / 2; + if ( abs( xc - xp ) + abs( yc - yp ) > SPLINE_THRESH ) + ppmd_spline3( + pixels, cols, rows, maxval, xb, yb, xc, yc, x2, y2, drawprocP, + clientdata ); + else + ppmd_line( + pixels, cols, rows, maxval, xb, yb, x2, y2, drawprocP, clientdata ); + } + +#if __STDC__ +void +ppmd_polyspline( pixel** pixels, int cols, int rows, pixval maxval, int x0, int y0, int nc, int* xc, int* yc, int x1, int y1, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata ) +#else /*__STDC__*/ +void +ppmd_polyspline( pixels, cols, rows, maxval, x0, y0, nc, xc, yc, x1, y1, drawprocP, clientdata ) + pixel** pixels; + int cols, rows, x0, y0, nc, x1, y1; + int* xc; + int* yc; + pixval maxval; + void (*drawprocP)(); + char* clientdata; +#endif /*__STDC__*/ + { + register int i, x, y, xn, yn; + + x = x0; + y = y0; + for ( i = 0; i < nc - 1; ++i ) + { + xn = ( xc[i] + xc[i + 1] ) / 2; + yn = ( yc[i] + yc[i + 1] ) / 2; + ppmd_spline3( + pixels, cols, rows, maxval, x, y, xc[i], yc[i], xn, yn, drawprocP, + clientdata ); + x = xn; + y = yn; + } + ppmd_spline3( + pixels, cols, rows, maxval, x, y, xc[nc - 1], yc[nc - 1], x1, y1, + drawprocP, clientdata ); + } + +#if __STDC__ +void +ppmd_circle( pixel** pixels, int cols, int rows, pixval maxval, int cx, int cy, int radius, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata ) +#else /*__STDC__*/ +void +ppmd_circle( pixels, cols, rows, maxval, cx, cy, radius, drawprocP, clientdata ) + pixel** pixels; + int cols, rows, cx, cy, radius; + pixval maxval; + void (*drawprocP)(); + char* clientdata; +#endif /*__STDC__*/ + { + register int x0, y0, x, y, prevx, prevy, nopointsyet; + register long sx, sy, e; + + x0 = x = radius; + y0 = y = 0; + sx = x * DDA_SCALE + DDA_SCALE / 2; + sy = y * DDA_SCALE + DDA_SCALE / 2; + e = DDA_SCALE / radius; + if ( drawprocP == PPMD_NULLDRAWPROC ) + pixels[y + cy][x + cx] = *( (pixel*) clientdata ); + else + (*drawprocP)( pixels, cols, rows, maxval, x + cx, y + cy, clientdata ); + nopointsyet = 1; + do + { + prevx = x; + prevy = y; + sx += e * sy / DDA_SCALE; + sy -= e * sx / DDA_SCALE; + x = sx / DDA_SCALE; + y = sy / DDA_SCALE; + if ( x != prevx || y != prevy ) + { + nopointsyet = 0; + if ( drawprocP == PPMD_NULLDRAWPROC ) + pixels[y + cy][x + cx] = *( (pixel*) clientdata ); + else + (*drawprocP)( + pixels, cols, rows, maxval, x + cx, y + cy, clientdata ); + } + } + while ( nopointsyet || x != x0 || y != y0 ); + } + + +/* Arbitrary fill stuff. */ + +typedef struct + { + short x; + short y; + short edge; + } coord; +typedef struct + { + int n; + int size; + int curedge; + int segstart; + int ydir; + int startydir; + coord* coords; + } fillobj; + +#define SOME 1000 + +static int oldclip; + +char* +ppmd_fill_init( ) + { + fillobj* fh; + + fh = (fillobj*) malloc( sizeof(fillobj) ); + if ( fh == 0 ) + pm_error( "out of memory allocating a fillhandle" ); + fh->n = 0; + fh->coords = (coord*) malloc( SOME * sizeof(coord) ); + if ( fh->coords == 0 ) + pm_error( "out of memory allocating a fillhandle" ); + fh->size = SOME; + fh->curedge = 0; + + /* Turn off line clipping. */ + oldclip = ppmd_setlineclip( 0 ); + + return (char*) fh; + } + +#if __STDC__ +void +ppmd_fill_drawproc( pixel** pixels, int cols, int rows, pixval maxval, int x, int y, char* clientdata ) +#else /*__STDC__*/ +void +ppmd_fill_drawproc( pixels, cols, rows, maxval, x, y, clientdata ) + pixel** pixels; + int cols, rows, x, y; + pixval maxval; + char* clientdata; +#endif /*__STDC__*/ + { + register fillobj* fh; + register coord* cp; + register coord* ocp; + + fh = (fillobj*) clientdata; + + if ( fh->n > 0 ) + { + /* If these are the same coords we saved last time, don't bother. */ + ocp = &(fh->coords[fh->n - 1]); + if ( x == ocp->x && y == ocp->y ) + return; + } + + /* Ok, these are new; check if there's room for two more coords. */ + if ( fh->n + 1 >= fh->size ) + { + fh->size += SOME; + fh->coords = (coord*) realloc( + (char*) fh->coords, fh->size * sizeof(coord) ); + if ( fh->coords == 0 ) + pm_error( "out of memory enlarging a fillhandle" ); + } + + /* Check for extremum and set the edge number. */ + if ( fh->n == 0 ) + { /* Start first segment. */ + fh->segstart = fh->n; + fh->ydir = 0; + fh->startydir = 0; + } + else + { + register int dx, dy; + + dx = x - ocp->x; + dy = y - ocp->y; + if ( dx < -1 || dx > 1 || dy < -1 || dy > 1 ) + { /* Segment break. Close off old one. */ + if ( fh->startydir != 0 && fh->ydir != 0 ) + if ( fh->startydir == fh->ydir ) + { /* Oops, first edge and last edge are the same. + ** Renumber the first edge in the old segment. */ + register coord* fcp; + int oldedge; + + fcp = &(fh->coords[fh->segstart]); + oldedge = fcp->edge; + for ( ; fcp->edge == oldedge; ++fcp ) + fcp->edge = ocp->edge; + } + /* And start new segment. */ + ++(fh->curedge); + fh->segstart = fh->n; + fh->ydir = 0; + fh->startydir = 0; + } + else + { /* Segment continues. */ + if ( dy != 0 ) + { + if ( fh->ydir != 0 && fh->ydir != dy ) + { /* Direction changed. Insert a fake coord, old + ** position but new edge number. */ + ++(fh->curedge); + cp = &(fh->coords[fh->n]); + cp->x = ocp->x; + cp->y = ocp->y; + cp->edge = fh->curedge; + ++(fh->n); + } + fh->ydir = dy; + if ( fh->startydir == 0 ) + fh->startydir = dy; + } + } + } + + /* Save this coord. */ + cp = &(fh->coords[fh->n]); + cp->x = x; + cp->y = y; + cp->edge = fh->curedge; + ++(fh->n); + } + +static int yx_compare ARGS((coord* c1, coord* c2)); +static int +yx_compare( c1, c2 ) + coord* c1; + coord* c2; + { + if ( c1->y > c2->y ) + return 1; + if ( c1->y < c2->y ) + return -1; + if ( c1->x > c2->x ) + return 1; + if ( c1->x < c2->x ) + return -1; + return 0; + } + +#if __STDC__ +void +ppmd_fill( pixel** pixels, int cols, int rows, pixval maxval, char* fillhandle, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata ) +#else /*__STDC__*/ +void +ppmd_fill( pixels, cols, rows, maxval, fillhandle, drawprocP, clientdata ) + pixel** pixels; + int cols, rows; + pixval maxval; + char* fillhandle; + void (*drawprocP)(); + char* clientdata; +#endif /*__STDC__*/ + { + register fillobj* fh; + int pedge, eq; + register int i, leftside, edge, lx, rx, py; + register coord* cp; + + fh = (fillobj*) fillhandle; + + /* Close off final segment. */ + if ( fh->n > 0 && fh->startydir != 0 && fh->ydir != 0 ) + if ( fh->startydir == fh->ydir ) + { /* Oops, first edge and last edge are the same. */ + register coord* fcp; + int lastedge, oldedge; + + lastedge = fh->coords[fh->n - 1].edge; + fcp = &(fh->coords[fh->segstart]); + oldedge = fcp->edge; + for ( ; fcp->edge == oldedge; ++fcp ) + fcp->edge = lastedge; + } + + /* Restore clipping now. */ + (void) ppmd_setlineclip( oldclip ); + + /* Sort the coords by Y, secondarily by X. */ + qsort( (char*) fh->coords, fh->n, sizeof(coord), yx_compare ); + + /* Find equal coords with different edge numbers, and swap if necessary. */ + edge = -1; + for ( i = 0; i < fh->n; ++i ) + { + cp = &(fh->coords[i]); + if ( i > 1 && eq && cp->edge != edge && cp->edge == pedge ) + { /* Swap .-1 and .-2. */ + coord t; + + t = fh->coords[i-1]; + fh->coords[i-1] = fh->coords[i-2]; + fh->coords[i-2] = t; + } + if ( i > 0 ) + { + if ( cp->x == lx && cp->y == py ) + { + eq = 1; + if ( cp->edge != edge && cp->edge == pedge ) + { /* Swap . and .-1. */ + coord t; + + t = *cp; + *cp = fh->coords[i-1]; + fh->coords[i-1] = t; + } + } + else + eq = 0; + } + lx = cp->x; + py = cp->y; + pedge = edge; + edge = cp->edge; + } + + /* Ok, now run through the coords filling spans. */ + for ( i = 0; i < fh->n; ++i ) + { + cp = &(fh->coords[i]); + if ( i == 0 ) + { + lx = rx = cp->x; + py = cp->y; + edge = cp->edge; + leftside = 1; + } + else + { + if ( cp->y != py ) + { /* Row changed. Emit old span and start a new one. */ + ppmd_filledrectangle( + pixels, cols, rows, maxval, lx, py, rx - lx + 1, 1, + drawprocP, clientdata); + lx = rx = cp->x; + py = cp->y; + edge = cp->edge; + leftside = 1; + } + else + { + if ( cp->edge == edge ) + { /* Continuation of side. */ + rx = cp->x; + } + else + { /* Edge changed. Is it a span? */ + if ( leftside ) + { + rx = cp->x; + leftside = 0; + } + else + { /* Got a span to fill. */ + ppmd_filledrectangle( + pixels, cols, rows, maxval, lx, py, rx - lx + 1, + 1, drawprocP, clientdata); + lx = rx = cp->x; + leftside = 1; + } + edge = cp->edge; + } + } + } + } + + /* All done. Free up the fillhandle and leave. */ + free( fh->coords ); + free( fh ); + } diff --git a/panda/src/pnm/pbm.h b/panda/src/pnm/pbm.h new file mode 100644 index 0000000000..8cdfaf044f --- /dev/null +++ b/panda/src/pnm/pbm.h @@ -0,0 +1,49 @@ +/* pbm.h - header file for libpbm portable bitmap library +*/ + +#ifndef _PBM_H_ +#define _PBM_H_ + +#include + +#include "pbmplus.h" + +typedef unsigned char bit; +#define PBM_WHITE 0 +#define PBM_BLACK 1 + + +/* Magic constants. */ + +#define PBM_MAGIC1 'P' +#define PBM_MAGIC2 '1' +#define RPBM_MAGIC2 '4' +#define PBM_FORMAT (PBM_MAGIC1 * 256 + PBM_MAGIC2) +#define RPBM_FORMAT (PBM_MAGIC1 * 256 + RPBM_MAGIC2) +#define PBM_TYPE PBM_FORMAT + + +/* Macro for turning a format number into a type number. */ + +#define PBM_FORMAT_TYPE(f) ((f) == PBM_FORMAT || (f) == RPBM_FORMAT ? PBM_TYPE : -1) + + +/* Declarations of routines. */ + +void pbm_init ARGS(( int* argcP, char* argv[] )); + +#define pbm_allocarray( cols, rows ) ((bit**) pm_allocarray( cols, rows, sizeof(bit) )) +#define pbm_allocrow( cols ) ((bit*) pm_allocrow( cols, sizeof(bit) )) +#define pbm_freearray( bits, rows ) pm_freearray( (char**) bits, rows ) +#define pbm_freerow( bitrow ) pm_freerow( (char*) bitrow ) + +bit** pbm_readpbm ARGS(( FILE* file, int* colsP, int* rowsP )); +void pbm_readpbminit ARGS(( FILE* file, int* colsP, int* rowsP, int* formatP )); +void pbm_readpbmrow ARGS(( FILE* file, bit* bitrow, int cols, int format )); +char* pm_read_unknown_size ARGS(( FILE* file, long* buf )); + +void pbm_writepbm ARGS(( FILE* file, bit** bits, int cols, int rows, int forceplain )); +void pbm_writepbminit ARGS(( FILE* file, int cols, int rows, int forceplain )); +void pbm_writepbmrow ARGS(( FILE* file, bit* bitrow, int cols, int forceplain )); + +#endif /*_PBM_H_*/ diff --git a/panda/src/pnm/pbmfont.h b/panda/src/pnm/pbmfont.h new file mode 100644 index 0000000000..0ecb0462b0 --- /dev/null +++ b/panda/src/pnm/pbmfont.h @@ -0,0 +1,27 @@ +/* pbmfont.h - header file for font routines in libpbm +*/ + +struct glyph { + int width, height; + int x, y; + int xadd; + char* bmap; +}; + +struct font { + int maxwidth, maxheight; + int x, y; + struct glyph* glyph[256]; + /* for compatibility with old pbmtext routines */ + /* oldfont is 0 if the font is BDF derived */ + bit** oldfont; + int fcols, frows; +}; + +struct font* pbm_defaultfont ARGS(( char* which )); +struct font* pbm_dissectfont ARGS(( bit** font, int frows, int fcols )); +struct font* pbm_loadfont ARGS(( char* filename )); +struct font* pbm_loadpbmfont ARGS(( char* filename )); +struct font* pbm_loadbdffont ARGS(( char* filename )); +void pbm_dumpfont ARGS(( struct font* fn )); +int mk_argvn ARGS(( char* s, char* vec[], int max )); diff --git a/panda/src/pnm/pbmplus.h b/panda/src/pnm/pbmplus.h new file mode 100644 index 0000000000..b61a94270b --- /dev/null +++ b/panda/src/pnm/pbmplus.h @@ -0,0 +1,289 @@ +/* pbmplus.h - header file for PBM, PGM, PPM, and PNM +** +** Copyright (C) 1988, 1989, 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#ifndef _PBMPLUS_H_ +#define _PBMPLUS_H_ + +#include + +#include +#include +#include +#if defined(VMS) || defined(PENV_WIN32) +#ifdef VMS +#include +#endif +#include +#endif + +#if defined(USG) || defined(SVR4) || defined(VMS) +#define SYSV +#endif +#if ! ( defined(BSD) || defined(SYSV) || defined(MSDOS) || defined(AMIGA) ) +/* CONFIGURE: If your system is >= 4.2BSD, set the BSD option; if you're a +** System V site, set the SYSV option; if you're IBM-compatible, set MSDOS; +** and if you run on an Amiga, set AMIGA. If your compiler is ANSI C, you're +** probably better off setting SYSV - all it affects is string handling. +*/ +/*#define BSD*/ +#define SYSV +/* #define MSDOS */ +/* #define AMIGA */ +#endif + +/* CONFIGURE: If you have an X11-style rgb color names file, define its +** path here. This is used by PPM to parse color names into rgb values. +** If you don't have such a file, comment this out and use the alternative +** hex and decimal forms to specify colors (see ppm/pgmtoppm.1 for details). +*/ +#ifndef RGB_DB +#define RGB_DB "/usr/lib/X11/rgb.txt" +/*#define RGB_DB "/usr/openwin/lib/rgb.txt"*/ +#ifdef VMS +#define RGB_DB "PBMplus_Dir:RGB.TXT" +#endif +#endif + +/* CONFIGURE: If you want to enable writing "raw" files, set this option. +** "Raw" files are smaller, and much faster to read and write, but you +** must have a filesystem that allows all 256 ASCII characters to be read +** and written. You will no longer be able to mail P?M files without +** using uuencode or the equivalent, or running the files through pnmnoraw. +** Note that reading "raw" files works whether writing is enabled or not. +*/ +#define PBMPLUS_RAWBITS + +/* CONFIGURE: PGM can store gray values as either bytes or shorts. For most +** applications, bytes will be big enough, and the memory savings can be +** substantial. However, if you need more than 8 bits of grayscale resolution, +** then define this symbol. Unless your computer is very slow or you are short +** of memory, there is no reason why you should not set this symbol. +*/ +#define PGM_BIGGRAYS + +/* CONFIGURE: Normally, PPM handles a pixel as a struct of three grays. +** If grays are stored in bytes, that's 24 bits per color pixel; if +** grays are stored as shorts, that's 48 bits per color pixel. PPM +** can also be configured to pack the three grays into a single longword, +** 10 bits each, 30 bits per pixel. +** +** If you have configured PGM with the PGM_BIGGRAYS option, AND you don't +** need more than 10 bits for each color component, AND you care more about +** memory use than speed, then this option might be a win. Under these +** circumstances it will make some of the programs use 1.5 times less space, +** but all of the programs will run about 1.4 times slower. +** +** If you are not using PGM_BIGGRAYS, then this option is useless -- it +** doesn't save any space, but it still slows things down. +*/ +/* #define PPM_PACKCOLORS */ + +/* CONFIGURE: uncomment this to enable debugging checks. */ +/* #define DEBUG */ + +#if ( defined(SYSV) || defined(AMIGA) ) + +#include +#define random rand +#define srandom(s) srand(s) +#ifndef __SASC +#define index(s,c) strchr(s,c) +#define rindex(s,c) strrchr(s,c) +#ifndef _DCC /* Amiga DICE Compiler */ +#define bzero(dst,len) memset(dst,0,len) +#define bcopy(src,dst,len) memcpy(dst,src,len) +#define bcmp memcmp +#ifndef __cplusplus +extern void srand(); +extern int rand(); +#endif +#endif /* _DCC */ +#endif /* __SASC */ + +#else /* SYSV or AMIGA */ + +#include +extern void srandom(); +extern long random(); + +#endif /*SYSV or AMIGA*/ + +#if (defined(MSDOS) || defined(AMIGA) ) +#include +#include +#include +#else +#ifndef VMS +#ifndef PENV_WIN32 +#include +#endif +#endif +#ifndef __cplusplus +extern int atoi(); +extern void exit(); +extern long time(); +extern int write(); +#endif +#endif + +/* CONFIGURE: On most BSD systems, malloc() gets declared in stdlib.h, on +** system V, it gets declared in malloc.h. On some systems, malloc.h +** doesn't declare these, so we have to do it here. On other systems, +** for example HP/UX, it declares them incompatibly. And some systems, +** for example Dynix, don't have a malloc.h at all. A sad situation. +** If you have compilation problems that point here, feel free to tweak +** or remove these declarations. +*/ +#ifdef BSD +#include +#endif +#if (defined(SYSV) && !defined(VMS)) +#include +#endif +/* extern char* malloc(); */ +/* extern char* realloc(); */ +/* extern char* calloc(); */ + +/* CONFIGURE: Some systems don't have vfprintf(), which we need for the +** error-reporting routines. If you compile and get a link error about +** this routine, uncomment the first define, which gives you a vfprintf +** that uses the theoretically non-portable but fairly common routine +** _doprnt(). If you then get a link error about _doprnt, or +** message-printing doesn't look like it's working, try the second +** define instead. +*/ +/* #define NEED_VFPRINTF1 */ +/* #define NEED_VFPRINTF2 */ + +/* CONFIGURE: Some systems don't have strstr(), which some routines need. +** If you compile and get a link error about this routine, uncomment the +** define, which gives you a strstr. +*/ +/* #define NEED_STRSTR */ + +/* CONFIGURE: If you don't want a fixed path to an X11 color name file +** compiled into PBMPlus, set this option. Now RGB_DB (see Makefile) +** defines the name of an environment-variable that holds the complete +** path and name of this file. +*/ +/* #define A_RGBENV */ +#ifdef A_RGBENV +#define RGB_DB "RGBDEF" /* name of env-var */ +#endif /* A_RGBENV */ + +/* CONFIGURE: Set this option if your compiler uses strerror(errno) +** instead of sys_errlist[errno] for error messages. +*/ +/* #define A_STRERROR */ + +/* CONFIGURE: On small systems without VM it is possible that there is +** enough memory for a large array, but it is fragmented. So the usual +** malloc( all-in-one-big-chunk ) fails. With this option, if the first +** method fails, pm_allocarray() tries to allocate the array row by row. +*/ +/* #define A_FRAGARRAY */ + +/* +** Some special things for the Amiga. +*/ + +/* End of configurable definitions. */ + + +#ifdef AMIGA +#include +#define getpid(x) ((long)FindTask(NULL)) +#endif /* AMIGA */ + +/* +#undef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#undef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#undef abs +#define abs(a) ((a) >= 0 ? (a) : -(a)) +#undef odd +#define odd(n) ((n) & 1) +*/ + +/* Definitions to make PBMPLUS work with either ANSI C or C Classic. */ + +#if __STDC__ +#define ARGS(alist) alist +#else /*__STDC__*/ +#define ARGS(alist) () +/*#define const*/ +#endif /*__STDC__*/ + + +/* Initialization. */ + +EXPCL_PANDA void pm_init( int* argcP, char* argv[] ); + +/* Variable-sized arrays definitions. */ + +char** pm_allocarray ARGS(( int cols, int rows, int size )); +EXPCL_PANDA char* pm_allocrow( int cols, int size ); +void pm_freearray ARGS(( char** its, int rows )); +EXPCL_PANDA void pm_freerow( char* itrow ); + + +/* Case-insensitive keyword matcher. */ + +int pm_keymatch ARGS(( char* str, char* keyword, int minchars )); + + +/* Log base two hacks. */ + +EXPCL_PANDA int pm_maxvaltobits( int maxval ); +EXPCL_PANDA int pm_bitstomaxval( int bits ); + + +/* Error handling definitions. */ + +EXPCL_PANDA void pm_message( char*, ... ); +EXPCL_PANDA void pm_error( char*, ... ); /* doesn't return */ +void pm_perror ARGS(( char* reason )); /* doesn't return */ +void pm_usage ARGS(( char* usage )); /* doesn't return */ + + +/* File open/close that handles "-" as stdin and checks errors. */ + +EXPCL_PANDA FILE* pm_openr( char* name ); +EXPCL_PANDA FILE* pm_openw( char* name ); +EXPCL_PANDA void pm_close( FILE* f ); + + +/* Endian I/O. */ + +EXPCL_PANDA int pm_readbigshort( FILE* in, short* sP ); +EXPCL_PANDA int pm_writebigshort( FILE* out, short s ); +EXPCL_PANDA int pm_readbiglong( FILE* in, long* lP ); +EXPCL_PANDA int pm_writebiglong( FILE* out, long l ); +EXPCL_PANDA int pm_readlittleshort( FILE* in, short* sP ); +EXPCL_PANDA int pm_writelittleshort( FILE* out, short s ); +EXPCL_PANDA int pm_readlittlelong( FILE* in, long* lP ); +EXPCL_PANDA int pm_writelittlelong( FILE* out, long l ); + + +/* Compatibility stuff */ + +#ifdef NEED_STRSTR +char *strstr ARGS((char *s1, char *s2)); +#endif + +#if defined(NEED_VFPRINTF1) || defined(NEED_VFPRINTF2) +int vfprintf ARGS(( FILE* stream, char* format, va_list args )); +#endif /*NEED_VFPRINTF*/ + + +#endif /*_PBMPLUS_H_*/ diff --git a/panda/src/pnm/pgm.h b/panda/src/pnm/pgm.h new file mode 100644 index 0000000000..378c3226ff --- /dev/null +++ b/panda/src/pnm/pgm.h @@ -0,0 +1,57 @@ +/* pgm.h - header file for libpgm portable graymap library +*/ + +#ifndef _PGM_H_ +#define _PGM_H_ + +#include + +#include "pbm.h" + +#ifdef PGM_BIGGRAYS +typedef unsigned short gray; +#define PGM_MAXMAXVAL 65535 +#else /*PGM_BIGGRAYS*/ +typedef unsigned char gray; +#define PGM_MAXMAXVAL 255 +#endif /*PGM_BIGGRAYS*/ + + +/* Magic constants. */ + +#define PGM_MAGIC1 'P' +#define PGM_MAGIC2 '2' +#define RPGM_MAGIC2 '5' +#define PGM_FORMAT (PGM_MAGIC1 * 256 + PGM_MAGIC2) +#define RPGM_FORMAT (PGM_MAGIC1 * 256 + RPGM_MAGIC2) +#define PGM_TYPE PGM_FORMAT + + +/* Macro for turning a format number into a type number. */ + +#define PGM_FORMAT_TYPE(f) ((f) == PGM_FORMAT || (f) == RPGM_FORMAT ? PGM_TYPE : PBM_FORMAT_TYPE(f)) + + +/* Declarations of routines. */ + +void pgm_init ARGS(( int* argcP, char* argv[] )); + +#define pgm_allocarray( cols, rows ) ((gray**) pm_allocarray( cols, rows, sizeof(gray) )) +#define pgm_allocrow( cols ) ((gray*) pm_allocrow( cols, sizeof(gray) )) +#define pgm_freearray( grays, rows ) pm_freearray( (char**) grays, rows ) +#define pgm_freerow( grayrow ) pm_freerow( (char*) grayrow ) + +gray** pgm_readpgm ARGS(( FILE* file, int* colsP, int* rowsP, gray* maxvalP )); +void pgm_readpgminit ARGS(( FILE* file, int* colsP, int* rowsP, gray* maxvalP, int* formatP )); +void pgm_readpgmrow ARGS(( FILE* file, gray* grayrow, int cols, gray maxval, int format )); + +void pgm_writepgm ARGS(( FILE* file, gray** grays, int cols, int rows, gray maxval, int forceplain )); +void pgm_writepgminit ARGS(( FILE* file, int cols, int rows, gray maxval, int forceplain )); +void pgm_writepgmrow ARGS(( FILE* file, gray* grayrow, int cols, gray maxval, int forceplain )); + +extern gray pgm_pbmmaxval; +/* This is the maxval used when a PGM program reads a PBM file. Normally +** it is 1; however, for some programs, a larger value gives better results +*/ + +#endif /*_PGM_H_*/ diff --git a/panda/src/pnm/pnm.h b/panda/src/pnm/pnm.h new file mode 100644 index 0000000000..4e3d981743 --- /dev/null +++ b/panda/src/pnm/pnm.h @@ -0,0 +1,48 @@ +/* pnm.h - header file for libpnm portable anymap library +*/ + +#ifndef _PNM_H_ +#define _PNM_H_ + +#include + +#include "ppm.h" +typedef pixel xel; +typedef pixval xelval; +#define PNM_MAXMAXVAL PPM_MAXMAXVAL +#define PNM_GET1(x) PPM_GETB(x) +#define PNM_ASSIGN1(x,v) PPM_ASSIGN(x,0,0,v) +#define PNM_EQUAL(x,y) PPM_EQUAL(x,y) +#define PNM_FORMAT_TYPE(f) PPM_FORMAT_TYPE(f) + +/* Declarations of routines. */ + +void pnm_init ARGS(( int* argcP, char* argv[] )); + +#define pnm_allocarray( cols, rows ) ((xel**) pm_allocarray( cols, rows, sizeof(xel) )) +#define pnm_allocrow( cols ) ((xel*) pm_allocrow( cols, sizeof(xel) )) +#define pnm_freearray( xels, rows ) pm_freearray( (char**) xels, rows ) +#define pnm_freerow( xelrow ) pm_freerow( (char*) xelrow ) + +xel** pnm_readpnm ARGS(( FILE* file, int* colsP, int* rowsP, xelval* maxvalP, int* formatP )); +EXPCL_PANDA void pnm_readpnminit( FILE* file, int* colsP, int* rowsP, xelval* maxvalP, int* formatP ); +EXPCL_PANDA void pnm_readpnmrow( FILE* file, xel* xelrow, int cols, xelval maxval, int format ); + +void pnm_writepnm ARGS(( FILE* file, xel** xels, int cols, int rows, xelval maxval, int format, int forceplain )); +EXPCL_PANDA void pnm_writepnminit( FILE* file, int cols, int rows, xelval maxval, int format, int forceplain ); +EXPCL_PANDA void pnm_writepnmrow( FILE* file, xel* xelrow, int cols, xelval maxval, int format, int forceplain ); + +xel pnm_backgroundxel ARGS(( xel** xels, int cols, int rows, xelval maxval, int format )); +xel pnm_backgroundxelrow ARGS(( xel* xelrow, int cols, xelval maxval, int format )); +xel pnm_whitexel ARGS(( xelval maxval, int format )); +xel pnm_blackxel ARGS(( xelval maxval, int format )); +void pnm_invertxel ARGS(( xel* x, xelval maxval, int format )); +void pnm_promoteformat ARGS(( xel** xels, int cols, int rows, xelval maxval, int format, xelval newmaxval, int newformat )); +void pnm_promoteformatrow ARGS(( xel* xelrow, int cols, xelval maxval, int format, xelval newmaxval, int newformat )); + +extern EXPCL_PANDA xelval pnm_pbmmaxval; +/* This is the maxval used when a PNM program reads a PBM file. Normally +** it is 1; however, for some programs, a larger value gives better results +*/ + +#endif /*_PNM_H_*/ diff --git a/panda/src/pnm/ppm.h b/panda/src/pnm/ppm.h new file mode 100644 index 0000000000..2b8064009f --- /dev/null +++ b/panda/src/pnm/ppm.h @@ -0,0 +1,107 @@ +/* ppm.h - header file for libppm portable pixmap library +*/ + +#ifndef _PPM_H_ +#define _PPM_H_ + +#include + +#include "pgm.h" + +typedef gray pixval; + +#ifdef PPM_PACKCOLORS + +#define PPM_MAXMAXVAL 1023 +typedef unsigned long pixel; +#define PPM_GETR(p) (((p) & 0x3ff00000) >> 20) +#define PPM_GETG(p) (((p) & 0xffc00) >> 10) +#define PPM_GETB(p) ((p) & 0x3ff) + +/************* added definitions *****************/ +#define PPM_PUTR(p, red) ((p) |= (((red) & 0x3ff) << 20)) +#define PPM_PUTG(p, grn) ((p) |= (((grn) & 0x3ff) << 10)) +#define PPM_PUTB(p, blu) ((p) |= ( (blu) & 0x3ff)) +/**************************************************/ + +#define PPM_ASSIGN(p,red,grn,blu) (p) = ((pixel) (red) << 20) | ((pixel) (grn) << 10) | (pixel) (blu) +#define PPM_EQUAL(p,q) ((p) == (q)) + +#else /*PPM_PACKCOLORS*/ + +#define PPM_MAXMAXVAL PGM_MAXMAXVAL +typedef struct + { + pixval r, g, b; + } pixel; +#define PPM_GETR(p) ((p).r) +#define PPM_GETG(p) ((p).g) +#define PPM_GETB(p) ((p).b) + +/************* added definitions *****************/ +#define PPM_PUTR(p,red) ((p).r = (red)) +#define PPM_PUTG(p,grn) ((p).g = (grn)) +#define PPM_PUTB(p,blu) ((p).b = (blu)) +/**************************************************/ + +#define PPM_ASSIGN(p,red,grn,blu) do { (p).r = (red); (p).g = (grn); (p).b = (blu); } while ( 0 ) +#define PPM_EQUAL(p,q) ( (p).r == (q).r && (p).g == (q).g && (p).b == (q).b ) + +#endif /*PPM_PACKCOLORS*/ + + +/* Magic constants. */ + +#define PPM_MAGIC1 'P' +#define PPM_MAGIC2 '3' +#define RPPM_MAGIC2 '6' +#define PPM_FORMAT (PPM_MAGIC1 * 256 + PPM_MAGIC2) +#define RPPM_FORMAT (PPM_MAGIC1 * 256 + RPPM_MAGIC2) +#define PPM_TYPE PPM_FORMAT + + +/* Macro for turning a format number into a type number. */ + +#define PPM_FORMAT_TYPE(f) ((f) == PPM_FORMAT || (f) == RPPM_FORMAT ? PPM_TYPE : PGM_FORMAT_TYPE(f)) + + +/* Declarations of routines. */ + +void ppm_init ARGS(( int* argcP, char* argv[] )); + +#define ppm_allocarray( cols, rows ) ((pixel**) pm_allocarray( cols, rows, sizeof(pixel) )) +#define ppm_allocrow( cols ) ((pixel*) pm_allocrow( cols, sizeof(pixel) )) +#define ppm_freearray( pixels, rows ) pm_freearray( (char**) pixels, rows ) +#define ppm_freerow( pixelrow ) pm_freerow( (char*) pixelrow ) + +pixel** ppm_readppm ARGS(( FILE* file, int* colsP, int* rowsP, pixval* maxvalP )); +void ppm_readppminit ARGS(( FILE* file, int* colsP, int* rowsP, pixval* maxvalP, int* formatP )); +void ppm_readppmrow ARGS(( FILE* file, pixel* pixelrow, int cols, pixval maxval, int format )); + +void ppm_writeppm ARGS(( FILE* file, pixel** pixels, int cols, int rows, pixval maxval, int forceplain )); +void ppm_writeppminit ARGS(( FILE* file, int cols, int rows, pixval maxval, int forceplain )); +void ppm_writeppmrow ARGS(( FILE* file, pixel* pixelrow, int cols, pixval maxval, int forceplain )); + +pixel ppm_parsecolor ARGS(( char* colorname, pixval maxval )); +char* ppm_colorname ARGS(( pixel* colorP, pixval maxval, int hexok )); + +extern pixval ppm_pbmmaxval; +/* This is the maxval used when a PPM program reads a PBM file. Normally +** it is 1; however, for some programs, a larger value gives better results +*/ + + +/* Color scaling macro -- to make writing ppmtowhatever easier. */ + +#define PPM_DEPTH(newp,p,oldmaxval,newmaxval) \ + PPM_ASSIGN( (newp), \ + ( (int) PPM_GETR(p) * (newmaxval) + (oldmaxval) / 2 ) / (oldmaxval), \ + ( (int) PPM_GETG(p) * (newmaxval) + (oldmaxval) / 2 ) / (oldmaxval), \ + ( (int) PPM_GETB(p) * (newmaxval) + (oldmaxval) / 2 ) / (oldmaxval) ) + + +/* Luminance macro. */ + +#define PPM_LUMIN(p) ( 0.299 * PPM_GETR(p) + 0.587 * PPM_GETG(p) + 0.114 * PPM_GETB(p) ) + +#endif /*_PPM_H_*/ diff --git a/panda/src/pnm/ppmcmap.h b/panda/src/pnm/ppmcmap.h new file mode 100644 index 0000000000..d52f71fe38 --- /dev/null +++ b/panda/src/pnm/ppmcmap.h @@ -0,0 +1,45 @@ +/* ppmcmap.h - header file for colormap routines in libppm +*/ + +/* Color histogram stuff. */ + +typedef struct colorhist_item* colorhist_vector; +struct colorhist_item + { + pixel color; + int value; + }; + +typedef struct colorhist_list_item* colorhist_list; +struct colorhist_list_item + { + struct colorhist_item ch; + colorhist_list next; + }; + +EXPCL_PANDA colorhist_vector ppm_computecolorhist( pixel** pixels, int cols, int rows, int maxcolors, int* colorsP ); +/* Returns a colorhist *colorsP long (with space allocated for maxcolors. */ + +void ppm_addtocolorhist ARGS(( colorhist_vector chv, int* colorsP, int maxcolors, pixel* colorP, int value, int position )); + +EXPCL_PANDA void ppm_freecolorhist( colorhist_vector chv ); + + +/* Color hash table stuff. */ + +typedef colorhist_list* colorhash_table; + +colorhash_table ppm_computecolorhash ARGS(( pixel** pixels, int cols, int rows, int maxcolors, int* colorsP )); + +EXPCL_PANDA int +ppm_lookupcolor( colorhash_table cht, pixel* colorP ); + +colorhist_vector ppm_colorhashtocolorhist ARGS(( colorhash_table cht, int maxcolors )); +EXPCL_PANDA colorhash_table ppm_colorhisttocolorhash( colorhist_vector chv, int colors ); + +int ppm_addtocolorhash ARGS(( colorhash_table cht, pixel* colorP, int value )); +/* Returns -1 on failure. */ + +colorhash_table ppm_alloccolorhash ARGS(( void )); + +void ppm_freecolorhash ARGS(( colorhash_table cht )); diff --git a/panda/src/pnm/ppmdraw.h b/panda/src/pnm/ppmdraw.h new file mode 100644 index 0000000000..14eea55fe9 --- /dev/null +++ b/panda/src/pnm/ppmdraw.h @@ -0,0 +1,103 @@ +/* ppmdraw.h - header file for simple drawing routines in libppm +** +** Simple, yes, and also fairly slow if the truth be told; but also very +** flexible and powerful. +** +** The two basic concepts are the drawproc and clientdata. All the drawing +** routines take a drawproc that does the actual drawing. A drawproc draws +** a single point, and it looks like this: +*/ +void ppmd_point_drawproc ARGS(( pixel** pixels, int cols, int rows, pixval maxval, int x, int y, char* clientdata )); +/* +** So, you call a drawing routine, e.g. ppmd_line(), and pass it a drawproc; +** it calls the drawproc for each point it wants to draw. Why so complicated? +** Because you can define your own drawprocs to do more interesting things than +** simply draw the point. For example, you could make one that calls back into +** another drawing routine, say ppmd_circle() to draw a circle at each point +** of a line. +** +** Slow? Well sure, we're talking results here, not realtime. You can do +** tricks with this arrangement that you couldn't even think of before. +** Still, to speed things up for the 90% case you can use this: +*/ +#if __STDC__ +#define PPMD_NULLDRAWPROC (void (*)(pixel**, int, int, pixval, int, int, char*)) 0 +#else /*__STDC__*/ +#define PPMD_NULLDRAWPROC (void (*)()) 0 +#endif /*__STDC__*/ +/* +** Just like ppmd_point_drawproc() it simply draws the point, but it's done +** inline, and clipping is assumed to be handled at a higher level. +** +** Now, what about clientdata. Well, it's an arbitrary pointer, and can +** mean something different to each different drawproc. For the above two +** drawprocs, clientdata should be a pointer to a pixel holding the color +** to be drawn. Other drawprocs can use it to point to something else, +** e.g. some structure to be modified, or they can ignore it. +*/ + + +/* Outline drawing routines. Lines, splines, circles, etc. */ + +int ppmd_setlinetype ARGS(( int type )); +#define PPMD_LINETYPE_NORMAL 0 +#define PPMD_LINETYPE_NODIAGS 1 +/* If you set NODIAGS, all pixels drawn by ppmd_line() will be 4-connected +** instead of 8-connected; in other words, no diagonals. This is useful +** for some applications, for example when you draw many parallel lines +** and you want them to fit together without gaps. +*/ + +int ppmd_setlineclip ARGS(( int clip )); +#define ppmd_setlineclipping(x) ppmd_setlineclip(x) +/* Normally, ppmd_line() clips to the edges of the pixmap. You can use this +** routine to disable the clipping, for example if you are using a drawproc +** that wants to do its own clipping. +*/ + +void ppmd_line ARGS(( pixel** pixels, int cols, int rows, pixval maxval, int x0, int y0, int x1, int y1, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata )); +/* Draws a line from (x0, y0) to (x1, y1). +*/ + +void ppmd_spline3 ARGS(( pixel** pixels, int cols, int rows, pixval maxval, int x0, int y0, int x1, int y1, int x2, int y2, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata )); +/* Draws a three-point spline from (x0, y0) to (x2, y2), with (x1, y1) as +** the control point. All drawing is done via ppmd_line(), so the routines +** that control it control ppmd_spline3() as well. +*/ + +void ppmd_polyspline ARGS(( pixel** pixels, int cols, int rows, pixval maxval, int x0, int y0, int nc, int* xc, int* yc, int x1, int y1, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata )); +/* Draws a bunch of splines end to end. (x0, y0) and (x1, y1) are the initial +** and final points, and the xc and yc are the intermediate control points. +** nc is the number of these control points. +*/ + +void ppmd_circle ARGS(( pixel** pixels, int cols, int rows, pixval maxval, int cx, int cy, int radius, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata )); +/* Draws a circle centered at (cx, cy) with the specified radius. +*/ + + +/* Simple filling routines. Ok, so there's only one. */ + +void ppmd_filledrectangle ARGS(( pixel** pixels, int cols, int rows, pixval maxval, int x, int y, int width, int height, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata )); +/* Fills in the rectangle [x, y, width, height]. +*/ + + +/* Arbitrary filling routines. With these you can fill any outline that +** you can draw with the outline routines. +*/ + +char* ppmd_fill_init ARGS(( void )); +/* Returns a blank fillhandle. +*/ + +void ppmd_fill_drawproc ARGS(( pixel** pixels, int cols, int rows, pixval maxval, int x, int y, char* clientdata )); +/* Use this drawproc to trace the outline you want filled. Be sure to use +** the fillhandle as the clientdata. +*/ + +void ppmd_fill ARGS(( pixel** pixels, int cols, int rows, pixval maxval, char* fillhandle, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata )); +/* Once you've traced the outline, give the fillhandle to this routine to +** do the actual drawing. As usual, it takes a drawproc and clientdata; +** you could define drawprocs to do stipple fills and such. +*/ diff --git a/panda/src/pnm/rast.h b/panda/src/pnm/rast.h new file mode 100644 index 0000000000..85567ffe23 --- /dev/null +++ b/panda/src/pnm/rast.h @@ -0,0 +1,111 @@ +/* rast.h - header file for Sun raster files +** +** The format of a Sun raster file is as follows. First, a struct +** rasterfile. Note the 32-bit magic number at the beginning; this +** identifies the file type and lets you figure out whether you need +** to do little-endian / big-endian byte-swapping or not. (The PBMPLUS +** implementation does not do byte-swapping; instead, it reads all +** multi-byte values a byte at a time.) +** +** After the struct is an optional colormap. If ras_maptype is RMT_NONE, +** no map is present; if it's RMT_EQUAL_RGB then the map consists of +** three unsigned-char arrays ras_maplength long, one each for r g and b. +** I don't know what RMT_RAW means. Black and white bitmaps are stored +** as ras_maptype == RMT_NONE and ras_depth == 1, with the bits stored +** eight to a byte MSB first. +** +** Finally comes the image data. If ras_type is RT_OLD or RT_STANDARD, +** the data is just plain old uncompressed bytes, padded out to a multiple +** of 16 bits in each row. If ras_type is RT_BYTE_ENCODED, a run-length +** compression scheme is used: an escape-byte of 128 indicates a run; +** the next byte is a count, and the one after that is the byte to be +** replicated. The one exception to this is if the count is 1; then +** there is no third byte in the packet, it means to put a single 128 +** in the data stream. +*/ + +#ifndef _RAST_H_ +#define _RAST_H_ + +#define PIX_ERR -1 + +struct rasterfile { + long ras_magic; +#define RAS_MAGIC 0x59a66a95 + long ras_width; + long ras_height; + long ras_depth; + long ras_length; + long ras_type; +#define RT_OLD 0 /* Raw pixrect image in 68000 byte order */ +#define RT_STANDARD 1 /* Raw pixrect image in 68000 byte order */ +#define RT_BYTE_ENCODED 2 /* Run-length compression of bytes */ +#define RT_FORMAT_RGB 3 /* XRGB or RGB instead of XBGR or BGR */ +#define RT_FORMAT_TIFF 4 /* tiff <-> standard rasterfile */ +#define RT_FORMAT_IFF 5 /* iff (TAAC format) <-> standard rasterfile */ +#define RT_EXPERIMENTAL 0xffff /* Reserved for testing */ + long ras_maptype; +#define RMT_NONE 0 +#define RMT_EQUAL_RGB 1 +#define RMT_RAW 2 + long ras_maplength; + }; + +struct pixrectops { + int (*pro_rop)(); + int (*pro_stencil)(); + int (*pro_batchrop)(); + int (*pro_nop)(); + int (*pro_destroy)(); + int (*pro_get)(); + int (*pro_put)(); + int (*pro_vector)(); + struct pixrect* (*pro_region)(); + int (*pro_putcolormap)(); + int (*pro_getcolormap)(); + int (*pro_putattributes)(); + int (*pro_getattributes)(); + }; + +struct pr_size { + int x, y; + }; +struct pr_pos { + int x, y; + }; + +struct pixrect { + struct pixrectops* pr_ops; + struct pr_size pr_size; + int pr_depth; + struct mpr_data* pr_data; /* work-alike only handles memory pixrects */ + }; + +struct mpr_data { + int md_linebytes; + unsigned char* md_image; /* note, byte not short -- avoid pr_flip() */ + struct pr_pos md_offset; + short md_primary; + short md_flags; + }; + +typedef struct { + int type; + int length; + unsigned char* map[3]; + } colormap_t; + +/* And the routine definitions. */ + +struct pixrect* mem_create ARGS(( int w, int h, int depth )); +void mem_free ARGS(( struct pixrect* p )); + +int pr_dump ARGS(( struct pixrect* p, FILE* out, colormap_t* colormap, int type, int copy_flag )); + +int pr_load_header ARGS(( FILE* in, struct rasterfile* hP )); + +int pr_load_colormap ARGS(( FILE* in, struct rasterfile* hP, colormap_t* colormap )); + +struct pixrect* pr_load_image ARGS(( FILE* in, struct rasterfile* hP, colormap_t* colormap )); + +#endif /*_RAST_H_*/ diff --git a/panda/src/pnm/version.h b/panda/src/pnm/version.h new file mode 100644 index 0000000000..8a4ff05eab --- /dev/null +++ b/panda/src/pnm/version.h @@ -0,0 +1,4 @@ +/* version.h - define the current version of the package +*/ + +#define PBMPLUS_VERSION "Netpbm 1 March 1994" diff --git a/panda/src/pnmimage/Sources.pp b/panda/src/pnmimage/Sources.pp new file mode 100644 index 0000000000..d630cb1c26 --- /dev/null +++ b/panda/src/pnmimage/Sources.pp @@ -0,0 +1,21 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET pnmimage + #define LOCAL_LIBS \ + pnm linmath + + #define SOURCES \ + config_pnmimage.cxx config_pnmimage.h pnm-image-filter.cxx \ + pnmFileType.cxx pnmFileType.h pnmFileTypeRegistry.cxx \ + pnmFileTypeRegistry.h pnmImage.I pnmImage.cxx pnmImage.h \ + pnmImageHeader.I pnmImageHeader.cxx pnmImageHeader.h pnmReader.I \ + pnmReader.cxx pnmReader.h pnmWriter.I pnmWriter.cxx pnmWriter.h + + #define INSTALL_HEADERS \ + config_pnmimage.h pnmFileType.h pnmFileTypeRegistry.h pnmImage.I \ + pnmImage.h pnmImageHeader.I pnmImageHeader.h pnmReader.I \ + pnmReader.h pnmWriter.I pnmWriter.h pnmimage_base.h + +#end lib_target + diff --git a/panda/src/pnmimage/config_pnmimage.cxx b/panda/src/pnmimage/config_pnmimage.cxx new file mode 100644 index 0000000000..4a88eab3b9 --- /dev/null +++ b/panda/src/pnmimage/config_pnmimage.cxx @@ -0,0 +1,16 @@ +// Filename: config_pnmimage.cxx +// Created by: drose (19Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_pnmimage.h" +#include "pnmFileType.h" + +#include + +Configure(config_pnmimage); +NotifyCategoryDef(pnmimage, ""); + +ConfigureFn(config_pnmimage) { + PNMFileType::init_type(); +} diff --git a/panda/src/pnmimage/config_pnmimage.h b/panda/src/pnmimage/config_pnmimage.h new file mode 100644 index 0000000000..e6ab45104b --- /dev/null +++ b/panda/src/pnmimage/config_pnmimage.h @@ -0,0 +1,14 @@ +// Filename: config_pnmimage.h +// Created by: drose (19Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_PNMIMAGE_H +#define CONFIG_PNMIMAGE_H + +#include +#include + +NotifyCategoryDecl(pnmimage, EXPCL_PANDA, EXPTP_PANDA); + +#endif diff --git a/panda/src/pnmimage/pnm-image-alias.cxx b/panda/src/pnmimage/pnm-image-alias.cxx new file mode 100644 index 0000000000..131add90a3 --- /dev/null +++ b/panda/src/pnmimage/pnm-image-alias.cxx @@ -0,0 +1,178 @@ +// pnm-image-alias.cc +// +// PNMImage::ReadAlias() +// PNMImage::WriteAlias() + +#include + +#include "pnmImage.h" +#include "pnmReader.h" +#include "pnmReaderTypes.h" +#include "pnmWriter.h" +#include "pnmWriterTypes.h" + +// Since Alias image files don't have a magic number, we'll make a little +// sanity check on the size of the image. If either the width or height is +// larger than this, it must be bogus. +#define INSANE_SIZE 20000 + +inline unsigned short +read_ushort(FILE *file) { + unsigned short x; + return pm_readbigshort(file, (short *)&x)==0 ? x : 0; +} + +inline unsigned char +read_uchar(FILE *file) { + int x; + x = getc(file); + return (x!=EOF) ? (unsigned char)x : 0; +} + +inline void +write_ushort(FILE *file, unsigned short x) { + pm_writebigshort(file, (short)x); +} + +inline void +write_uchar(FILE *file, unsigned char x) { + putc(x, file); +} + +PNMReaderAlias:: +PNMReaderAlias(FILE *file, int already_read_magic) : PNMReader(file) { + if (already_read_magic >= 0) { + cols = already_read_magic; + } else { + cols = read_ushort(file); + } + rows = read_ushort(file); + + if (cols==0 || rows==0 || + cols>INSANE_SIZE || rows>INSANE_SIZE) { + valid = false; + return; + } + + read_ushort(file); + read_ushort(file); + + int bpp = read_ushort(file); + + switch (bpp) { + case 8: + color_type = PNMImage::Grayscale; + break; + + case 24: + color_type = PNMImage::Color; + break; + + default: + valid = false; + return; + } + + maxval = 255; +} + +bool PNMReaderAlias:: +ReadRow(xel *row, xelval *) { + int x; + int num; + unsigned char red, grn, blu; + + x = 0; + while (x < cols) { + num = read_uchar(file); + if (num==0 || x+num > cols) { + return false; + } + blu = read_uchar(file); + + if (color_type == PNMImage::Color) { + grn = read_uchar(file); + red = read_uchar(file); + while (num>0) { + PPM_ASSIGN(row[x], red, grn, blu); + x++; + num--; + } + } else { + while (num>0) { + PPM_PUTB(row[x], blu); + x++; + num--; + } + } + } + + return true; +} + +static unsigned char last_red = 0, last_blu = 0, last_grn = 0; +static int num_count = 0; + +static void +flush_color(FILE *file) { + if (num_count>0) { + write_uchar(file, num_count); + write_uchar(file, last_blu); + write_uchar(file, last_grn); + write_uchar(file, last_red); + num_count = 0; + } +} + +static void +write_color(FILE *file, + unsigned char red, unsigned char blu, unsigned char grn) { + if (red==last_red && blu==last_blu && grn==last_grn && num_count<0377) { + num_count++; + } else { + flush_color(file); + last_red = red; + last_grn = grn; + last_blu = blu; + num_count = 1; + } +} + +bool PNMWriterAlias:: +WriteHeader() { + write_ushort(file, cols); + write_ushort(file, rows); + + write_ushort(file, 0); + write_ushort(file, 0); + + // We'll always write full-color Alias images, even if the source was + // grayscale. Many programs don't seem to understand grayscale Alias images. + write_ushort(file, 24); + return true; +} + +bool PNMWriterAlias:: +WriteRow(xel *row_data, xelval *) { + int x; + unsigned char red, grn, blu; + + int is_grayscale = PNMImage::IsGrayscale(color_type); + + for (x = 0; x + +#include + +#include "pnmImage.h" + +// WorkType is an abstraction that allows the filtering process to be +// recompiled to use either floating-point or integer arithmetic. On SGI +// machines, there doesn't seem to be much of a performance difference-- +// if anything, the integer arithmetic is slower--though certainly other +// architectures may differ. + +// A StoreType is a numeric type that is used to store the intermediate values +// of the filtering computation; temporary calculations are performed using +// WorkTypes. source_max represents the largest value that will be stored in +// a StoreType, while filter_max represents the largest value that will +// multiplied by it as a weighted filter (source_max * filter_max must fit +// comfortably within a WorkType, with room to spare). + +// Floating-point arithmetic is slightly faster and slightly more precise, +// but the main reason to use it is that it is conceptually easy to understand. +// All values are scaled in the range 0..1, as they should be. + +// The biggest reason to use integer arithmetic is space. A table of +// StoreTypes must be allocated to match the size of the image. On an SGI, +// sizeof(float) is 4, while sizeof(short) is 2 and sizeof(char) is, of +// course, 1. Since the source precision is probably 8 bits anyway (there +// are usually 8 bits per channel), it doesn't cost any precision at all to +// use shorts, and not very much to use chars. + +/* +// To use double-precision floating point, 8 bytes: (strictly for the neurotic) +typedef double WorkType; +typedef double StoreType; +static const WorkType source_max = 1.0; +static const WorkType filter_max = 1.0; +*/ + +// To use single-precision floating point, 4 bytes: +typedef double WorkType; +typedef float StoreType; +static const WorkType source_max = 1.0; +static const WorkType filter_max = 1.0; + +/* +// To use 16-bit integer arithmetic, 2 bytes: +typedef unsigned long WorkType; +typedef unsigned short StoreType; +static const WorkType source_max = 65535; +static const WorkType filter_max = 255; +*/ + +/* +// To use 8-bit integer arithmetic, 1 byte: +typedef unsigned long WorkType; +typedef unsigned char StoreType; +static const WorkType source_max = 255; +static const WorkType filter_max = 255; +*/ + + + +// filter_row() filters a single row by convolving with a one-dimensional +// kernel filter. The kernel is defined by an array of weights in filter[], +// where the ith element of filter corresponds to abs(d * scale), if scale>1.0, +// and abs(d), if scale<=1.0, where d is the offset from the center and varies +// from -filter_width to filter_width. + +// Note that filter_width is not necessarily the length of the array; it is +// the radius of interest of the filter function. The array may need to be +// larger (by a factor of scale), to adequately cover all the values. + +static void +filter_row(StoreType dest[], int dest_len, + const StoreType source[], int source_len, + double scale, // == dest_len / source_len + const WorkType filter[], + double filter_width) { + // If we are expanding the row (scale>1.0), we need to look at a fractional + // granularity. Hence, we scale our filter index by scale. If we are + // compressing (scale<1.0), we don't need to fiddle with the filter index, so + // we leave it at one. + double iscale = max(scale, 1.0); + + // Similarly, if we are expanding the row, we want to start the new row at + // the far left edge of the original pixel, not in the center. So we will + // have a non-zero offset. + int offset = (int)floor(iscale/2.0); + + for (int dest_x=0; dest_x0) { + dest[dest_x] = (StoreType)(net_value / net_weight); + } else { + dest[dest_x] = 0; + } + } +} + + +// The various filter functions are called before each axis scaling to build +// an kernel array suitable for the given scaling factor. Given a scaling +// ratio of the axis (dest_len / source_len), and a width parameter supplied +// by the user, they must build an array of filter values (described above) +// and also set the radius of interest of the filter function. + +// The values of the elements of filter must completely cover the range +// 0..filter_max; the array must have enough elements to include all indices +// corresponding to values in the range -filter_width to filter_width. + +typedef void FilterFunction(double scale, double width, + WorkType *&filter, double &filter_width); + +static void +box_filter_impl(double scale, double width, + WorkType *&filter, double &filter_width) { + double fscale; + if (scale < 1.0) { + // If we are compressing the image, we want to expand the range of + // the filter function to prevent dropping below the Nyquist rate. + // Hence, we divide by scale. + fscale = 1.0 / scale; + } else { + + // If we are expanding the image, we want to increase the granularity + // of the filter function since we will need to access fractional cel + // values. Hence, we multiply by scale. + fscale = scale; + } + filter_width = width; + int actual_width = (int)ceil((filter_width+1) * fscale); + + filter = new WorkType[actual_width]; + + for (int i=0; ix_last) { + box_filter_xel(image, x, y, x1-(double)x_last, y_contrib, + red, grn, blu, alpha, pixel_count); + } + } +} + +static void +box_filter_region(const PNMImage &image, + double x0, double y0, double x1, double y1, + xel &result, xelval &alpha_result) { + double red = 0.0, grn = 0.0, blu = 0.0, alpha = 0.0; + double pixel_count = 0.0; + + int y = (int)floor(y0); + // Get the first (partial) row + box_filter_line(image, x0, y, x1, (double)(y+1)-y0, + red, grn, blu, alpha, pixel_count); + + int y_last = (int)floor(y1); + if (y < y_last) { + y++; + while (y < y_last) { + // Get each consecutive (complete) row + box_filter_line(image, x0, y, x1, 1.0, + red, grn, blu, alpha, pixel_count); + y++; + } + + // Get the final (partial) row + if (y1>y_last) { + box_filter_line(image, x0, y, x1, y1-(double)y_last, + red, grn, blu, alpha, pixel_count); + } + } + + PPM_ASSIGN(result, + (xelval)(red / pixel_count), + (xelval)(grn / pixel_count), + (xelval)(blu / pixel_count)); + + alpha_result = (xelval)(alpha / pixel_count); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::quick_filter_from +// Access: Public +// Description: Resizes from the given image, with a fixed radius of +// 0.5. This is a very specialized and simple algorithm +// that doesn't handle dropping below the Nyquist rate +// very well, but is quite a bit faster than the more +// general box_filter(), above. If borders are +// specified, they will further restrict the size of the +// resulting image. There's no point in using +// quick_box_filter() on a single image. +//////////////////////////////////////////////////////////////////// +void PNMImage:: +quick_filter_from(const PNMImage &from, int xborder, int yborder) { + int from_xs = from.get_x_size(); + int from_ys = from.get_y_size(); + + int to_xs = get_x_size() - xborder; + int to_ys = get_y_size() - yborder; + + int to_xoff = xborder / 2; + int to_yoff = yborder / 2; + + double from_x0, from_x1, from_y0, from_y1; + int to_x, to_y; + + double x_scale = (double)from_xs / (double)to_xs; + double y_scale = (double)from_ys / (double)to_ys; + + from_y0 = max(0, -to_yoff) * y_scale; + for (to_y = max(0, -to_yoff); + to_y < min(to_ys, get_y_size()-to_yoff); + to_y++) { + from_y1 = (to_y+1) * y_scale; + + from_x0 = max(0, -to_xoff) * x_scale; + for (to_x = max(0, -to_xoff); + to_x < min(to_xs, get_x_size()-to_xoff); + to_x++) { + from_x1 = (to_x+1) * x_scale; + + // Now the box from (from_x0, from_y0) - (from_x1, from_y1) + // but not including (from_x1, from_y1) maps to the pixel (to_x, to_y). + xelval alpha_result; + box_filter_region(from, + from_x0, from_y0, from_x1, from_y1, + (*this)[to_yoff + to_y][to_xoff + to_x], + alpha_result); + if (has_alpha()) { + set_alpha_val(to_xoff+to_x, to_yoff+to_y, alpha_result); + } + + from_x0 = from_x1; + } + from_y0 = from_y1; + } +} diff --git a/panda/src/pnmimage/pnm-image-img.cxx b/panda/src/pnmimage/pnm-image-img.cxx new file mode 100644 index 0000000000..a33aff2360 --- /dev/null +++ b/panda/src/pnmimage/pnm-image-img.cxx @@ -0,0 +1,103 @@ +// pnm-image-img.cc +// +// PNMImage::ReadImg() +// PNMImage::WriteImg() + +#include + +#include "pnmImage.h" +#include "pnmReader.h" +#include "pnmReaderTypes.h" +#include "pnmWriter.h" +#include "pnmWriterTypes.h" + +// Since raw image files don't have a magic number, we'll make a little +// sanity check on the size of the image. If either the width or height is +// larger than this, it must be bogus. +#define INSANE_SIZE 20000 + + +inline unsigned long +read_ulong(FILE *file) { + unsigned long x; + return pm_readbiglong(file, (long *)&x)==0 ? x : 0; +} + +inline unsigned short +read_ushort(FILE *file) { + unsigned short x; + return pm_readbigshort(file, (short *)&x)==0 ? x : 0; +} + +inline unsigned char +read_uchar(FILE *file) { + int x; + x = getc(file); + return (x!=EOF) ? (unsigned char)x : 0; +} + +inline void +write_ulong(FILE *file, unsigned long x) { + pm_writebiglong(file, (long)x); +} + +inline void +write_uchar(FILE *file, unsigned char x) { + putc(x, file); +} + +PNMReaderIMG:: +PNMReaderIMG(FILE *file, int already_read_magic) : PNMReader(file) { + if (already_read_magic>=0) { + cols = (already_read_magic << 16) | read_ushort(file); + } else { + cols = (int)read_ulong(file); + } + + rows = (int)read_ulong(file); + color_type = PNMImage::Color; + + if (cols==0 || rows==0 || + cols>INSANE_SIZE || rows>INSANE_SIZE) { + valid = false; + return; + } + + maxval = 255; +} + +bool PNMReaderIMG:: +ReadRow(xel *row, xelval *) { + int x; + xelval red, grn, blu; + for (x = 0; x + +#include +#include +#ifndef WIN32VC +#ifndef PENV_WIN32 +#include +#endif +#endif + +#include "pnmImage.h" +#include "pnmReader.h" +#include "pnmReaderTypes.h" +#include "pnmWriter.h" +#include "pnmWriterTypes.h" + +extern "C" { + #include "color.h" + #include "resolu.h" + + void setcolrgam(double); + int checkheader(FILE *, char *, FILE *); + int fgetresolu(int *, int *, FILE *); + int freadcolrs(COLR *, int, FILE *); + int fwritecolrs(COLR *, unsigned, FILE *); + void fputformat(char *, FILE *); + void shiftcolrs(COLR *, int, int); + void colrs_gambs(COLR *, int); + void newheader(char *, FILE *); + void printargs(int, char **, FILE *); + void gambs_colrs(COLR *, int); +} + +double gamcor = 2.2; /* gamma correction */ + +int bradj = 0; /* brightness adjustment */ + +static const int COLR_MAX = 255; + + +PNMReaderRadiance:: +PNMReaderRadiance(FILE *file, int already_read_magic) : PNMReader(file) { + setcolrgam(gamcor); /* set up gamma correction */ + + if (already_read_magic >= 0) { + ungetc(already_read_magic >> 8, file); + ungetc(already_read_magic & 0xff, file); + } + + /* get our header */ + if (checkheader(file, COLRFMT, NULL) < 0 || + fgetresolu(&cols, &rows, file) < 0) { + valid = false; + return; + } + + color_type = PNMImage::Color; + maxval = COLR_MAX; +} + +bool PNMReaderRadiance:: +ReadRow(xel *row_data, xelval *) { + COLR *scanin; + int x; + + scanin = (COLR *)alloca(cols * sizeof(COLR)); + + if (freadcolrs(scanin, cols, file) < 0) { + return false; + } + + if (bradj) { /* adjust exposure */ + shiftcolrs(scanin, cols, bradj); + } + + colrs_gambs(scanin, cols); /* gamma correction */ + + for (x = 0; x < cols; x++) { + PPM_ASSIGN(row_data[x], scanin[x][RED], scanin[x][GRN], scanin[x][BLU]); + } + + return true; +} + +bool PNMWriterRadiance:: +WriteHeader() { + setcolrgam(gamcor); /* set up gamma correction */ + + /* put our header */ + newheader("RADIANCE", file); + fputs("Generated via DRR's pnm-image library\n", file); + fputformat(COLRFMT, file); + putc('\n', file); + fprtresolu(cols, rows, file); + + return true; +} + +bool PNMWriterRadiance:: +WriteRow(xel *row_data, xelval *) { + /* convert file */ + int is_grayscale = PNMImage::IsGrayscale(color_type); + + COLR *scanout; + int x; + + /* allocate scanline */ + scanout = (COLR *)alloca(cols * sizeof(COLR)); + + /* convert image */ + for (x = 0; x < cols; x++) { + if (is_grayscale) { + scanout[x][RED] = + scanout[x][GRN] = + scanout[x][BLU] = (BYTE)((int)COLR_MAX * PPM_GETB(row_data[x]) / maxval); + } else { + scanout[x][RED] = (BYTE)((int)COLR_MAX * PPM_GETR(row_data[x]) / maxval); + scanout[x][GRN] = (BYTE)((int)COLR_MAX * PPM_GETG(row_data[x]) / maxval); + scanout[x][BLU] = (BYTE)((int)COLR_MAX * PPM_GETB(row_data[x]) / maxval); + } + } + + /* undo gamma */ + gambs_colrs(scanout, cols); + if (bradj) /* adjust exposure */ + shiftcolrs(scanout, cols, bradj); + if (fwritecolrs(scanout, cols, file) < 0) + return false; + + return true; +} diff --git a/panda/src/pnmimage/pnm-image-readbmp.cxx b/panda/src/pnmimage/pnm-image-readbmp.cxx new file mode 100644 index 0000000000..e02b086a87 --- /dev/null +++ b/panda/src/pnmimage/pnm-image-readbmp.cxx @@ -0,0 +1,518 @@ +// pnm-image-readbmp.cc +// +// PNMImage::ReadBMP() and supporting functions. + + + +// Much code in this file is borrowed from Netpbm, specifically bmptoppm.c. +/*\ + * $Id$ + * + * bmptoppm.c - Converts from a Microsoft Windows or OS/2 .BMP file to a + * PPM file. + * + * The current implementation is probably not complete, but it works for + * all the BMP files I have. I welcome feedback. + * + * Copyright (C) 1992 by David W. Sanderson. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. This software is provided "as is" + * without express or implied warranty. + * + * $Log$ + * Revision 1.1 2000/10/04 01:14:42 drose + * Initial revision + * + * Revision 1.10 1992/11/24 19:38:17 dws + * Added code to verify that reading occurred at the correct offsets. + * Added copyright. + * + * Revision 1.9 1992/11/17 02:15:24 dws + * Changed to include bmp.h. + * Eliminated need for fseek(), and therefore the need for a + * temporary file. + * + * Revision 1.8 1992/11/13 23:48:57 dws + * Made definition of Seekable() static, to match its prototype. + * + * Revision 1.7 1992/11/11 00:17:50 dws + * Generalized to use bitio routines. + * + * Revision 1.6 1992/11/10 23:51:44 dws + * Enhanced command-line handling. + * + * Revision 1.5 1992/11/08 00:38:46 dws + * Changed some names to help w/ addition of ppmtobmp. + * + * Revision 1.4 1992/10/27 06:28:28 dws + * Corrected stupid typo. + * + * Revision 1.3 1992/10/27 06:17:10 dws + * Removed a magic constant value. + * + * Revision 1.2 1992/10/27 06:09:58 dws + * Made stdin seekable. + * + * Revision 1.1 1992/10/27 05:31:41 dws + * Initial revision +\*/ + +#ifndef WIN32VC +#ifndef PENV_WIN32 +#include +#endif +#endif + +#include "pnmImage.h" +#include "pnmReader.h" +#include "pnmReaderTypes.h" + +extern "C" { + #include "../pnm/bmp.h" + #include "../pnm/bitio.h" +} + +static char *ifname; + +/* + * Utilities + */ + +static int GetByte ARGS((FILE * fp)); +static short GetShort ARGS((FILE * fp)); +static long GetLong ARGS((FILE * fp)); +static void readto ARGS((FILE *fp, unsigned long *ppos, unsigned long dst)); +static void BMPreadfileheader ARGS((FILE *fp, unsigned long *ppos, + unsigned long *poffBits)); +static void BMPreadinfoheader ARGS((FILE *fp, unsigned long *ppos, + unsigned long *pcx, unsigned long *pcy, unsigned short *pcBitCount, + int *pclassv)); +static int BMPreadrgbtable ARGS((FILE *fp, unsigned long *ppos, + unsigned short cBitCount, int classv, pixval *R, pixval *G, pixval *B)); +static int BMPreadrow ARGS((FILE *fp, unsigned long *ppos, pixel *row, + unsigned long cx, unsigned short cBitCount, pixval *R, pixval *G, pixval *B)); +static pixel ** BMPreadbits ARGS((FILE *fp, unsigned long *ppos, + unsigned long offBits, unsigned long cx, unsigned long cy, + unsigned short cBitCount, int classv, pixval *R, pixval *G, pixval *B)); + +static char er_read[] = "%s: read error"; +static char er_seek[] = "%s: seek error"; + +static int +GetByte(FILE *fp) +{ + int v; + + if ((v = getc(fp)) == EOF) + { + pm_error(er_read, ifname); + } + + return v; +} + +static short +GetShort(FILE *fp) +{ + short v; + + if (pm_readlittleshort(fp, &v) == -1) + { + pm_error(er_read, ifname); + } + + return v; +} + +static long +GetLong(FILE *fp) +{ + long v; + + if (pm_readlittlelong(fp, &v) == -1) + { + pm_error(er_read, ifname); + } + + return v; +} + +/* + * readto - read as many bytes as necessary to position the + * file at the desired offset. + */ + +static void +readto(FILE *fp, + unsigned long *ppos, /* pointer to number of bytes read from fp */ + unsigned long dst) +{ + unsigned long pos; + + if(!fp || !ppos) + return; + + pos = *ppos; + + if(pos > dst) + pm_error("%s: internal error in readto()", ifname); + + for(; pos < dst; pos++) + { + if (getc(fp) == EOF) + { + pm_error(er_read, ifname); + } + } + + *ppos = pos; +} + + +/* + * BMP reading routines + */ + +static void +BMPreadfileheader( + FILE *fp, + unsigned long *ppos, /* number of bytes read from fp */ + unsigned long *poffBits) +{ + /* + unsigned long cbSize; + unsigned short xHotSpot; + unsigned short yHotSpot; + */ + unsigned long offBits; + + /* + We've already read the magic number. + if (GetByte(fp) != 'B') + { + pm_error("%s is not a BMP file", ifname); + } + if (GetByte(fp) != 'M') + { + pm_error("%s is not a BMP file", ifname); + } + */ + + /* cbSize = */ GetLong(fp); + /* xHotSpot = */ GetShort(fp); + /* yHotSpot = */ GetShort(fp); + offBits = GetLong(fp); + + *poffBits = offBits; + + *ppos += 14; +} + +static void +BMPreadinfoheader( + FILE *fp, + unsigned long *ppos, /* number of bytes read from fp */ + unsigned long *pcx, + unsigned long *pcy, + unsigned short *pcBitCount, + int *pclassv) +{ + unsigned long cbFix; + unsigned short cPlanes; + + unsigned long cx; + unsigned long cy; + unsigned short cBitCount; + int classv; + + cbFix = GetLong(fp); + + switch (cbFix) + { + case 12: + classv = C_OS2; + + cx = GetShort(fp); + cy = GetShort(fp); + cPlanes = GetShort(fp); + cBitCount = GetShort(fp); + + break; + case 40: + classv = C_WIN; + + cx = GetLong(fp); + cy = GetLong(fp); + cPlanes = GetShort(fp); + cBitCount = GetShort(fp); + + /* + * We've read 16 bytes so far, need to read 24 more + * for the required total of 40. + */ + + GetLong(fp); + GetLong(fp); + GetLong(fp); + GetLong(fp); + GetLong(fp); + GetLong(fp); + + break; + default: + pm_error("%s: unknown cbFix: %d", ifname, cbFix); + break; + } + + if (cPlanes != 1) + { + pm_error("%s: don't know how to handle cPlanes = %d" + ,ifname + ,cPlanes); + } + + switch (classv) + { + case C_WIN: + pm_message("Windows BMP, %dx%dx%d" + ,cx + ,cy + ,cBitCount); + break; + case C_OS2: + pm_message("OS/2 BMP, %dx%dx%d" + ,cx + ,cy + ,cBitCount); + break; + } + +#ifdef DEBUG + pm_message("cbFix: %d", cbFix); + pm_message("cx: %d", cx); + pm_message("cy: %d", cy); + pm_message("cPlanes: %d", cPlanes); + pm_message("cBitCount: %d", cBitCount); +#endif + + *pcx = cx; + *pcy = cy; + *pcBitCount = cBitCount; + *pclassv = classv; + + *ppos += cbFix; +} + +/* + * returns the number of bytes read, or -1 on error. + */ +static int +BMPreadrgbtable( + FILE *fp, + unsigned long *ppos, /* number of bytes read from fp */ + unsigned short cBitCount, + int classv, + pixval *R, + pixval *G, + pixval *B) +{ + int i; + int nbyte = 0; + + long ncolors = (1 << cBitCount); + + for (i = 0; i < ncolors; i++) + { + B[i] = (pixval) GetByte(fp); + G[i] = (pixval) GetByte(fp); + R[i] = (pixval) GetByte(fp); + nbyte += 3; + + if (classv == C_WIN) + { + (void) GetByte(fp); + nbyte++; + } + } + + *ppos += nbyte; + return nbyte; +} + +/* + * returns the number of bytes read, or -1 on error. + */ +static int +BMPreadrow( + FILE *fp, + unsigned long *ppos, /* number of bytes read from fp */ + pixel *row, + unsigned long cx, + unsigned short cBitCount, + int indexed, + pixval *R, + pixval *G, + pixval *B) +{ + BITSTREAM b; + unsigned nbyte = 0; + int rc; + unsigned x; + + if (indexed) { + if ((b = pm_bitinit(fp, "r")) == (BITSTREAM) 0) + { + return -1; + } + } + + for (x = 0; x < cx; x++, row++) + { + unsigned long v; + + if (!indexed) { + int r, g, b; + b = GetByte(fp); + g = GetByte(fp); + r = GetByte(fp); + nbyte += 3; + PPM_ASSIGN(*row, r, g, b); + } else { + if ((rc = pm_bitread(b, cBitCount, &v)) == -1) + { + return -1; + } + nbyte += rc; + + PPM_ASSIGN(*row, R[v], G[v], B[v]); + } + } + + if (indexed) { + if ((rc = pm_bitfini(b)) != 0) + { + return -1; + } + } + + /* + * Make sure we read a multiple of 4 bytes. + */ + while (nbyte % 4) + { + GetByte(fp); + nbyte++; + } + + *ppos += nbyte; + return nbyte; +} + +static void +BMPreadbits(xel *array, + FILE *fp, + unsigned long *ppos, /* number of bytes read from fp */ + unsigned long offBits, + unsigned long cx, + unsigned long cy, + unsigned short cBitCount, + int /* classv */, + int indexed, + pixval *R, + pixval *G, + pixval *B) +{ + long y; + + readto(fp, ppos, offBits); + + if(cBitCount > 24) + { + pm_error("%s: cannot handle cBitCount: %d" + ,ifname + ,cBitCount); + } + + /* + * The picture is stored bottom line first, top line last + */ + + for (y = (long)cy - 1; y >= 0; y--) + { + int rc; + rc = BMPreadrow(fp, ppos, array + y*cx, cx, cBitCount, indexed, R, G, B); + if(rc == -1) + { + pm_error("%s: couldn't read row %d" + ,ifname + ,y); + } + if(rc%4) + { + pm_error("%s: row had bad number of bytes: %d" + ,ifname + ,rc); + } + } + +} + +PNMReaderBMP:: +PNMReaderBMP(FILE *file, int already_read_magic) : PNMReader(file) { + int rc; + + unsigned long cx; + unsigned long cy; + + if (already_read_magic < 0) { + // Skip the magic number. + GetByte(file); + GetByte(file); + } + + pos = 0; + + BMPreadfileheader(file, &pos, &offBits); + BMPreadinfoheader(file, &pos, &cx, &cy, &cBitCount, &classv); + + if (offBits != BMPoffbits(classv, cBitCount)) { + pm_message("warning: offBits is %d, expected %d", + offBits, + BMPoffbits(classv, cBitCount)); + } + + indexed = false; + + if (cBitCount <= 8) { + indexed = true; + rc = BMPreadrgbtable(file, &pos, cBitCount, classv, R, G, B); + + if (rc != BMPlenrgbtable(classv, cBitCount)) { + pm_message("warning: %d-byte RGB table, expected %d bytes", + rc, + BMPlenrgbtable(classv, cBitCount)); + } + } + + color_type = Color; + cols = (int)cx; + rows = (int)cy; + maxval = 255; +} + +int PNMReaderBMP:: +ReadData(xel *array, xelval *) { + BMPreadbits(array, file, &pos, offBits, cols, rows, + cBitCount, classv, indexed, R, G, B); + + if (pos != BMPlenfile(classv, cBitCount, cols, rows)) { + pm_message("warning: read %d bytes, expected to read %d bytes", + pos, BMPlenfile(classv, cBitCount, cols, rows)); + } + + return rows; +} diff --git a/panda/src/pnmimage/pnm-image-readsgi.cxx b/panda/src/pnmimage/pnm-image-readsgi.cxx new file mode 100644 index 0000000000..d5d41f550e --- /dev/null +++ b/panda/src/pnmimage/pnm-image-readsgi.cxx @@ -0,0 +1,434 @@ +// pnm-image-readsgi.cc +// +// PNMImage::ReadSGI() and supporting functions. + + + +// Much code in this file is borrowed from Netpbm, specifically sgitopnm.c. + +/* sgitopnm.c - read an SGI image and and produce a portable anymap +** +** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) +** +** Based on the SGI image description v0.9 by Paul Haeberli (paul@sgi.comp) +** Available via ftp from sgi.com:graphics/SGIIMAGESPEC +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +** +** 29Jan94: first version +** 08Feb94: minor bugfix +*/ + +#ifndef PENV_WIN32 + #include +#endif + +#include "pnmImage.h" +#include "pnmReader.h" +#include "pnmReaderTypes.h" +#include "../pnm/sgi.h" +#include + +/* entry in RLE offset table */ +typedef PNMReaderSGI::TabEntry TabEntry; + +typedef short ScanElem; +typedef ScanElem * ScanLine; + +/* prototypes */ +static unsigned char get_byte ( FILE* f ); +static long get_big_long (FILE *f); +static short get_big_short (FILE *f); +static short get_byte_as_short (FILE *f); +static int readerr (FILE *f); +static void * xmalloc (int bytes); +#define MALLOC(n, type) (type *)xmalloc((n) * sizeof(type)) +//static char * compression_name (char compr); +static void read_bytes (FILE *ifp, int n, char *buf); +static void read_header(FILE *ifp, Header *head, int already_read_magic); +static TabEntry * read_table (FILE *ifp, int tablen); +static void read_channel (FILE *ifp, int xsize, int ysize, + int zsize, int bpc, TabEntry *table, + ScanElem *channel_data, long table_start, + int channel, int row); +static void rle_decompress (ScanElem *src, long srclen, ScanElem *dest, long destlen); + +#define WORSTCOMPR(x) (2*(x) + 2) + +#define MAXVAL_BYTE 255 +#define MAXVAL_WORD 65535 + +static int eof_err = false; + +PNMReaderSGI:: +PNMReaderSGI(FILE *file, int already_read_magic) : PNMReader(file) { + eof_err = false; + + table = NULL; + Header head; + + read_header(file, &head, already_read_magic); + + long pixmax = (head.bpc == 1) ? MAXVAL_BYTE : MAXVAL_WORD; + if( pixmax > PNM_MAXMAXVAL ) + pm_error("pixel values too large - try reconfiguring with PGM_BIGGRAYS\n or without PPM_PACKCOLORS"); + + maxval = (xelval)pixmax; + + table_start = ftell(file); + if( head.storage != STORAGE_VERBATIM ) + table = read_table(file, head.ysize * head.zsize); + + cols = head.xsize; + rows = head.ysize; + zsize = head.zsize; + bpc = head.bpc; + + switch (zsize) { + case 1: + color_type = PNMImage::Grayscale; + break; + + case 2: + color_type = PNMImage::TwoChannel; + break; + + case 3: + color_type = PNMImage::Color; + break; + + case 4: + color_type = PNMImage::FourChannel; + break; + + default: + pm_error("Can't happen."); + } + + current_row = rows-1; +} + +bool PNMReaderSGI:: +ReadRow(xel *row_data, xelval *alpha_data) { + nassertr(current_row >= 0, false); + + int is_grayscale = PNMImage::IsGrayscale(color_type); + int has_alpha = PNMImage::HasAlpha(color_type); + int col; + + ScanElem *red = (ScanElem *)alloca(cols * sizeof(ScanElem)); + ScanElem *grn = (ScanElem *)alloca(cols * sizeof(ScanElem)); + ScanElem *blu = (ScanElem *)alloca(cols * sizeof(ScanElem)); + ScanElem *alpha = (ScanElem *)alloca(cols * sizeof(ScanElem)); + + read_channel(file, cols, rows, zsize, bpc, table, red, table_start, + 0, current_row); + + if (!is_grayscale) { + read_channel(file, cols, rows, zsize, bpc, table, grn, table_start, + 1, current_row); + read_channel(file, cols, rows, zsize, bpc, table, blu, table_start, + 2, current_row); + } + + if (has_alpha) { + read_channel(file, cols, rows, zsize, bpc, table, alpha, table_start, + zsize-1, current_row); + } + + for ( col = 0; col < cols; col++ ) { + if (is_grayscale) { + PPM_PUTB(row_data[col], (xelval)red[col]); + } else { + xelval r, g, b; + r = (xelval)red[col]; + g = (xelval)grn[col]; + b = (xelval)blu[col]; + PPM_ASSIGN(row_data[col], r, g, b); + } + + if (has_alpha) { + alpha_data[col] = (xelval)alpha[col]; + } + } + current_row--; + return true; +} + +PNMReaderSGI:: +~PNMReaderSGI() { + if (table != NULL) { + free(table); + } +} + + + +static void +read_header(FILE *ifp, Header *head, int already_read_magic) { + if (already_read_magic >= 0) { + head->magic = (short)already_read_magic; + } else { + head->magic = get_big_short(ifp); + } + + head->storage = get_byte(ifp); + head->bpc = get_byte(ifp); + head->dimension = get_big_short(ifp); + head->xsize = get_big_short(ifp); + head->ysize = get_big_short(ifp); + head->zsize = get_big_short(ifp); + head->pixmin = get_big_long(ifp); + head->pixmax = get_big_long(ifp); + read_bytes(ifp, 4, head->dummy1); + read_bytes(ifp, 80, head->name); + head->colormap = get_big_long(ifp); + read_bytes(ifp, 404, head->dummy2); + + if( head->magic != SGI_MAGIC ) + pm_error("bad magic number - not an SGI image"); + if( head->storage != 0 && head->storage != 1 ) + pm_error("unknown compression type"); + if( head->bpc < 1 || head->bpc > 2 ) + pm_error("illegal precision value %d (only 1-2 allowed)", head->bpc ); + if( head->colormap != CMAP_NORMAL ) + pm_message("unsupported non-normal pixel data (%d)", + head->colormap); + + /* adjust ysize/zsize to dimension, just to be sure */ + switch( head->dimension ) { + case 1: + head->ysize = 1; + break; + case 2: + head->zsize = 1; + break; + case 3: + switch( head->zsize ) { + case 1: + case 2: + head->dimension = 2; + break; + case 3: + case 4: + break; + + default: + pm_message("%d-channel image, using only first 4 channels", head->zsize); + head->zsize = 4; + break; + } + break; + default: + pm_error("illegal dimension value %d (only 1-3 allowed)", head->dimension); + } + +#ifdef DEBUG + fprintf(stderr, "raster size %dx%d, %d channels\n", head->xsize, head->ysize, head->zsize); + fprintf(stderr, "compression: %d = %s\n", head->storage, compression_name(head->storage)); + head->name[79] = '\0'; /* just to be safe */ + fprintf(stderr, "Image name: \"%s\"\n", head->name); + fprintf(stderr, "bpc: %d dimension: %d zsize: %d\n", head->bpc, head->dimension, head->zsize); + fprintf(stderr, "pixmin: %ld pixmax: %ld colormap: %ld\n", head->pixmin, head->pixmax, head->colormap); +#endif +} + + +static TabEntry * +read_table(FILE *ifp, int tablen) { + TabEntry *table; + int i; + + table = MALLOC(tablen, TabEntry); + + for( i = 0; i < tablen; i++ ) + table[i].start = get_big_long(ifp); + for( i = 0; i < tablen; i++ ) + table[i].length = get_big_long(ifp); + + return table; +} + + + +static void +read_channel(FILE *ifp, + int xsize, int ysize, int, int bpc, + TabEntry *table, + ScanElem *channel_data, long table_start, + int channel, int row) { + ScanElem *temp; + int sgi_index, i; + long offset, length; + + short (*func)(FILE *); + func = (bpc==1) ? get_byte_as_short : get_big_short; + + if ( table ) + temp = (ScanElem *)alloca(WORSTCOMPR(xsize) * sizeof(ScanElem)); + + sgi_index = channel * ysize + row; + if( table ) { + offset = table[sgi_index].start; + length = table[sgi_index].length; + if( bpc == 2 ) + length /= 2; /* doc says length is in bytes, we are reading words */ + if( fseek(ifp, offset, SEEK_SET) != 0 ) + pm_error("seek error for offset %ld", offset); + + for( i = 0; i < length; i++ ) + temp[i] = (*func)(ifp); + + rle_decompress(temp, length, channel_data, xsize); + } + else { + offset = sgi_index * xsize + table_start; + if( fseek(ifp, offset, SEEK_SET) != 0 ) + pm_error("seek error for offset %ld", offset); + for( i = 0; i < xsize; i++ ) + channel_data[i] = (*func)(ifp); + } +} + + + +static void +rle_decompress(ScanElem *src, + long srcleft, + ScanElem *dest, + long destleft) { + int count; + unsigned char el; + + while( srcleft ) { + el = (unsigned char)(*src++ & 0xff); + --srcleft; + count = (int)(el & 0x7f); + + if( count == 0 ) + return; + if( destleft < count ) + pm_error("RLE error: too much input data (space left %d, need %d)", destleft, count); + destleft -= count; + if( el & 0x80 ) { + if( srcleft < count ) + pm_error("RLE error: not enough data for literal run (data left %d, need %d)", srcleft, count); + srcleft -= count; + while( count-- ) + *dest++ = *src++; + } + else { + if( srcleft == 0 ) + pm_error("RLE error: not enough data for replicate run"); + while( count-- ) + *dest++ = *src; + ++src; + --srcleft; + } + } + pm_error("RLE error: no terminating 0-byte"); +} + + +/* basic I/O functions, taken from ilbmtoppm.c */ + +static short +get_big_short(FILE *ifp) { + short s; + + if( pm_readbigshort(ifp, &s) == -1 ) + s = readerr(ifp); + + return s; +} + +static long +get_big_long(FILE *ifp) { + long l; + + if( pm_readbiglong(ifp, &l) == -1 ) + l = readerr(ifp); + + return l; +} + +static unsigned char +get_byte(FILE *ifp) { + int i; + + i = getc(ifp); + if( i == EOF ) + i = readerr(ifp); + + return (unsigned char) i; +} + + +static int +readerr(FILE *f) { + // This will return only if the error is EOF. + if( ferror(f) ) + pm_error("read error"); + + static FILE *last_eof = NULL; + + if (!eof_err) { + fprintf(stderr, "Warning: premature EOF on file\n"); + eof_err = true; + } + + return 0; +} + + +static void +read_bytes(FILE *ifp, + int n, + char *buf) { + int r; + + r = fread((void *)buf, 1, n, ifp); + if( r != n ) { + readerr(ifp); + memset(buf+r, 0, n-r); + } +} + + +static short +get_byte_as_short(FILE *ifp) { + return (short)get_byte(ifp); +} + + +static void * +xmalloc(int bytes) { + void *mem; + + if( bytes == 0 ) + return NULL; + + mem = malloc(bytes); + if( mem == NULL ) + pm_error("out of memory allocating %d bytes", bytes); + return mem; +} + +/* +static char * +compression_name(char compr) { + switch( compr ) { + case STORAGE_VERBATIM: + return "none"; + case STORAGE_RLE: + return "RLE"; + default: + return "unknown"; + } +} +*/ diff --git a/panda/src/pnmimage/pnm-image-readyuv.cxx b/panda/src/pnmimage/pnm-image-readyuv.cxx new file mode 100644 index 0000000000..f27e883ad9 --- /dev/null +++ b/panda/src/pnmimage/pnm-image-readyuv.cxx @@ -0,0 +1,114 @@ +// pnm-image-readyuv.cc +// +// PNMImage::ReadYUV() and supporting functions. + + + +// Much code in this file is borrowed from Netpbm, specifically yuvtoppm.c. +/* yuvtoppm.c - convert Abekas YUV bytes into a portable pixmap +** +** by Marc Boucher +** Internet: marc@PostImage.cxxOM +** +** Based on Example Conversion Program, A60/A64 Digital Video Interface +** Manual, page 69 +** +** Uses integer arithmetic rather than floating point for better performance +** +** Copyright (C) 1991 by DHD PostImage Inc. +** Copyright (C) 1987 by Abekas Video Systems Inc. +** Copyright (C) 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + + + +#include "pnmImage.h" +#include "pnmReader.h" +#include "pnmReaderTypes.h" + +#include + +// YUV format doesn't include an image size specification, so the image size +// must be specified externally. The defaults here are likely candidates, +// since this is the Abekas native size; the ysize is automatically adjusted +// down to account for a short file. +int yuv_xsize = 720; +int yuv_ysize = 486; + +/* x must be signed for the following to work correctly */ +#define limit(x) (xelval)(((x>0xffffff)?0xff0000:((x<=0xffff)?0:x&0xff0000))>>16) + +PNMReaderYUV:: +PNMReaderYUV(FILE *file, int already_read_magic) : PNMReader(file) { + yuvbuf = NULL; + + if (already_read_magic >= 0) { + ungetc(already_read_magic >> 8, file); + ungetc(already_read_magic & 0xff, file); + } + + cols = yuv_xsize; + rows = yuv_ysize; + color_type = PNMImage::Color; + + if (cols <= 0 || rows <= 0) { + valid = false; + return; + } + + nassertv(255 <= PGM_MAXMAXVAL); + + yuvbuf = (long *) pm_allocrow(cols, 2); + + maxval = 255; +} + +bool PNMReaderYUV:: +ReadRow(xel *row_data, xelval *) { + long tmp, y, u, v, y1, r, g, b, *yuvptr; + int col; + + if (fread(yuvbuf, cols * 2, 1, file) != 1) { + // Short file--perhaps it's just a field instead of a full frame. + // Since the YUV format does not include a length designation, we'll + // have to assume this is not a problem and just truncate here. + return false; + } + + for (col = 0, yuvptr = yuvbuf; col < cols; col += 2) { + tmp = *yuvptr++; + u = (0xff & (tmp >> 24)) - 128; + y = ((0xff & (tmp >> 16)) - 16); + if (y < 0) y = 0; + + v = (0xff & (tmp >> 8)) - 128; + y1 = ((0xff & tmp) - 16); + if (y1 < 0) y1 = 0; + + r = 104635 * v; + g = -25690 * u + -53294 * v; + b = 132278 * u; + + y*=76310; y1*=76310; + + PPM_ASSIGN(row_data[col], limit(r+y), limit(g+y), limit(b+y)); + PPM_ASSIGN(row_data[col+1], limit(r+y1), limit(g+y1), limit(b+y1)); + } + return true; +} + +PNMReaderYUV:: +~PNMReaderYUV() { + if (yuvbuf!=NULL) { + pm_freerow((char *)yuvbuf); + } +} + + diff --git a/panda/src/pnmimage/pnm-image-softimage.cxx b/panda/src/pnmimage/pnm-image-softimage.cxx new file mode 100644 index 0000000000..fb88ea2808 --- /dev/null +++ b/panda/src/pnmimage/pnm-image-softimage.cxx @@ -0,0 +1,539 @@ +// pnm-image-softimage.cc +// +// PNMImage::ReadSoftImage() +// PNMImage::WriteSoftImage() + +#include "pnmImage.h" +#include "pnmReader.h" +#include "pnmReaderTypes.h" +#include "pnmWriter.h" +#include "pnmWriterTypes.h" +#include "config_pnmimage.h" +#include + +#include + +static const float imageVersionNumber = 3.0; +static const int imageCommentLength = 80; +static const char imageComment[imageCommentLength+1] = + "Written by DRR's PNMImage library."; + + + +// Values to indicate compressed/uncompressed types +#define UNCOMPRESSED 0x00 +#define MIXED_RUN_LENGTH 0x02 + +// Bits to indicate channel type +#define RGB_CHANNEL 0xe0 +#define ALPHA_CHANNEL 0x10 + +inline float +read_float(FILE *file) { + long l; + + if (pm_readbiglong(file, &l)==0) { + return *(float *)&l; + } else { + return 0.0; + } +} + +inline unsigned short +read_ushort(FILE *file) { + unsigned short x; + return pm_readbigshort(file, (short *)&x)==0 ? x : 0; +} + +inline unsigned char +read_uchar(FILE *file) { + int x; + x = getc(file); + return (x!=EOF) ? (unsigned char)x : 0; +} + +inline void +write_ushort(FILE *file, unsigned short x) { + pm_writebigshort(file, (short)x); +} + +inline void +write_uchar(FILE *file, unsigned char x) { + putc(x, file); +} + +inline void +write_float(FILE *file, float x) { + pm_writebiglong(file, *(long *)&x); +} + +static int +read_channel_pkt(FILE *file, + int &chained, int &size, int &type, int &channel) { + chained = read_uchar(file); + size = read_uchar(file); + type = read_uchar(file); + channel = read_uchar(file); + + if (feof(file)) { + return false; + } + + if (size!=8) { + pnmimage_cat.error() + << "Don't know how to interpret " << size << " bits per pixel!\n"; + return false; + } + + return true; +} + +static void +read_rgb(xel *row_data, xelval *, FILE *file, int x, int repeat) { + xelval red, grn, blu; + red = read_uchar(file); + grn = read_uchar(file); + blu = read_uchar(file); + + while (repeat>0) { + PPM_ASSIGN(row_data[x], red, grn, blu); + x++; + repeat--; + } +} + +static void +read_alpha(xel *, xelval *alpha_data, FILE *file, int x, int repeat) { + xelval alpha = read_uchar(file); + + while (repeat>0) { + alpha_data[x] = alpha; + x++; + repeat--; + } +} + +static void +read_rgba(xel *row_data, xelval *alpha_data, FILE *file, int x, int repeat) { + xelval red, grn, blu, alpha; + red = read_uchar(file); + grn = read_uchar(file); + blu = read_uchar(file); + alpha = read_uchar(file); + + while (repeat>0) { + PPM_ASSIGN(row_data[x], red, grn, blu); + alpha_data[x] = alpha; + x++; + repeat--; + } +} + + +static int +read_scanline(xel *row_data, xelval *alpha_data, int cols, FILE *file, + void (*read_data)(xel *row_data, xelval *alpha_data, FILE *file, + int x, int repeat), + int ctype) { + if (ctype==UNCOMPRESSED) { + for (int x = 0; x cols) { + return false; + } + while (num>0) { + read_data(row_data, alpha_data, file, x, 1); + if (feof(file)) { + return false; + } + x++; + num--; + } + } else { + // Sequence of repeated values. + if (num==128) { + num = read_ushort(file); + } else { + num -= 127; + } + if (x+num > cols) { + return false; + } + read_data(row_data, alpha_data, file, x, num); + if (feof(file)) { + return false; + } + x += num; + } + } + + return (x==cols); + } +} + + +PNMReaderSoftImage:: +PNMReaderSoftImage(FILE *file, int already_read_magic) : PNMReader(file) { + unsigned short magic1, magic2; + + magic1 = (already_read_magic >= 0) ? already_read_magic : read_ushort(file); + if (magic1 != SOFTIMAGE_MAGIC1) { + valid = false; + return; + } + + magic2 = read_ushort(file); + if (magic2 != SOFTIMAGE_MAGIC2) { + valid = false; + return; + } + + // skip version number + read_float(file); + + // Skip comment + fseek(file, imageCommentLength, SEEK_CUR); + + char pict_id[4]; + if (fread(pict_id, 1, 4, file) < 4) { + valid = false; + return; + } + + if (memcmp(pict_id, "PICT", 4)!=0) { + valid = false; + return; + } + + cols = read_ushort(file); + rows = read_ushort(file); + + float ratio = read_float(file); + int fields = read_ushort(file); + read_ushort(file); + + int chained, size, channel; + if (!read_channel_pkt(file, chained, size, rgb_ctype, channel)) { + valid = false; + return; + } + + soft_color = unknown; + + if (channel == (RGB_CHANNEL | ALPHA_CHANNEL)) { + // Four components in the first part: RGBA. + soft_color = rgba; + + } else if (channel == RGB_CHANNEL) { + // Three components in the first part: RGB. + soft_color = rgb; + + if (chained) { + if (!read_channel_pkt(file, chained, size, alpha_ctype, channel)) { + valid = false; + return; + } + + if (channel == ALPHA_CHANNEL) { + // Alpha component in the second part: RGBA. + soft_color = rgb_a; + } + } + } + + switch (soft_color) { + case rgb: + color_type = PNMImage::Color; + break; + + case rgba: + case rgb_a: + color_type = PNMImage::FourChannel; + break; + + default: + pnmimage_cat.error() + << "Image is not RGB or RGBA!\n"; + valid = false; + return; + } + + if (chained) { + pnmimage_cat.error() + << "Unexpected additional channels in image file.\n"; + valid = false; + return; + } + + maxval = 255; +} + +bool PNMReaderSoftImage:: +ReadRow(xel *row_data, xelval *alpha_data) { + switch (soft_color) { + case rgb: + if (!read_scanline(row_data, alpha_data, cols, file, + read_rgb, rgb_ctype)) { + return false; + } + break; + + case rgba: + if (!read_scanline(row_data, alpha_data, cols, file, + read_rgba, rgb_ctype)) { + return false; + } + break; + + case rgb_a: + if (!read_scanline(row_data, alpha_data, cols, file, + read_rgb, rgb_ctype)) { + return false; + } + if (!read_scanline(row_data, alpha_data, cols, file, + read_alpha, alpha_ctype)) { + return false; + } + break; + } + + return true; +} + + +static void +write_channel_pkt(FILE *file, + int chained, int size, int type, int channel) { + write_uchar(file, chained); + write_uchar(file, size); + write_uchar(file, type); + write_uchar(file, channel); +} + +static void +write_rgb(xel *row_data, xelval *, FILE *file, int x) { + write_uchar(file, PPM_GETR(row_data[x])); + write_uchar(file, PPM_GETG(row_data[x])); + write_uchar(file, PPM_GETB(row_data[x])); +} + +static int +compare_rgb(xel *row_data, xelval *, int x1, int x2) { + return PPM_EQUAL(row_data[x1], row_data[x2]); +} + +static void +write_gray(xel *row_data, xelval *, FILE *file, int x) { + write_uchar(file, PPM_GETB(row_data[x])); + write_uchar(file, PPM_GETB(row_data[x])); + write_uchar(file, PPM_GETB(row_data[x])); +} + +static int +compare_gray(xel *row_data, xelval *, int x1, int x2) { + return (PPM_GETB(row_data[x1])==PPM_GETB(row_data[x2])); +} + +static void +write_alpha(xel *, xelval *alpha_data, FILE *file, int x) { + write_uchar(file, alpha_data[x]); +} + +static int +compare_alpha(xel *, xelval *alpha_data, int x1, int x2) { + return (alpha_data[x1]==alpha_data[x2]); +} + +static void +write_diff(xel *row_data, xelval *alpha_data, FILE *file, + void (*write_data)(xel *row_data, xelval *alpha_data, FILE *file, + int x), + int tox, int length) { + if (length>0) { + nassertv(length<=128); + + write_uchar(file, length-1); + while (length>0) { + length--; + write_data(row_data, alpha_data, file, tox-length); + } + } +} + +static void +write_same(xel *row_data, xelval *alpha_data, FILE *file, + void (*write_data)(xel *row_data, xelval *alpha_data, FILE *file, + int x), + int tox, int length) { + if (length==1) { + write_diff(row_data, alpha_data, file, write_data, tox, length); + + } else if (length>0) { + if (length<128) { + write_uchar(file, length+127); + } else { + write_uchar(file, 128); + write_ushort(file, length); + } + write_data(row_data, alpha_data, file, tox); + } +} + + +static void +write_scanline(xel *row_data, xelval *alpha_data, int cols, FILE *file, + int (*compare_data)(xel *row_data, xelval *alpha_data, + int x1, int x2), + void (*write_data)(xel *row_data, xelval *alpha_data, + FILE *file, int x)) { + int run_length = 0; + + int x = 0; + int same = true; + + // Go through each value in the scanline, from beginning to end, looking + // for runs of identical values. + while (x < cols) { + + if (same) { + + // We have been scanning past a run of identical values. In this case, + // the run is the sequence of values from x-run_length to x-1. + + if (!compare_data(row_data, alpha_data, x, x-run_length)) { + // Oops, the end of a run. + + if (run_length <= 1) { + // If run_length is only 1, no big deal--this is actually the + // beginning of a different-valued run. + + same = false; + + } else { + // Write out the old run and begin a new one. We'll be optimistic + // and hope the new run will also represent a sequence of identical + // values (until we find otherwise). + + write_same(row_data, alpha_data, file, write_data, x-1, run_length); + same = true; + run_length = 0; + } + } + + } else { // !same + + // We have been scanning past a run of different values. In this case, + // the run is the sequence of values from x-run_length to x-1. + + if (run_length>128) { + // We can't have different runs of more than 128 characters. Close + // off the old run. + + int excess = run_length - 128; + write_diff(row_data, alpha_data, file, write_data, x-excess-1, 128); + run_length = excess; + + } else if (run_length > 2 && + compare_data(row_data, alpha_data, x, x-1) && + compare_data(row_data, alpha_data, x, x-2)) { + + // If the last three values have been the same, then it's time to + // begin a new run of similar values. Close off the old run. + + write_diff(row_data, alpha_data, file, write_data, x-3, run_length-2); + same = true; + run_length = 2; + } + } + + x++; + run_length++; + } + + // We made it all the way to the end. Flush out the last run. + + if (run_length>0) { + if (same) { + write_same(row_data, alpha_data, file, write_data, cols-1, run_length); + } else { + + // Mighty unlikely, but we might have just run over the + // 128-pixel limit. + if (run_length>128) { + int excess = run_length - 128; + write_diff(row_data, alpha_data, file, write_data, cols-excess-1, 128); + run_length = excess; + } + + write_diff(row_data, alpha_data, file, write_data, cols-1, run_length); + } + } +} + + +bool PNMWriterSoftImage:: +WriteHeader() { + write_ushort(file, SOFTIMAGE_MAGIC1); + write_ushort(file, SOFTIMAGE_MAGIC2); + write_float(file, imageVersionNumber); + + fwrite(imageComment, 1, imageCommentLength, file); + fwrite("PICT", 1, 4, file); + + write_ushort(file, cols); + write_ushort(file, rows); + + write_float(file, 1.0); // pixel aspect ratio; we don't know. + write_ushort(file, 3); // fields value; we don't really know either. + write_ushort(file, 0); // padding + + // There doesn't seem to be a variation on SoftImage image formats for + // grayscale images. We'll write out grayscale as a 3-channel image. + + switch (color_type) { + case PNMImage::Grayscale: + case PNMImage::Color: + write_channel_pkt(file, 0, 8, MIXED_RUN_LENGTH, RGB_CHANNEL); + break; + + case PNMImage::FourChannel: + case PNMImage::TwoChannel: + write_channel_pkt(file, 1, 8, MIXED_RUN_LENGTH, RGB_CHANNEL); + write_channel_pkt(file, 0, 8, MIXED_RUN_LENGTH, ALPHA_CHANNEL); + break; + } + + return true; +} + +bool PNMWriterSoftImage:: +WriteRow(xel *row_data, xelval *alpha_data) { + if (PNMImage::IsGrayscale(color_type)) { + write_scanline(row_data, alpha_data, cols, file, compare_gray, write_gray); + + } else { + write_scanline(row_data, alpha_data, cols, file, compare_rgb, write_rgb); + } + + if (PNMImage::HasAlpha(color_type)) { + write_scanline(row_data, alpha_data, cols, file, compare_alpha, write_alpha); + } + + return !ferror(file); +} + + + diff --git a/panda/src/pnmimage/pnm-image-tiff.cxx b/panda/src/pnmimage/pnm-image-tiff.cxx new file mode 100644 index 0000000000..abef65440c --- /dev/null +++ b/panda/src/pnmimage/pnm-image-tiff.cxx @@ -0,0 +1,567 @@ +// pnm-image-readtiff.cc +// +// PNMImage::ReadTIFF() and supporting functions. + + + +// Much code in this file is borrowed from Netpbm, specifically tifftopnm.c +// and pnmtotiff.c. +/* +** tifftopnm.c - converts a Tagged Image File to a portable anymap +** +** Derived by Jef Poskanzer from tif2ras.c, which is: +** +** Copyright (c) 1990 by Sun Microsystems, Inc. +** +** Author: Patrick J. Naughton +** naughton@wind.sun.com +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, +** provided that the above copyright notice appear in all copies and that +** both that copyright notice and this permission notice appear in +** supporting documentation. +** +** This file is provided AS IS with no warranties of any kind. The author +** shall have no liability with respect to the infringement of copyrights, +** trade secrets or any patents by this file or any part thereof. In no +** event will the author be liable for any lost revenue or profits or +** other special, indirect and consequential damages. +*/ + +#ifndef WIN32VC +#ifndef PENV_WIN32 +#include +#endif +#endif + +#include "pnmImage.h" +#include "pnmReader.h" +#include "pnmReaderTypes.h" +#include "pnmWriter.h" +#include "pnmWriterTypes.h" + +extern "C" { +#include "../pnm/sgi.h" +#include "../pnm/ppmcmap.h" +#include "../tiff/tiff.h" +#include "../tiff/tiffio.h" +} + +// true to display image information on load; false otherwise. +int tiff_headerdump = false; + +// These are configurable parameters to specify TIFF details on output. +// See tools/drr/../pnm/libtiff/tiff.h or type man pnmtotiff for a better +// explanation of options. + +unsigned short tiff_compression = COMPRESSION_LZW; +/* One of: + COMPRESSION_NONE + COMPRESSION_CCITTRLE + COMPRESSION_CCITTFAX3 + COMPRESSION_CCITTFAX4 + COMPRESSION_LZW + COMPRESSION_JPEG + COMPRESSION_NEXT + COMPRESSION_CCITTRLEW + COMPRESSION_PACKBITS + COMPRESSION_THUNDERSCAN + */ + +long tiff_g3options = 0; +/* One or more of: + GROUP3OPT_2DENCODING + GROUP3OPT_FILLBITS + + meaningful when tiff_compression == COMPRESSION_CCITTFAX3. + */ + +unsigned short tiff_fillorder = FILLORDER_MSB2LSB; +/* One of: + FILLORDER_MSB2LSB + FILLORDER_LSB2MSB + */ + +short tiff_predictor = 0; +/* 0, 1, or 2; meaningful when tiff_compression == COMPRESSION_LZW. */ + + +long tiff_rowsperstrip = 0; +/* 0 or any positive number */ + +#define MAXCOLORS 1024 +#ifndef PHOTOMETRIC_DEPTH +#define PHOTOMETRIC_DEPTH 32768 +#endif + +static tsize_t +StdioReadProc(thandle_t fd, tdata_t buf, tsize_t size) +{ + return ((tsize_t)fread((void *)buf, 1, (size_t) size, (FILE *)fd)); +} + +static tsize_t +StdioWriteProc(thandle_t fd, tdata_t buf, tsize_t size) +{ + return ((tsize_t)fwrite((void *)buf, 1, (size_t) size, (FILE *)fd)); +} + +static toff_t +StdioSeekProc(thandle_t fd, off_t off, int whence) +{ + fseek((FILE *)fd, (long)off, whence); + return (toff_t)ftell((FILE *)fd); +} + +static int +StdioCloseProc(thandle_t) +{ + // We don't actually close the file; we'll leave that up to our calling + // procedure. + return true; +} + +static toff_t +StdioSizeProc(thandle_t fd) +{ + fseek((FILE *)fd, 0, SEEK_END); + return (toff_t)ftell((FILE *)fd); +} + +static int +StdioMapProc(thandle_t, tdata_t*, toff_t*) +{ + return (0); +} + +static void +StdioUnmapProc(thandle_t, tdata_t, toff_t) +{ +} + + +PNMReaderTIFF:: +PNMReaderTIFF(FILE *file, int already_read_magic) : PNMReader(file) { + int grayscale; + int numcolors; + int i; + unsigned short* redcolormap; + unsigned short* greencolormap; + unsigned short* bluecolormap; + + if (already_read_magic >= 0) { + ungetc(already_read_magic >> 8, file); + ungetc(already_read_magic & 0xff, file); + } + + tif = TIFFClientOpen("TIFF file", "r", + (thandle_t) file, + StdioReadProc, StdioWriteProc, + (TIFFSeekProc)StdioSeekProc, + StdioCloseProc, StdioSizeProc, + StdioMapProc, StdioUnmapProc); + + if ( tif == NULL ) + pm_error( "error opening TIFF file" ); + + if ( tiff_headerdump ) + TIFFPrintDirectory( tif, stderr, TIFFPRINT_NONE ); + + if ( ! TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &bps ) ) + bps = 1; + if ( ! TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &spp ) ) + spp = 1; + if ( ! TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photomet ) ) + pm_error( "error getting photometric" ); + + switch ( spp ) + { + case 1: + color_type = PNMImage::Grayscale; + break; + + case 3: + color_type = PNMImage::Color; + break; + + case 4: + color_type = PNMImage::FourChannel; + break; + + default: + pm_error( + "can only handle 1-channel gray scale or 1- or 3-channel color" ); + } + + (void) TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &cols ); + (void) TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &rows ); + + if ( tiff_headerdump ) + { + pm_message( "%dx%dx%d image", cols, rows, bps * spp ); + pm_message( "%d bits/sample, %d samples/pixel", bps, spp ); + } + + maxval = ( 1 << bps ) - 1; + if ( maxval == 1 && spp == 1 ) + { + if ( tiff_headerdump ) + pm_message("monochrome" ); + grayscale = 1; + } + else + { + switch ( photomet ) + { + case PHOTOMETRIC_MINISBLACK: + if ( tiff_headerdump ) + pm_message( "%d graylevels (min=black)", maxval + 1 ); + grayscale = 1; + break; + + case PHOTOMETRIC_MINISWHITE: + if ( tiff_headerdump ) + pm_message( "%d graylevels (min=white)", maxval + 1 ); + grayscale = 1; + break; + + case PHOTOMETRIC_PALETTE: + if ( tiff_headerdump ) + pm_message( "colormapped" ); + if ( ! TIFFGetField( tif, TIFFTAG_COLORMAP, &redcolormap, &greencolormap, &bluecolormap ) ) + pm_error( "error getting colormaps" ); + numcolors = maxval + 1; + if ( numcolors > MAXCOLORS ) + pm_error( "too many colors" ); + maxval = PNM_MAXMAXVAL; + grayscale = 0; + for ( i = 0; i < numcolors; ++i ) + { + xelval r, g, b; + r = (xelval)(maxval * (double)(redcolormap[i] / 65535.0)); + g = (xelval)(maxval * (double)(greencolormap[i] / 65535.0)); + b = (xelval)(maxval * (double)(bluecolormap[i] / 65535.0)); + PPM_ASSIGN( colormap[i], r, g, b ); + } + break; + + case PHOTOMETRIC_RGB: + if ( tiff_headerdump ) + pm_message( "truecolor" ); + grayscale = 0; + break; + + case PHOTOMETRIC_MASK: + pm_error( "don't know how to handle PHOTOMETRIC_MASK" ); + + case PHOTOMETRIC_DEPTH: + pm_error( "don't know how to handle PHOTOMETRIC_DEPTH" ); + + default: + pm_error( "unknown photometric: %d", photomet ); + } + } + if ( maxval > PNM_MAXMAXVAL ) + pm_error( + "bits/sample is too large - try reconfiguring with PGM_BIGGRAYS\n or without PPM_PACKCOLORS" ); + + + if ( grayscale ) { + color_type = PNMImage::Grayscale; + } else if (color_type == PNMImage::Grayscale) { + color_type = PNMImage::Color; + } + + current_row = 0; +} + +#define NEXTSAMPLE \ + { \ + if ( bitsleft == 0 ) \ + { \ + ++inP; \ + bitsleft = 8; \ + } \ + bitsleft -= bps; \ + sample = ( *inP >> bitsleft ) & maxval; \ + } + +bool PNMReaderTIFF:: +ReadRow(xel *row_data, xelval *alpha_data) { + unsigned char *buf = (unsigned char*) alloca((size_t)TIFFScanlineSize(tif)); + int col; + unsigned char sample; + + if ( TIFFReadScanline( tif, buf, current_row, 0 ) < 0 ) + pm_error( "bad data read on line %d", current_row ); + unsigned char *inP = buf; + int bitsleft = 8; + + switch ( photomet ) { + case PHOTOMETRIC_MINISBLACK: + for ( col = 0; col < cols; ++col ) + { + NEXTSAMPLE; + PPM_PUTB(row_data[col], sample); + } + break; + + case PHOTOMETRIC_MINISWHITE: + for ( col = 0; col < cols; ++col ) + { + NEXTSAMPLE; + sample = maxval - sample; + PPM_PUTB(row_data[col], sample); + } + break; + + case PHOTOMETRIC_PALETTE: + for ( col = 0; col < cols; ++col ) + { + NEXTSAMPLE; + row_data[col] = colormap[sample]; + } + break; + + case PHOTOMETRIC_RGB: + for ( col = 0; col < cols; ++col ) { + xelval r, g, b; + + NEXTSAMPLE; + r = sample; + NEXTSAMPLE; + g = sample; + NEXTSAMPLE; + b = sample; + PPM_ASSIGN(row_data[col], r, g, b); + if ( spp == 4 ) { + NEXTSAMPLE; // Alpha channel + alpha_data[col] = sample; + } + } + break; + + default: + pm_error( "unknown photometric: %d", photomet ); + } + + current_row++; + return true; +} + +PNMReaderTIFF:: +~PNMReaderTIFF() { + TIFFClose( tif ); + // owns_file = false; +} + + +int PNMWriterTIFF:: +WriteData(xel *array, xelval *alpha) { + colorhist_vector chv; + colorhash_table cht; + unsigned short red[MAXCOLORS], grn[MAXCOLORS], blu[MAXCOLORS]; + int row, colors, i; + register int col; + int grayscale; + struct tiff * tif; + short photometric; + short samplesperpixel; + short bitspersample; + int bytesperrow; + unsigned char* buf; + unsigned char* tP; + + /* Check for grayscale. */ + int is_grayscale = PNMImage::IsGrayscale(color_type); + + switch ( color_type ) { + case PNMImage::Color: + pm_message( "computing colormap..." ); + + // This call is a bit of fakery to convert our proper 2-d array of + // xels to an indirect 2-d array of pixels. We make it look like a + // single row of cols * rows pixels. + chv = ppm_computecolorhist( (pixel **)&array, cols * rows, 1, + MAXCOLORS, &colors ); + if ( chv == (colorhist_vector) 0 ) { + pm_message("Too many colors - proceeding to write a 24-bit RGB file." ); + pm_message("If you want an 8-bit palette file, try doing a 'ppmquant %d'.", + MAXCOLORS ); + grayscale = 0; + } else { + pm_message( "%d colors found", colors ); + grayscale = 1; + for ( i = 0; i < colors; ++i ) { + register xelval r, g, b; + + r = PPM_GETR( chv[i].color ); + g = PPM_GETG( chv[i].color ); + b = PPM_GETB( chv[i].color ); + if ( r != g || g != b ) { + grayscale = 0; + break; + } + } + } + break; + + case PNMImage::FourChannel: + chv = (colorhist_vector) 0; + grayscale = 0; + break; + + case PNMImage::TwoChannel: // We don't yet support two-channel output for TIFF's. + case PNMImage::Grayscale: + chv = (colorhist_vector) 0; + grayscale = 1; + break; + } + + /* Open output file. */ + tif = TIFFClientOpen("TIFF file", "w", + (thandle_t) file, + StdioReadProc, StdioWriteProc, + (TIFFSeekProc)StdioSeekProc, + StdioCloseProc, StdioSizeProc, + StdioMapProc, StdioUnmapProc); + if ( tif == NULL ) + return 0; + + /* Figure out TIFF parameters. */ + switch ( color_type ) { + case PNMImage::Color: + case PNMImage::FourChannel: + if ( chv == (colorhist_vector) 0 ) { + samplesperpixel = (color_type==PNMImage::FourChannel ? 4 : 3); + bitspersample = 8; + photometric = PHOTOMETRIC_RGB; + bytesperrow = cols * samplesperpixel; + } else if ( grayscale ) { + samplesperpixel = 1; + bitspersample = pm_maxvaltobits( maxval ); + photometric = PHOTOMETRIC_MINISBLACK; + bytesperrow = ( cols + i - 1 ) / i; + } else { + samplesperpixel = 1; + bitspersample = 8; + photometric = PHOTOMETRIC_PALETTE; + bytesperrow = cols; + } + break; + + case PNMImage::Grayscale: + case PNMImage::TwoChannel: + samplesperpixel = 1; + bitspersample = pm_maxvaltobits( maxval ); + photometric = PHOTOMETRIC_MINISBLACK; + i = 8 / bitspersample; + bytesperrow = ( cols + i - 1 ) / i; + break; + } + + if ( tiff_rowsperstrip == 0 ) + tiff_rowsperstrip = ( 8 * 1024 ) / bytesperrow; + buf = (unsigned char*) malloc( bytesperrow ); + if ( buf == (unsigned char*) 0 ) + pm_error( "can't allocate memory for row buffer" ); + + /* Set TIFF parameters. */ + TIFFSetField( tif, TIFFTAG_IMAGEWIDTH, cols ); + TIFFSetField( tif, TIFFTAG_IMAGELENGTH, rows ); + TIFFSetField( tif, TIFFTAG_BITSPERSAMPLE, bitspersample ); + TIFFSetField( tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT ); + TIFFSetField( tif, TIFFTAG_COMPRESSION, tiff_compression ); + if ( tiff_compression == COMPRESSION_CCITTFAX3 && tiff_g3options != 0 ) + TIFFSetField( tif, TIFFTAG_GROUP3OPTIONS, tiff_g3options ); + if ( tiff_compression == COMPRESSION_LZW && tiff_predictor != 0 ) + TIFFSetField( tif, TIFFTAG_PREDICTOR, tiff_predictor ); + TIFFSetField( tif, TIFFTAG_PHOTOMETRIC, photometric ); + TIFFSetField( tif, TIFFTAG_FILLORDER, tiff_fillorder ); + //TIFFSetField( tif, TIFFTAG_DOCUMENTNAME, "TIFF Image File"); + TIFFSetField( tif, TIFFTAG_IMAGEDESCRIPTION, + "Generated via DRR's pnm-image library\n" ); + TIFFSetField( tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel ); + TIFFSetField( tif, TIFFTAG_ROWSPERSTRIP, tiff_rowsperstrip ); + /* TIFFSetField( tif, TIFFTAG_STRIPBYTECOUNTS, rows / tiff_rowsperstrip ); */ + TIFFSetField( tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG ); + + if ( chv == (colorhist_vector) 0 ) { + cht = (colorhash_table) 0; + } else { + /* Make TIFF colormap. */ + for ( i = 0; i < colors; ++i ) { + red[i] = (unsigned short) (PPM_GETR( chv[i].color ) * 65535L / maxval); + grn[i] = (unsigned short) (PPM_GETG( chv[i].color ) * 65535L / maxval); + blu[i] = (unsigned short) (PPM_GETB( chv[i].color ) * 65535L / maxval); + } + TIFFSetField( tif, TIFFTAG_COLORMAP, red, grn, blu ); + + /* Convert color vector to color hash table, for fast lookup. */ + cht = ppm_colorhisttocolorhash( chv, colors ); + ppm_freecolorhist( chv ); + } + + /* Now write the TIFF data. */ + for ( row = 0; row < rows; ++row ) { + xel *row_data = array + row*cols; + xelval *alpha_data = alpha + row*cols; + + if ( !is_grayscale && ! grayscale ) { + if ( cht == (colorhash_table) 0 ) { + tP = buf; + for ( col = 0; col < cols; ++col ) { + *tP++ = (unsigned char)(255 * PPM_GETR(row_data[col]) / maxval); + *tP++ = (unsigned char)(255 * PPM_GETG(row_data[col]) / maxval); + *tP++ = (unsigned char)(255 * PPM_GETB(row_data[col]) / maxval); + if (samplesperpixel==4) { + *tP++ = (unsigned char)(255 * alpha_data[col] / maxval); + } + } + } else { + tP = buf; + for ( col = 0; col < cols; ++col ) { + register int s; + + s = ppm_lookupcolor( cht, (pixel *)(&row_data[col]) ); + if ( s == -1 ) + pm_error("color not found?!? row=%d col=%d", row, col); + *tP++ = (unsigned char) s; + } + } + } else { + register xelval bigger_maxval; + register int bitshift; + register unsigned char byte; + register xelval s; + + bigger_maxval = pm_bitstomaxval( bitspersample ); + bitshift = 8 - bitspersample; + byte = 0; + tP = buf; + for ( col = 0; col < cols; ++col ) { + s = PPM_GETB(row_data[col]); + if ( maxval != bigger_maxval ) + s = (xelval)((long) s * bigger_maxval / maxval); + byte |= s << bitshift; + bitshift -= bitspersample; + if ( bitshift < 0 ) { + *tP++ = byte; + bitshift = 8 - bitspersample; + byte = 0; + } + } + if ( bitshift != 8 - bitspersample ) + *tP++ = byte; + } + + if ( TIFFWriteScanline( tif, buf, row, 0 ) < 0 ) + pm_error( "failed a scanline write on row %d", row ); + } + TIFFFlushData( tif ); + TIFFClose( tif ); + // owns_file = false; + + return rows; +} diff --git a/panda/src/pnmimage/pnm-image-writebmp.cxx b/panda/src/pnmimage/pnm-image-writebmp.cxx new file mode 100644 index 0000000000..5067269f3c --- /dev/null +++ b/panda/src/pnmimage/pnm-image-writebmp.cxx @@ -0,0 +1,609 @@ +// pnm-image-writebmp.cc +// +// PNMImage::WriteBMP() and supporting functions. + + + +// Much code in this file is borrowed from Netpbm, specifically ppmtobmp.c. +/*\ + * $Id$ + * + * ppmtobmp.c - Converts from a PPM file to a Microsoft Windows or OS/2 + * .BMP file. + * + * The current implementation is probably not complete, but it works for + * me. I welcome feedback. + * + * Copyright (C) 1992 by David W. Sanderson. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. This software is provided "as is" + * without express or implied warranty. + * + * $Log$ + * Revision 1.1 2000/10/04 01:14:42 drose + * Initial revision + * + * Revision 1.9 1992/11/24 19:39:33 dws + * Added copyright. + * + * Revision 1.8 1992/11/17 02:16:52 dws + * Moved length functions to bmp.h. + * + * Revision 1.7 1992/11/11 23:18:16 dws + * Modified to adjust the bits per pixel to 1, 4, or 8. + * + * Revision 1.6 1992/11/11 22:43:39 dws + * Commented out a superfluous message. + * + * Revision 1.5 1992/11/11 05:58:06 dws + * First version that works. + * + * Revision 1.4 1992/11/11 03:40:32 dws + * Moved calculation of bits per pixel to BMPEncode. + * + * Revision 1.3 1992/11/11 03:02:34 dws + * Added BMPEncode function. + * + * Revision 1.2 1992/11/08 01:44:35 dws + * Added option processing and reading of PPM file. + * + * Revision 1.1 1992/11/08 00:46:07 dws + * Initial revision +\*/ + +#ifndef WIN32VC +#ifndef PENV_WIN32 +#include +#endif +#endif + +#include "pnmImage.h" +#include "pnmWriter.h" +#include "pnmWriterTypes.h" + +extern "C" { + #include "../pnm/bmp.h" + #include "../pnm/ppmcmap.h" + #include "../pnm/bitio.h" +} + +#define MAXCOLORS 256 + +/* + * Utilities + */ + +static char er_write[] = "stdout: write error"; + +/* prototypes */ +static void PutByte ARGS((FILE *fp, char v)); +static void PutShort ARGS((FILE *fp, short v)); +static void PutLong ARGS((FILE *fp, long v)); +static int BMPwritefileheader ARGS((FILE *fp, int classv, unsigned long bitcount, + unsigned long x, unsigned long y)); +static int BMPwriteinfoheader ARGS((FILE *fp, int classv, unsigned long bitcount, + unsigned long x, unsigned long y)); +static int BMPwritergb ARGS((FILE *fp, int classv, pixval R, pixval G, pixval B)); +static int BMPwritergbtable ARGS((FILE *fp, int classv, int bpp, int colors, + pixval *R, pixval *G, pixval *B)); +static int BMPwriterow ARGS((FILE *fp, pixel *row, unsigned long cx, + unsigned short bpp, colorhash_table cht)); +static int BMPwritebits ARGS((FILE *fp, unsigned long cx, unsigned long cy, + unsigned short cBitCount, pixel **pixels, colorhash_table cht)); +static int colorstobpp ARGS((int colors)); +static void BMPEncode ARGS((FILE *fp, int classv, int x, int y, pixel **pixels, + int colors, colorhash_table cht, pixval *R, pixval *G, pixval *B)); +static void +PutByte( + FILE *fp, + char v) +{ + if (putc(v, fp) == EOF) + { + pm_error(er_write); + } +} + +static void +PutShort( + FILE *fp, + short v) +{ + if (pm_writelittleshort(fp, v) == -1) + { + pm_error(er_write); + } +} + +static void +PutLong( + FILE *fp, + long v) +{ + if (pm_writelittlelong(fp, v) == -1) + { + pm_error(er_write); + } +} + +/* + * BMP writing + */ + +/* + * returns the number of bytes written, or -1 on error. + */ +static int +BMPwritefileheader( + FILE *fp, + int classv, + unsigned long bitcount, + unsigned long x, + unsigned long y) +{ + PutByte(fp, 'B'); + PutByte(fp, 'M'); + + /* cbSize */ + PutLong(fp, (long)BMPlenfile(classv, bitcount, x, y)); + + /* xHotSpot */ + PutShort(fp, 0); + + /* yHotSpot */ + PutShort(fp, 0); + + /* offBits */ + PutLong(fp, (long)BMPoffbits(classv, bitcount)); + + return 14; +} + +/* + * returns the number of bytes written, or -1 on error. + */ +static int +BMPwriteinfoheader( + FILE *fp, + int classv, + unsigned long bitcount, + unsigned long x, + unsigned long y) +{ + long cbFix; + + /* cbFix */ + switch (classv) + { + case C_WIN: + cbFix = 40; + PutLong(fp, cbFix); + + /* cx */ + PutLong(fp, (long)x); + /* cy */ + PutLong(fp, (long)y); + /* cPlanes */ + PutShort(fp, 1); + /* cBitCount */ + PutShort(fp, (short)bitcount); + + /* + * We've written 16 bytes so far, need to write 24 more + * for the required total of 40. + */ + + PutLong(fp, 0); + PutLong(fp, 0); + PutLong(fp, 0); + PutLong(fp, 0); + PutLong(fp, 0); + PutLong(fp, 0); + + + break; + case C_OS2: + cbFix = 12; + PutLong(fp, cbFix); + + /* cx */ + PutShort(fp, (short)x); + /* cy */ + PutShort(fp, (short)y); + /* cPlanes */ + PutShort(fp, 1); + /* cBitCount */ + PutShort(fp, (short)bitcount); + + break; + default: + pm_error(er_internal, "BMPwriteinfoheader"); + } + + return cbFix; +} + +/* + * returns the number of bytes written, or -1 on error. + */ +static int +BMPwritergb( + FILE *fp, + int classv, + pixval R, + pixval G, + pixval B) +{ + switch (classv) + { + case C_WIN: + PutByte(fp, B); + PutByte(fp, G); + PutByte(fp, R); + PutByte(fp, 0); + return 4; + case C_OS2: + PutByte(fp, B); + PutByte(fp, G); + PutByte(fp, R); + return 3; + default: + pm_error(er_internal, "BMPwritergb"); + } + return -1; +} + +/* + * returns the number of bytes written, or -1 on error. + */ +static int +BMPwritergbtable( + FILE *fp, + int classv, + int bpp, + int colors, + pixval *R, + pixval *G, + pixval *B) +{ + int nbyte = 0; + int i; + long ncolors; + + for (i = 0; i < colors; i++) + { + nbyte += BMPwritergb(fp,classv,R[i],G[i],B[i]); + } + + ncolors = (1 << bpp); + + for (; i < ncolors; i++) + { + nbyte += BMPwritergb(fp,classv,0,0,0); + } + + return nbyte; +} + +/* + * returns the number of bytes written, or -1 on error. + */ +static int +BMPwriterow( + FILE *fp, + pixel *row, + unsigned long cx, + unsigned short bpp, + int indexed, + colorhash_table cht) +{ + BITSTREAM b; + unsigned nbyte = 0; + int rc; + unsigned x; + + if (indexed) { + if ((b = pm_bitinit(fp, "w")) == (BITSTREAM) 0) + { + return -1; + } + + for (x = 0; x < cx; x++, row++) + { + if ((rc = pm_bitwrite(b, bpp, ppm_lookupcolor(cht, row))) == -1) + { + return -1; + } + nbyte += rc; + } + + if ((rc = pm_bitfini(b)) == -1) + { + return -1; + } + nbyte += rc; + } else { + + for (x = 0; x < cx; x++, row++) + { + PutByte(fp, PPM_GETB(*row)); + PutByte(fp, PPM_GETG(*row)); + PutByte(fp, PPM_GETR(*row)); + nbyte += 3; + } + } + + /* + * Make sure we write a multiple of 4 bytes. + */ + while (nbyte % 4) + { + PutByte(fp, 0); + nbyte++; + } + + return nbyte; +} + +/* + * returns the number of bytes written, or -1 on error. + */ +static int +BMPwritebits( + FILE *fp, + unsigned long cx, + unsigned long cy, + unsigned short cBitCount, + pixel **pixels, + int indexed, + colorhash_table cht) +{ + int nbyte = 0; + long y; + + if(cBitCount > 24) + { + pm_error("cannot handle cBitCount: %d" + ,cBitCount); + } + + /* + * The picture is stored bottom line first, top line last + */ + + for (y = (long)cy - 1; y >= 0; y--) + { + int rc; + rc = BMPwriterow(fp, pixels[y], cx, cBitCount, indexed, cht); + + if(rc == -1) + { + pm_error("couldn't write row %d" + ,y); + } + if(rc%4) + { + pm_error("row had bad number of bytes: %d" + ,rc); + } + nbyte += rc; + } + + return nbyte; +} + +/* + * Return the number of bits per pixel required to represent the + * given number of colors. + */ + +static int +colorstobpp(int colors) +{ + int bpp; + + if (colors < 1) + { + pm_error("can't have less than one color"); + } + + if ((bpp = pm_maxvaltobits(colors - 1)) > 8) + { + pm_error("can't happen"); + } + + return bpp; +} + +/* + * Write a BMP file of the given classv. + * + * Note that we must have 'colors' in order to know exactly how many + * colors are in the R, G, B, arrays. Entries beyond those in the + * arrays are undefined. + */ +static void +BMPEncode( + FILE *fp, + int classv, + int x, + int y, + pixel **pixels, + int colors, /* number of valid entries in R,G,B */ + colorhash_table cht, + pixval *R, + pixval *G, + pixval *B) +{ + int bpp; /* bits per pixel */ + unsigned long nbyte = 0; + + bpp = colorstobpp(colors); + + /* + * I have found empirically at least one BMP-displaying program + * that can't deal with (for instance) using 3 bits per pixel. + * I have seen no programs that can deal with using 3 bits per + * pixel. I have seen programs which can deal with 1, 4, and + * 8 bits per pixel. + * + * Based on this, I adjust actual the number of bits per pixel + * as follows. If anyone knows better, PLEASE tell me! + */ + switch(bpp) + { + case 2: + case 3: + bpp = 4; + break; + case 5: + case 6: + case 7: + bpp = 8; + break; + } + + pm_message("Using %d bits per pixel", bpp); + + nbyte += BMPwritefileheader(fp, classv, bpp, x, y); + nbyte += BMPwriteinfoheader(fp, classv, bpp, x, y); + nbyte += BMPwritergbtable(fp, classv, bpp, colors, R, G, B); + + if(nbyte != ( BMPlenfileheader(classv) + + BMPleninfoheader(classv) + + BMPlenrgbtable(classv, bpp))) + { + pm_error(er_internal, "BMPEncode"); + } + + nbyte += BMPwritebits(fp, x, y, bpp, pixels, true, cht); + if(nbyte != BMPlenfile(classv, bpp, x, y)) + { + pm_error(er_internal, "BMPEncode"); + } +} + +/* + * Write a BMP file of the given class, with 24 bits per pixel nonindexed. + */ +static void +BMPEncode24( + FILE *fp, + int classv, + int x, + int y, + pixel **pixels) +{ + unsigned long nbyte = 0; + int bpp = 24; + + pm_message("Using %d bits per pixel", bpp); + + nbyte += BMPwritefileheader(fp, classv, bpp, x, y); + nbyte += BMPwriteinfoheader(fp, classv, bpp, x, y); + + if(nbyte != ( BMPlenfileheader(classv) + + BMPleninfoheader(classv))) + { + pm_error(er_internal, "BMPEncode24"); + } + + nbyte += BMPwritebits(fp, x, y, bpp, pixels, false, colorhash_table()); + if(nbyte != BMPlenfile(classv, bpp, x, y)) + { + pm_error(er_internal, "BMPEncode24"); + } +} + +int PNMWriterBMP:: +WriteData(xel *array, xelval *) { + if (rows<=0 || cols<=0) { + return 0; + } + + int classv = C_WIN; + + int colors; + int i; + colorhist_vector chv; + pixval Red[MAXCOLORS]; + pixval Green[MAXCOLORS]; + pixval Blue[MAXCOLORS]; + + pixel** pixels; + colorhash_table cht; + +#if 0 + { + char *name; + switch (classv) + { + case C_WIN: + name = "a Windows"; + break; + case C_OS2: + name = "an OS/2"; + break; + default: + pm_error(er_internal, "report"); + break; + } + pm_message("generating %s BMP file", name); + } +#endif + + // We need an honest 2-d array of pixels, instead of one big 1-d array. + pixels = (pixel **)alloca(sizeof(pixel *) * rows); + for (i = 0; i < rows; i++) { + pixels[i] = (pixel *)(array + i * cols); + } + + /* Figure out the colormap. */ + pm_message("computing colormap..."); + chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors); + if (chv == (colorhist_vector) 0) { + pm_message("too many colors; generating 24-bit BMP file"); + + BMPEncode24(file, classv, cols, rows, pixels); + + } else { + pm_message("%d colors found", colors); + + /* + * Now turn the ppm colormap into the appropriate GIF + * colormap. + */ + if (maxval > 255) + { + pm_message("maxval is not 255 - automatically rescaling colors"); + } + for (i = 0; i < colors; ++i) + { + if (maxval == 255) + { + Red[i] = PPM_GETR(chv[i].color); + Green[i] = PPM_GETG(chv[i].color); + Blue[i] = PPM_GETB(chv[i].color); + } + else + { + Red[i] = (pixval) PPM_GETR(chv[i].color) * 255 / maxval; + Green[i] = (pixval) PPM_GETG(chv[i].color) * 255 / maxval; + Blue[i] = (pixval) PPM_GETB(chv[i].color) * 255 / maxval; + } + } + + /* And make a hash table for fast lookup. */ + cht = ppm_colorhisttocolorhash(chv, colors); + ppm_freecolorhist(chv); + + BMPEncode(file, classv, cols, rows, pixels, colors, cht, + Red, Green, Blue); + } + + return rows; +} diff --git a/panda/src/pnmimage/pnm-image-writesgi.cxx b/panda/src/pnmimage/pnm-image-writesgi.cxx new file mode 100644 index 0000000000..3bc12270eb --- /dev/null +++ b/panda/src/pnmimage/pnm-image-writesgi.cxx @@ -0,0 +1,310 @@ +// pnm-image-writesgi.cc +// +// PNMImage::WriteSGI() and supporting functions. + + + +// Much code in this file originally came from from Netpbm, +// specifically pnmtosgi.c. It has since been fairly heavily +// modified. + +/* pnmtosgi.c - convert portable anymap to SGI image +** +** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) +** +** Based on the SGI image description v0.9 by Paul Haeberli (paul@sgi.comp) +** Available via ftp from sgi.com:graphics/SGIIMAGESPEC +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +** +** 29Jan94: first version +*/ + +#ifndef WIN32VC + #ifndef PENV_WIN32 + #include + #endif +#endif + +#include "pnmImage.h" +#include "pnmWriter.h" +#include "pnmWriterTypes.h" +#include "../pnm/sgi.h" + + + +#define WORSTCOMPR(x) (2*(x) + 2) + + +#define MAXVAL_BYTE 255 +#define MAXVAL_WORD 65535 + +static char storage = STORAGE_RLE; + +inline void +put_byte(FILE *out_file, unsigned char b) { + putc(b, out_file); +} + +static void +put_big_short(FILE *out_file, short s) { + if ( pm_writebigshort( out_file, s ) == -1 ) + pm_error( "write error" ); +} + + +static void +put_big_long(FILE *out_file, long l) { + if ( pm_writebiglong( out_file, l ) == -1 ) + pm_error( "write error" ); +} + + +static void +put_short_as_byte(FILE *out_file, short s) { + put_byte(out_file, (unsigned char)s); +} + +PNMWriterSGI:: +~PNMWriterSGI() { + if (table!=NULL) { + // Rewrite the table with the correct values in it. + fseek(file, table_start, SEEK_SET); + WriteTable(); + delete[] table; + } +} + +bool PNMWriterSGI:: +WriteHeader() { + table = NULL; + + switch (color_type) { + case PNMImage::Grayscale: + dimensions = 2; channels = 1; + break; + + case PNMImage::TwoChannel: + dimensions = 2; channels = 2; + break; + + case PNMImage::Color: + dimensions = 3; channels = 3; + break; + + case PNMImage::FourChannel: + dimensions = 3; channels = 4; + break; + } + + // For some reason, we have problems with SGI image files whose pixmax value + // is not 255 or 65535. So, we'll round up when writing. + if( maxval <= MAXVAL_BYTE ) { + bpc = 1; + new_maxval = MAXVAL_BYTE; + } else if( maxval <= MAXVAL_WORD ) { + bpc = 2; + new_maxval = MAXVAL_WORD; + } else { + return false; + } + + if( storage != STORAGE_VERBATIM ) { + table = new TabEntry[channels * rows]; + memset(table, 0, channels * rows * sizeof(TabEntry)); + } + + write_header("no name"); + if (table!=NULL) { + table_start = ftell(file); + + // The first time we write the table, it has zeroes. We'll correct + // this later. + WriteTable(); + } + + current_row = rows-1; + return true; +} + + +bool PNMWriterSGI:: +WriteRow(xel *row_data, xelval *alpha_data) { + ScanLine channel[4]; + + BuildScanline(channel, row_data, alpha_data); + + if( bpc == 1 ) + WriteChannels(channel, put_short_as_byte); + else + WriteChannels(channel, put_big_short); + + for (int i = 0; i < channels; i++) { + delete[] channel[i].data; + } + + current_row--; + return true; +} + + +void PNMWriterSGI:: +write_header(char *imagename) { + int i; + + put_big_short(file, SGI_MAGIC); + put_byte(file, storage); + put_byte(file, (char)bpc); + put_big_short(file, dimensions); + put_big_short(file, cols); + put_big_short(file, rows); + put_big_short(file, channels); + put_big_long(file, 0); /* PIXMIN */ + put_big_long(file, maxval); /* PIXMAX */ + for( i = 0; i < 4; i++ ) + put_byte(file, 0); + for( i = 0; i < 79 && imagename[i] != '\0'; i++ ) + put_byte(file, imagename[i]); + for(; i < 80; i++ ) + put_byte(file, 0); + put_big_long(file, CMAP_NORMAL); + for( i = 0; i < 404; i++ ) + put_byte(file, 0); +} + + +void PNMWriterSGI:: +WriteTable() { + int i; + int tabsize = rows*channels; + + for( i = 0; i < tabsize; i++ ) { + put_big_long(file, table[i].start); + } + for( i = 0; i < tabsize; i++ ) + put_big_long(file, table[i].length); +} + + +void PNMWriterSGI:: +WriteChannels(ScanLine channel[], void (*put)(FILE *, short)) { + int i, col; + + for( i = 0; i < channels; i++ ) { + Table(i).start = ftell(file); + Table(i).length = channel[i].length * bpc; + + for( col = 0; col < channel[i].length; col++ ) { + (*put)(file, channel[i].data[col]); + } + } +} + + +void PNMWriterSGI:: +BuildScanline(ScanLine output[], xel *row_data, xelval *alpha_data) { + int col; + ScanElem *temp; + + if( storage != STORAGE_VERBATIM ) { + rletemp = (ScanElem *)alloca(WORSTCOMPR(cols) * sizeof(ScanElem)); + } + temp = new ScanElem[cols]; + + if( channels <= 2 ) { + for( col = 0; col < cols; col++ ) + temp[col] = (ScanElem) + (new_maxval * PPM_GETB(row_data[col]) / maxval); + temp = Compress(temp, output[0]); + + if (channels == 2) { + for( col = 0; col < cols; col++ ) + temp[col] = (ScanElem) + (new_maxval * alpha_data[col] / maxval); + temp = Compress(temp, output[1]); + } + + } else { + for( col = 0; col < cols; col++ ) + temp[col] = (ScanElem) + (new_maxval * PPM_GETR(row_data[col]) / maxval); + temp = Compress(temp, output[0]); + for( col = 0; col < cols; col++ ) + temp[col] = (ScanElem) + (new_maxval * PPM_GETG(row_data[col]) / maxval); + temp = Compress(temp, output[1]); + for( col = 0; col < cols; col++ ) + temp[col] = (ScanElem) + (new_maxval * PPM_GETB(row_data[col]) / maxval); + temp = Compress(temp, output[2]); + if (channels == 4) { + for( col = 0; col < cols; col++ ) + temp[col] = (ScanElem) + (new_maxval * alpha_data[col] / maxval); + temp = Compress(temp, output[3]); + } + } + + delete[] temp; +} + + +PNMWriterSGI::ScanElem *PNMWriterSGI:: +Compress(ScanElem *temp, ScanLine &output) { + int len; + + switch( storage ) { + case STORAGE_VERBATIM: + output.length = cols; + output.data = temp; + temp = new ScanElem[cols]; + break; + case STORAGE_RLE: + len = RLECompress(temp, cols); /* writes result into rletemp */ + output.length = len; + output.data = new ScanElem[len]; + memcpy(output.data, rletemp, len * sizeof(ScanElem)); + break; + default: + pm_error("unknown storage type - can\'t happen"); + } + return temp; +} + + +/* +slightly modified RLE algorithm from ppmtoilbm.c +written by Robert A. Knop (rknop@mop.caltech.edu) +*/ +int PNMWriterSGI:: +RLECompress(ScanElem *inbuf, int size) { + int in, out, hold, count; + ScanElem *outbuf = rletemp; + + in=out=0; + while( in=size-2)&&(in=127 ) + break; + } + outbuf[hold]=(ScanElem)(count | 0x80); + } + } + outbuf[out++] = (ScanElem)0; /* terminator */ + return(out); +} + diff --git a/panda/src/pnmimage/pnm-image-writeyuv.cxx b/panda/src/pnmimage/pnm-image-writeyuv.cxx new file mode 100644 index 0000000000..3c293e3b79 --- /dev/null +++ b/panda/src/pnmimage/pnm-image-writeyuv.cxx @@ -0,0 +1,96 @@ +// pnm-image-writeyuv.cc +// +// PNMImage::WriteYUV() and supporting functions. + + + +// Much code in this file is borrowed from Netpbm, specifically ppmtoyuv.c. +/* ppmtoyuv.c - convert a portable pixmap into an Abekas YUV file +** +** by Marc Boucher +** Internet: marc@PostImage.cxxOM +** +** Based on Example Conversion Program, A60/A64 Digital Video Interface +** Manual, page 69. +** +** Copyright (C) 1991 by DHD PostImage Inc. +** Copyright (C) 1987 by Abekas Video Systems Inc. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + + + +#include "pnmImage.h" +#include "pnmWriter.h" +#include "pnmWriterTypes.h" + + +bool PNMWriterYUV:: +WriteHeader() { + if (yuvbuf!=NULL) { + pm_freerow((char *)yuvbuf); + } + + yuvbuf = (unsigned char *) pm_allocrow( cols, 2 ); + + return true; +} + +bool PNMWriterYUV:: +WriteRow(xel *row_data, xelval *) { + int col; + unsigned long y1, y2=0, u=0, v=0, u0=0, u1, u2, v0=0, v1, v2; + static const int max_byte = 255; + + unsigned char *yuvptr; + + for (col = 0, yuvptr=yuvbuf; col < cols; col += 2) { + pixval r, g, b; + + /* first pixel gives Y and 0.5 of chroma */ + r = (pixval)(max_byte * PPM_GETR(row_data[col])/maxval); + g = (pixval)(max_byte * PPM_GETG(row_data[col])/maxval); + b = (pixval)(max_byte * PPM_GETB(row_data[col])/maxval); + + y1 = 16829 * r + 33039 * g + 6416 * b + (0xffff & y2); + u1 = -4853 * r - 9530 * g + 14383 * b; + v1 = 14386 * r - 12046 * g - 2340 * b; + + /* second pixel just yields a Y and 0.25 U, 0.25 V */ + r = (pixval)(max_byte * PPM_GETR(row_data[col])/maxval); + g = (pixval)(max_byte * PPM_GETG(row_data[col])/maxval); + b = (pixval)(max_byte * PPM_GETB(row_data[col])/maxval); + + y2 = 16829 * r + 33039 * g + 6416 * b + (0xffff & y1); + u2 = -2426 * r - 4765 * g + 7191 * b; + v2 = 7193 * r - 6023 * g - 1170 * b; + + /* filter the chroma */ + u = u0 + u1 + u2 + (0xffff & u); + v = v0 + v1 + v2 + (0xffff & v); + + u0 = u2; + v0 = v2; + + *yuvptr++ = (unsigned char)((u >> 16) + 128); + *yuvptr++ = (unsigned char)((y1 >> 16) + 16); + *yuvptr++ = (unsigned char)((v >> 16) + 128); + *yuvptr++ = (unsigned char)((y2 >> 16) + 16); + } + fwrite(yuvbuf, cols*2, 1, file); + + return true; +} + +PNMWriterYUV:: +~PNMWriterYUV() { + if (yuvbuf!=NULL) { + pm_freerow((char *)yuvbuf); + } +} diff --git a/panda/src/pnmimage/pnmFileType.cxx b/panda/src/pnmimage/pnmFileType.cxx new file mode 100644 index 0000000000..b1a9623aaf --- /dev/null +++ b/panda/src/pnmimage/pnmFileType.cxx @@ -0,0 +1,154 @@ +// Filename: pnmFileType.cxx +// Created by: drose (15Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmFileType.h" + +#include +#include + +bool PNMFileType::_did_init_pnm = false; +TypeHandle PNMFileType::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileType::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileType:: +PNMFileType() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileType::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileType:: +~PNMFileType() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileType::get_num_extensions +// Access: Public, Virtual +// Description: Returns the number of different possible filename +// extensions associated with this particular file type. +//////////////////////////////////////////////////////////////////// +int PNMFileType:: +get_num_extensions() const { + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileType::get_extension +// Access: Public, Virtual +// Description: Returns the nth possible filename extension +// associated with this particular file type, without a +// leading dot. +//////////////////////////////////////////////////////////////////// +string PNMFileType:: +get_extension(int) const { + nassertr(false, string()); + return string(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileType::get_suggested_extension +// Access: Public, Virtual +// Description: Returns a suitable filename extension (without a +// leading dot) to suggest for files of this type, or +// empty string if no suggestions are available. +//////////////////////////////////////////////////////////////////// +string PNMFileType:: +get_suggested_extension() const { + if (get_num_extensions() > 0) { + return get_extension(0); + } + return string(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileType::has_magic_number +// Access: Public, Virtual +// Description: Returns true if this particular file type uses a +// magic number to identify it, false otherwise. +//////////////////////////////////////////////////////////////////// +bool PNMFileType:: +has_magic_number() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileType::matches_magic_number +// Access: Public, Virtual +// Description: Returns true if the indicated "magic number" byte +// stream (the initial few bytes read from the file) +// matches this particular file type, false otherwise. +//////////////////////////////////////////////////////////////////// +bool PNMFileType:: +matches_magic_number(const string &) const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileType::make_reader +// Access: Public, Virtual +// Description: Allocates and returns a new PNMReader suitable for +// reading from this file type, if possible. If reading +// from this file type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMReader *PNMFileType:: +make_reader(FILE *, bool, const string &) { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileType::make_writer +// Access: Public, Virtual +// Description: Allocates and returns a new PNMWriter suitable for +// reading from this file type, if possible. If writing +// files of this type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMWriter *PNMFileType:: +make_writer(FILE *, bool) { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileType::init_pnm +// Access: Protected, Static +// Description: Initializes the underlying PNM library, if it has not +// already been initialized. This should be called by +// every implementation of make_reader() and +// make_writer(), to ensure that the library is properly +// initialized before any I/O is attempted. +//////////////////////////////////////////////////////////////////// +void PNMFileType:: +init_pnm() { + if (!_did_init_pnm) { + _did_init_pnm = true; + + // Make an argc/argv style copy of the ExecutionEnvironment's + // args. We do this because pm_init() might attempt to change + // this (for instance, to remove arguments it finds). + + int argc = ExecutionEnvironment::get_num_args() + 1; + char **argv = new char *[argc + 1]; + argv[0] = strdup(ExecutionEnvironment::get_binary_name().c_str()); + int i; + for (i = 1; i < argc; i++) { + argv[i] = strdup(ExecutionEnvironment::get_arg(i - 1).c_str()); + } + argv[argc] = (char *)NULL; + + pm_init(&argc, argv); + + // Now deallocate all the stuff we just allocated. + for (i = 0; i < argc; i++) { + delete[] argv[i]; + } + delete[] argv; + } +} + diff --git a/panda/src/pnmimage/pnmFileType.h b/panda/src/pnmimage/pnmFileType.h new file mode 100644 index 0000000000..3686dac58b --- /dev/null +++ b/panda/src/pnmimage/pnmFileType.h @@ -0,0 +1,69 @@ +// Filename: pnmFileType.h +// Created by: drose (15Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMFILETYPE_H +#define PNMFILETYPE_H + +#include + +#include "pnmimage_base.h" + +#include + +class PNMReader; +class PNMWriter; + +//////////////////////////////////////////////////////////////////// +// Class : PNMFileType +// Description : This is the base class of a family of classes that +// represent particular image file types that PNMImage +// supports. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PNMFileType : public TypedObject { +protected: + PNMFileType(); + +public: + virtual ~PNMFileType(); + + virtual string get_name() const=0; + + virtual int get_num_extensions() const; + virtual string get_extension(int n) const; + virtual string get_suggested_extension() const; + + virtual bool has_magic_number() const; + virtual bool matches_magic_number(const string &magic_number) const; + + virtual PNMReader *make_reader(FILE *file, bool owns_file = true, + const string &magic_number = string()); + virtual PNMWriter *make_writer(FILE *file, bool owns_file = true); + +protected: + static void init_pnm(); + +private: + static bool _did_init_pnm; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedObject::init_type(); + register_type(_type_handle, "PNMFileType", + TypedObject::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif + diff --git a/panda/src/pnmimage/pnmFileTypeRegistry.cxx b/panda/src/pnmimage/pnmFileTypeRegistry.cxx new file mode 100644 index 0000000000..79dcc12e93 --- /dev/null +++ b/panda/src/pnmimage/pnmFileTypeRegistry.cxx @@ -0,0 +1,232 @@ +// Filename: pnmFileTypeRegistry.cxx +// Created by: drose (15Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmFileTypeRegistry.h" +#include "pnmFileType.h" +#include "config_pnmimage.h" + +#include +#include + +#include + +PNMFileTypeRegistry *PNMFileTypeRegistry::_global_ptr; + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRegistry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeRegistry:: +PNMFileTypeRegistry() { + _requires_sort = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRegistry::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeRegistry:: +~PNMFileTypeRegistry() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRegistry::get_ptr +// Access: Public, Static +// Description: Returns a pointer to the global PNMFileTypeRegistry +// object. +//////////////////////////////////////////////////////////////////// +PNMFileTypeRegistry *PNMFileTypeRegistry:: +get_ptr() { + if (_global_ptr == (PNMFileTypeRegistry *)NULL) { + _global_ptr = new PNMFileTypeRegistry; + } + return _global_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRegistry::get_num_types +// Access: Public +// Description: Returns the total number of types registered. +//////////////////////////////////////////////////////////////////// +int PNMFileTypeRegistry:: +get_num_types() const { + if (_requires_sort) { + ((PNMFileTypeRegistry *)this)->sort_preferences(); + } + return _types.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRegistry::get_type +// Access: Public +// Description: Returns the nth type registered. +//////////////////////////////////////////////////////////////////// +PNMFileType *PNMFileTypeRegistry:: +get_type(int n) const { + nassertr(n >= 0 && n < _types.size(), NULL); + return _types[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRegistry::get_type_from_extension +// Access: Public +// Description: Tries to determine what the PNMFileType is likely to +// be for a particular image file based on its +// extension. Returns a suitable PNMFileType pointer, +// or NULL if no type can be determined. +//////////////////////////////////////////////////////////////////// +PNMFileType *PNMFileTypeRegistry:: +get_type_from_extension(const string &filename) const { + if (_requires_sort) { + ((PNMFileTypeRegistry *)this)->sort_preferences(); + } + + // Extract the extension from the filename; if there is no dot, use + // the whole filename as the extension. This allows us to pass in + // just a dotless extension name in lieu of a filename. + + string extension; + size_t dot = filename.rfind('.'); + + if (dot == string::npos) { + extension = filename; + } else { + extension = filename.substr(dot + 1); + } + + if (extension.find('/') != string::npos) { + // If we picked the whole filename and it contains slashes, or if + // the rightmost dot wasn't in the basename of the filename, then + // it's actually a filename without an extension. + extension = ""; + } + + Extensions::const_iterator ei; + ei = _extensions.find(extension); + if (ei == _extensions.end() || (*ei).second.empty()) { + // Nothing matches that string. + return NULL; + } + + // Return the first file type associated with the given extension. + return (*ei).second.front(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRegistry::get_type_from_magic_number +// Access: Public +// Description: Tries to determine what the PNMFileType is likely to +// be for a particular image file based on its +// magic number, the first two bytes read from the +// file. Returns a suitable PNMFileType pointer, or +// NULL if no type can be determined. +//////////////////////////////////////////////////////////////////// +PNMFileType *PNMFileTypeRegistry:: +get_type_from_magic_number(const string &magic_number) const { + if (_requires_sort) { + ((PNMFileTypeRegistry *)this)->sort_preferences(); + } + + Types::const_iterator ti; + for (ti = _types.begin(); ti != _types.end(); ++ti) { + PNMFileType *type = (*ti); + if (type->has_magic_number() && + type->matches_magic_number(magic_number)) { + return type; + } + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRegistry::write_types +// Access: Public +// Description: Writes a list of supported image file types to the +// indicated output stream, one per line. +//////////////////////////////////////////////////////////////////// +void PNMFileTypeRegistry:: +write_types(ostream &out, int indent_level) const { + if (_types.empty()) { + indent(out, indent_level) << "(No image types are known).\n"; + } else { + Types::const_iterator ti; + for (ti = _types.begin(); ti != _types.end(); ++ti) { + PNMFileType *type = (*ti); + string name = type->get_name(); + indent(out, indent_level) << name; + indent(out, max(30 - (int)name.length(), 0)) << " "; + + int num_extensions = type->get_num_extensions(); + if (num_extensions == 1) { + out << "." << type->get_extension(0); + } else if (num_extensions > 1) { + out << "." << type->get_extension(0); + for (int i = 1; i < num_extensions; i++) { + out << ", ." << type->get_extension(i); + } + } + out << "\n"; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRegistry::register_type +// Access: Public +// Description: Defines a new PNMFileType in the universe. +//////////////////////////////////////////////////////////////////// +void PNMFileTypeRegistry:: +register_type(PNMFileType *type) { + // Make sure we haven't already registered this type. + if (find(_types.begin(), _types.end(), type) != _types.end()) { + pnmimage_cat.warning() + << "Attempt to register PNMFileType " << type->get_name() + << " (" << type->get_type() << ") more than once.\n"; + return; + } + + _types.push_back(type); + + // Collect the unique extensions associated with the type. + set unique_extensions; + int num_extensions = type->get_num_extensions(); + for (int i = 0; i < num_extensions; i++) { + string extension = downcase(type->get_extension(i)); + + if (!unique_extensions.insert(extension).second) { + pnmimage_cat.warning() + << "PNMFileType " << type->get_name() + << " (" << type->get_type() << ") defined extension " + << extension << " more than once.\n"; + } + } + + set::iterator ui; + for (ui = unique_extensions.begin(); ui != unique_extensions.end(); ++ui) { + _extensions[*ui].push_back(type); + } + + _requires_sort = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRegistry::sort_preferences +// Access: Private +// Description: Sorts the PNMFileType pointers in order according to +// user-specified preferences in the config file. This +// allows us to choose a particular PNMFileType over +// another for particular extensions when multiple file +// types map to the same extension, or for file types +// that have no magic number. +//////////////////////////////////////////////////////////////////// +void PNMFileTypeRegistry:: +sort_preferences() { + // So, we don't do anything here yet. One day we will. + + _requires_sort = false; +} diff --git a/panda/src/pnmimage/pnmFileTypeRegistry.h b/panda/src/pnmimage/pnmFileTypeRegistry.h new file mode 100644 index 0000000000..71fc06a8d9 --- /dev/null +++ b/panda/src/pnmimage/pnmFileTypeRegistry.h @@ -0,0 +1,56 @@ +// Filename: pnmFileTypeRegistry.h +// Created by: drose (15Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMFILETYPEREGISTRY_H +#define PNMFILETYPEREGISTRY_H + +#include + +#include + +#include + +class PNMFileType; + +//////////////////////////////////////////////////////////////////// +// Class : PNMFileTypeRegistry +// Description : This class maintains the set of all known +// PNMFileTypes in the universe. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PNMFileTypeRegistry { +protected: + PNMFileTypeRegistry(); + +public: + ~PNMFileTypeRegistry(); + + static PNMFileTypeRegistry *get_ptr(); + + int get_num_types() const; + PNMFileType *get_type(int n) const; + + PNMFileType *get_type_from_extension(const string &filename) const; + PNMFileType *get_type_from_magic_number(const string &magic_number) const; + + void write_types(ostream &out, int indent_level = 0) const; + + void register_type(PNMFileType *type); + +private: + void sort_preferences(); + + typedef vector Types; + Types _types; + + typedef map Extensions; + Extensions _extensions; + + bool _requires_sort; + + static PNMFileTypeRegistry *_global_ptr; +}; + +#endif + diff --git a/panda/src/pnmimage/pnmImage.I b/panda/src/pnmimage/pnmImage.I new file mode 100644 index 0000000000..9280e0487c --- /dev/null +++ b/panda/src/pnmimage/pnmImage.I @@ -0,0 +1,757 @@ +// Filename: pnmImage.I +// Created by: drose (15Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PNMImage:: +PNMImage() { + _array = NULL; + _alpha = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PNMImage:: +PNMImage(const Filename &filename, PNMFileType *type) { + _array = NULL; + _alpha = NULL; + + read(filename, type); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PNMImage:: +PNMImage(int x_size, int y_size, int num_channels, xelval maxval, + PNMFileType *type) { + _array = NULL; + _alpha = NULL; + + clear(x_size, y_size, num_channels, maxval, type); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PNMImage:: +PNMImage(const PNMImage ©) { + // We don't need to invoke PNMImageHeader's copy constructor, + // because we'll just call copy_from(). + _array = NULL; + _alpha = NULL; + + copy_from(copy); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::Copy Assigment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +operator = (const PNMImage ©) { + copy_from(copy); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PNMImage:: +~PNMImage() { + clear(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::clamp_val +// Access: Public +// Description: A handy function to clamp values to +// [0..get_maxval()]. +//////////////////////////////////////////////////////////////////// +INLINE xelval PNMImage:: +clamp_val(int input_value) const { + return (xelval)min(max(0, input_value), (int)get_maxval()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::to_val +// Access: Public +// Description: A handy function to scale values from [0..1] to +// [0..get_maxval()]. +//////////////////////////////////////////////////////////////////// +INLINE xelval PNMImage:: +to_val(double input_value) const { + return clamp_val(input_value * get_maxval() + 0.5); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::from_val +// Access: Public +// Description: A handy function to scale values from +// [0..get_maxval()] to [0..1]. +//////////////////////////////////////////////////////////////////// +INLINE double PNMImage:: +from_val(xelval input_value) const { + return (double)input_value / (double)get_maxval(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::fill +// Access: Public +// Description: Sets the entire image (except the alpha channel) to +// the given color. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +fill(double red, double green, double blue) { + fill_val((xelval)(red * get_maxval()), + (xelval)(green * get_maxval()), + (xelval)(blue * get_maxval())); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::fill +// Access: Public +// Description: Sets the entire image (except the alpha channel) to +// the given grayscale level. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +fill(double gray) { + fill(gray, gray, gray); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::fill_val +// Access: Public +// Description: Sets the entire image (except the alpha channel) to +// the given grayscale level. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +fill_val(xelval gray) { + fill_val(gray, gray, gray); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::alpha_fill +// Access: Public +// Description: Sets the entire alpha channel to the given level. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +alpha_fill(double alpha) { + alpha_fill_val((xelval)(alpha * get_maxval())); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::is_valid +// Access: Public +// Description: Returns true if the image has been read in or +// correctly initialized with a height and width. If +// this returns false, virtually all member functions +// except clear() and read() are invalid function calls. +//////////////////////////////////////////////////////////////////// +INLINE bool PNMImage:: +is_valid() const { + return (_array != NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_num_channels +// Access: Public +// Description: Changes the number of channels associated with the +// image. The new number of channels must be an integer +// in the range 1 through 4, inclusive. This will +// allocate and/or deallocate memory as necessary to +// accomodate; see set_color_type(). +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_num_channels(int num_channels) { + set_color_type((ColorType)num_channels); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::add_alpha +// Access: Public +// Description: Adds an alpha channel to the image, if it does not +// already have one. The alpha channel is initialized +// to zeros. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +add_alpha() { + set_color_type(is_grayscale() ? CT_two_channel : CT_four_channel); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::remove_alpha +// Access: Public +// Description: Removes the image's alpha channel, if it exists. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +remove_alpha() { + set_color_type(is_grayscale() ? CT_grayscale : CT_color); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::make_grayscale +// Access: Public +// Description: Converts the image from RGB to grayscale. Any alpha +// channel, if present, is left undisturbed. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +make_grayscale() { + make_grayscale(_default_rc, _default_gc, _default_bc); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::make_rgb +// Access: Public +// Description: Converts the image from grayscale to RGB. Any alpha +// channel, if present, is left undisturbed. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +make_rgb() { + set_color_type(has_alpha() ? CT_four_channel : CT_color); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_xel_val +// Access: Public +// Description: Returns the RGB color at the indicated pixel. Each +// component is in the range 0..maxval. +//////////////////////////////////////////////////////////////////// +INLINE const xel &PNMImage:: +get_xel_val(int x, int y) const { + return row(y)[x]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_xel_val +// Access: Public +// Description: Changes the RGB color at the indicated pixel. Each +// component is in the range 0..maxval. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_xel_val(int x, int y, const xel &value) { + row(y)[x] = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_xel_val +// Access: Public +// Description: Changes the RGB color at the indicated pixel. Each +// component is in the range 0..maxval. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_xel_val(int x, int y, xelval r, xelval g, xelval b) { + PPM_ASSIGN(row(y)[x], r, g, b); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_xel_val +// Access: Public +// Description: Changes all three color components at the indicated +// pixel to the same value. The value is in the range +// 0..maxval. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_xel_val(int x, int y, xelval gray) { + PPM_ASSIGN(row(y)[x], gray, gray, gray); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_red_val +// Access: Public +// Description: Returns the red component color at the indicated +// pixel. The value returned is in the range 0..maxval. +//////////////////////////////////////////////////////////////////// +INLINE xelval PNMImage:: +get_red_val(int x, int y) const { + return PPM_GETR(get_xel_val(x, y)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_green_val +// Access: Public +// Description: Returns the green component color at the indicated +// pixel. The value returned is in the range 0..maxval. +//////////////////////////////////////////////////////////////////// +INLINE xelval PNMImage:: +get_green_val(int x, int y) const { + return PPM_GETG(get_xel_val(x, y)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_blue_val +// Access: Public +// Description: Returns the blue component color at the indicated +// pixel. The value returned is in the range 0..maxval. +//////////////////////////////////////////////////////////////////// +INLINE xelval PNMImage:: +get_blue_val(int x, int y) const { + return PPM_GETB(get_xel_val(x, y)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_gray_val +// Access: Public +// Description: Returns the gray component color at the indicated +// pixel. This only has a meaningful value for +// grayscale images; for other image types, this returns +// the value of the blue channel only. However, also +// see the get_bright() function. The value returned is +// in the range 0..maxval. +//////////////////////////////////////////////////////////////////// +INLINE xelval PNMImage:: +get_gray_val(int x, int y) const { + return PPM_GETB(get_xel_val(x, y)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_alpha_val +// Access: Public +// Description: Returns the alpha component color at the indicated +// pixel. It is an error to call this unless +// has_alpha() is true. The value returned is in the +// range 0..maxval. +//////////////////////////////////////////////////////////////////// +INLINE xelval PNMImage:: +get_alpha_val(int x, int y) const { + return alpha_row(y)[x]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_red_val +// Access: Public +// Description: Sets the red component color only at the indicated +// pixel. The value given should be in the range +// 0..maxval. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_red_val(int x, int y, xelval r) { + PPM_PUTR(row(y)[x], r); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_green_val +// Access: Public +// Description: Sets the green component color only at the indicated +// pixel. The value given should be in the range +// 0..maxval. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_green_val(int x, int y, xelval g) { + PPM_PUTG(row(y)[x], g); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_blue_val +// Access: Public +// Description: Sets the blue component color only at the indicated +// pixel. The value given should be in the range +// 0..maxval. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_blue_val(int x, int y, xelval b) { + PPM_PUTB(row(y)[x], b); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_gray_val +// Access: Public +// Description: Sets the gray component color at the indicated +// pixel. This is only meaningful for grayscale images; +// for other image types, this simply sets the blue +// component color. However, also see set_xel_val(), +// which can set all the component colors to the same +// grayscale level, and hence works correctly both for +// grayscale and color images. The value given should +// be in the range 0..maxval. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_gray_val(int x, int y, xelval gray) { + PPM_PUTB(row(y)[x], gray); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_alpha_val +// Access: Public +// Description: Sets the alpha component color only at the indicated +// pixel. It is an error to call this unless +// has_alpha() is true. The value given should be in +// the range 0..maxval. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_alpha_val(int x, int y, xelval a) { + alpha_row(y)[x] = a; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_xel +// Access: Public +// Description: Returns the RGB color at the indicated pixel. Each +// component is a double in the range 0..1. +//////////////////////////////////////////////////////////////////// +INLINE RGBColord PNMImage:: +get_xel(int x, int y) const { + return RGBColord(from_val(get_red_val(x, y)), + from_val(get_green_val(x, y)), + from_val(get_blue_val(x, y))); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_xel +// Access: Public +// Description: Changes the RGB color at the indicated pixel. Each +// component is a double in the range 0..1. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_xel(int x, int y, const RGBColord &value) { + set_xel_val(x, y, to_val(value[0]), to_val(value[1]), to_val(value[2])); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_xel +// Access: Public +// Description: Changes the RGB color at the indicated pixel. Each +// component is a double in the range 0..1. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_xel(int x, int y, double r, double g, double b) { + set_xel_val(x, y, to_val(r), to_val(g), to_val(b)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_xel +// Access: Public +// Description: Changes all three color components at the indicated +// pixel to the same value. The value is a double in +// the range 0..1. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_xel(int x, int y, double gray) { + set_xel_val(x, y, to_val(gray), to_val(gray), to_val(gray)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_red +// Access: Public +// Description: Returns the red component color at the indicated +// pixel. The value returned is a double in the range +// 0..1. +//////////////////////////////////////////////////////////////////// +INLINE double PNMImage:: +get_red(int x, int y) const { + return from_val(get_red_val(x, y)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_green +// Access: Public +// Description: Returns the green component color at the indicated +// pixel. The value returned is a double in the range +// 0..1. +//////////////////////////////////////////////////////////////////// +INLINE double PNMImage:: +get_green(int x, int y) const { + return from_val(get_green_val(x, y)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_blue +// Access: Public +// Description: Returns the blue component color at the indicated +// pixel. The value returned is a double in the range +// 0..1. +//////////////////////////////////////////////////////////////////// +INLINE double PNMImage:: +get_blue(int x, int y) const { + return from_val(get_blue_val(x, y)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_gray +// Access: Public +// Description: Returns the gray component color at the indicated +// pixel. This only has a meaningful value for +// grayscale images; for other image types, this returns +// the value of the blue channel only. However, also +// see the get_bright() function. The value returned is +// a double in the range 0..1. +//////////////////////////////////////////////////////////////////// +INLINE double PNMImage:: +get_gray(int x, int y) const { + return from_val(get_gray_val(x, y)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_alpha +// Access: Public +// Description: Returns the alpha component color at the indicated +// pixel. It is an error to call this unless +// has_alpha() is true. The value returned is a double +// in the range 0..1. +//////////////////////////////////////////////////////////////////// +INLINE double PNMImage:: +get_alpha(int x, int y) const { + return from_val(get_alpha_val(x, y)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_red +// Access: Public +// Description: Sets the red component color only at the indicated +// pixel. The value given should be a double in the +// range 0..1. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_red(int x, int y, double r) { + set_red_val(x, y, to_val(r)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_green +// Access: Public +// Description: Sets the green component color only at the indicated +// pixel. The value given should be a double in the +// range 0..1. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_green(int x, int y, double r) { + set_green_val(x, y, to_val(r)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_blue +// Access: Public +// Description: Sets the blue component color only at the indicated +// pixel. The value given should be a double in the +// range 0..1. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_blue(int x, int y, double r) { + set_blue_val(x, y, to_val(r)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_gray +// Access: Public +// Description: Sets the gray component color at the indicated +// pixel. This is only meaningful for grayscale images; +// for other image types, this simply sets the blue +// component color. However, also see set_xel(), which +// can set all the component colors to the same +// grayscale level, and hence works correctly both for +// grayscale and color images. The value given should +// be a double in the range 0..1. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_gray(int x, int y, double r) { + set_gray_val(x, y, to_val(r)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_alpha +// Access: Public +// Description: Sets the alpha component color only at the indicated +// pixel. It is an error to call this unless +// has_alpha() is true. The value given should be in +// the range 0..1. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_alpha(int x, int y, double r) { + set_alpha_val(x, y, to_val(r)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_channel +// Access: Public +// Description: Returns the nth component color at the indicated +// pixel. The channel index should be in the range +// 0..(get_num_channels()-1). The channels are ordered B, +// G, R, A. This is slightly less optimal than +// accessing the component values directly by named +// methods. The value returned is a double in the range +// 0..1. +//////////////////////////////////////////////////////////////////// +INLINE double PNMImage:: +get_channel(int x, int y, int channel) const { + return from_val(get_channel_val(x, y, channel)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_channel_val +// Access: Public +// Description: Sets the nth component color at the indicated +// pixel. The channel index should be in the range +// 0..(get_num_channels()-1). The channels are ordered B, +// G, R, A. This is slightly less optimal than +// setting the component values directly by named +// methods. The value given should be a double in the +// range 0..1. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +set_channel(int x, int y, int channel, double value) { + set_channel_val(x, y, channel, to_val(value)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_bright +// Access: Public +// Description: Returns the linear brightness of the given xel, as a +// double in the range 0..1. This flavor of +// get_bright() returns the correct grayscale brightness +// level for both full-color and grayscale images. +//////////////////////////////////////////////////////////////////// +INLINE double PNMImage:: +get_bright(int x, int y) const { + return get_bright(x, y, _default_rc, _default_gc, _default_bc); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_bright +// Access: Public +// Description: This flavor of get_bright() works correctly only for +// color images. It returns a single brightness value +// for the RGB color at the indicated pixel, based on +// the supplied weights for each component. +//////////////////////////////////////////////////////////////////// +INLINE double PNMImage:: +get_bright(int x, int y, double rc, double gc, double bc) const { + return from_val(rc * get_red_val(x, y) + + gc * get_green_val(x, y) + + bc * get_blue_val(x, y)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_bright +// Access: Public +// Description: This flavor of get_bright() works correctly only for +// four-channel images. It returns a single brightness +// value for the RGBA color at the indicated pixel, +// based on the supplied weights for each component. +//////////////////////////////////////////////////////////////////// +INLINE double PNMImage:: +get_bright(int x, int y, double rc, double gc, double bc, double ac) const { + return from_val(rc * get_red_val(x, y) + + gc * get_green_val(x, y) + + bc * get_blue_val(x, y) + + ac * get_alpha_val(x, y)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::blend +// Access: Public +// Description: Smoothly blends the indicated pixel value in with +// whatever was already in the image, based on the given +// alpha value. An alpha of 1.0 is fully opaque and +// completely replaces whatever was there previously; +// alpha of 0.0 is fully transparent and does nothing. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +blend(int x, int y, const RGBColord &val, double alpha) { + blend(x, y, val[0], val[1], val[2], alpha); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::Array Operator +// Access: Public +// Description: Allows the PNMImage to appear to be a 2-d array of +// xels. +//////////////////////////////////////////////////////////////////// +INLINE xel *PNMImage:: +operator [] (int y) { + return row(y); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::Array Operator +// Access: Public +// Description: Allows the PNMImage to appear to be a 2-d array of +// xels. +//////////////////////////////////////////////////////////////////// +INLINE const xel *PNMImage:: +operator [] (int y) const { + return row(y); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::box_filter +// Access: Public +// Description: This flavor of box_filter() will apply the filter +// over the entire image without resizing or copying; +// the effect is that of a blur operation. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +box_filter(double radius) { + box_filter_from(radius, *this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::gaussian_filter +// Access: Public +// Description: This flavor of gaussian_filter() will apply the filter +// over the entire image without resizing or copying; +// the effect is that of a blur operation. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +gaussian_filter(double radius) { + gaussian_filter_from(radius, *this); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::allocate_array +// Access: Private +// Description: Allocates the internal memory for the RGB or +// grayscale pixels in the image (except alpha). +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +allocate_array() { + _array = new xel[_x_size * _y_size]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::allocate_alpha +// Access: Private +// Description: Allocates the internal memory for the alpha pixels in +// the image. +//////////////////////////////////////////////////////////////////// +INLINE void PNMImage:: +allocate_alpha() { + _alpha = new xelval[_x_size * _y_size]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::row +// Access: Private +// Description: Returns an array of xels corresponding to the nth row +// of the image. +//////////////////////////////////////////////////////////////////// +INLINE xel *PNMImage:: +row(int y) const { + return _array + y * _x_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::alpha_row +// Access: Private +// Description: Returns an array of xelvals corresponding to the nth +// row of the alpha channel. +//////////////////////////////////////////////////////////////////// +INLINE xelval *PNMImage:: +alpha_row(int y) const { + return _alpha + y * _x_size; +} diff --git a/panda/src/pnmimage/pnmImage.cxx b/panda/src/pnmimage/pnmImage.cxx new file mode 100644 index 0000000000..eff8057faf --- /dev/null +++ b/panda/src/pnmimage/pnmImage.cxx @@ -0,0 +1,561 @@ +// Filename: pnmImage.cxx +// Created by: drose (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmImage.h" +#include "pnmReader.h" +#include "pnmWriter.h" + +extern "C" { +#include "../pnm/pnm.h" +#include "../pnm/ppm.h" +#include "../pnm/pgm.h" +#include "../pnm/pbm.h" +#include "../pnm/libppm.h" +#include "../pnm/libpgm.h" +#include "../pnm/libpbm.h" +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::clear +// Access: Public +// Description: Frees all memory allocated for the image, and clears +// all its parameters (size, color, type, etc). +//////////////////////////////////////////////////////////////////// +void PNMImage:: +clear() { + if (_array != (xel *)NULL) { + delete[] _array; + _array = (xel *)NULL; + } + if (_alpha != (xelval *)NULL) { + delete[] _alpha; + _alpha = (xelval *)NULL; + } + _x_size = 0; + _y_size = 0; + _num_channels = 0; + _maxval = 255; + _type = (PNMFileType *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::clear +// Access: Public +// Description: This flavor of clear() reinitializes the image to an +// empty (black) image with the given dimensions. +//////////////////////////////////////////////////////////////////// +void PNMImage:: +clear(int x_size, int y_size, int num_channels, + xelval maxval, PNMFileType *type) { + clear(); + + _x_size = x_size; + _y_size = y_size; + _num_channels = num_channels; + _maxval = maxval; + _type = type; + + if (has_alpha()) { + allocate_alpha(); + memset(_alpha, 0, sizeof(xelval) * _y_size * _x_size); + } + + allocate_array(); + memset(_array, 0, sizeof(xel) * _y_size * _x_size); + + setup_rc(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::copy_from +// Access: Public +// Description: Makes this image become a copy of the other image. +//////////////////////////////////////////////////////////////////// +void PNMImage:: +copy_from(const PNMImage ©) { + clear(); + + if (copy.is_valid()) { + copy_header_from(copy); + + if (has_alpha()) { + memcpy(_alpha, copy._alpha, sizeof(xelval) * _y_size * _x_size); + } + memcpy(_array, copy._array, sizeof(xel) * _y_size * _x_size); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::copy_header_from +// Access: Public +// Description: Copies just the header information into this image. +// This will blow away any image data stored in the +// image. The new image data will be allocated, but +// left unitialized. +//////////////////////////////////////////////////////////////////// +void PNMImage:: +copy_header_from(const PNMImageHeader &header) { + clear(); + PNMImageHeader::operator = (header); + + if (has_alpha()) { + allocate_alpha(); + } + + allocate_array(); + setup_rc(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::fill_val +// Access: Public +// Description: Sets the entire image (except the alpha channel) to +// the given color. +//////////////////////////////////////////////////////////////////// +void PNMImage:: +fill_val(xelval red, xelval green, xelval blue) { + if (is_valid()) { + for (int y = 0; y < get_y_size(); y++) { + for (int x = 0; x < get_x_size(); x++) { + set_xel_val(x, y, red, green, blue); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::alpha_fill_val +// Access: Public +// Description: Sets the entire alpha channel to the given level. +//////////////////////////////////////////////////////////////////// +void PNMImage:: +alpha_fill_val(xelval alpha) { + if (is_valid()) { + if (!has_alpha()) { + add_alpha(); + } + + for (int y = 0; y < get_y_size(); y++) { + for (int x = 0; x < get_x_size(); x++) { + set_alpha_val(x, y, alpha); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::read +// Access: Public +// Description: Reads the indicated image filename. If type is +// non-NULL, it is a suggestion for the type of file it +// is. Returns true if successful, false on error. +//////////////////////////////////////////////////////////////////// +bool PNMImage:: +read(const Filename &filename, PNMFileType *type) { + clear(); + + PNMReader *reader = PNMImageHeader::make_reader(filename, type); + if (reader == (PNMReader *)NULL) { + return false; + } + return read(reader); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::read +// Access: Public +// Description: This flavor of read() uses an already-existing +// PNMReader to read the image file. You can get a +// reader via the PNMImageHeader::make_reader() methods. +// This is a good way to examine the header of a file +// (for instance, to determine its size) before actually +// reading the entire image. +// +// The PNMReader is always deleted upon completion, +// whether succesful or not. +//////////////////////////////////////////////////////////////////// +bool PNMImage:: +read(PNMReader *reader) { + clear(); + + if (reader == NULL) { + return false; + } + + if (!reader->is_valid()) { + delete reader; + return false; + } + + copy_header_from(*reader); + _type = reader->get_type(); + + // We reassign y_size after reading because we might have read a + // truncated file. + _y_size = reader->read_data(_array, _alpha); + delete reader; + + if (_y_size == 0) { + clear(); + return false; + } else { + setup_rc(); + return true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::write +// Access: Public +// Description: Writes the image to the indicated filename. If type +// is non-NULL, it is a suggestion for the type of image +// file to write. +//////////////////////////////////////////////////////////////////// +bool PNMImage:: +write(const Filename &filename, PNMFileType *type) const { + if (!is_valid()) { + return false; + } + + PNMWriter *writer = PNMImageHeader::make_writer(filename, type); + if (writer == (PNMWriter *)NULL) { + return false; + } + + return write(writer); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::write +// Access: Public +// Description: This flavor of write() uses an already-existing +// PNMWriter to write the image file. You can get a +// writer via the PNMImageHeader::make_writer() methods. +// +// The PNMWriter is always deleted upon completion, +// whether succesful or not. +//////////////////////////////////////////////////////////////////// +bool PNMImage:: +write(PNMWriter *writer) const { + if (writer == NULL) { + return false; + } + + if (!is_valid()) { + delete writer; + return false; + } + + writer->copy_header_from(*this); + int result = writer->write_data(_array, _alpha); + delete writer; + + return (result == _y_size); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_color_type +// Access: Public +// Description: Translates the image to or from grayscale, color, or +// four-color mode. Grayscale images are converted to +// full-color images with R, G, B set to the original +// gray level; color images are converted to grayscale +// according to the value of Bright(). The alpha +// channel, if added, is initialized to zero. +//////////////////////////////////////////////////////////////////// +void PNMImage:: +set_color_type(PNMImage::ColorType color_type) { + if (color_type == get_color_type()) { + return; + } + + if (!is_grayscale() && is_grayscale(color_type)) { + // convert to grayscale from color + for (int y = 0; y < get_y_size(); y++) { + for (int x = 0; x < get_x_size(); x++) { + set_gray(x, y, get_bright(x, y)); + } + } + + } else if (is_grayscale() && !is_grayscale(color_type)) { + // convert to color from grayscale + for (int y = 0; y < get_y_size(); y++) { + for (int x = 0; x 0.0); + + if (maxval != _maxval) { + double ratio = maxval / _maxval; + + if (is_grayscale()) { + for (int y = 0; y < get_y_size(); y++) { + for (int x = 0; x < get_x_size(); x++) { + set_gray_val(x, y, (xelval)((long)get_gray_val(x, y) * ratio)); + } + } + } else { + for (int y = 0; y < get_y_size(); y++) { + for (int x = 0; x < get_x_size(); x++) { + set_red_val(x, y, (xelval)((long)get_red_val(x, y) * ratio)); + set_green_val(x, y, (xelval)((long)get_green_val(x, y) * ratio)); + set_blue_val(x, y, (xelval)((long)get_blue_val(x, y) * ratio)); + } + } + } + + if (has_alpha()) { + for (int y = 0; y < get_y_size(); y++) { + for (int x = 0; x < get_x_size(); x++) { + set_alpha_val(x, y, + (xelval)((long)get_alpha_val(x, y) * ratio)); + } + } + } + _maxval = maxval; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_channel_val +// Access: Public +// Description: Returns the nth component color at the indicated +// pixel. The channel index should be in the range +// 0..(get_num_channels()-1). The channels are ordered B, +// G, R, A. This is slightly less optimal than +// accessing the component values directly by named +// methods. The value returned is in the range +// 0..maxval. +//////////////////////////////////////////////////////////////////// +xelval PNMImage:: +get_channel_val(int x, int y, int channel) const { + switch (channel) { + case 0: + return get_blue_val(x, y); + + case 1: + return (_num_channels == 2) ? get_alpha_val(x, y) : get_green_val(x, y); + + case 2: + return get_red_val(x, y); + + case 3: + return get_alpha_val(x, y); + + default: + nassertr(false, 0); + return 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_channel_val +// Access: Public +// Description: Sets the nth component color at the indicated +// pixel. The channel index should be in the range +// 0..(get_num_channels()-1). The channels are ordered B, +// G, R, A. This is slightly less optimal than +// setting the component values directly by named +// methods. The value given should be in the range +// 0..maxval. +//////////////////////////////////////////////////////////////////// +void PNMImage:: +set_channel_val(int x, int y, int channel, xelval value) { + switch (channel) { + case 0: + set_blue_val(x, y, value); + break; + + case 1: + if (_num_channels == 2) { + set_alpha_val(x, y, value); + } else { + set_green_val(x, y, value); + } + break; + + case 2: + set_red_val(x, y, value); + break; + + case 3: + set_alpha_val(x, y, value); + break; + + default: + nassertv(false); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::blend +// Access: Public +// Description: Smoothly blends the indicated pixel value in with +// whatever was already in the image, based on the given +// alpha value. An alpha of 1.0 is fully opaque and +// completely replaces whatever was there previously; +// alpha of 0.0 is fully transparent and does nothing. +//////////////////////////////////////////////////////////////////// +void PNMImage:: +blend(int x, int y, double r, double g, double b, double alpha) { + if (alpha >= 1.0) { + // Completely replace the previous color. + if (has_alpha()) { + set_alpha(x, y, 1.0); + } + set_xel(x, y, r, g, b); + + } else if (alpha > 0.0) { + // Blend with the previous color. + double prev_alpha = has_alpha() ? get_alpha(x, y) : 1.0; + + if (prev_alpha == 0.0) { + // Nothing there previously; replace with this new color. + set_alpha(x, y, alpha); + set_xel(x, y, r, g, b); + + } else { + // Blend the color with the previous color. + RGBColord prev_rgb = get_xel(x, y); + r = r + (1.0 - alpha) * (get_red(x, y) - r); + g = g + (1.0 - alpha) * (get_green(x, y) - g); + b = b + (1.0 - alpha) * (get_blue(x, y) - b); + alpha = prev_alpha + alpha * (1.0 - prev_alpha); + + if (has_alpha()) { + set_alpha(x, y, alpha); + } + set_xel(x, y, r, g, b); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::copy_sub_image +// Access: Public +// Description: Copies a rectangular area of another image into a +// rectangular area of this image. Both images must +// already have been initialized. The upper-left corner +// of the region in both images is specified, and the +// size of the area; if the size is omitted, it defaults +// to the entire other image, or the largest piece that +// will fit. +//////////////////////////////////////////////////////////////////// +void PNMImage:: +copy_sub_image(const PNMImage ©, int xto, int yto, + int xfrom, int yfrom, int x_size, int y_size) { + if (xfrom < 0) { + xto += -xfrom; + xfrom = 0; + } + if (yfrom < 0) { + yto += -yfrom; + yfrom = 0; + } + + x_size = (x_size < 0) ? + copy.get_x_size() : + min(x_size, copy.get_x_size() - xfrom); + y_size = (y_size < 0) ? + copy.get_y_size() : + min(y_size, copy.get_y_size() - yfrom); + + int xmin = max(0, xto); + int ymin = max(0, yto); + + int xmax = min(xmin + x_size, get_x_size()); + int ymax = min(ymin + y_size, get_y_size()); + + int x, y; + for (y = ymin; y < ymax; y++) { + for (x = xmin; x < xmax; x++) { + set_xel(x, y, copy.get_xel(x - xmin + xfrom, y - ymin + yfrom)); + } + } + + if (has_alpha() && copy.has_alpha()) { + for (y = ymin; y < ymax; y++) { + for (x = xmin; x < xmax; x++) { + set_alpha(x, y, copy.get_alpha(x - xmin + xfrom, y - ymin + yfrom)); + } + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::setup_rc +// Access: Public +// Description: Sets the _default_rc,bc,gc values appropriately +// according to the color type of the image, so that +// get_bright() will return a meaningful value for both +// color and grayscale images. +//////////////////////////////////////////////////////////////////// +void PNMImage:: +setup_rc() { + if (is_grayscale()) { + _default_rc = 0.0; + _default_gc = 0.0; + _default_bc = 1.0; + } else { + _default_rc = lumin_red; + _default_gc = lumin_grn; + _default_bc = lumin_blu; + } +} diff --git a/panda/src/pnmimage/pnmImage.h b/panda/src/pnmimage/pnmImage.h new file mode 100644 index 0000000000..6e902f14d2 --- /dev/null +++ b/panda/src/pnmimage/pnmImage.h @@ -0,0 +1,187 @@ +// Filename: pnmImage.h +// Created by: drose (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMIMAGE_H +#define PNMIMAGE_H + +#include + +#include "pnmImageHeader.h" + +#include + +class PNMReader; +class PNMWriter; +class PNMFileType; + +//////////////////////////////////////////////////////////////////// +// Class : PNMImage +// Description : Conceptually, a PNMImage is a two-dimensional array +// of xels, which are the PNM-defined generic pixel +// type. Each xel may have a red, green, and blue +// component, or (if the image is grayscale) a gray +// component. The image may be read in, the individual +// xels manipulated, and written out again, or a black +// image may be constructed from scratch. +// +// The image is of size XSize() by YSize() xels, +// numbered from top to bottom, left to right, beginning +// at zero. +// +// Files can be specified by filename, or by a +// stdio-style FILE pointer. Unfortunately, C++ streams +// cannot be supported, because of the underlying C code +// in libpnm. The filename "-" refers to stdin or +// stdout. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PNMImage : public PNMImageHeader { +public: + INLINE PNMImage(); + INLINE PNMImage(const Filename &filename, PNMFileType *type = NULL); + INLINE PNMImage(int x_size, int y_size, int num_channels = 3, + xelval maxval = 255, PNMFileType *type = NULL); + INLINE PNMImage(const PNMImage ©); + INLINE void operator = (const PNMImage ©); + + INLINE ~PNMImage(); + + INLINE xelval clamp_val(int input_value) const; + INLINE xelval to_val(double input_value) const; + INLINE double from_val(xelval input_value) const; + + void clear(); + void clear(int x_size, int y_size, int num_channels = 3, + xelval maxval = 255, PNMFileType *type = NULL); + + void copy_from(const PNMImage ©); + void copy_header_from(const PNMImageHeader &header); + + INLINE void fill(double red, double green, double blue); + INLINE void fill(double gray = 0.0); + + void fill_val(xelval red, xelval green, xelval blue); + INLINE void fill_val(xelval gray = 0); + + INLINE void alpha_fill(double alpha = 0.0); + void alpha_fill_val(xelval alpha = 0); + + bool read(const Filename &filename, PNMFileType *type = NULL); + bool read(PNMReader *reader); + + bool write(const Filename &filename, PNMFileType *type = NULL) const; + bool write(PNMWriter *writer) const; + + INLINE bool is_valid() const; + + INLINE void set_num_channels(int num_channels); + void set_color_type(ColorType color_type); + + INLINE void add_alpha(); + INLINE void remove_alpha(); + INLINE void make_grayscale(); + void make_grayscale(double rc, double gc, double bc); + INLINE void make_rgb(); + + void set_maxval(xelval maxval); + + // The *_val() functions return or set the color values in the range + // [0..get_maxval()]. This range may be different for different + // images! Use the corresponding functions (without _val()) to work + // in the normalized range [0..1]. + + INLINE const xel &get_xel_val(int x, int y) const; + INLINE void set_xel_val(int x, int y, const xel &value); + INLINE void set_xel_val(int x, int y, xelval r, xelval g, xelval b); + INLINE void set_xel_val(int x, int y, xelval gray); + + INLINE xelval get_red_val(int x, int y) const; + INLINE xelval get_green_val(int x, int y) const; + INLINE xelval get_blue_val(int x, int y) const; + INLINE xelval get_gray_val(int x, int y) const; + INLINE xelval get_alpha_val(int x, int y) const; + + INLINE void set_red_val(int x, int y, xelval r); + INLINE void set_green_val(int x, int y, xelval g); + INLINE void set_blue_val(int x, int y, xelval b); + INLINE void set_gray_val(int x, int y, xelval gray); + INLINE void set_alpha_val(int x, int y, xelval a); + + xelval get_channel_val(int x, int y, int channel) const; + void set_channel_val(int x, int y, int channel, xelval value); + + // The corresponding get_xel(), set_xel(), get_red(), etc. functions + // automatically scale their values by get_maxval() into the range + // [0..1]. + + INLINE RGBColord get_xel(int x, int y) const; + INLINE void set_xel(int x, int y, const RGBColord &value); + INLINE void set_xel(int x, int y, double r, double g, double b); + INLINE void set_xel(int x, int y, double gray); + + INLINE double get_red(int x, int y) const; + INLINE double get_green(int x, int y) const; + INLINE double get_blue(int x, int y) const; + INLINE double get_gray(int x, int y) const; + INLINE double get_alpha(int x, int y) const; + + INLINE void set_red(int x, int y, double r); + INLINE void set_green(int x, int y, double g); + INLINE void set_blue(int x, int y, double b); + INLINE void set_gray(int x, int y, double gray); + INLINE void set_alpha(int x, int y, double a); + + INLINE double get_channel(int x, int y, int channel) const; + INLINE void set_channel(int x, int y, int channel, double value); + + INLINE double get_bright(int x, int y) const; + INLINE double get_bright(int x, int y, double rc, double gc, + double bc) const; + INLINE double get_bright(int x, int y, double rc, double gc, + double bc, double ac) const; + + INLINE void blend(int x, int y, const RGBColord &val, double alpha); + void blend(int x, int y, double r, double g, double b, double alpha); + + // If you're used to the NetPBM library and like working with a 2-d + // array of xels, and using the PNM macros to access their components, + // you may treat the PNMImage as such directly. + + INLINE xel *operator [] (int y); + INLINE const xel *operator [] (int y) const; + + + void copy_sub_image(const PNMImage ©, int xto, int yto, + int xfrom = 0, int yfrom = 0, + int x_size = -1, int y_size = -1); + + // The bodies for the non-inline *_filter() functions can be found + // in the file pnm-image-filter.cxx. + + INLINE void box_filter(double radius = 1.0); + INLINE void gaussian_filter(double radius = 1.0); + + void box_filter_from(double radius, const PNMImage ©); + void gaussian_filter_from(double radius, const PNMImage ©); + void quick_filter_from(const PNMImage ©, + int xborder = 0, int yborder = 0); + +private: + INLINE void allocate_array(); + INLINE void allocate_alpha(); + + INLINE xel *row(int row) const; + INLINE xelval *alpha_row(int row) const; + + void setup_rc(); + +private: + xel *_array; + xelval *_alpha; + double _default_rc, _default_gc, _default_bc; +}; + +#include "pnmImage.I" + +#endif diff --git a/panda/src/pnmimage/pnmImageHeader.I b/panda/src/pnmimage/pnmImageHeader.I new file mode 100644 index 0000000000..ab03cbdaec --- /dev/null +++ b/panda/src/pnmimage/pnmImageHeader.I @@ -0,0 +1,191 @@ +// Filename: pnmImageHeader.I +// Created by: drose (15Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PNMImageHeader:: +PNMImageHeader() { + _x_size = 0; + _y_size = 0; + _num_channels = 0; + _maxval = 255; + _type = (PNMFileType *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PNMImageHeader:: +PNMImageHeader(const PNMImageHeader ©) : + _x_size(copy._x_size), + _y_size(copy._y_size), + _num_channels(copy._num_channels), + _maxval(copy._maxval), + _type(copy._type) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PNMImageHeader:: +operator = (const PNMImageHeader ©) { + _x_size = copy._x_size; + _y_size = copy._y_size; + _num_channels = copy._num_channels; + _maxval = copy._maxval; + _type = copy._type; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PNMImageHeader:: +~PNMImageHeader() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::get_color_type +// Access: Public +// Description: Returns the image type of the image, as an enumerated +// value. This is really just the number of channels +// cast to the enumerated type. +//////////////////////////////////////////////////////////////////// +INLINE PNMImageHeader::ColorType PNMImageHeader:: +get_color_type() const { + nassertr(_num_channels >= 1 && _num_channels <= 4, CT_invalid); + return (ColorType)_num_channels; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::get_num_channels +// Access: Public +// Description: Returns the number of channels in the image. +//////////////////////////////////////////////////////////////////// +INLINE int PNMImageHeader:: +get_num_channels() const { + nassertr(_num_channels >= 1 && _num_channels <= 4, 0); + return _num_channels; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::is_grayscale +// Access: Public, Static +// Description: This static variant of is_grayscale() returns true if +// the indicated image type represents a grayscale +// image, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool PNMImageHeader:: +is_grayscale(PNMImageHeader::ColorType color_type) { + return (color_type == CT_grayscale || color_type == CT_two_channel); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::is_grayscale +// Access: Public +// Description: Returns false if the image is a full-color image, and +// has red, green, and blue components; true if it is a +// grayscale image and has only a gray component. (The +// gray color is actually stored in the blue channel, +// and the red and green channels are ignored.) +//////////////////////////////////////////////////////////////////// +INLINE bool PNMImageHeader:: +is_grayscale() const { + return is_grayscale(get_color_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::has_alpha +// Access: Public, Static +// Description: This static variant of has_alpha() returns true if +// the indicated image type includes an alpha channel, +// false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool PNMImageHeader:: +has_alpha(PNMImageHeader::ColorType color_type) { + return (color_type == CT_two_channel || color_type == CT_four_channel); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::has_alpha +// Access: Public +// Description: Returns true if the image includes an alpha channel, +// false otherwise. Unlike is_grayscale(), if this +// returns false it is an error to call any of the +// functions accessing the alpha channel. +//////////////////////////////////////////////////////////////////// +INLINE bool PNMImageHeader:: +has_alpha() const { + return has_alpha(get_color_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::get_maxval +// Access: Public +// Description: Returns the maximum channel value allowable for any +// pixel in this image; for instance, 255 for a typical +// 8-bit-per-channel image. A pixel with this value is +// full on. +//////////////////////////////////////////////////////////////////// +INLINE xelval PNMImageHeader:: +get_maxval() const { + return _maxval; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::get_x_size +// Access: Public +// Description: Returns the number of pixels in the X direction. +// This is one more than the largest allowable X +// coordinate. +//////////////////////////////////////////////////////////////////// +INLINE int PNMImageHeader:: +get_x_size() const { + return _x_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::get_y_size +// Access: Public +// Description: Returns the number of pixels in the Y direction. +// This is one more than the largest allowable Y +// coordinate. +//////////////////////////////////////////////////////////////////// +INLINE int PNMImageHeader:: +get_y_size() const { + return _y_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::has_type +// Access: Public +// Description: Returns true if the PNMImageHeader knows what type it +// is, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool PNMImageHeader:: +has_type() const { + return _type != (PNMFileType *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::get_type +// Access: Public +// Description: If the file type is known (e.g. has_type() returns +// true), returns its PNMFileType pointer; otherwise, +// returns NULL. +//////////////////////////////////////////////////////////////////// +INLINE PNMFileType *PNMImageHeader:: +get_type() const { + return _type; +} diff --git a/panda/src/pnmimage/pnmImageHeader.cxx b/panda/src/pnmimage/pnmImageHeader.cxx new file mode 100644 index 0000000000..bf45861409 --- /dev/null +++ b/panda/src/pnmimage/pnmImageHeader.cxx @@ -0,0 +1,348 @@ +// Filename: pnmImageHeader.cxx +// Created by: drose (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmImageHeader.h" +#include "pnmFileTypeRegistry.h" +#include "pnmFileType.h" +#include "pnmReader.h" +#include "config_pnmimage.h" + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::read_header +// Access: Public, Static +// Description: Opens up the image file and tries to read its header +// information to determine its size, number of +// channels, etc. If successful, updates the header +// information and returns true; otherwise, returns +// false. +//////////////////////////////////////////////////////////////////// +bool PNMImageHeader:: +read_header(const Filename &filename, PNMFileType *type) { + PNMReader *reader = make_reader(filename, type); + if (reader != (PNMReader *)NULL) { + (*this) = (*reader); + _type = reader->get_type(); + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::make_reader +// Access: Public, Static +// Description: Returns a PNMReader of the suitable type for reading +// from the indicated image filename, or NULL if the +// filename cannot be read for some reason. The +// filename "-" always stands for standard input. If +// type is specified, it is a suggestion for the file +// type to use. +//////////////////////////////////////////////////////////////////// +PNMReader *PNMImageHeader:: +make_reader(const Filename &filename, PNMFileType *type) const { + if (pnmimage_cat.is_debug()) { + pnmimage_cat.debug() + << "Reading image from " << filename << "\n"; + } + Filename actual_name = Filename::binary_filename(filename); + bool owns_file; + FILE *file; + + if (filename == "-") { + owns_file = false; + file = stdin; + + if (pnmimage_cat.is_debug()) { + pnmimage_cat.debug() + << "(reading standard input)\n"; + } + + } else { + if (!actual_name.exists()) { + if (pnmimage_cat.is_debug()) { + pnmimage_cat.debug() + << "File " << filename << " does not exist.\n"; + } + // The file doesn't exist. Don't pass it through libpnm, which + // will abort. + return NULL; + } + owns_file = true; + file = pm_openr((char *)actual_name.to_os_specific().c_str()); + } + + if (file == (FILE *)NULL) { + if (pnmimage_cat.is_debug()) { + pnmimage_cat.debug() + << "Unable to open file.\n"; + } + return NULL; + } + + return make_reader(file, owns_file, filename, string(), type); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::make_reader +// Access: Public, Static +// Description: Returns a PNMReader of the suitable type for reading +// from the already-opened image file, or NULL if the +// file cannot be read for some reason. +// +// owns_file should be set true if the PNMReader is to +// be considered the owner of the FILE stream (in which +// case the stream will be closed on completion, whether +// successful or not), or false if it should not close +// it. +// +// The filename parameter is optional here, since the +// file has already been opened; it is only used to +// examine the extension and attempt to guess the file +// type. +// +// If magic_number is nonempty, it is assumed to +// represent the first few bytes that have already been +// read from the file. Some file types may have +// difficulty if this is more than two bytes. +// +// If type is non-NULL, it is a suggestion for the file +// type to use. +//////////////////////////////////////////////////////////////////// +PNMReader *PNMImageHeader:: +make_reader(FILE *file, bool owns_file, const Filename &filename, + string magic_number, PNMFileType *type) const { + if (type == (PNMFileType *)NULL) { + if (!read_magic_number(file, magic_number, 2)) { + // No magic number. No image. + if (pnmimage_cat.is_debug()) { + pnmimage_cat.debug() + << "Image file appears to be empty.\n"; + } + if (owns_file) { + pm_close(file); + } + return NULL; + } + + type = PNMFileTypeRegistry::get_ptr()-> + get_type_from_magic_number(magic_number); + + if (pnmimage_cat.is_debug()) { + if (type != (PNMFileType *)NULL) { + pnmimage_cat.debug() + << "By magic number, image file appears to be type " + << type->get_name() << ".\n"; + } else { + pnmimage_cat.debug() + << "Unable to determine image file type from magic number.\n"; + } + } + } + + if (type == (PNMFileType *)NULL && !filename.empty()) { + // We still don't know the type; attempt to guess it from the + // filename extension. + type = PNMFileTypeRegistry::get_ptr()->get_type_from_extension(filename); + + if (pnmimage_cat.is_debug()) { + if (type != (PNMFileType *)NULL) { + pnmimage_cat.debug() + << "From its extension, image file is probably type " + << type->get_name() << ".\n"; + } else { + pnmimage_cat.debug() + << "Unable to guess image file type from its extension.\n"; + } + } + } + + if (type == (PNMFileType *)NULL) { + // No? How about the default type associated with this image header. + type = _type; + + if (pnmimage_cat.is_debug() && type != (PNMFileType *)NULL) { + pnmimage_cat.debug() + << "Assuming image file type is " << type->get_name() << ".\n"; + } + } + + if (type == (PNMFileType *)NULL) { + // We can't figure out what type the file is; give up. + if (pnmimage_cat.is_error()) { + pnmimage_cat.error() + << "Cannot determine type of image file " << filename << ".\n" + << "Currently supported image types:\n"; + PNMFileTypeRegistry::get_ptr()-> + write_types(pnmimage_cat.error(false), 2); + } + if (owns_file) { + pm_close(file); + } + return NULL; + } + + PNMReader *reader = type->make_reader(file, owns_file, magic_number); + if (reader == NULL && owns_file) { + pm_close(file); + } + + if (!reader->is_valid()) { + delete reader; + reader = NULL; + } + + return reader; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::make_writer +// Access: Public, Static +// Description: Returns a PNMWriter of the suitable type for writing +// an image to the indicated filename, or NULL if the +// filename cannot be written for some reason. The +// filename "-" always stands for standard output. If +// type is specified, it is a suggestion for the file +// type to use. +//////////////////////////////////////////////////////////////////// +PNMWriter *PNMImageHeader:: +make_writer(const Filename &filename, PNMFileType *type) const { + if (pnmimage_cat.is_debug()) { + pnmimage_cat.debug() + << "Writing image to " << filename << "\n"; + } + Filename actual_name = Filename::binary_filename(filename); + bool owns_file; + FILE *file; + + if (filename == "-") { + owns_file = false; + file = stdout; + + if (pnmimage_cat.is_debug()) { + pnmimage_cat.debug() + << "(writing to standard output)\n"; + } + + } else { + owns_file = true; + file = pm_openw((char *)actual_name.to_os_specific().c_str()); + } + + if (file == (FILE *)NULL) { + if (pnmimage_cat.is_debug()) { + pnmimage_cat.debug() + << "Unable to write to file.\n"; + } + return NULL; + } + + return make_writer(file, owns_file, filename, type); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::make_writer +// Access: Public, Static +// Description: Returns a PNMWriter of the suitable type for writing +// to the already-opened image file, or NULL if the +// file cannot be written for some reason. +// +// owns_file should be set true if the PNMWriter is to +// be considered the owner of the FILE stream (in which +// case the stream will be closed on completion, whether +// successful or not), or false if it should not close +// it. +// +// The filename parameter is optional here, since the +// file has already been opened; it is only used to +// examine the extension and attempt to guess the +// intended file type. +// +// If type is non-NULL, it is a suggestion for the file +// type to use. +//////////////////////////////////////////////////////////////////// +PNMWriter *PNMImageHeader:: +make_writer(FILE *file, bool owns_file, const Filename &filename, + PNMFileType *type) const { + if (type == (PNMFileType *)NULL && !filename.empty()) { + // We don't know the type; attempt to guess it from the filename + // extension. + type = PNMFileTypeRegistry::get_ptr()->get_type_from_extension(filename); + + if (pnmimage_cat.is_debug()) { + if (type != (PNMFileType *)NULL) { + pnmimage_cat.debug() + << "From its extension, image file is intended to be type " + << type->get_name() << ".\n"; + } else { + pnmimage_cat.debug() + << "Unable to guess image file type from its extension.\n"; + } + } + } + + if (type == (PNMFileType *)NULL) { + // No? How about the default type associated with this image header. + type = _type; + + if (pnmimage_cat.is_debug() && type != (PNMFileType *)NULL) { + pnmimage_cat.debug() + << "Assuming image file type is " << type->get_name() << ".\n"; + } + } + + if (type == (PNMFileType *)NULL) { + // We can't figure out what type the file is; give up. + if (pnmimage_cat.is_debug()) { + pnmimage_cat.debug() + << "Cannot determine type of image file " << filename << ".\n"; + } + if (owns_file) { + pm_close(file); + } + return NULL; + } + + PNMWriter *writer = type->make_writer(file, owns_file); + if (writer == NULL && owns_file) { + pm_close(file); + } + + return writer; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::read_magic_number +// Access: Public, Static +// Description: Ensures that the first n bytes of the file are read +// into magic_number. If magic_number is initially +// nonempty, assumes these represent the first few bytes +// already extracted. Returns true if successful, false +// if an end of file or error occurred before num_bytes +// could be read. +//////////////////////////////////////////////////////////////////// +bool PNMImageHeader:: +read_magic_number(FILE *file, string &magic_number, int num_bytes) { + while (magic_number.size() < num_bytes) { + int ch = getc(file); + if (ch == EOF) { + return false; + } + magic_number += (char)ch; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void PNMImageHeader:: +output(ostream &out) const { + out << "image: " << _x_size << " by " << _y_size << " pixels, " + << _num_channels << " channels, " << _maxval << " maxval."; +} diff --git a/panda/src/pnmimage/pnmImageHeader.h b/panda/src/pnmimage/pnmImageHeader.h new file mode 100644 index 0000000000..6e1eea07da --- /dev/null +++ b/panda/src/pnmimage/pnmImageHeader.h @@ -0,0 +1,99 @@ +// Filename: pnmImageHeader.h +// Created by: drose (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMIMAGEHEADER_H +#define PNMIMAGEHEADER_H + +#include + +#include "pnmimage_base.h" + +#include +#include + +class PNMFileType; +class PNMReader; +class PNMWriter; + +//////////////////////////////////////////////////////////////////// +// Class : PNMImageHeader +// Description : This is the base class of PNMImage, PNMReader, and +// PNMWriter. It encapsulates all the information +// associated with an image that describes its size, +// number of channels, etc; that is, all the information +// about the image except the image data itself. It's +// the sort of information you typically read from the +// image file's header. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PNMImageHeader { +public: + INLINE PNMImageHeader(); + INLINE PNMImageHeader(const PNMImageHeader ©); + INLINE void operator = (const PNMImageHeader ©); + INLINE ~PNMImageHeader(); + + // This enumerated type indicates the number of channels in the + // image, and also implies an image type. You can treat it either + // as an integer number of channels or as an enumerated image type. + enum ColorType { + CT_invalid = 0, + CT_grayscale = 1, + CT_two_channel = 2, + CT_color = 3, + CT_four_channel = 4, + }; + + INLINE ColorType get_color_type() const; + INLINE int get_num_channels() const; + + INLINE static bool is_grayscale(ColorType color_type); + INLINE bool is_grayscale() const; + + INLINE static bool has_alpha(ColorType color_type); + INLINE bool has_alpha() const; + + INLINE xelval get_maxval() const; + + INLINE int get_x_size() const; + INLINE int get_y_size() const; + + INLINE bool has_type() const; + INLINE PNMFileType *get_type() const; + + bool read_header(const Filename &filename, PNMFileType *type = NULL); + + PNMReader *make_reader(const Filename &filename, + PNMFileType *type = NULL) const; + PNMReader *make_reader(FILE *file, bool owns_file = true, + const Filename &filename = Filename(), + string magic_number = string(), + PNMFileType *type = NULL) const; + + PNMWriter *make_writer(const Filename &filename, + PNMFileType *type = NULL) const; + PNMWriter *make_writer(FILE *file, bool owns_file = true, + const Filename &filename = Filename(), + PNMFileType *type = NULL) const; + + static bool read_magic_number(FILE *file, string &magic_number, + int num_bytes); + + void output(ostream &out) const; + +protected: + int _x_size, _y_size; + int _num_channels; + xelval _maxval; + PNMFileType *_type; +}; + +INLINE ostream &operator << (ostream &out, const PNMImageHeader &header) { + header.output(out); + return out; +} + +#include "pnmImageHeader.I" + +#endif diff --git a/panda/src/pnmimage/pnmReader.I b/panda/src/pnmimage/pnmReader.I new file mode 100644 index 0000000000..a114b77533 --- /dev/null +++ b/panda/src/pnmimage/pnmReader.I @@ -0,0 +1,40 @@ +// Filename: pnmReader.I +// Created by: drose (16Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: PNMReader::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PNMReader:: +PNMReader(PNMFileType *type, FILE *file, bool owns_file) : + _type(type), + _file(file), + _owns_file(owns_file), + _is_valid(true) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMReader::get_type +// Access: Public +// Description: Returns a pointer to the PNMFileType object that +// created this PNMReader. +//////////////////////////////////////////////////////////////////// +INLINE PNMFileType *PNMReader:: +get_type() const { + return _type; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMReader::is_valid +// Access: Public +// Description: Returns true if the PNMReader can be used to read +// data, false if something is wrong. +//////////////////////////////////////////////////////////////////// +INLINE bool PNMReader:: +is_valid() const { + return _is_valid; +} diff --git a/panda/src/pnmimage/pnmReader.cxx b/panda/src/pnmimage/pnmReader.cxx new file mode 100644 index 0000000000..1a24360968 --- /dev/null +++ b/panda/src/pnmimage/pnmReader.cxx @@ -0,0 +1,91 @@ +// Filename: pnmReader.cxx +// Created by: drose (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmReader.h" + +//////////////////////////////////////////////////////////////////// +// Function: PNMReader::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PNMReader:: +~PNMReader() { + if (_owns_file) { + pm_close(_file); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMReader::read_data +// Access: Public, Virtual +// Description: Reads in an entire image all at once, storing it in +// the pre-allocated _x_size * _y_size array and alpha +// pointers. (If the image type has no alpha channel, +// alpha is ignored.) Returns the number of rows +// correctly read. +// +// Derived classes need not override this if they +// instead provide supports_read_row() and read_row(), +// below. +//////////////////////////////////////////////////////////////////// +int PNMReader:: +read_data(xel *array, xelval *alpha) { + if (!is_valid()) { + return 0; + } + + int y; + for (y = 0; y < _y_size; y++) { + if (!read_row(array + y * _x_size, alpha + y * _x_size)) { + return y; + } + } + + return _y_size; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMReader::supports_read_row +// Access: Public, Virtual +// Description: Returns true if this particular PNMReader is capable +// of returning the data one row at a time, via repeated +// calls to read_row(). Returns false if the only way +// to read from this file is all at once, via +// read_data(). +//////////////////////////////////////////////////////////////////// +bool PNMReader:: +supports_read_row() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMReader::read_row +// Access: Public, Virtual +// Description: If supports_read_row(), above, returns true, this +// function may be called repeatedly to read the image, +// one horizontal row at a time, beginning from the top. +// Returns true if the row is successfully read, false +// if there is an error or end of file. +//////////////////////////////////////////////////////////////////// +bool PNMReader:: +read_row(xel *, xelval *) { + return false; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMReader::supports_stream_read +// Access: Public, Virtual +// Description: Returns true if this particular PNMReader can read +// from a general stream (including pipes, etc.), or +// false if the reader must occasionally fseek() on its +// input stream, and thus only disk streams are +// supported. +//////////////////////////////////////////////////////////////////// +bool PNMReader:: +supports_stream_read() const { + return false; +} diff --git a/panda/src/pnmimage/pnmReader.h b/panda/src/pnmimage/pnmReader.h new file mode 100644 index 0000000000..69d5e30263 --- /dev/null +++ b/panda/src/pnmimage/pnmReader.h @@ -0,0 +1,48 @@ +// Filename: pnmReader.h +// Created by: drose (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMREADER_H +#define PNMREADER_H + +#include + +#include "pnmImageHeader.h" + + +//////////////////////////////////////////////////////////////////// +// Class : PNMReader +// Description : This is an abstract base class that defines the +// interface for reading image files of various types. +// Any particular image file type that can be read must +// define a class that inherits from PNMReader to read +// it. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PNMReader : public PNMImageHeader { +protected: + INLINE PNMReader(PNMFileType *type, FILE *file, bool owns_file); + +public: + virtual ~PNMReader(); + + INLINE PNMFileType *get_type() const; + + virtual int read_data(xel *array, xelval *alpha); + virtual bool supports_read_row() const; + virtual bool read_row(xel *array, xelval *alpha); + + virtual bool supports_stream_read() const; + + INLINE bool is_valid() const; + +protected: + PNMFileType *_type; + bool _owns_file; + FILE *_file; + bool _is_valid; +}; + +#include "pnmReader.I" + +#endif diff --git a/panda/src/pnmimage/pnmReaderTypes.h b/panda/src/pnmimage/pnmReaderTypes.h new file mode 100644 index 0000000000..241bcb53c9 --- /dev/null +++ b/panda/src/pnmimage/pnmReaderTypes.h @@ -0,0 +1,159 @@ +// Filename: pnmReaderTypes.h +// Created by: drose (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMREADERTYPES_H +#define PNMREADERTYPES_H + +#include + +#include "pnmReader.h" + +class EXPCL_PANDA PNMReaderPNM : public PNMReader { +public: + PNMReaderPNM(FILE *file, int already_read_magic); + + virtual FileType Type() const { + return PNMImageHeader::PNM; + } + + virtual bool ReadRow(xel *row_data, xelval *alpha_data); + + int ftype; +}; + +class EXPCL_PANDA PNMReaderSGI : public PNMReader { +public: + PNMReaderSGI(FILE *file, int already_read_magic); + + virtual FileType Type() const { + return PNMImageHeader::SGI; + } + + virtual bool ReadRow(xel *row_data, xelval *alpha_data); + ~PNMReaderSGI(); + + typedef struct { + long start; /* offset in file */ + long length; /* length of compressed scanline */ + } TabEntry; + + TabEntry *table; + long table_start; + int current_row; + int zsize, bpc; +}; + +class EXPCL_PANDA PNMReaderAlias : public PNMReader { +public: + PNMReaderAlias(FILE *file, int already_read_magic); + + virtual FileType Type() const { + return PNMImageHeader::Alias; + } + + virtual bool ReadRow(xel *row_data, xelval *alpha_data); +}; + +class EXPCL_PANDA PNMReaderRadiance : public PNMReader { +public: + PNMReaderRadiance(FILE *file, int already_read_magic); + + virtual FileType Type() const { + return PNMImageHeader::Radiance; + } + + virtual bool ReadRow(xel *row_data, xelval *alpha_data); +}; + +#ifndef WIN32_VC +class EXPCL_PANDA PNMReaderTIFF : public PNMReader { +public: + PNMReaderTIFF(FILE *file, int already_read_magic); + + virtual FileType Type() const { + return PNMImageHeader::TIFF; + } + + virtual bool ReadRow(xel *row_data, xelval *alpha_data); + ~PNMReaderTIFF(); + + unsigned short photomet; + unsigned short bps, spp; + xel colormap[1024]; // == MAXCOLORS in pnm-image-tiff.cxx + + int current_row; + struct tiff *tif; +}; +#endif + +class EXPCL_PANDA PNMReaderIMG : public PNMReader { +public: + PNMReaderIMG(FILE *file, int already_read_magic); + + virtual FileType Type() const { + return PNMImageHeader::IMG; + } + + virtual bool ReadRow(xel *row_data, xelval *alpha_data); +}; + +class EXPCL_PANDA PNMReaderSoftImage : public PNMReader { +public: + PNMReaderSoftImage(FILE *file, int already_read_magic); + + virtual FileType Type() const { + return PNMImageHeader::SoftImage; + } + + virtual bool ReadRow(xel *row_data, xelval *alpha_data); + + enum { unknown, rgb, rgba, rgb_a } soft_color; + int rgb_ctype, alpha_ctype; +}; + +class EXPCL_PANDA PNMReaderYUV : public PNMReader { +public: + PNMReaderYUV(FILE *file, int already_read_magic); + + virtual FileType Type() const { + return PNMImageHeader::YUV; + } + + virtual bool ReadRow(xel *row_data, xelval *alpha_data); + ~PNMReaderYUV(); + + long *yuvbuf; +}; + +class EXPCL_PANDA PNMReaderBMP : public PNMReader { +public: + PNMReaderBMP(FILE *file, int already_read_magic); + + virtual FileType Type() const { + return PNMImageHeader::BMP; + } + + virtual int ReadData(xel *array, xelval *alpha); + + virtual bool SupportsStreaming() const { + return false; + } + + unsigned long pos; + + unsigned long offBits; + + unsigned short cBitCount; + int indexed; + int classv; + + pixval R[256]; /* reds */ + pixval G[256]; /* greens */ + pixval B[256]; /* blues */ +}; + +#endif + + diff --git a/panda/src/pnmimage/pnmWriter.I b/panda/src/pnmimage/pnmWriter.I new file mode 100644 index 0000000000..ee0edac3c1 --- /dev/null +++ b/panda/src/pnmimage/pnmWriter.I @@ -0,0 +1,95 @@ +// Filename: pnmWriter.I +// Created by: drose (16Jun00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PNMWriter::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PNMWriter:: +PNMWriter(PNMFileType *type, FILE *file, bool owns_file) : + _type(type), + _file(file), + _owns_file(owns_file) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMWriter::get_type +// Access: Public +// Description: Returns a pointer to the PNMFileType object that +// created this PNMWriter. +//////////////////////////////////////////////////////////////////// +INLINE PNMFileType *PNMWriter:: +get_type() const { + return _type; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMWriter::set_color_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PNMWriter:: +set_color_type(ColorType type) { + set_num_channels((int)type); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMWriter::set_num_channels +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PNMWriter:: +set_num_channels(int num_channels) { + nassertv(num_channels >= 1 && num_channels <= 4); + _num_channels = num_channels; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMWriter::set_maxval +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PNMWriter:: +set_maxval(xelval maxval) { + _maxval = maxval; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMWriter::set_x_size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PNMWriter:: +set_x_size(int x_size) { + nassertv(x_size >= 0); + _x_size = x_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMWriter::set_y_size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PNMWriter:: +set_y_size(int y_size) { + nassertv(y_size >= 0); + _y_size = y_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMWriter::copy_header_from +// Access: Public +// Description: Initializes all the data in the header (x_size, +// y_size, num_channels, etc.) to the same values +// indicated in the given header. This should be done +// before writing anything to the file. +//////////////////////////////////////////////////////////////////// +INLINE void PNMWriter:: +copy_header_from(const PNMImageHeader &header) { + PNMImageHeader::operator = (header); +} diff --git a/panda/src/pnmimage/pnmWriter.cxx b/panda/src/pnmimage/pnmWriter.cxx new file mode 100644 index 0000000000..8f4c7fd895 --- /dev/null +++ b/panda/src/pnmimage/pnmWriter.cxx @@ -0,0 +1,128 @@ +// Filename: pnmWriter.cxx +// Created by: drose (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmWriter.h" + +//////////////////////////////////////////////////////////////////// +// Function: PNMWriter::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PNMWriter:: +~PNMWriter() { + if (_owns_file) { + pm_close(_file); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMWriter::write_data +// Access: Public, Virtual +// Description: Writes out an entire image all at once, including the +// header, based on the image data stored in the given +// _x_size * _y_size array and alpha pointers. (If the +// image type has no alpha channel, alpha is ignored.) +// Returns the number of rows correctly written. +// +// It is the user's responsibility to fill in the header +// data via calls to set_x_size(), set_num_channels(), +// etc., or copy_header_from(), before calling +// write_data(). +// +// It is important to delete the PNMWriter class after +// successfully writing the data. Failing to do this +// may result in some data not getting flushed! +// +// Derived classes need not override this if they +// instead provide supports_streaming() and write_row(), +// below. +//////////////////////////////////////////////////////////////////// +int PNMWriter:: +write_data(xel *array, xelval *alpha) { + if (_x_size <= 0 || _y_size <= 0) { + return 0; + } + + if (!write_header()) { + return 0; + } + + int y; + for (y = 0; y < _y_size; y++) { + if (!write_row(array + y * _x_size, alpha + y * _x_size)) { + return y; + } + } + + return _y_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMWriter::supports_write_row +// Access: Public, Virtual +// Description: Returns true if this particular PNMWriter supports a +// streaming interface to writing the data: that is, it +// is capable of writing the image one row at a time, +// via repeated calls to write_row(). Returns false if +// the only way to write from this file is all at once, +// via write_data(). +//////////////////////////////////////////////////////////////////// +bool PNMWriter:: +supports_write_row() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMWriter::write_header +// Access: Public, Virtual +// Description: If supports_write_row(), above, returns true, this +// function may be called to write out the image header +// in preparation to writing out the image data one row +// at a time. Returns true if the header is +// successfully written, false if there is an error. +// +// It is the user's responsibility to fill in the header +// data via calls to set_x_size(), set_num_channels(), +// etc., or copy_header_from(), before calling +// write_header(). +//////////////////////////////////////////////////////////////////// +bool PNMWriter:: +write_header() { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMWriter::write_row +// Access: Public, Virtual +// Description: If supports_write_row(), above, returns true, this +// function may be called repeatedly to write the image, +// one horizontal row at a time, beginning from the top. +// Returns true if the row is successfully written, +// false if there is an error. +// +// You must first call write_header() before writing the +// individual rows. It is also important to delete the +// PNMWriter class after successfully writing the last +// row. Failing to do this may result in some data not +// getting flushed! +//////////////////////////////////////////////////////////////////// +bool PNMWriter:: +write_row(xel *, xelval *) { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMWriter::supports_stream_write +// Access: Public, Virtual +// Description: Returns true if this particular PNMWriter can write +// to a general stream (including pipes, etc.), or +// false if the writer must occasionally fseek() on its +// output stream, and thus only disk streams are +// supported. +//////////////////////////////////////////////////////////////////// +bool PNMWriter:: +supports_stream_write() const { + return false; +} diff --git a/panda/src/pnmimage/pnmWriter.h b/panda/src/pnmimage/pnmWriter.h new file mode 100644 index 0000000000..930d04891a --- /dev/null +++ b/panda/src/pnmimage/pnmWriter.h @@ -0,0 +1,57 @@ +// Filename: pnmWriter.h +// Created by: drose (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMWRITER_H +#define PNMWRITER_H + +#include + +#include "pnmImageHeader.h" + +//////////////////////////////////////////////////////////////////// +// Class : PNMWriter +// Description : This is an abstract base class that defines the +// interface for writing image files of various types. +// Any particular image file type that can be written +// must define a class that inherits from PNMWriter to +// write it. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PNMWriter : public PNMImageHeader { +protected: + INLINE PNMWriter(PNMFileType *type, FILE *file, bool owns_file); + +public: + + // It is important to delete the PNMWriter class after successfully + // writing the data. Failing to do this may result in some data not + // getting flushed! + virtual ~PNMWriter(); + + INLINE PNMFileType *get_type() const; + + INLINE void set_color_type(ColorType type); + INLINE void set_num_channels(int num_channels); + INLINE void set_maxval(xelval maxval); + INLINE void set_x_size(int x_size); + INLINE void set_y_size(int y_size); + + INLINE void copy_header_from(const PNMImageHeader &header); + + virtual int write_data(xel *array, xelval *alpha); + virtual bool supports_write_row() const; + virtual bool write_header(); + virtual bool write_row(xel *array, xelval *alpha); + + virtual bool supports_stream_write() const; + +protected: + PNMFileType *_type; + bool _owns_file; + FILE *_file; +}; + +#include "pnmWriter.I" + +#endif diff --git a/panda/src/pnmimage/pnmWriterTypes.h b/panda/src/pnmimage/pnmWriterTypes.h new file mode 100644 index 0000000000..61bb6f7651 --- /dev/null +++ b/panda/src/pnmimage/pnmWriterTypes.h @@ -0,0 +1,171 @@ +// Filename: pnmWriterTypes.h +// Created by: drose (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMWRITERTYPES_H +#define PNMWRITERTYPES_H + +#include + +#include "pnmWriter.h" + +class EXPCL_PANDA PNMWriterPNM : public PNMWriter { +public: + PNMWriterPNM(FILE *file) : PNMWriter(file) {} + + virtual FileType Type() const { + return PNMImageHeader::PNM; + } + + virtual bool WriteHeader(); + virtual bool WriteRow(xel *row_data, xelval *alpha_data); + + int pnm_format; +}; + +class EXPCL_PANDA PNMWriterSGI : public PNMWriter { +public: + PNMWriterSGI(FILE *file) : PNMWriter(file) {} + ~PNMWriterSGI(); + + virtual FileType Type() const { + return PNMImageHeader::SGI; + } + + virtual bool WriteHeader(); + virtual bool WriteRow(xel *row_data, xelval *alpha_data); + + +protected: + + typedef struct { + long start; /* offset in file */ + long length; /* length of compressed scanline */ + } TabEntry; + + typedef short ScanElem; + typedef struct { + ScanElem * data; + long length; + } ScanLine; + + TabEntry &Table(int chan) { + return table[chan * rows + current_row]; + } + + void write_header(char *imagename); + void WriteTable(); + void WriteChannels(ScanLine channel[], void (*put)(FILE *, short)); + void BuildScanline(ScanLine output[], xel *row_data, xelval *alpha_data); + ScanElem *Compress(ScanElem *temp, ScanLine &output); + int RLECompress(ScanElem *inbuf, int size); + + TabEntry *table; + long table_start; + int current_row; + int zsize, bpc; + int channels, dimensions; + int new_maxval; + + ScanElem *rletemp; +}; + +class EXPCL_PANDA PNMWriterAlias : public PNMWriter { +public: + PNMWriterAlias(FILE *file) : PNMWriter(file) {} + + virtual FileType Type() const { + return PNMImageHeader::Alias; + } + + virtual bool WriteHeader(); + virtual bool WriteRow(xel *row_data, xelval *alpha_data); +}; + +class EXPCL_PANDA PNMWriterRadiance : public PNMWriter { +public: + PNMWriterRadiance(FILE *file) : PNMWriter(file) {} + + virtual FileType Type() const { + return PNMImageHeader::Radiance; + } + + virtual bool WriteHeader(); + virtual bool WriteRow(xel *row_data, xelval *alpha_data); +}; + +#ifndef WIN32_VC +class EXPCL_PANDA PNMWriterTIFF : public PNMWriter { +public: + PNMWriterTIFF(FILE *file) : PNMWriter(file) {} + + virtual FileType Type() const { + return PNMImageHeader::TIFF; + } + + virtual int WriteData(xel *array, xelval *alpha); + virtual bool SupportsStreaming() const { + return false; + } +}; +#endif + +class EXPCL_PANDA PNMWriterIMG : public PNMWriter { +public: + PNMWriterIMG(FILE *file) : PNMWriter(file) {} + + virtual FileType Type() const { + return PNMImageHeader::IMG; + } + + virtual bool WriteHeader(); + virtual bool WriteRow(xel *row_data, xelval *alpha_data); +}; + +class EXPCL_PANDA PNMWriterSoftImage : public PNMWriter { +public: + PNMWriterSoftImage(FILE *file) : PNMWriter(file) {} + + virtual FileType Type() const { + return PNMImageHeader::SoftImage; + } + + virtual bool WriteHeader(); + virtual bool WriteRow(xel *row_data, xelval *alpha_data); +}; + +class EXPCL_PANDA PNMWriterYUV : public PNMWriter { +public: + PNMWriterYUV(FILE *file) : PNMWriter(file) { + yuvbuf = NULL; + } + + virtual FileType Type() const { + return PNMImageHeader::YUV; + } + + virtual bool WriteHeader(); + virtual bool WriteRow(xel *row_data, xelval *alpha_data); + ~PNMWriterYUV(); + + unsigned char *yuvbuf; +}; + +class EXPCL_PANDA PNMWriterBMP : public PNMWriter { +public: + PNMWriterBMP(FILE *file) : PNMWriter(file) {} + + virtual FileType Type() const { + return PNMImageHeader::BMP; + } + + virtual int WriteData(xel *array, xelval *alpha); + virtual bool SupportsStreaming() const { + return false; + } +}; + +#endif + + diff --git a/panda/src/pnmimage/pnmimage_base.h b/panda/src/pnmimage/pnmimage_base.h new file mode 100644 index 0000000000..f7ca11a4e4 --- /dev/null +++ b/panda/src/pnmimage/pnmimage_base.h @@ -0,0 +1,52 @@ +// Filename: pnmimage_base.h +// Created by: drose (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMIMAGE_BASE_H +#define PNMIMAGE_BASE_H + +// This header file make a few typedefs and other definitions +// essential to everything in the PNMImage package. + +#include + +#include +#include + +// These include files are kludges against slightly incorrect headers given +// in pbm*.h. +#include +#include +#include + +extern "C" { + #include + #include + #include + #include + #include +} + + +// Got to undefine the stupid macros that pbmplus leaves us with. +#ifdef index + #undef index + #undef rindex +#endif + +// These ratios are used to compute the brightness of a colored pixel; they +// define the relative contributions of each of the components. +static const double lumin_red = 0.299; +static const double lumin_grn = 0.587; +static const double lumin_blu = 0.114; + +// A handy template function. +template +INLINE Type +bounds(const Type &value, const Type &lower, const Type &upper) { + return min(max(value, lower), upper); +} + + +#endif diff --git a/panda/src/pnmimage/pnmminmax.h b/panda/src/pnmimage/pnmminmax.h new file mode 100644 index 0000000000..f41079b230 --- /dev/null +++ b/panda/src/pnmimage/pnmminmax.h @@ -0,0 +1,52 @@ +// Filename: minmax.h -- +// Description: +// Created by: drose (23Aug96) +// +// Revision Overview: +// +// +//////////////////////////////////////////////////////////////////// +// Copyright (C) 1992,93,94,95 Walt Disney Imagineering, Inc. +// +// These coded instructions, statements, data structures and +// computer programs contain unpublished proprietary information of +// Walt Disney Imagineering and are protected by Federal copyright +// law. They may not be disclosed to third parties or copied or +// duplicated in any form, in whole or in part, without the prior +// written consent of Walt Disney Imagineering Inc. +//////////////////////////////////////////////////////////////////// +// +// RCSID: +// $Header$ +// + +#ifndef _PNMMINMAX_H_ +#define _PNMMINMAX_H_ + +#include + +#include + +// We now inherit these functions from STL. + +/* +template +inline Type +max(const Type &a, const Type &b) { + return (a +inline Type +min(const Type &a, const Type &b) { + return (a +inline Type +bounds(const Type &value, const Type &lower, const Type &upper) { + return min(max(value, lower), upper); +} + +#endif diff --git a/panda/src/pnmimagetypes/Sources.pp b/panda/src/pnmimagetypes/Sources.pp new file mode 100644 index 0000000000..65b5a73c05 --- /dev/null +++ b/panda/src/pnmimagetypes/Sources.pp @@ -0,0 +1,21 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET pnmimagetypes + #define LOCAL_LIBS \ + pnm tiff pnmimage + + #define SOURCES \ + config_pnmimagetypes.cxx config_pnmimagetypes.h \ + pnmFileTypeAlias.cxx pnmFileTypeAlias.h pnmFileTypeBMP.cxx \ + pnmFileTypeBMP.h pnmFileTypeBMPReader.cxx pnmFileTypeBMPWriter.cxx \ + pnmFileTypeIMG.cxx pnmFileTypeIMG.h pnmFileTypePNM.cxx \ + pnmFileTypePNM.h pnmFileTypeRadiance.cxx pnmFileTypeRadiance.h \ + pnmFileTypeSGI.cxx pnmFileTypeSGI.h pnmFileTypeSGIReader.cxx \ + pnmFileTypeSGIWriter.cxx pnmFileTypeSoftImage.cxx \ + pnmFileTypeSoftImage.h pnmFileTypeTIFF.cxx pnmFileTypeTIFF.h \ + pnmFileTypeYUV.cxx pnmFileTypeYUV.h \ + color.c colrops.c resolu.c header.c + +#end lib_target + diff --git a/panda/src/pnmimagetypes/bmp.h b/panda/src/pnmimagetypes/bmp.h new file mode 100644 index 0000000000..53bc3e13b8 --- /dev/null +++ b/panda/src/pnmimagetypes/bmp.h @@ -0,0 +1,198 @@ +/*\ + * $Id$ + * + * bmp.h - routines to calculate sizes of parts of BMP files + * + * Some fields in BMP files contain offsets to other parts + * of the file. These routines allow us to calculate these + * offsets, so that we can read and write BMP files without + * the need to fseek(). + * + * Copyright (C) 1992 by David W. Sanderson. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. This software is provided "as is" + * without express or implied warranty. + * + * $Log$ + * Revision 1.1 2000/10/04 01:14:42 drose + * Initial revision + * + * Revision 1.3 1992/11/24 19:39:56 dws + * Added copyright. + * + * Revision 1.2 1992/11/17 02:13:37 dws + * Adjusted a string's name. + * + * Revision 1.1 1992/11/16 19:54:44 dws + * Initial revision + * +\*/ + +#ifndef _BMP_H_ +#define _BMP_H_ + +#include + +#include "pbmplus.h" + +/* prototypes */ +static unsigned long BMPlenfileheader(int classv); +static unsigned long BMPleninfoheader(int classv); +static unsigned long BMPlenrgbtable(int classv, unsigned long bitcount); +static unsigned long BMPlenline(int classv, unsigned long bitcount, unsigned long x); +static unsigned long BMPlenbits(int classv, unsigned long bitcount, unsigned long x, unsigned long y); +static unsigned long BMPlenfile(int classv, unsigned long bitcount, unsigned long x, unsigned long y); +static unsigned long BMPoffbits(int classv, unsigned long bitcount); +/* + * Classves of BMP files + */ + +#define C_WIN 1 +#define C_OS2 2 + +static char er_internal[] = "%s: internal error!"; + +static unsigned long +BMPlenfileheader(int classv) +{ + switch (classv) + { + case C_WIN: + return 14; + case C_OS2: + return 14; + default: + pm_error(er_internal, "BMPlenfileheader"); + return 0; + } +} + +static unsigned long +BMPleninfoheader(int classv) +{ + switch (classv) + { + case C_WIN: + return 40; + case C_OS2: + return 12; + default: + pm_error(er_internal, "BMPleninfoheader"); + return 0; + } +} + +static unsigned long +BMPlenrgbtable(int classv, unsigned long bitcount) +{ + unsigned long lenrgb; + + if (bitcount > 8) { + return 0; + } + + if (bitcount < 1) + { + pm_error(er_internal, "BMPlenrgbtable"); + return 0; + } + switch (classv) + { + case C_WIN: + lenrgb = 4; + break; + case C_OS2: + lenrgb = 3; + break; + default: + pm_error(er_internal, "BMPlenrgbtable"); + return 0; + } + + return (1 << bitcount) * lenrgb; +} + +/* + * length, in bytes, of a line of the image + * + * Evidently each row is padded on the right as needed to make it a + * multiple of 4 bytes long. This appears to be true of both + * OS/2 and Windows BMP files. + */ +static unsigned long +BMPlenline(int classv, unsigned long bitcount, unsigned long x) +{ + unsigned long bitsperline; + + switch (classv) + { + case C_WIN: + break; + case C_OS2: + break; + default: + pm_error(er_internal, "BMPlenline"); + return 0; + } + + bitsperline = x * bitcount; + + /* + * if bitsperline is not a multiple of 32, then round + * bitsperline up to the next multiple of 32. + */ + if ((bitsperline % 32) != 0) + { + bitsperline += (32 - (bitsperline % 32)); + } + + if ((bitsperline % 32) != 0) + { + pm_error(er_internal, "BMPlenline"); + return 0; + } + + /* number of bytes per line == bitsperline/8 */ + return bitsperline >> 3; +} + +/* return the number of bytes used to store the image bits */ +static unsigned long +BMPlenbits( + int classv, + unsigned long bitcount, + unsigned long x, + unsigned long y) +{ + return y * BMPlenline(classv, bitcount, x); +} + +/* return the offset to the BMP image bits */ +static unsigned long +BMPoffbits( + int classv, + unsigned long bitcount) +{ + return BMPlenfileheader(classv) + + BMPleninfoheader(classv) + + BMPlenrgbtable(classv, bitcount); +} + +/* return the size of the BMP file in bytes */ +static unsigned long +BMPlenfile( + int classv, + unsigned long bitcount, + unsigned long x, + unsigned long y) +{ + return BMPoffbits(classv, bitcount) + + BMPlenbits(classv, bitcount, x, y); +} + +#endif /* _BMP_H_ */ + diff --git a/panda/src/pnmimagetypes/color.c b/panda/src/pnmimagetypes/color.c new file mode 100644 index 0000000000..3949af53bc --- /dev/null +++ b/panda/src/pnmimagetypes/color.c @@ -0,0 +1,280 @@ +/* Copyright (c) 1991 Regents of the University of California */ + +#ifndef lint +static char SCCSid[] = "@(#)color.c 2.8 3/26/94 LBL"; +#endif + +/* + * color.c - routines for color calculations. + * + * 10/10/85 + */ + +#include + +#include + +#include "color.h" + +#ifdef WIN32VC +#include +#endif + +#define MINELEN 8 /* minimum scanline length for encoding */ +#define MAXELEN 0x7fff /* maximum scanline length for encoding */ +#define MINRUN 4 /* minimum run length */ + +void setcolr(register COLR, double, double, double); +void colr_color(register COLOR, register COLR); + + +char * +tempbuffer(unsigned len) /* get a temporary buffer */ +{ +#ifndef WIN32VC + extern char *malloc(size_t), *realloc(void *, size_t); +#endif + static char *tempbuf = NULL; + static unsigned tempbuflen = 0; + + if (len > tempbuflen) { + if (tempbuflen > 0) + tempbuf = (char *)realloc(tempbuf, len); + else + tempbuf = (char *)malloc(len); + tempbuflen = tempbuf==NULL ? 0 : len; + } + return(tempbuf); +} + + +fwritecolrs(register COLR *scanline, unsigned len, register FILE *fp) + /* write out a colr scanline */ +{ + register int i, j, beg, cnt; + int c2; + + if (len < MINELEN | len > MAXELEN) /* OOBs, write out flat */ + return(fwrite((char *)scanline,sizeof(COLR),len,fp) - len); + /* put magic header */ + putc(2, fp); + putc(2, fp); + putc(len>>8, fp); + putc(len&255, fp); + /* put components seperately */ + for (i = 0; i < 4; i++) { + for (j = 0; j < len; j += cnt) { /* find next run */ + for (beg = j; beg < len; beg += cnt) { + for (cnt = 1; cnt < 127 && beg+cnt < len && + scanline[beg+cnt][i] == scanline[beg][i]; cnt++) + ; + if (cnt >= MINRUN) + break; /* long enough */ + } + if (beg-j > 1 && beg-j < MINRUN) { + c2 = j+1; + while (scanline[c2++][i] == scanline[j][i]) + if (c2 == beg) { /* short run */ + putc(128+beg-j, fp); + putc(scanline[j][i], fp); + j = beg; + break; + } + } + while (j < beg) { /* write out non-run */ + if ((c2 = beg-j) > 128) c2 = 128; + putc(c2, fp); + while (c2--) + putc(scanline[j++][i], fp); + } + if (cnt >= MINRUN) { /* write out run */ + putc(128+cnt, fp); + putc(scanline[beg][i], fp); + } else + cnt = 0; + } + } + return(ferror(fp) ? -1 : 0); +} + +int oldreadcolrs(register COLR *scanline, int len, register FILE *fp); + +freadcolrs(register COLR *scanline, int len, register FILE *fp) + /* read in an encoded colr scanline */ +{ + register int i, j; + int code, val; + /* determine scanline type */ + if (len < MINELEN | len > MAXELEN) + return(oldreadcolrs(scanline, len, fp)); + if ((i = getc(fp)) == EOF) + return(-1); + if (i != 2) { + ungetc(i, fp); + return(oldreadcolrs(scanline, len, fp)); + } + scanline[0][GRN] = getc(fp); + scanline[0][BLU] = getc(fp); + if ((i = getc(fp)) == EOF) + return(-1); + if (scanline[0][GRN] != 2 || scanline[0][BLU] & 128) { + scanline[0][RED] = 2; + scanline[0][EXP] = i; + return(oldreadcolrs(scanline+1, len-1, fp)); + } + if ((scanline[0][BLU]<<8 | i) != len) + return(-1); /* length mismatch! */ + /* read each component */ + for (i = 0; i < 4; i++) + for (j = 0; j < len; ) { + if ((code = getc(fp)) == EOF) + return(-1); + if (code > 128) { /* run */ + code &= 127; + val = getc(fp); + while (code--) + scanline[j++][i] = val; + } else /* non-run */ + while (code--) + scanline[j++][i] = getc(fp); + } + return(feof(fp) ? -1 : 0); +} + + + /* read in an old colr scanline */ +int oldreadcolrs(register COLR *scanline, int len, register FILE *fp) +{ + int rshift; + register int i; + + rshift = 0; + + while (len > 0) { + scanline[0][RED] = getc(fp); + scanline[0][GRN] = getc(fp); + scanline[0][BLU] = getc(fp); + scanline[0][EXP] = getc(fp); + if (feof(fp) || ferror(fp)) + return(-1); + if (scanline[0][RED] == 1 && + scanline[0][GRN] == 1 && + scanline[0][BLU] == 1) { + for (i = scanline[0][EXP] << rshift; i > 0; i--) { + copycolr(scanline[0], scanline[-1]); + scanline++; + len--; + } + rshift += 8; + } else { + scanline++; + len--; + rshift = 0; + } + } + return(0); +} + + + /* write out a scanline */ +fwritescan(register COLOR *scanline, int len, FILE *fp) +{ + COLR *clrscan; + int n; + register COLR *sp; + /* get scanline buffer */ + if ((sp = (COLR *)tempbuffer(len*sizeof(COLR))) == NULL) + return(-1); + clrscan = sp; + /* convert scanline */ + n = len; + while (n-- > 0) { + setcolr(sp[0], scanline[0][RED], + scanline[0][GRN], + scanline[0][BLU]); + scanline++; + sp++; + } + return(fwritecolrs(clrscan, len, fp)); +} + + +freadscan(register COLOR *scanline, int len, FILE *fp) + /* read in a scanline */ +{ + register COLR *clrscan; + + if ((clrscan = (COLR *)tempbuffer(len*sizeof(COLR))) == NULL) + return(-1); + if (freadcolrs(clrscan, len, fp) < 0) + return(-1); + /* convert scanline */ + colr_color(scanline[0], clrscan[0]); + while (--len > 0) { + scanline++; clrscan++; + if (clrscan[0][RED] == clrscan[-1][RED] && + clrscan[0][GRN] == clrscan[-1][GRN] && + clrscan[0][BLU] == clrscan[-1][BLU] && + clrscan[0][EXP] == clrscan[-1][EXP]) + copycolor(scanline[0], scanline[-1]); + else + colr_color(scanline[0], clrscan[0]); + } + return(0); +} + + +void +setcolr(register COLR clr, double r, double g, double b) + /* assign a short color value */ +{ + double d; + int e; + + d = r > g ? r : g; + if (b > d) d = b; + + if (d <= 1e-32) { + clr[RED] = clr[GRN] = clr[BLU] = 0; + clr[EXP] = 0; + return; + } + + d = frexp(d, &e) * 255.9999 / d; + + clr[RED] = r * d; + clr[GRN] = g * d; + clr[BLU] = b * d; + clr[EXP] = e + COLXS; +} + + +void +colr_color(register COLOR col, register COLR clr) + /* convert short to float color */ +{ + double f; + + if (clr[EXP] == 0) + col[RED] = col[GRN] = col[BLU] = 0.0; + else { + f = ldexp(1.0, (int)clr[EXP]-(COLXS+8)); + col[RED] = (clr[RED] + 0.5)*f; + col[GRN] = (clr[GRN] + 0.5)*f; + col[BLU] = (clr[BLU] + 0.5)*f; + } +} + + +int +bigdiff(register COLOR c1, register COLOR c2, double md) + /* c1 delta c2 > md? */ +{ + register int i; + + for (i = 0; i < 3; i++) + if (colval(c1,i)-colval(c2,i) > md*colval(c2,i) || + colval(c2,i)-colval(c1,i) > md*colval(c1,i)) + return(1); + return(0); +} diff --git a/panda/src/pnmimagetypes/color.h b/panda/src/pnmimagetypes/color.h new file mode 100644 index 0000000000..e69f956754 --- /dev/null +++ b/panda/src/pnmimagetypes/color.h @@ -0,0 +1,142 @@ +/* Copyright (c) 1991 Regents of the University of California */ + +/* SCCSid "@(#)color.h 2.9 9/2/94 LBL" */ + +/* + * color.h - header for routines using pixel color values. + * + * 12/31/85 + * + * Two color representations are used, one for calculation and + * another for storage. Calculation is done with three floats + * for speed. Stored color values use 4 bytes which contain + * three single byte mantissas and a common exponent. + */ + +#define RED 0 +#define GRN 1 +#define BLU 2 +#define EXP 3 +#define COLXS 128 /* excess used for exponent */ + +typedef unsigned char BYTE; /* 8-bit unsigned integer */ + +typedef BYTE COLR[4]; /* red, green, blue, exponent */ + +#define copycolr(c1,c2) (c1[0]=c2[0],c1[1]=c2[1], \ + c1[2]=c2[2],c1[3]=c2[3]) + +typedef float COLOR[3]; /* red, green, blue */ + +#define colval(col,pri) ((col)[pri]) + +#define setcolor(col,r,g,b) ((col)[RED]=(r),(col)[GRN]=(g),(col)[BLU]=(b)) + +#define copycolor(c1,c2) ((c1)[0]=(c2)[0],(c1)[1]=(c2)[1],(c1)[2]=(c2)[2]) + +#define scalecolor(col,sf) ((col)[0]*=(sf),(col)[1]*=(sf),(col)[2]*=(sf)) + +#define addcolor(c1,c2) ((c1)[0]+=(c2)[0],(c1)[1]+=(c2)[1],(c1)[2]+=(c2)[2]) + +#define multcolor(c1,c2) ((c1)[0]*=(c2)[0],(c1)[1]*=(c2)[1],(c1)[2]*=(c2)[2]) + +#ifdef NTSC +#define CIE_x_r 0.670 /* standard NTSC primaries */ +#define CIE_y_r 0.330 +#define CIE_x_g 0.210 +#define CIE_y_g 0.710 +#define CIE_x_b 0.140 +#define CIE_y_b 0.080 +#define CIE_x_w 0.3333 /* use true white */ +#define CIE_y_w 0.3333 +#else +#define CIE_x_r 0.640 /* nominal CRT primaries */ +#define CIE_y_r 0.330 +#define CIE_x_g 0.290 +#define CIE_y_g 0.600 +#define CIE_x_b 0.150 +#define CIE_y_b 0.060 +#define CIE_x_w 0.3333 /* use true white */ +#define CIE_y_w 0.3333 +#endif + +#define CIE_D ( CIE_x_r*(CIE_y_g - CIE_y_b) + \ + CIE_x_g*(CIE_y_b - CIE_y_r) + \ + CIE_x_b*(CIE_y_r - CIE_y_g) ) +#define CIE_C_rD ( (1./CIE_y_w) * \ + ( CIE_x_w*(CIE_y_g - CIE_y_b) - \ + CIE_y_w*(CIE_x_g - CIE_x_b) + \ + CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g ) ) +#define CIE_C_gD ( (1./CIE_y_w) * \ + ( CIE_x_w*(CIE_y_b - CIE_y_r) - \ + CIE_y_w*(CIE_x_b - CIE_x_r) - \ + CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r ) ) +#define CIE_C_bD ( (1./CIE_y_w) * \ + ( CIE_x_w*(CIE_y_r - CIE_y_g) - \ + CIE_y_w*(CIE_x_r - CIE_x_g) + \ + CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r ) ) + +#define CIE_rf (CIE_y_r*CIE_C_rD/CIE_D) +#define CIE_gf (CIE_y_g*CIE_C_gD/CIE_D) +#define CIE_bf (CIE_y_b*CIE_C_bD/CIE_D) + +/* As of 9-94, CIE_rf=.265074126, CIE_gf=.670114631 and CIE_bf=.064811243 */ + +#define bright(col) (CIE_rf*(col)[RED]+CIE_gf*(col)[GRN]+CIE_bf*(col)[BLU]) +#define normbright(c) ( ( (long)(CIE_rf*256.+.5)*(c)[RED] + \ + (long)(CIE_gf*256.+.5)*(c)[GRN] + \ + (long)(CIE_bf*256.+.5)*(c)[BLU] ) >> 8 ) + + /* luminous efficacies over visible spectrum */ +#define MAXEFFICACY 683. /* defined maximum at 550 nm */ +#define WHTEFFICACY 179. /* uniform white light */ +#define D65EFFICACY 203. /* standard illuminant D65 */ +#define INCEFFICACY 160. /* illuminant A (incand.) */ +#define SUNEFFICACY 208. /* illuminant B (solar dir.) */ +#define SKYEFFICACY D65EFFICACY /* skylight */ +#define DAYEFFICACY D65EFFICACY /* combined sky and solar */ + +#define luminance(col) (WHTEFFICACY * bright(col)) + +#define intens(col) ( (col)[0] > (col)[1] \ + ? (col)[0] > (col)[2] ? (col)[0] : (col)[2] \ + : (col)[1] > (col)[2] ? (col)[1] : (col)[2] ) + +#define colrval(c,p) ( (c)[EXP] ? \ + ldexp((c)[p]+.5,(int)(c)[EXP]-(COLXS+8)) : \ + 0. ) + +#define WHTCOLOR {1.0,1.0,1.0} +#define BLKCOLOR {0.0,0.0,0.0} +#define WHTCOLR {128,128,128,COLXS+1} +#define BLKCOLR {0,0,0,0} + + /* picture format identifier */ +#define COLRFMT "32-bit_rle_rgbe" + + /* macros for exposures */ +#define EXPOSSTR "EXPOSURE=" +#define LEXPOSSTR 9 +#define isexpos(hl) (!strncmp(hl,EXPOSSTR,LEXPOSSTR)) +#define exposval(hl) atof((hl)+LEXPOSSTR) +#define fputexpos(ex,fp) fprintf(fp,"%s%e\n",EXPOSSTR,ex) + + /* macros for pixel aspect ratios */ +#define ASPECTSTR "PIXASPECT=" +#define LASPECTSTR 10 +#define isaspect(hl) (!strncmp(hl,ASPECTSTR,LASPECTSTR)) +#define aspectval(hl) atof((hl)+LASPECTSTR) +#define fputaspect(pa,fp) fprintf(fp,"%s%f\n",ASPECTSTR,pa) + + /* macros for color correction */ +#define COLCORSTR "COLORCORR=" +#define LCOLCORSTR 10 +#define iscolcor(hl) (!strncmp(hl,COLCORSTR,LCOLCORSTR)) +#define colcorval(cc,hl) sscanf(hl+LCOLCORSTR,"%f %f %f", \ + &(cc)[RED],&(cc)[GRN],&(cc)[BLU]) +#define fputcolcor(cc,fp) fprintf(fp,"%s %f %f %f\n",COLCORSTR, \ + (cc)[RED],(cc)[GRN],(cc)[BLU]) + +#ifdef DCL_ATOF +extern double atof(), ldexp(), frexp(); +#endif diff --git a/panda/src/pnmimagetypes/colrops.c b/panda/src/pnmimagetypes/colrops.c new file mode 100644 index 0000000000..2a0d88d7b8 --- /dev/null +++ b/panda/src/pnmimagetypes/colrops.c @@ -0,0 +1,231 @@ +/* Copyright (c) 1992 Regents of the University of California */ + +#ifndef lint +static char SCCSid[] = "@(#)colrops.c 2.4 10/2/92 LBL"; +#endif + +/* + * Integer operations on COLR scanlines + */ + +#include "color.h" + +#define NULL 0 + +#define bmalloc malloc +#ifndef WIN32VC +extern char *bmalloc(int); +#else +#include +#include +#endif + +#define MAXGSHIFT 31 /* maximum shift for gamma table */ + +static BYTE *g_mant = NULL, *g_nexp = NULL; + +static BYTE (*g_bval)[256] = NULL; + +#ifndef pow +#ifndef WIN32VC +extern double pow(double, double); +#endif +#endif + + +setcolrcor(double (*f)(double, double), double a2) + /* set brightness correction */ +{ + double mult; + register int i, j; + /* allocate tables */ + if (g_bval == NULL && (g_bval = + (BYTE (*)[256])bmalloc((MAXGSHIFT+1)*256)) == NULL) + return(-1); + /* compute colr -> gamb mapping */ + mult = 1.0/256.0; + for (i = 0; i <= MAXGSHIFT; i++) { + for (j = 0; j < 256; j++) + g_bval[i][j] = 256.0 * (*f)((j+.5)*mult, a2); + mult *= 0.5; + } + return(0); +} + + +setcolrinv(double (*f)(double, double), double a2) + /* set inverse brightness correction */ +{ + double mult; + register int i, j; + /* allocate tables */ + if (g_mant == NULL && (g_mant = (BYTE *)bmalloc(256)) == NULL) + return(-1); + if (g_nexp == NULL && (g_nexp = (BYTE *)bmalloc(256)) == NULL) + return(-1); + /* compute gamb -> colr mapping */ + i = 0; + mult = 256.0; + for (j = 255; j > 0; j--) { + while ((g_mant[j] = mult * (*f)(j/256.0, a2)) < 128) { + i++; + mult *= 2.0; + } + g_nexp[j] = i; + } + g_mant[0] = 0; + g_nexp[0] = COLXS; + return(0); +} + + +setcolrgam(double g) /* set gamma conversion */ +{ + if (setcolrcor(pow, 1.0/g) < 0) + return(-1); + return(setcolrinv(pow, g)); +} + + +colrs_gambs(register COLR *scan, int len) + /* convert scanline of colrs to gamma bytes */ +{ + register int i, expo; + + if (g_bval == NULL) + return(-1); + while (len-- > 0) { + expo = scan[0][EXP] - COLXS; + if (expo < -MAXGSHIFT) { + if (expo < -MAXGSHIFT-8) { + scan[0][RED] = + scan[0][GRN] = + scan[0][BLU] = 0; + } else { + i = (-MAXGSHIFT-1) - expo; + scan[0][RED] = + g_bval[MAXGSHIFT][((scan[0][RED]>>i)+1)>>1]; + scan[0][GRN] = + g_bval[MAXGSHIFT][((scan[0][GRN]>>i)+1)>>1]; + scan[0][BLU] = + g_bval[MAXGSHIFT][((scan[0][BLU]>>i)+1)>>1]; + } + } else if (expo > 0) { + if (expo > 8) { + scan[0][RED] = + scan[0][GRN] = + scan[0][BLU] = 255; + } else { + i = (scan[0][RED]<<1 | 1) << (expo-1); + scan[0][RED] = i > 255 ? 255 : g_bval[0][i]; + i = (scan[0][GRN]<<1 | 1) << (expo-1); + scan[0][GRN] = i > 255 ? 255 : g_bval[0][i]; + i = (scan[0][BLU]<<1 | 1) << (expo-1); + scan[0][BLU] = i > 255 ? 255 : g_bval[0][i]; + } + } else { + scan[0][RED] = g_bval[-expo][scan[0][RED]]; + scan[0][GRN] = g_bval[-expo][scan[0][GRN]]; + scan[0][BLU] = g_bval[-expo][scan[0][BLU]]; + } + scan[0][EXP] = COLXS; + scan++; + } + return(0); +} + + +gambs_colrs(register COLR *scan, int len) + /* convert gamma bytes to colr scanline */ +{ + register int nexpo; + + if (g_mant == NULL | g_nexp == NULL) + return(-1); + while (len-- > 0) { + nexpo = g_nexp[scan[0][RED]]; + if (g_nexp[scan[0][GRN]] < nexpo) + nexpo = g_nexp[scan[0][GRN]]; + if (g_nexp[scan[0][BLU]] < nexpo) + nexpo = g_nexp[scan[0][BLU]]; + if (nexpo < g_nexp[scan[0][RED]]) + scan[0][RED] = g_mant[scan[0][RED]] + >> (g_nexp[scan[0][RED]]-nexpo); + else + scan[0][RED] = g_mant[scan[0][RED]]; + if (nexpo < g_nexp[scan[0][GRN]]) + scan[0][GRN] = g_mant[scan[0][GRN]] + >> (g_nexp[scan[0][GRN]]-nexpo); + else + scan[0][GRN] = g_mant[scan[0][GRN]]; + if (nexpo < g_nexp[scan[0][BLU]]) + scan[0][BLU] = g_mant[scan[0][BLU]] + >> (g_nexp[scan[0][BLU]]-nexpo); + else + scan[0][BLU] = g_mant[scan[0][BLU]]; + scan[0][EXP] = COLXS - nexpo; + scan++; + } + return(0); +} + + +void +shiftcolrs(register COLR *scan, register int len, register int adjust) + /* shift a scanline of colors by 2^adjust */ +{ + int minexp; + + if (adjust == 0) + return; + minexp = adjust < 0 ? -adjust : 0; + while (len-- > 0) { + if (scan[0][EXP] <= minexp) + scan[0][RED] = scan[0][GRN] = scan[0][BLU] = + scan[0][EXP] = 0; + else + scan[0][EXP] += adjust; + scan++; + } +} + + +void +normcolrs(register COLR *scan, int len, int adjust) +/* normalize a scanline of colrs */ +{ + register int c; + register int shift; + + while (len-- > 0) { + shift = scan[0][EXP] + adjust - COLXS; + if (shift > 0) { + if (shift > 8) { + scan[0][RED] = + scan[0][GRN] = + scan[0][BLU] = 255; + } else { + shift--; + c = (scan[0][RED]<<1 | 1) << shift; + scan[0][RED] = c > 255 ? 255 : c; + c = (scan[0][GRN]<<1 | 1) << shift; + scan[0][GRN] = c > 255 ? 255 : c; + c = (scan[0][BLU]<<1 | 1) << shift; + scan[0][BLU] = c > 255 ? 255 : c; + } + } else if (shift < 0) { + if (shift < -8) { + scan[0][RED] = + scan[0][GRN] = + scan[0][BLU] = 0; + } else { + shift = -1-shift; + scan[0][RED] = ((scan[0][RED]>>shift)+1)>>1; + scan[0][GRN] = ((scan[0][GRN]>>shift)+1)>>1; + scan[0][BLU] = ((scan[0][BLU]>>shift)+1)>>1; + } + } + scan[0][EXP] = COLXS - adjust; + scan++; + } +} diff --git a/panda/src/pnmimagetypes/config_pnmimagetypes.cxx b/panda/src/pnmimagetypes/config_pnmimagetypes.cxx new file mode 100644 index 0000000000..978417f8d9 --- /dev/null +++ b/panda/src/pnmimagetypes/config_pnmimagetypes.cxx @@ -0,0 +1,100 @@ +// Filename: config_pnmimagetypes.cxx +// Created by: drose (17Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_pnmimagetypes.h" +#include "pnmFileTypePNM.h" +#include "pnmFileTypeSGI.h" +#include "pnmFileTypeAlias.h" +#include "pnmFileTypeRadiance.h" +#include "pnmFileTypeTIFF.h" +#include "pnmFileTypeYUV.h" +#include "pnmFileTypeIMG.h" +#include "pnmFileTypeSoftImage.h" +#include "pnmFileTypeBMP.h" +#include "sgi.h" + +#include +#include +#include +#include + +Configure(config_pnmimagetypes); +NotifyCategoryDef(pnmimage_pnm, pnmimage_cat); +NotifyCategoryDef(pnmimage_sgi, pnmimage_cat); +NotifyCategoryDef(pnmimage_alias, pnmimage_cat); +NotifyCategoryDef(pnmimage_radiance, pnmimage_cat); +NotifyCategoryDef(pnmimage_tiff, pnmimage_cat); +NotifyCategoryDef(pnmimage_yuv, pnmimage_cat); +NotifyCategoryDef(pnmimage_img, pnmimage_cat); +NotifyCategoryDef(pnmimage_soft, pnmimage_cat); +NotifyCategoryDef(pnmimage_bmp, pnmimage_cat); + +int sgi_storage_type = STORAGE_RLE; +const string sgi_imagename = config_pnmimagetypes.GetString("sgi-imagename", ""); +const double radiance_gamma_correction = config_pnmimagetypes.GetDouble("radiance-gamma-correction", 2.2); +const int radiance_brightness_adjustment = config_pnmimagetypes.GetInt("radiance-brightness-adjustment", 0); + +// YUV format doesn't include an image size specification, so the +// image size must be specified externally. The defaults here are +// likely candidates, since this is the Abekas native size; the ysize +// is automatically adjusted down to account for a short file. +const int yuv_xsize = config_pnmimagetypes.GetInt("yuv-xsize", 720); +const int yuv_ysize = config_pnmimagetypes.GetInt("yuv-ysize", 486); + +// IMG format is just a sequential string of r, g, b bytes. However, +// it may or may not include a "header" which consists of the xsize +// and the ysize of the image, either as shorts or as longs. +IMGHeaderType img_header_type; +// The following are only used if header_type is 'none'. +const int img_xsize = config_pnmimagetypes.GetInt("img-xsize", 0); +const int img_ysize = config_pnmimagetypes.GetInt("img-ysize", 0); + +ConfigureFn(config_pnmimagetypes) { + PNMFileTypePNM::init_type(); + PNMFileTypeSGI::init_type(); + PNMFileTypeAlias::init_type(); + PNMFileTypeRadiance::init_type(); + PNMFileTypeTIFF::init_type(); + PNMFileTypeYUV::init_type(); + PNMFileTypeIMG::init_type(); + PNMFileTypeSoftImage::init_type(); + PNMFileTypeBMP::init_type(); + + string sgi_storage_type_str = + config_pnmimagetypes.GetString("sgi-storage-type", "rle"); + if (cmp_nocase(sgi_storage_type_str, "rle") == 0) { + sgi_storage_type = STORAGE_RLE; + } else if (cmp_nocase(sgi_storage_type_str, "verbatim") == 0) { + sgi_storage_type = STORAGE_VERBATIM; + } else { + pnmimage_sgi_cat->error() + << "Invalid sgi-storage-type: " << sgi_storage_type_str << "\n"; + } + + string img_header_type_str = + config_pnmimagetypes.GetString("img-header-type", "long"); + if (cmp_nocase(img_header_type_str, "none") == 0) { + img_header_type = IHT_none; + } else if (cmp_nocase(img_header_type_str, "short") == 0) { + img_header_type = IHT_short; + } else if (cmp_nocase(img_header_type_str, "long") == 0) { + img_header_type = IHT_long; + } else { + pnmimage_img_cat->error() + << "Invalid img-header-type: " << img_header_type_str << "\n"; + } + + PNMFileTypeRegistry *tr = PNMFileTypeRegistry::get_ptr(); + + tr->register_type(new PNMFileTypePNM); + tr->register_type(new PNMFileTypeSGI); + tr->register_type(new PNMFileTypeAlias); + tr->register_type(new PNMFileTypeRadiance); + tr->register_type(new PNMFileTypeTIFF); + tr->register_type(new PNMFileTypeYUV); + tr->register_type(new PNMFileTypeIMG); + tr->register_type(new PNMFileTypeSoftImage); + tr->register_type(new PNMFileTypeBMP); +} diff --git a/panda/src/pnmimagetypes/config_pnmimagetypes.h b/panda/src/pnmimagetypes/config_pnmimagetypes.h new file mode 100644 index 0000000000..0209cb3f0b --- /dev/null +++ b/panda/src/pnmimagetypes/config_pnmimagetypes.h @@ -0,0 +1,39 @@ +// Filename: config_pnmimagetypes.h +// Created by: drose (17Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_PNMIMAGETYPES_H +#define CONFIG_PNMIMAGETYPES_H + +#include +#include + +NotifyCategoryDecl(pnmimage_pnm, EXPCL_PANDA, EXPTP_PANDA); +NotifyCategoryDecl(pnmimage_sgi, EXPCL_PANDA, EXPTP_PANDA); +NotifyCategoryDecl(pnmimage_alias, EXPCL_PANDA, EXPTP_PANDA); +NotifyCategoryDecl(pnmimage_radiance, EXPCL_PANDA, EXPTP_PANDA); +NotifyCategoryDecl(pnmimage_tiff, EXPCL_PANDA, EXPTP_PANDA); +NotifyCategoryDecl(pnmimage_yuv, EXPCL_PANDA, EXPTP_PANDA); +NotifyCategoryDecl(pnmimage_img, EXPCL_PANDA, EXPTP_PANDA); +NotifyCategoryDecl(pnmimage_soft, EXPCL_PANDA, EXPTP_PANDA); +NotifyCategoryDecl(pnmimage_bmp, EXPCL_PANDA, EXPTP_PANDA); + +extern int sgi_storage_type; +extern const string sgi_imagename; +extern const double radiance_gamma_correction; +extern const int radiance_brightness_adjustment; +extern const int yuv_xsize; +extern const int yuv_ysize; + +enum IMGHeaderType { + IHT_none, + IHT_short, + IHT_long, +}; + +extern IMGHeaderType img_header_type; +extern const int img_xsize; +extern const int img_ysize; + +#endif diff --git a/panda/src/pnmimagetypes/header.c b/panda/src/pnmimagetypes/header.c new file mode 100644 index 0000000000..66356092ac --- /dev/null +++ b/panda/src/pnmimagetypes/header.c @@ -0,0 +1,225 @@ +/* Copyright (c) 1994 Regents of the University of California */ + +#ifndef lint +static char SCCSid[] = "@(#)header.c 2.4 2/27/94 LBL"; +#endif + +/* + * header.c - routines for reading and writing information headers. + * + * 8/19/88 + * + * newheader(t,fp) start new information header identified by string t + * isheadid(s) returns true if s is a header id line + * headidval(r,s) copy header identifier value in s to r + * printargs(ac,av,fp) print an argument list to fp, followed by '\n' + * isformat(s) returns true if s is of the form "FORMAT=*" + * formatval(r,s) copy the format value in s to r + * fputformat(s,fp) write "FORMAT=%s" to fp + * getheader(fp,f,p) read header from fp, calling f(s,p) on each line + * checkheader(i,p,o) check header format from i against p and copy to o + * + * To copy header from input to output, use getheader(fin, fputs, fout) + */ + +#include +#include +#include + +#define MAXLINE 512 + +char HDRSTR[] = "#?"; /* information header magic number */ + +char FMTSTR[] = "FORMAT="; /* format identifier */ + + +void +newheader(char *s, register FILE *fp) /* identifying line of information header */ +{ + fputs(HDRSTR, fp); + fputs(s, fp); + putc('\n', fp); +} + + +headidval(register char *r, register char *s) /* get header id (return true if is id) */ +{ + register char *cp = HDRSTR; + + while (*cp) if (*cp++ != *s++) return(0); + if (r == NULL) return(1); + while (*s) *r++ = *s++; + *r = '\0'; + return(1); +} + + +isheadid(char *s) /* check to see if line is header id */ +{ + return(headidval(NULL, s)); +} + + +void +printargs(int ac, char **av, register FILE *fp) /* print arguments to a file */ +{ + int quote; + + while (ac-- > 0) { + if (strchr(*av, ' ') != NULL) { /* quote it */ + if (strchr(*av, '\'') != NULL) + quote = '"'; + else + quote = '\''; + putc(quote, fp); + fputs(*av++, fp); + putc(quote, fp); + } else + fputs(*av++, fp); + putc(' ', fp); + } + putc('\n', fp); +} + + +formatval(register char *r, register char *s) /* get format value (return true if format) */ +{ + register char *cp = FMTSTR; + + while (*cp) if (*cp++ != *s++) return(0); + while (isspace(*s)) s++; + if (!*s) return(0); + if (r == NULL) return(1); + while(*s) *r++ = *s++; + while (isspace(r[-1])) r--; + *r = '\0'; + return(1); +} + + +isformat(char *s) /* is line a format line? */ +{ + return(formatval(NULL, s)); +} + + +void +fputformat(char *s, FILE *fp) /* put out a format value */ +{ + fputs(FMTSTR, fp); + fputs(s, fp); + putc('\n', fp); +} + + +int +getheader(FILE *fp, int (*f)(char *, char *), char *p) /* get header from file */ +{ + char buf[MAXLINE]; + + for ( ; ; ) { + buf[MAXLINE-2] = '\n'; + if (fgets(buf, MAXLINE, fp) == NULL) + return(-1); + if (buf[0] == '\n') + return(0); +#ifdef MSDOS + if (buf[0] == '\r' && buf[1] == '\n') + return(0); +#endif + if (buf[MAXLINE-2] != '\n') { + ungetc(buf[MAXLINE-2], fp); /* prevent false end */ + buf[MAXLINE-2] = '\0'; + } + if (f != NULL) + (*f)(buf, p); + } +} + + +struct check { + FILE *fp; + char fs[64]; +}; + + +static void +mycheck(char *s, register struct check *cp) /* check a header line for format info. */ +{ + if (!formatval(cp->fs, s) && cp->fp != NULL) + fputs(s, cp->fp); +} + + +/* + * Copymatch(pat,str) checks pat for wildcards, and + * copies str into pat if there is a match (returning true). + */ + +#ifdef COPYMATCH +copymatch(char *pat, char *str) +{ + int docopy = 0; + register char *p = pat, *s = str; + + do { + switch (*p) { + case '?': /* match any character */ + if (!*s++) + return(0); + docopy++; + break; + case '*': /* match any string */ + while (p[1] == '*') p++; + do + if ( (p[1]=='?' || p[1]==*s) + && copymatch(p+1,s) ) { + strcpy(pat, str); + return(1); + } + while (*s++); + return(0); + case '\\': /* literal next */ + p++; + /* fall through */ + default: /* normal character */ + if (*p != *s) + return(0); + s++; + break; + } + } while (*p++); + if (docopy) + strcpy(pat, str); + return(1); +} +#else +#define copymatch(pat, s) (!strcmp(pat, s)) +#endif + + +/* + * Checkheader(fin,fmt,fout) returns a value of 1 if the input format + * matches the specification in fmt, 0 if no input format was found, + * and -1 if the input format does not match or there is an + * error reading the header. If fmt is empty, then -1 is returned + * if any input format is found (or there is an error), and 0 otherwise. + * If fmt contains any '*' or '?' characters, then checkheader + * does wildcard expansion and copies a matching result into fmt. + * Be sure that fmt is big enough to hold the match in such cases! + * The input header (minus any format lines) is copied to fout + * if fout is not NULL. + */ + +checkheader(FILE *fin, char *fmt, FILE *fout) +{ + struct check cdat; + + cdat.fp = fout; + cdat.fs[0] = '\0'; + if (getheader(fin, (int (*)(char *, char *))mycheck, (char *)&cdat) < 0) + return(-1); + if (cdat.fs[0] != '\0') + return(copymatch(fmt, cdat.fs) ? 1 : -1); + return(0); +} diff --git a/panda/src/pnmimagetypes/pnmFileTypeAlias.cxx b/panda/src/pnmimagetypes/pnmFileTypeAlias.cxx new file mode 100644 index 0000000000..b166f22518 --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeAlias.cxx @@ -0,0 +1,373 @@ +// Filename: pnmFileTypeAlias.cxx +// Created by: drose (17Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmFileTypeAlias.h" +#include "config_pnmimagetypes.h" + +// Since Alias image files don't have a magic number, we'll make a +// little sanity check on the size of the image. If either the width +// or height is larger than this, it must be bogus. +#define INSANE_SIZE 20000 + +static const char * const extensions[] = { + "pix", "als" +}; +static const int num_extensions = sizeof(extensions) / sizeof(const char *); + +TypeHandle PNMFileTypeAlias::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeAlias::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeAlias:: +PNMFileTypeAlias() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeAlias::get_name +// Access: Public, Virtual +// Description: Returns a few words describing the file type. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeAlias:: +get_name() const { + return "Alias"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeAlias::get_num_extensions +// Access: Public, Virtual +// Description: Returns the number of different possible filename +// extensions associated with this particular file type. +//////////////////////////////////////////////////////////////////// +int PNMFileTypeAlias:: +get_num_extensions() const { + return num_extensions; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeAlias::get_extension +// Access: Public, Virtual +// Description: Returns the nth possible filename extension +// associated with this particular file type, without a +// leading dot. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeAlias:: +get_extension(int n) const { + nassertr(n >= 0 && n < num_extensions, string()); + return extensions[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeAlias::get_suggested_extension +// Access: Public, Virtual +// Description: Returns a suitable filename extension (without a +// leading dot) to suggest for files of this type, or +// empty string if no suggestions are available. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeAlias:: +get_suggested_extension() const { + return "pix"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeAlias::make_reader +// Access: Public, Virtual +// Description: Allocates and returns a new PNMReader suitable for +// reading from this file type, if possible. If reading +// from this file type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMReader *PNMFileTypeAlias:: +make_reader(FILE *file, bool owns_file, const string &magic_number) { + return new Reader(this, file, owns_file, magic_number); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeAlias::make_writer +// Access: Public, Virtual +// Description: Allocates and returns a new PNMWriter suitable for +// reading from this file type, if possible. If writing +// files of this type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMWriter *PNMFileTypeAlias:: +make_writer(FILE *file, bool owns_file) { + return new Writer(this, file, owns_file); +} + + + +inline unsigned short +read_ushort(FILE *file) { + unsigned short x; + return pm_readbigshort(file, (short *)&x)==0 ? x : 0; +} + +inline unsigned char +read_uchar(FILE *file) { + int x; + x = getc(file); + return (x!=EOF) ? (unsigned char)x : 0; +} + +inline void +write_ushort(FILE *file, unsigned short x) { + pm_writebigshort(file, (short)x); +} + +inline void +write_uchar(FILE *file, unsigned char x) { + putc(x, file); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeAlias::Reader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeAlias::Reader:: +Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number) : + PNMReader(type, file, owns_file) +{ + if (!read_magic_number(_file, magic_number, 4)) { + // Although Alias files have no magic number, they do have a + // number of ushorts at the beginning. If these aren't present, + // we have a problem. + if (pnmimage_alias_cat.is_debug()) { + pnmimage_alias_cat.debug() + << "Alias image file appears to be empty.\n"; + } + _is_valid = false; + return; + } + + _x_size = + ((unsigned char)magic_number[0] << 8) | + ((unsigned char)magic_number[1]); + _y_size = + ((unsigned char)magic_number[2] << 8) | + ((unsigned char)magic_number[3]); + + if (_x_size == 0 || _y_size == 0 || + _x_size > INSANE_SIZE || _y_size > INSANE_SIZE) { + _is_valid = false; + pnmimage_alias_cat.debug() + << "File is not a valid Alias image.\n"; + return; + } + + read_ushort(_file); + read_ushort(_file); + + int bpp = read_ushort(_file); + + switch (bpp) { + case 8: + _num_channels = 1; + break; + + case 24: + _num_channels = 3; + break; + + default: + _is_valid = false; + return; + } + + _maxval = 255; + + if (pnmimage_alias_cat.is_debug()) { + pnmimage_alias_cat.debug() + << "Reading Alias " << *this << "\n"; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeAlias::Reader::supports_read_row +// Access: Public, Virtual +// Description: Returns true if this particular PNMReader supports a +// streaming interface to reading the data: that is, it +// is capable of returning the data one row at a time, +// via repeated calls to read_row(). Returns false if +// the only way to read from this file is all at once, +// via read_data(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeAlias::Reader:: +supports_read_row() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeAlias::Reader::read_row +// Access: Public, Virtual +// Description: If supports_read_row(), above, returns true, this +// function may be called repeatedly to read the image, +// one horizontal row at a time, beginning from the top. +// Returns true if the row is successfully read, false +// if there is an error or end of file. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeAlias::Reader:: +read_row(xel *row_data, xelval *) { + if (!is_valid()) { + return false; + } + + int x; + int num; + unsigned char red, grn, blu; + + x = 0; + while (x < _x_size) { + num = read_uchar(_file); + if (num==0 || x+num > _x_size) { + return false; + } + blu = read_uchar(_file); + + if (get_color_type() == PNMImageHeader::CT_color) { + grn = read_uchar(_file); + red = read_uchar(_file); + while (num>0) { + PPM_ASSIGN(row_data[x], red, grn, blu); + x++; + num--; + } + } else { + while (num>0) { + PPM_PUTB(row_data[x], blu); + x++; + num--; + } + } + } + + return true; +} + +static unsigned char last_red = 0, last_blu = 0, last_grn = 0; +static int num_count = 0; + +static void +flush_color(FILE *file) { + if (num_count>0) { + write_uchar(file, num_count); + write_uchar(file, last_blu); + write_uchar(file, last_grn); + write_uchar(file, last_red); + num_count = 0; + } +} + +static void +write_color(FILE *file, + unsigned char red, unsigned char blu, unsigned char grn) { + if (red==last_red && blu==last_blu && grn==last_grn && num_count<0377) { + num_count++; + } else { + flush_color(file); + last_red = red; + last_grn = grn; + last_blu = blu; + num_count = 1; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeAlias::Writer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeAlias::Writer:: +Writer(PNMFileType *type, FILE *file, bool owns_file) : + PNMWriter(type, file, owns_file) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeAlias::Writer::supports_write_row +// Access: Public, Virtual +// Description: Returns true if this particular PNMWriter supports a +// streaming interface to writing the data: that is, it +// is capable of writing the image one row at a time, +// via repeated calls to write_row(). Returns false if +// the only way to write from this file is all at once, +// via write_data(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeAlias::Writer:: +supports_write_row() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeAlias::Writer::write_header +// Access: Public, Virtual +// Description: If supports_write_row(), above, returns true, this +// function may be called to write out the image header +// in preparation to writing out the image data one row +// at a time. Returns true if the header is +// successfully written, false if there is an error. +// +// It is the user's responsibility to fill in the header +// data via calls to set_x_size(), set_num_channels(), +// etc., or copy_header_from(), before calling +// write_header(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeAlias::Writer:: +write_header() { + write_ushort(_file, _x_size); + write_ushort(_file, _y_size); + + write_ushort(_file, 0); + write_ushort(_file, 0); + + // We'll always write full-color Alias images, even if the source + // was grayscale. Many programs don't seem to understand grayscale + // Alias images. + write_ushort(_file, 24); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeAlias::Writer::write_row +// Access: Public, Virtual +// Description: If supports_write_row(), above, returns true, this +// function may be called repeatedly to write the image, +// one horizontal row at a time, beginning from the top. +// Returns true if the row is successfully written, +// false if there is an error. +// +// You must first call write_header() before writing the +// individual rows. It is also important to delete the +// PNMWriter class after successfully writing the last +// row. Failing to do this may result in some data not +// getting flushed! +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeAlias::Writer:: +write_row(xel *row_data, xelval *) { + int x; + unsigned char red, grn, blu; + + bool grayscale = is_grayscale(); + + for (x = 0; x < _x_size; x++) { + if (grayscale) { + red = grn = blu = (unsigned char)(255*PPM_GETB(row_data[x]) / _maxval); + } else { + red = (unsigned char)(255*PPM_GETR(row_data[x]) / _maxval); + grn = (unsigned char)(255*PPM_GETG(row_data[x]) / _maxval); + blu = (unsigned char)(255*PPM_GETB(row_data[x]) / _maxval); + } + + write_color(_file, red, blu, grn); + } + flush_color(_file); + + return true; +} + + diff --git a/panda/src/pnmimagetypes/pnmFileTypeAlias.h b/panda/src/pnmimagetypes/pnmFileTypeAlias.h new file mode 100644 index 0000000000..394d9c17a2 --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeAlias.h @@ -0,0 +1,72 @@ +// Filename: pnmFileTypeAlias.h +// Created by: drose (17Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMFILETYPEALIAS_H +#define PNMFILETYPEALIAS_H + +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : PNMFileTypeAlias +// Description : For reading and Alias native image files. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PNMFileTypeAlias : public PNMFileType { +public: + PNMFileTypeAlias(); + + virtual string get_name() const; + + virtual int get_num_extensions() const; + virtual string get_extension(int n) const; + virtual string get_suggested_extension() const; + + virtual PNMReader *make_reader(FILE *file, bool owns_file = true, + const string &magic_number = string()); + virtual PNMWriter *make_writer(FILE *file, bool owns_file = true); + +public: + class Reader : public PNMReader { + public: + Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number); + + virtual bool supports_read_row() const; + virtual bool read_row(xel *array, xelval *alpha); + }; + + class Writer : public PNMWriter { + public: + Writer(PNMFileType *type, FILE *file, bool owns_file); + + virtual bool supports_write_row() const; + virtual bool write_header(); + virtual bool write_row(xel *array, xelval *alpha); + }; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PNMFileType::init_type(); + register_type(_type_handle, "PNMFileTypeAlias", + PNMFileType::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif + + diff --git a/panda/src/pnmimagetypes/pnmFileTypeBMP.cxx b/panda/src/pnmimagetypes/pnmFileTypeBMP.cxx new file mode 100644 index 0000000000..1aedc4ed52 --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeBMP.cxx @@ -0,0 +1,118 @@ +// Filename: pnmFileTypeBMP.cxx +// Created by: drose (19Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmFileTypeBMP.h" +#include "config_pnmimagetypes.h" + +static const char * const extensions[] = { + "bmp" +}; +static const int num_extensions = sizeof(extensions) / sizeof(const char *); + +TypeHandle PNMFileTypeBMP::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeBMP::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeBMP:: +PNMFileTypeBMP() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeBMP::get_name +// Access: Public, Virtual +// Description: Returns a few words describing the file type. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeBMP:: +get_name() const { + return "Windows BMP"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeBMP::get_num_extensions +// Access: Public, Virtual +// Description: Returns the number of different possible filename +// extensions associated with this particular file type. +//////////////////////////////////////////////////////////////////// +int PNMFileTypeBMP:: +get_num_extensions() const { + return num_extensions; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeBMP::get_extension +// Access: Public, Virtual +// Description: Returns the nth possible filename extension +// associated with this particular file type, without a +// leading dot. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeBMP:: +get_extension(int n) const { + nassertr(n >= 0 && n < num_extensions, string()); + return extensions[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeBMP::get_suggested_extension +// Access: Public, Virtual +// Description: Returns a suitable filename extension (without a +// leading dot) to suggest for files of this type, or +// empty string if no suggestions are available. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeBMP:: +get_suggested_extension() const { + return "bmp"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeBMP::has_magic_number +// Access: Public, Virtual +// Description: Returns true if this particular file type uses a +// magic number to identify it, false otherwise. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeBMP:: +has_magic_number() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeBMP::matches_magic_number +// Access: Public, Virtual +// Description: Returns true if the indicated "magic number" byte +// stream (the initial few bytes read from the file) +// matches this particular file type, false otherwise. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeBMP:: +matches_magic_number(const string &magic_number) const { + nassertr(magic_number.size() >= 2, false); + return (magic_number.substr(0, 2) == "BM"); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeBMP::make_reader +// Access: Public, Virtual +// Description: Allocates and returns a new PNMReader suitable for +// reading from this file type, if possible. If reading +// from this file type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMReader *PNMFileTypeBMP:: +make_reader(FILE *file, bool owns_file, const string &magic_number) { + return new Reader(this, file, owns_file, magic_number); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeBMP::make_writer +// Access: Public, Virtual +// Description: Allocates and returns a new PNMWriter suitable for +// reading from this file type, if possible. If writing +// files of this type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMWriter *PNMFileTypeBMP:: +make_writer(FILE *file, bool owns_file) { + return new Writer(this, file, owns_file); +} + diff --git a/panda/src/pnmimagetypes/pnmFileTypeBMP.h b/panda/src/pnmimagetypes/pnmFileTypeBMP.h new file mode 100644 index 0000000000..7527e9ecfc --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeBMP.h @@ -0,0 +1,85 @@ +// Filename: pnmFileTypeBMP.h +// Created by: drose (17Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMFILETYPEBMP_H +#define PNMFILETYPEBMP_H + +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : PNMFileTypeBMP +// Description : For reading and writing Windows BMP files. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PNMFileTypeBMP : public PNMFileType { +public: + PNMFileTypeBMP(); + + virtual string get_name() const; + + virtual int get_num_extensions() const; + virtual string get_extension(int n) const; + virtual string get_suggested_extension() const; + + virtual bool has_magic_number() const; + virtual bool matches_magic_number(const string &magic_number) const; + + virtual PNMReader *make_reader(FILE *file, bool owns_file = true, + const string &magic_number = string()); + virtual PNMWriter *make_writer(FILE *file, bool owns_file = true); + +public: + class Reader : public PNMReader { + public: + Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number); + + virtual int read_data(xel *array, xelval *alpha); + + private: + unsigned long pos; + + unsigned long offBits; + + unsigned short cBitCount; + int indexed; + int classv; + + pixval R[256]; /* reds */ + pixval G[256]; /* greens */ + pixval B[256]; /* blues */ + }; + + class Writer : public PNMWriter { + public: + Writer(PNMFileType *type, FILE *file, bool owns_file); + + virtual int write_data(xel *array, xelval *alpha); + }; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PNMFileType::init_type(); + register_type(_type_handle, "PNMFileTypeBMP", + PNMFileType::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif + + diff --git a/panda/src/pnmimagetypes/pnmFileTypeBMPReader.cxx b/panda/src/pnmimagetypes/pnmFileTypeBMPReader.cxx new file mode 100644 index 0000000000..bab54eb9e3 --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeBMPReader.cxx @@ -0,0 +1,547 @@ +// Filename: pnmFileTypeBMPReader.cxx +// Created by: drose (19Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmFileTypeBMP.h" +#include "config_pnmimagetypes.h" + +extern "C" { + #include "bmp.h" + #include "../pnm/bitio.h" +} + +// Much code in this file is borrowed from Netpbm, specifically bmptoppm.c. +/*\ + * $Id$ + * + * bmptoppm.c - Converts from a Microsoft Windows or OS/2 .BMP file to a + * PPM file. + * + * The current implementation is probably not complete, but it works for + * all the BMP files I have. I welcome feedback. + * + * Copyright (C) 1992 by David W. Sanderson. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. This software is provided "as is" + * without express or implied warranty. + * + * $Log$ + * Revision 1.1 2000/10/04 01:14:42 drose + * Initial revision + * + * Revision 1.10 1992/11/24 19:38:17 dws + * Added code to verify that reading occurred at the correct offsets. + * Added copyright. + * + * Revision 1.9 1992/11/17 02:15:24 dws + * Changed to include bmp.h. + * Eliminated need for fseek(), and therefore the need for a + * temporary file. + * + * Revision 1.8 1992/11/13 23:48:57 dws + * Made definition of Seekable() static, to match its prototype. + * + * Revision 1.7 1992/11/11 00:17:50 dws + * Generalized to use bitio routines. + * + * Revision 1.6 1992/11/10 23:51:44 dws + * Enhanced command-line handling. + * + * Revision 1.5 1992/11/08 00:38:46 dws + * Changed some names to help w/ addition of ppmtobmp. + * + * Revision 1.4 1992/10/27 06:28:28 dws + * Corrected stupid typo. + * + * Revision 1.3 1992/10/27 06:17:10 dws + * Removed a magic constant value. + * + * Revision 1.2 1992/10/27 06:09:58 dws + * Made stdin seekable. + * + * Revision 1.1 1992/10/27 05:31:41 dws + * Initial revision +\*/ + +/* + * Utilities + */ + +static int GetByte ARGS((FILE * fp)); +static short GetShort ARGS((FILE * fp)); +static long GetLong ARGS((FILE * fp)); +static void readto ARGS((FILE *fp, unsigned long *ppos, unsigned long dst)); +static void BMPreadfileheader ARGS((FILE *fp, unsigned long *ppos, + unsigned long *poffBits)); +static void BMPreadinfoheader ARGS((FILE *fp, unsigned long *ppos, + unsigned long *pcx, unsigned long *pcy, unsigned short *pcBitCount, + int *pclassv)); +static int BMPreadrgbtable ARGS((FILE *fp, unsigned long *ppos, + unsigned short cBitCount, int classv, pixval *R, pixval *G, pixval *B)); +static int BMPreadrow ARGS((FILE *fp, unsigned long *ppos, pixel *row, + unsigned long cx, unsigned short cBitCount, pixval *R, pixval *G, pixval *B)); +static pixel ** BMPreadbits ARGS((FILE *fp, unsigned long *ppos, + unsigned long offBits, unsigned long cx, unsigned long cy, + unsigned short cBitCount, int classv, pixval *R, pixval *G, pixval *B)); + +static const char *ifname = "BMP"; +static char er_read[] = "%s: read error"; +static char er_seek[] = "%s: seek error"; + +static int +GetByte(FILE *fp) +{ + int v; + + if ((v = getc(fp)) == EOF) + { + pm_error(er_read, ifname); + } + + return v; +} + +static short +GetShort(FILE *fp) +{ + short v; + + if (pm_readlittleshort(fp, &v) == -1) + { + pm_error(er_read, ifname); + } + + return v; +} + +static long +GetLong(FILE *fp) +{ + long v; + + if (pm_readlittlelong(fp, &v) == -1) + { + pm_error(er_read, ifname); + } + + return v; +} + +/* + * readto - read as many bytes as necessary to position the + * file at the desired offset. + */ + +static void +readto(FILE *fp, + unsigned long *ppos, /* pointer to number of bytes read from fp */ + unsigned long dst) +{ + unsigned long pos; + + if(!fp || !ppos) + return; + + pos = *ppos; + + if(pos > dst) + pm_error("%s: internal error in readto()", ifname); + + for(; pos < dst; pos++) + { + if (getc(fp) == EOF) + { + pm_error(er_read, ifname); + } + } + + *ppos = pos; +} + + +/* + * BMP reading routines + */ + +static void +BMPreadfileheader( + FILE *fp, + unsigned long *ppos, /* number of bytes read from fp */ + unsigned long *poffBits) +{ + /* + unsigned long cbSize; + unsigned short xHotSpot; + unsigned short yHotSpot; + */ + unsigned long offBits; + + /* + We've already read the magic number. + if (GetByte(fp) != 'B') + { + pm_error("%s is not a BMP file", ifname); + } + if (GetByte(fp) != 'M') + { + pm_error("%s is not a BMP file", ifname); + } + */ + + /* cbSize = */ GetLong(fp); + /* xHotSpot = */ GetShort(fp); + /* yHotSpot = */ GetShort(fp); + offBits = GetLong(fp); + + *poffBits = offBits; + + *ppos += 14; +} + +static void +BMPreadinfoheader( + FILE *fp, + unsigned long *ppos, /* number of bytes read from fp */ + unsigned long *pcx, + unsigned long *pcy, + unsigned short *pcBitCount, + int *pclassv) +{ + unsigned long cbFix; + unsigned short cPlanes; + + unsigned long cx; + unsigned long cy; + unsigned short cBitCount; + int classv; + + cbFix = GetLong(fp); + + switch (cbFix) + { + case 12: + classv = C_OS2; + + cx = GetShort(fp); + cy = GetShort(fp); + cPlanes = GetShort(fp); + cBitCount = GetShort(fp); + + break; + case 40: + classv = C_WIN; + + cx = GetLong(fp); + cy = GetLong(fp); + cPlanes = GetShort(fp); + cBitCount = GetShort(fp); + + /* + * We've read 16 bytes so far, need to read 24 more + * for the required total of 40. + */ + + GetLong(fp); + GetLong(fp); + GetLong(fp); + GetLong(fp); + GetLong(fp); + GetLong(fp); + + break; + default: + pm_error("%s: unknown cbFix: %d", ifname, cbFix); + break; + } + + if (cPlanes != 1) + { + pm_error("%s: don't know how to handle cPlanes = %d" + ,ifname + ,cPlanes); + } + + switch (classv) + { + case C_WIN: + pm_message("Windows BMP, %dx%dx%d" + ,cx + ,cy + ,cBitCount); + break; + case C_OS2: + pm_message("OS/2 BMP, %dx%dx%d" + ,cx + ,cy + ,cBitCount); + break; + } + +#ifdef DEBUG + pm_message("cbFix: %d", cbFix); + pm_message("cx: %d", cx); + pm_message("cy: %d", cy); + pm_message("cPlanes: %d", cPlanes); + pm_message("cBitCount: %d", cBitCount); +#endif + + *pcx = cx; + *pcy = cy; + *pcBitCount = cBitCount; + *pclassv = classv; + + *ppos += cbFix; +} + +/* + * returns the number of bytes read, or -1 on error. + */ +static int +BMPreadrgbtable( + FILE *fp, + unsigned long *ppos, /* number of bytes read from fp */ + unsigned short cBitCount, + int classv, + pixval *R, + pixval *G, + pixval *B) +{ + int i; + int nbyte = 0; + + long ncolors = (1 << cBitCount); + + for (i = 0; i < ncolors; i++) + { + B[i] = (pixval) GetByte(fp); + G[i] = (pixval) GetByte(fp); + R[i] = (pixval) GetByte(fp); + nbyte += 3; + + if (classv == C_WIN) + { + (void) GetByte(fp); + nbyte++; + } + } + + *ppos += nbyte; + return nbyte; +} + +/* + * returns the number of bytes read, or -1 on error. + */ +static int +BMPreadrow( + FILE *fp, + unsigned long *ppos, /* number of bytes read from fp */ + pixel *row, + unsigned long cx, + unsigned short cBitCount, + int indexed, + pixval *R, + pixval *G, + pixval *B) +{ + BITSTREAM b; + unsigned nbyte = 0; + int rc; + unsigned x; + + if (indexed) { + if ((b = pm_bitinit(fp, "r")) == (BITSTREAM) 0) + { + return -1; + } + } + + for (x = 0; x < cx; x++, row++) + { + unsigned long v; + + if (!indexed) { + int r, g, b; + b = GetByte(fp); + g = GetByte(fp); + r = GetByte(fp); + nbyte += 3; + PPM_ASSIGN(*row, r, g, b); + } else { + if ((rc = pm_bitread(b, cBitCount, &v)) == -1) + { + return -1; + } + nbyte += rc; + + PPM_ASSIGN(*row, R[v], G[v], B[v]); + } + } + + if (indexed) { + if ((rc = pm_bitfini(b)) != 0) + { + return -1; + } + } + + /* + * Make sure we read a multiple of 4 bytes. + */ + while (nbyte % 4) + { + GetByte(fp); + nbyte++; + } + + *ppos += nbyte; + return nbyte; +} + +static void +BMPreadbits(xel *array, + FILE *fp, + unsigned long *ppos, /* number of bytes read from fp */ + unsigned long offBits, + unsigned long cx, + unsigned long cy, + unsigned short cBitCount, + int /* classv */, + int indexed, + pixval *R, + pixval *G, + pixval *B) +{ + long y; + + readto(fp, ppos, offBits); + + if(cBitCount > 24) + { + pm_error("%s: cannot handle cBitCount: %d" + ,ifname + ,cBitCount); + } + + /* + * The picture is stored bottom line first, top line last + */ + + for (y = (long)cy - 1; y >= 0; y--) + { + int rc; + rc = BMPreadrow(fp, ppos, array + y*cx, cx, cBitCount, indexed, R, G, B); + if(rc == -1) + { + pm_error("%s: couldn't read row %d" + ,ifname + ,y); + } + if(rc%4) + { + pm_error("%s: row had bad number of bytes: %d" + ,ifname + ,rc); + } + } + +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeBMP::Reader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeBMP::Reader:: +Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number) : + PNMReader(type, file, owns_file) +{ + if (!read_magic_number(_file, magic_number, 2)) { + // No magic number, no image. + if (pnmimage_bmp_cat.is_debug()) { + pnmimage_bmp_cat.debug() + << "BMP image file appears to be empty.\n"; + } + _is_valid = false; + return; + } + + if (magic_number != string("BM")) { + pnmimage_bmp_cat.error() + << "File is not a valid BMP file.\n"; + _is_valid = false; + return; + } + + int rc; + + unsigned long cx; + unsigned long cy; + + pos = 0; + + BMPreadfileheader(file, &pos, &offBits); + BMPreadinfoheader(file, &pos, &cx, &cy, &cBitCount, &classv); + + if (offBits != BMPoffbits(classv, cBitCount)) { + pnmimage_bmp_cat.warning() + << "offBits is " << offBits << ", expected " + << BMPoffbits(classv, cBitCount) << "\n"; + } + + indexed = false; + + if (cBitCount <= 8) { + indexed = true; + rc = BMPreadrgbtable(file, &pos, cBitCount, classv, R, G, B); + + if (rc != BMPlenrgbtable(classv, cBitCount)) { + pnmimage_bmp_cat.warning() + << rc << "-byte RGB table, expected " + << BMPlenrgbtable(classv, cBitCount) << " bytes\n"; + } + } + + _num_channels = 3; + _x_size = (int)cx; + _y_size = (int)cy; + _maxval = 255; + + if (pnmimage_bmp_cat.is_debug()) { + pnmimage_bmp_cat.debug() + << "Reading BMP " << *this << "\n"; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeBMP::Reader::read_data +// Access: Public, Virtual +// Description: Reads in an entire image all at once, storing it in +// the pre-allocated _x_size * _y_size array and alpha +// pointers. (If the image type has no alpha channel, +// alpha is ignored.) Returns the number of rows +// correctly read. +// +// Derived classes need not override this if they +// instead provide supports_read_row() and read_row(), +// below. +//////////////////////////////////////////////////////////////////// +int PNMFileTypeBMP::Reader:: +read_data(xel *array, xelval *) { + BMPreadbits(array, _file, &pos, offBits, _x_size, _y_size, + cBitCount, classv, indexed, R, G, B); + + if (pos != BMPlenfile(classv, cBitCount, _x_size, _y_size)) { + pnmimage_bmp_cat.warning() + << "Read " << pos << " bytes, expected to read " + << BMPlenfile(classv, cBitCount, _x_size, _y_size) << " bytes\n"; + } + + return _y_size; +} diff --git a/panda/src/pnmimagetypes/pnmFileTypeBMPWriter.cxx b/panda/src/pnmimagetypes/pnmFileTypeBMPWriter.cxx new file mode 100644 index 0000000000..71759e701c --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeBMPWriter.cxx @@ -0,0 +1,636 @@ +// Filename: pnmFileTypeBMPWriter.cxx +// Created by: drose (19Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmFileTypeBMP.h" +#include "config_pnmimagetypes.h" + +#include +#include + +extern "C" { + #include "bmp.h" + #include "../pnm/ppmcmap.h" + #include "../pnm/bitio.h" +} + +// Much code in this file is borrowed from Netpbm, specifically ppmtobmp.c. +/*\ + * $Id$ + * + * ppmtobmp.c - Converts from a PPM file to a Microsoft Windows or OS/2 + * .BMP file. + * + * The current implementation is probably not complete, but it works for + * me. I welcome feedback. + * + * Copyright (C) 1992 by David W. Sanderson. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. This software is provided "as is" + * without express or implied warranty. + * + * $Log$ + * Revision 1.1 2000/10/04 01:14:42 drose + * Initial revision + * + * Revision 1.9 1992/11/24 19:39:33 dws + * Added copyright. + * + * Revision 1.8 1992/11/17 02:16:52 dws + * Moved length functions to bmp.h. + * + * Revision 1.7 1992/11/11 23:18:16 dws + * Modified to adjust the bits per pixel to 1, 4, or 8. + * + * Revision 1.6 1992/11/11 22:43:39 dws + * Commented out a superfluous message. + * + * Revision 1.5 1992/11/11 05:58:06 dws + * First version that works. + * + * Revision 1.4 1992/11/11 03:40:32 dws + * Moved calculation of bits per pixel to BMPEncode. + * + * Revision 1.3 1992/11/11 03:02:34 dws + * Added BMPEncode function. + * + * Revision 1.2 1992/11/08 01:44:35 dws + * Added option processing and reading of PPM file. + * + * Revision 1.1 1992/11/08 00:46:07 dws + * Initial revision +\*/ + +#define MAXCOLORS 256 + +/* + * Utilities + */ + +static char er_write[] = "stdout: write error"; + +/* prototypes */ +static void PutByte ARGS((FILE *fp, char v)); +static void PutShort ARGS((FILE *fp, short v)); +static void PutLong ARGS((FILE *fp, long v)); +static int BMPwritefileheader ARGS((FILE *fp, int classv, unsigned long bitcount, + unsigned long x, unsigned long y)); +static int BMPwriteinfoheader ARGS((FILE *fp, int classv, unsigned long bitcount, + unsigned long x, unsigned long y)); +static int BMPwritergb ARGS((FILE *fp, int classv, pixval R, pixval G, pixval B)); +static int BMPwritergbtable ARGS((FILE *fp, int classv, int bpp, int colors, + pixval *R, pixval *G, pixval *B)); +static int BMPwriterow ARGS((FILE *fp, pixel *row, unsigned long cx, + unsigned short bpp, colorhash_table cht)); +static int BMPwritebits ARGS((FILE *fp, unsigned long cx, unsigned long cy, + unsigned short cBitCount, pixel **pixels, colorhash_table cht)); +static int colorstobpp ARGS((int colors)); +static void BMPEncode ARGS((FILE *fp, int classv, int x, int y, pixel **pixels, + int colors, colorhash_table cht, pixval *R, pixval *G, pixval *B)); +static void +PutByte( + FILE *fp, + char v) +{ + if (putc(v, fp) == EOF) + { + pm_error(er_write); + } +} + +static void +PutShort( + FILE *fp, + short v) +{ + if (pm_writelittleshort(fp, v) == -1) + { + pm_error(er_write); + } +} + +static void +PutLong( + FILE *fp, + long v) +{ + if (pm_writelittlelong(fp, v) == -1) + { + pm_error(er_write); + } +} + +/* + * BMP writing + */ + +/* + * returns the number of bytes written, or -1 on error. + */ +static int +BMPwritefileheader( + FILE *fp, + int classv, + unsigned long bitcount, + unsigned long x, + unsigned long y) +{ + PutByte(fp, 'B'); + PutByte(fp, 'M'); + + /* cbSize */ + PutLong(fp, (long)BMPlenfile(classv, bitcount, x, y)); + + /* xHotSpot */ + PutShort(fp, 0); + + /* yHotSpot */ + PutShort(fp, 0); + + /* offBits */ + PutLong(fp, (long)BMPoffbits(classv, bitcount)); + + return 14; +} + +/* + * returns the number of bytes written, or -1 on error. + */ +static int +BMPwriteinfoheader( + FILE *fp, + int classv, + unsigned long bitcount, + unsigned long x, + unsigned long y) +{ + long cbFix; + + /* cbFix */ + switch (classv) + { + case C_WIN: + cbFix = 40; + PutLong(fp, cbFix); + + /* cx */ + PutLong(fp, (long)x); + /* cy */ + PutLong(fp, (long)y); + /* cPlanes */ + PutShort(fp, 1); + /* cBitCount */ + PutShort(fp, (short)bitcount); + + /* + * We've written 16 bytes so far, need to write 24 more + * for the required total of 40. + */ + + PutLong(fp, 0); + PutLong(fp, 0); + PutLong(fp, 0); + PutLong(fp, 0); + PutLong(fp, 0); + PutLong(fp, 0); + + + break; + case C_OS2: + cbFix = 12; + PutLong(fp, cbFix); + + /* cx */ + PutShort(fp, (short)x); + /* cy */ + PutShort(fp, (short)y); + /* cPlanes */ + PutShort(fp, 1); + /* cBitCount */ + PutShort(fp, (short)bitcount); + + break; + default: + pm_error(er_internal, "BMPwriteinfoheader"); + } + + return cbFix; +} + +/* + * returns the number of bytes written, or -1 on error. + */ +static int +BMPwritergb( + FILE *fp, + int classv, + pixval R, + pixval G, + pixval B) +{ + switch (classv) + { + case C_WIN: + PutByte(fp, B); + PutByte(fp, G); + PutByte(fp, R); + PutByte(fp, 0); + return 4; + case C_OS2: + PutByte(fp, B); + PutByte(fp, G); + PutByte(fp, R); + return 3; + default: + pm_error(er_internal, "BMPwritergb"); + } + return -1; +} + +/* + * returns the number of bytes written, or -1 on error. + */ +static int +BMPwritergbtable( + FILE *fp, + int classv, + int bpp, + int colors, + pixval *R, + pixval *G, + pixval *B) +{ + int nbyte = 0; + int i; + long ncolors; + + for (i = 0; i < colors; i++) + { + nbyte += BMPwritergb(fp,classv,R[i],G[i],B[i]); + } + + ncolors = (1 << bpp); + + for (; i < ncolors; i++) + { + nbyte += BMPwritergb(fp,classv,0,0,0); + } + + return nbyte; +} + +/* + * returns the number of bytes written, or -1 on error. + */ +static int +BMPwriterow( + FILE *fp, + pixel *row, + unsigned long cx, + unsigned short bpp, + int indexed, + colorhash_table cht) +{ + BITSTREAM b; + unsigned nbyte = 0; + int rc; + unsigned x; + + if (indexed) { + if ((b = pm_bitinit(fp, "w")) == (BITSTREAM) 0) + { + return -1; + } + + for (x = 0; x < cx; x++, row++) + { + if ((rc = pm_bitwrite(b, bpp, ppm_lookupcolor(cht, row))) == -1) + { + return -1; + } + nbyte += rc; + } + + if ((rc = pm_bitfini(b)) == -1) + { + return -1; + } + nbyte += rc; + } else { + + for (x = 0; x < cx; x++, row++) + { + PutByte(fp, PPM_GETB(*row)); + PutByte(fp, PPM_GETG(*row)); + PutByte(fp, PPM_GETR(*row)); + nbyte += 3; + } + } + + /* + * Make sure we write a multiple of 4 bytes. + */ + while (nbyte % 4) + { + PutByte(fp, 0); + nbyte++; + } + + return nbyte; +} + +/* + * returns the number of bytes written, or -1 on error. + */ +static int +BMPwritebits( + FILE *fp, + unsigned long cx, + unsigned long cy, + unsigned short cBitCount, + pixel **pixels, + int indexed, + colorhash_table cht) +{ + int nbyte = 0; + long y; + + if(cBitCount > 24) + { + pm_error("cannot handle cBitCount: %d" + ,cBitCount); + } + + /* + * The picture is stored bottom line first, top line last + */ + + for (y = (long)cy - 1; y >= 0; y--) + { + int rc; + rc = BMPwriterow(fp, pixels[y], cx, cBitCount, indexed, cht); + + if(rc == -1) + { + pm_error("couldn't write row %d" + ,y); + } + if(rc%4) + { + pm_error("row had bad number of bytes: %d" + ,rc); + } + nbyte += rc; + } + + return nbyte; +} + +/* + * Return the number of bits per pixel required to represent the + * given number of colors. + */ + +static int +colorstobpp(int colors) +{ + int bpp; + + if (colors < 1) + { + pm_error("can't have less than one color"); + } + + if ((bpp = pm_maxvaltobits(colors - 1)) > 8) + { + pm_error("can't happen"); + } + + return bpp; +} + +/* + * Write a BMP file of the given classv. + * + * Note that we must have 'colors' in order to know exactly how many + * colors are in the R, G, B, arrays. Entries beyond those in the + * arrays are undefined. + */ +static void +BMPEncode( + FILE *fp, + int classv, + int x, + int y, + pixel **pixels, + int colors, /* number of valid entries in R,G,B */ + colorhash_table cht, + pixval *R, + pixval *G, + pixval *B) +{ + int bpp; /* bits per pixel */ + unsigned long nbyte = 0; + + bpp = colorstobpp(colors); + + /* + * I have found empirically at least one BMP-displaying program + * that can't deal with (for instance) using 3 bits per pixel. + * I have seen no programs that can deal with using 3 bits per + * pixel. I have seen programs which can deal with 1, 4, and + * 8 bits per pixel. + * + * Based on this, I adjust actual the number of bits per pixel + * as follows. If anyone knows better, PLEASE tell me! + */ + switch(bpp) + { + case 2: + case 3: + bpp = 4; + break; + case 5: + case 6: + case 7: + bpp = 8; + break; + } + + pm_message("Using %d bits per pixel", bpp); + + nbyte += BMPwritefileheader(fp, classv, bpp, x, y); + nbyte += BMPwriteinfoheader(fp, classv, bpp, x, y); + nbyte += BMPwritergbtable(fp, classv, bpp, colors, R, G, B); + + if(nbyte != ( BMPlenfileheader(classv) + + BMPleninfoheader(classv) + + BMPlenrgbtable(classv, bpp))) + { + pm_error(er_internal, "BMPEncode"); + } + + nbyte += BMPwritebits(fp, x, y, bpp, pixels, true, cht); + if(nbyte != BMPlenfile(classv, bpp, x, y)) + { + pm_error(er_internal, "BMPEncode"); + } +} + +/* + * Write a BMP file of the given class, with 24 bits per pixel nonindexed. + */ +static void +BMPEncode24( + FILE *fp, + int classv, + int x, + int y, + pixel **pixels) +{ + unsigned long nbyte = 0; + int bpp = 24; + + pm_message("Using %d bits per pixel", bpp); + + nbyte += BMPwritefileheader(fp, classv, bpp, x, y); + nbyte += BMPwriteinfoheader(fp, classv, bpp, x, y); + + if(nbyte != ( BMPlenfileheader(classv) + + BMPleninfoheader(classv))) + { + pm_error(er_internal, "BMPEncode24"); + } + + nbyte += BMPwritebits(fp, x, y, bpp, pixels, false, colorhash_table()); + if(nbyte != BMPlenfile(classv, bpp, x, y)) + { + pm_error(er_internal, "BMPEncode24"); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeBMP::Writer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeBMP::Writer:: +Writer(PNMFileType *type, FILE *file, bool owns_file) : + PNMWriter(type, file, owns_file) +{ +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeBMP::Writer::write_data +// Access: Public, Virtual +// Description: Writes out an entire image all at once, including the +// header, based on the image data stored in the given +// _x_size * _y_size array and alpha pointers. (If the +// image type has no alpha channel, alpha is ignored.) +// Returns the number of rows correctly written. +// +// It is the user's responsibility to fill in the header +// data via calls to set_x_size(), set_num_channels(), +// etc., or copy_header_from(), before calling +// write_data(). +// +// It is important to delete the PNMWriter class after +// successfully writing the data. Failing to do this +// may result in some data not getting flushed! +// +// Derived classes need not override this if they +// instead provide supports_streaming() and write_row(), +// below. +//////////////////////////////////////////////////////////////////// +int PNMFileTypeBMP::Writer:: +write_data(xel *array, xelval *) { + if (_y_size<=0 || _x_size<=0) { + return 0; + } + + int classv = C_WIN; + + int colors; + int i; + colorhist_vector chv; + pixval Red[MAXCOLORS]; + pixval Green[MAXCOLORS]; + pixval Blue[MAXCOLORS]; + + pixel** pixels; + colorhash_table cht; + +#if 0 + { + char *name; + switch (classv) + { + case C_WIN: + name = "a Windows"; + break; + case C_OS2: + name = "an OS/2"; + break; + default: + pm_error(er_internal, "report"); + break; + } + pm_message("generating %s BMP file", name); + } +#endif + + // We need an honest 2-d array of pixels, instead of one big 1-d array. + pixels = (pixel **)alloca(sizeof(pixel *) * _y_size); + for (i = 0; i < _y_size; i++) { + pixels[i] = (pixel *)(array + i * _x_size); + } + + /* Figure out the colormap. */ + chv = ppm_computecolorhist(pixels, _x_size, _y_size, MAXCOLORS, &colors); + if (chv == (colorhist_vector) 0) { + pnmimage_bmp_cat.debug() + << "too many colors; generating 24-bit BMP file\n"; + + BMPEncode24(_file, classv, _x_size, _y_size, pixels); + + } else { + pnmimage_bmp_cat.debug() + << colors << " colors found\n"; + + /* + * Now turn the ppm colormap into the appropriate BMP colormap. + */ + if (_maxval > 255) { + pnmimage_bmp_cat.debug() + << "maxval is not 255 - automatically rescaling colors\n"; + } + + for (i = 0; i < colors; ++i) { + if (_maxval == 255) { + Red[i] = PPM_GETR(chv[i].color); + Green[i] = PPM_GETG(chv[i].color); + Blue[i] = PPM_GETB(chv[i].color); + } else { + Red[i] = (pixval) PPM_GETR(chv[i].color) * 255 / _maxval; + Green[i] = (pixval) PPM_GETG(chv[i].color) * 255 / _maxval; + Blue[i] = (pixval) PPM_GETB(chv[i].color) * 255 / _maxval; + } + } + + /* And make a hash table for fast lookup. */ + cht = ppm_colorhisttocolorhash(chv, colors); + ppm_freecolorhist(chv); + + BMPEncode(_file, classv, _x_size, _y_size, pixels, colors, cht, + Red, Green, Blue); + } + + return _y_size; +} diff --git a/panda/src/pnmimagetypes/pnmFileTypeIMG.cxx b/panda/src/pnmimagetypes/pnmFileTypeIMG.cxx new file mode 100644 index 0000000000..3a985c8600 --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeIMG.cxx @@ -0,0 +1,332 @@ +// Filename: pnmFileTypeIMG.cxx +// Created by: drose (19Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmFileTypeIMG.h" +#include "config_pnmimagetypes.h" + +// Since raw image files don't have a magic number, we'll make a little +// sanity check on the size of the image. If either the width or height is +// larger than this, it must be bogus. +#define INSANE_SIZE 20000 + +static const char * const extensions[] = { + "img" +}; +static const int num_extensions = sizeof(extensions) / sizeof(const char *); + +TypeHandle PNMFileTypeIMG::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeIMG::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeIMG:: +PNMFileTypeIMG() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeIMG::get_name +// Access: Public, Virtual +// Description: Returns a few words describing the file type. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeIMG:: +get_name() const { + return "Raw binary RGB"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeIMG::get_num_extensions +// Access: Public, Virtual +// Description: Returns the number of different possible filename +// extensions associated with this particular file type. +//////////////////////////////////////////////////////////////////// +int PNMFileTypeIMG:: +get_num_extensions() const { + return num_extensions; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeIMG::get_extension +// Access: Public, Virtual +// Description: Returns the nth possible filename extension +// associated with this particular file type, without a +// leading dot. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeIMG:: +get_extension(int n) const { + nassertr(n >= 0 && n < num_extensions, string()); + return extensions[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeIMG::get_suggested_extension +// Access: Public, Virtual +// Description: Returns a suitable filename extension (without a +// leading dot) to suggest for files of this type, or +// empty string if no suggestions are available. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeIMG:: +get_suggested_extension() const { + return "img"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeIMG::make_reader +// Access: Public, Virtual +// Description: Allocates and returns a new PNMReader suitable for +// reading from this file type, if possible. If reading +// from this file type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMReader *PNMFileTypeIMG:: +make_reader(FILE *file, bool owns_file, const string &magic_number) { + return new Reader(this, file, owns_file, magic_number); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeIMG::make_writer +// Access: Public, Virtual +// Description: Allocates and returns a new PNMWriter suitable for +// reading from this file type, if possible. If writing +// files of this type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMWriter *PNMFileTypeIMG:: +make_writer(FILE *file, bool owns_file) { + return new Writer(this, file, owns_file); +} + + +inline unsigned long +read_ulong(FILE *file) { + unsigned long x; + return pm_readbiglong(file, (long *)&x)==0 ? x : 0; +} + +inline unsigned short +read_ushort(FILE *file) { + unsigned short x; + return pm_readbigshort(file, (short *)&x)==0 ? x : 0; +} + +inline unsigned char +read_uchar(FILE *file) { + int x; + x = getc(file); + return (x!=EOF) ? (unsigned char)x : 0; +} + +inline void +write_ulong(FILE *file, unsigned long x) { + pm_writebiglong(file, (long)x); +} + +inline void +write_ushort(FILE *file, unsigned long x) { + pm_writebigshort(file, (short)(long)x); +} + +inline void +write_uchar(FILE *file, unsigned char x) { + putc(x, file); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeIMG::Reader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeIMG::Reader:: +Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number) : + PNMReader(type, file, owns_file) +{ + if (img_header_type == IHT_long) { + if (!read_magic_number(_file, magic_number, 8)) { + // Although raw IMG files have no magic number, they may have a + // pair of ushorts or ulongs at the beginning to indicate the file + // size. + if (pnmimage_img_cat.is_debug()) { + pnmimage_img_cat.debug() + << "IMG image file appears to be empty.\n"; + } + _is_valid = false; + return; + } + + _x_size = + ((unsigned char)magic_number[0] << 24) | + ((unsigned char)magic_number[1] << 16) | + ((unsigned char)magic_number[2] << 8) | + ((unsigned char)magic_number[3]); + + _y_size = + ((unsigned char)magic_number[4] << 24) | + ((unsigned char)magic_number[5] << 16) | + ((unsigned char)magic_number[6] << 8) | + ((unsigned char)magic_number[7]); + + } else if (img_header_type == IHT_short) { + if (!read_magic_number(_file, magic_number, 4)) { + if (pnmimage_img_cat.is_debug()) { + pnmimage_img_cat.debug() + << "IMG image file appears to be empty.\n"; + } + _is_valid = false; + return; + } + + _x_size = + ((unsigned char)magic_number[0] << 8) | + ((unsigned char)magic_number[1]); + + _y_size = + ((unsigned char)magic_number[2] << 8) | + ((unsigned char)magic_number[3]); + + } else { + _x_size = img_xsize; + _y_size = img_ysize; + } + + if (_x_size == 0 || _y_size == 0 || + _x_size > INSANE_SIZE || _y_size > INSANE_SIZE) { + _is_valid = false; + if (img_header_type == IHT_none) { + pnmimage_img_cat.error() + << "Must specify img-xsize and img-ysize to load headerless raw files.\n"; + } else { + pnmimage_img_cat.debug() + << "IMG file does not have a valid xsize,ysize header.\n"; + } + return; + } + + _maxval = 255; + _num_channels = 3; + + if (pnmimage_img_cat.is_debug()) { + pnmimage_img_cat.debug() + << "Reading IMG " << *this << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeIMG::Reader::supports_read_row +// Access: Public, Virtual +// Description: Returns true if this particular PNMReader supports a +// streaming interface to reading the data: that is, it +// is capable of returning the data one row at a time, +// via repeated calls to read_row(). Returns false if +// the only way to read from this file is all at once, +// via read_data(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeIMG::Reader:: +supports_read_row() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeIMG::Reader::read_row +// Access: Public, Virtual +// Description: If supports_read_row(), above, returns true, this +// function may be called repeatedly to read the image, +// one horizontal row at a time, beginning from the top. +// Returns true if the row is successfully read, false +// if there is an error or end of file. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeIMG::Reader:: +read_row(xel *row_data, xelval *) { + int x; + xelval red, grn, blu; + for (x = 0; x < _x_size; x++) { + red = read_uchar(_file); + grn = read_uchar(_file); + blu = read_uchar(_file); + + PPM_ASSIGN(row_data[x], red, grn, blu); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeIMG::Writer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeIMG::Writer:: +Writer(PNMFileType *type, FILE *file, bool owns_file) : + PNMWriter(type, file, owns_file) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeIMG::Writer::supports_write_row +// Access: Public, Virtual +// Description: Returns true if this particular PNMWriter supports a +// streaming interface to writing the data: that is, it +// is capable of writing the image one row at a time, +// via repeated calls to write_row(). Returns false if +// the only way to write from this file is all at once, +// via write_data(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeIMG::Writer:: +supports_write_row() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeIMG::Writer::write_header +// Access: Public, Virtual +// Description: If supports_write_row(), above, returns true, this +// function may be called to write out the image header +// in preparation to writing out the image data one row +// at a time. Returns true if the header is +// successfully written, false if there is an error. +// +// It is the user's responsibility to fill in the header +// data via calls to set_x_size(), set_num_channels(), +// etc., or copy_header_from(), before calling +// write_header(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeIMG::Writer:: +write_header() { + if (img_header_type == IHT_long) { + write_ulong(_file, _x_size); + write_ulong(_file, _y_size); + } else if (img_header_type == IHT_short) { + write_ushort(_file, _x_size); + write_ushort(_file, _y_size); + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeIMG::Writer::write_row +// Access: Public, Virtual +// Description: If supports_write_row(), above, returns true, this +// function may be called repeatedly to write the image, +// one horizontal row at a time, beginning from the top. +// Returns true if the row is successfully written, +// false if there is an error. +// +// You must first call write_header() before writing the +// individual rows. It is also important to delete the +// PNMWriter class after successfully writing the last +// row. Failing to do this may result in some data not +// getting flushed! +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeIMG::Writer:: +write_row(xel *row_data, xelval *) { + int x; + for (x = 0; x < _x_size; x++) { + write_uchar(_file, (unsigned char)(255*PPM_GETR(row_data[x])/_maxval)); + write_uchar(_file, (unsigned char)(255*PPM_GETG(row_data[x])/_maxval)); + write_uchar(_file, (unsigned char)(255*PPM_GETB(row_data[x])/_maxval)); + } + + return true; +} + + diff --git a/panda/src/pnmimagetypes/pnmFileTypeIMG.h b/panda/src/pnmimagetypes/pnmFileTypeIMG.h new file mode 100644 index 0000000000..72774a5b52 --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeIMG.h @@ -0,0 +1,72 @@ +// Filename: pnmFileTypeIMG.h +// Created by: drose (17Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMFILETYPEIMG_H +#define PNMFILETYPEIMG_H + +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : PNMFileTypeIMG +// Description : For reading and writing headerless R,G,B files. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PNMFileTypeIMG : public PNMFileType { +public: + PNMFileTypeIMG(); + + virtual string get_name() const; + + virtual int get_num_extensions() const; + virtual string get_extension(int n) const; + virtual string get_suggested_extension() const; + + virtual PNMReader *make_reader(FILE *file, bool owns_file = true, + const string &magic_number = string()); + virtual PNMWriter *make_writer(FILE *file, bool owns_file = true); + +public: + class Reader : public PNMReader { + public: + Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number); + + virtual bool supports_read_row() const; + virtual bool read_row(xel *array, xelval *alpha); + }; + + class Writer : public PNMWriter { + public: + Writer(PNMFileType *type, FILE *file, bool owns_file); + + virtual bool supports_write_row() const; + virtual bool write_header(); + virtual bool write_row(xel *array, xelval *alpha); + }; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PNMFileType::init_type(); + register_type(_type_handle, "PNMFileTypeIMG", + PNMFileType::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif + + diff --git a/panda/src/pnmimagetypes/pnmFileTypePNM.cxx b/panda/src/pnmimagetypes/pnmFileTypePNM.cxx new file mode 100644 index 0000000000..42db279bba --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypePNM.cxx @@ -0,0 +1,313 @@ +// Filename: pnm-rw-pnm.cxx +// Created by: drose (04Apr98) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmFileTypePNM.h" +#include "config_pnmimagetypes.h" + +extern "C" { +#include "../pnm/pnm.h" +#include "../pnm/ppm.h" +#include "../pnm/pgm.h" +#include "../pnm/pbm.h" +#include "../pnm/libppm.h" +#include "../pnm/libpgm.h" +#include "../pnm/libpbm.h" +} + +static const char * const extensions[] = { + "pbm", "ppm", "pnm" +}; +static const int num_extensions = sizeof(extensions) / sizeof(const char *); + +TypeHandle PNMFileTypePNM::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypePNM::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypePNM:: +PNMFileTypePNM() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypePNM::get_name +// Access: Public, Virtual +// Description: Returns a few words describing the file type. +//////////////////////////////////////////////////////////////////// +string PNMFileTypePNM:: +get_name() const { + return "NetPBM-style PBM/PPM/PNM"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypePNM::get_num_extensions +// Access: Public, Virtual +// Description: Returns the number of different possible filename +// extensions associated with this particular file type. +//////////////////////////////////////////////////////////////////// +int PNMFileTypePNM:: +get_num_extensions() const { + return num_extensions; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypePNM::get_extension +// Access: Public, Virtual +// Description: Returns the nth possible filename extension +// associated with this particular file type, without a +// leading dot. +//////////////////////////////////////////////////////////////////// +string PNMFileTypePNM:: +get_extension(int n) const { + nassertr(n >= 0 && n < num_extensions, string()); + return extensions[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypePNM::get_suggested_extension +// Access: Public, Virtual +// Description: Returns a suitable filename extension (without a +// leading dot) to suggest for files of this type, or +// empty string if no suggestions are available. +//////////////////////////////////////////////////////////////////// +string PNMFileTypePNM:: +get_suggested_extension() const { + return "pnm"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypePNM::has_magic_number +// Access: Public, Virtual +// Description: Returns true if this particular file type uses a +// magic number to identify it, false otherwise. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypePNM:: +has_magic_number() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypePNM::matches_magic_number +// Access: Public, Virtual +// Description: Returns true if the indicated "magic number" byte +// stream (the initial few bytes read from the file) +// matches this particular file type, false otherwise. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypePNM:: +matches_magic_number(const string &magic_number) const { + return (magic_number.size() >= 2) && + magic_number[0] == 'P' && + (magic_number[1] >= '1' && magic_number[1] <= '6'); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypePNM::make_reader +// Access: Public, Virtual +// Description: Allocates and returns a new PNMReader suitable for +// reading from this file type, if possible. If reading +// from this file type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMReader *PNMFileTypePNM:: +make_reader(FILE *file, bool owns_file, const string &magic_number) { + return new Reader(this, file, owns_file, magic_number); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypePNM::make_writer +// Access: Public, Virtual +// Description: Allocates and returns a new PNMWriter suitable for +// reading from this file type, if possible. If writing +// files of this type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMWriter *PNMFileTypePNM:: +make_writer(FILE *file, bool owns_file) { + return new Writer(this, file, owns_file); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypePNM::Reader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypePNM::Reader:: +Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number) : + PNMReader(type, file, owns_file) +{ + if (!read_magic_number(_file, magic_number, 2)) { + // No magic number. No image. + if (pnmimage_pnm_cat.is_debug()) { + pnmimage_pnm_cat.debug() + << "PNM file appears to be empty.\n"; + } + _is_valid = false; + return; + } + + // pnm_pbmmaxval = PNM_MAXMAXVAL; /* use larger value for better results */ + + _ftype = + ((unsigned char)magic_number[0] << 8) | + (unsigned char)magic_number[1]; + + switch ( PNM_FORMAT_TYPE(_ftype) ) { + case PPM_TYPE: + ppm_readppminitrest( file, &_x_size, &_y_size, &_maxval ); + _num_channels = 3; + break; + + case PGM_TYPE: + pgm_readpgminitrest( file, &_x_size, &_y_size, &_maxval ); + _num_channels = 1; + break; + + case PBM_TYPE: + pbm_readpbminitrest( file, &_x_size, &_y_size ); + _num_channels = 1; + _maxval = pnm_pbmmaxval; + break; + + default: + _is_valid = false; + } + + if (pnmimage_pnm_cat.is_debug()) { + if (is_valid()) { + pnmimage_pnm_cat.debug() + << "Reading "; + switch (PNM_FORMAT_TYPE(_ftype)) { + case PPM_TYPE: + pnmimage_pnm_cat.debug(false) << "PPM"; + break; + case PGM_TYPE: + pnmimage_pnm_cat.debug(false) << "PGM"; + break; + case PBM_TYPE: + pnmimage_pnm_cat.debug(false) << "PBM"; + break; + } + pnmimage_pnm_cat.debug(false) + << " " << *this << "\n"; + } else { + pnmimage_pnm_cat.debug() + << "File is not a valid PNM image.\n"; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypePNM::Reader::supports_read_row +// Access: Public, Virtual +// Description: Returns true if this particular PNMReader supports a +// streaming interface to reading the data: that is, it +// is capable of returning the data one row at a time, +// via repeated calls to read_row(). Returns false if +// the only way to read from this file is all at once, +// via read_data(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypePNM::Reader:: +supports_read_row() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypePNM::Reader::read_row +// Access: Public, Virtual +// Description: If supports_read_row(), above, returns true, this +// function may be called repeatedly to read the image, +// one horizontal row at a time, beginning from the top. +// Returns true if the row is successfully read, false +// if there is an error or end of file. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypePNM::Reader:: +read_row(xel *row_data, xelval *) { + if (!is_valid()) { + return false; + } + pnm_readpnmrow(_file, row_data, _x_size, _maxval, _ftype); + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypePNM::Writer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypePNM::Writer:: +Writer(PNMFileType *type, FILE *file, bool owns_file) : + PNMWriter(type, file, owns_file) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypePNM::Writer::supports_write_row +// Access: Public, Virtual +// Description: Returns true if this particular PNMWriter supports a +// streaming interface to writing the data: that is, it +// is capable of writing the image one row at a time, +// via repeated calls to write_row(). Returns false if +// the only way to write from this file is all at once, +// via write_data(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypePNM::Writer:: +supports_write_row() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypePNM::Writer::write_header +// Access: Public, Virtual +// Description: If supports_write_row(), above, returns true, this +// function may be called to write out the image header +// in preparation to writing out the image data one row +// at a time. Returns true if the header is +// successfully written, false if there is an error. +// +// It is the user's responsibility to fill in the header +// data via calls to set_x_size(), set_num_channels(), +// etc., or copy_header_from(), before calling +// write_header(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypePNM::Writer:: +write_header() { + switch (get_color_type()) { + case PNMImageHeader::CT_grayscale: + case PNMImageHeader::CT_two_channel: + _pnm_format = PGM_TYPE; + break; + + case PNMImageHeader::CT_color: + case PNMImageHeader::CT_four_channel: + _pnm_format = PPM_TYPE; + break; + } + + pnm_writepnminit(_file, _x_size, _y_size, _maxval, _pnm_format, 0); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypePNM::Writer::write_row +// Access: Public, Virtual +// Description: If supports_write_row(), above, returns true, this +// function may be called repeatedly to write the image, +// one horizontal row at a time, beginning from the top. +// Returns true if the row is successfully written, +// false if there is an error. +// +// You must first call write_header() before writing the +// individual rows. It is also important to delete the +// PNMWriter class after successfully writing the last +// row. Failing to do this may result in some data not +// getting flushed! +//////////////////////////////////////////////////////////////////// +bool PNMFileTypePNM::Writer:: +write_row(xel *row_data, xelval *) { + pnm_writepnmrow(_file, row_data, _x_size, _maxval, _pnm_format, 0); + + return true; +} diff --git a/panda/src/pnmimagetypes/pnmFileTypePNM.h b/panda/src/pnmimagetypes/pnmFileTypePNM.h new file mode 100644 index 0000000000..173de17ab0 --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypePNM.h @@ -0,0 +1,81 @@ +// Filename: pnmFileTypePNM.h +// Created by: drose (17Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMFILETYPEPNM_H +#define PNMFILETYPEPNM_H + +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : PNMFileTypePNM +// Description : For reading and writing basic PNM files--*.pbm, +// *.ppm, *.pnm. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PNMFileTypePNM : public PNMFileType { +public: + PNMFileTypePNM(); + + virtual string get_name() const; + + virtual int get_num_extensions() const; + virtual string get_extension(int n) const; + virtual string get_suggested_extension() const; + + virtual bool has_magic_number() const; + virtual bool matches_magic_number(const string &magic_number) const; + + virtual PNMReader *make_reader(FILE *file, bool owns_file = true, + const string &magic_number = string()); + virtual PNMWriter *make_writer(FILE *file, bool owns_file = true); + +public: + class Reader : public PNMReader { + public: + Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number); + + virtual bool supports_read_row() const; + virtual bool read_row(xel *array, xelval *alpha); + + private: + int _ftype; + }; + + class Writer : public PNMWriter { + public: + Writer(PNMFileType *type, FILE *file, bool owns_file); + + virtual bool supports_write_row() const; + virtual bool write_header(); + virtual bool write_row(xel *array, xelval *alpha); + + private: + int _pnm_format; + }; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PNMFileType::init_type(); + register_type(_type_handle, "PNMFileTypePNM", + PNMFileType::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif + + diff --git a/panda/src/pnmimagetypes/pnmFileTypeRadiance.cxx b/panda/src/pnmimagetypes/pnmFileTypeRadiance.cxx new file mode 100644 index 0000000000..090fdff0a1 --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeRadiance.cxx @@ -0,0 +1,335 @@ +// Filename: pnmFileTypeRadiance.cxx +// Created by: drose (17Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmFileTypeRadiance.h" +#include "config_pnmimagetypes.h" + +extern "C" { + #include "color.h" + #include "resolu.h" + + void setcolrgam(double); + int checkheader(FILE *, char *, FILE *); + int fgetresolu(int *, int *, FILE *); + int freadcolrs(COLR *, int, FILE *); + int fwritecolrs(COLR *, unsigned, FILE *); + void fputformat(char *, FILE *); + void shiftcolrs(COLR *, int, int); + void colrs_gambs(COLR *, int); + void newheader(char *, FILE *); + void printargs(int, char **, FILE *); + void gambs_colrs(COLR *, int); +} + +static const char * const extensions[] = { + "pic", "rad" +}; +static const int num_extensions = sizeof(extensions) / sizeof(const char *); + +TypeHandle PNMFileTypeRadiance::_type_handle; + + +static const int COLR_MAX = 255; + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRadiance::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeRadiance:: +PNMFileTypeRadiance() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRadiance::get_name +// Access: Public, Virtual +// Description: Returns a few words describing the file type. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeRadiance:: +get_name() const { + return "Radiance"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRadiance::get_num_extensions +// Access: Public, Virtual +// Description: Returns the number of different possible filename +// extensions associated with this particular file type. +//////////////////////////////////////////////////////////////////// +int PNMFileTypeRadiance:: +get_num_extensions() const { + return num_extensions; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRadiance::get_extension +// Access: Public, Virtual +// Description: Returns the nth possible filename extension +// associated with this particular file type, without a +// leading dot. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeRadiance:: +get_extension(int n) const { + nassertr(n >= 0 && n < num_extensions, string()); + return extensions[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRadiance::get_suggested_extension +// Access: Public, Virtual +// Description: Returns a suitable filename extension (without a +// leading dot) to suggest for files of this type, or +// empty string if no suggestions are available. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeRadiance:: +get_suggested_extension() const { + return "rad"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRadiance::has_magic_number +// Access: Public, Virtual +// Description: Returns true if this particular file type uses a +// magic number to identify it, false otherwise. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeRadiance:: +has_magic_number() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRadiance::matches_magic_number +// Access: Public, Virtual +// Description: Returns true if the indicated "magic number" byte +// stream (the initial few bytes read from the file) +// matches this particular file type, false otherwise. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeRadiance:: +matches_magic_number(const string &magic_number) const { + nassertr(magic_number.size() >= 2, false); + return (magic_number.substr(0, 2) == "#?"); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRadiance::make_reader +// Access: Public, Virtual +// Description: Allocates and returns a new PNMReader suitable for +// reading from this file type, if possible. If reading +// from this file type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMReader *PNMFileTypeRadiance:: +make_reader(FILE *file, bool owns_file, const string &magic_number) { + return new Reader(this, file, owns_file, magic_number); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRadiance::make_writer +// Access: Public, Virtual +// Description: Allocates and returns a new PNMWriter suitable for +// reading from this file type, if possible. If writing +// files of this type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMWriter *PNMFileTypeRadiance:: +make_writer(FILE *file, bool owns_file) { + return new Writer(this, file, owns_file); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRadiance::Reader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeRadiance::Reader:: +Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number) : + PNMReader(type, file, owns_file) +{ + setcolrgam(radiance_gamma_correction); + + // Hope we can ungetc() more than one character. + for (string::reverse_iterator mi = magic_number.rbegin(); + mi != magic_number.rend(); + mi++) { + ungetc(*mi, _file); + } + + /* get our header */ + if (checkheader(file, COLRFMT, NULL) < 0 || + fgetresolu(&_x_size, &_y_size, _file) < 0) { + _is_valid = false; + pnmimage_radiance_cat.debug() + << "File is not a valid Radiance image.\n"; + return; + } + + _num_channels = 3; + _maxval = COLR_MAX; + + if (pnmimage_radiance_cat.is_debug()) { + pnmimage_radiance_cat.debug() + << "Reading Radiance image: " << _x_size << " by " << _y_size + << " pixels.\n" + << "gamma correction is " << radiance_gamma_correction + << ", brightness adjustment is " << radiance_brightness_adjustment + << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRadiance::Reader::supports_read_row +// Access: Public, Virtual +// Description: Returns true if this particular PNMReader supports a +// streaming interface to reading the data: that is, it +// is capable of returning the data one row at a time, +// via repeated calls to read_row(). Returns false if +// the only way to read from this file is all at once, +// via read_data(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeRadiance::Reader:: +supports_read_row() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRadiance::Reader::read_row +// Access: Public, Virtual +// Description: If supports_read_row(), above, returns true, this +// function may be called repeatedly to read the image, +// one horizontal row at a time, beginning from the top. +// Returns true if the row is successfully read, false +// if there is an error or end of file. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeRadiance::Reader:: +read_row(xel *row_data, xelval *) { + if (!is_valid()) { + return false; + } + + COLR *scanin; + int x; + + scanin = (COLR *)alloca(_x_size * sizeof(COLR)); + + if (freadcolrs(scanin, _x_size, _file) < 0) { + return false; + } + + if (radiance_brightness_adjustment) { + shiftcolrs(scanin, _x_size, radiance_brightness_adjustment); + } + + colrs_gambs(scanin, _x_size); /* gamma correction */ + + for (x = 0; x < _x_size; x++) { + PPM_ASSIGN(row_data[x], scanin[x][RED], scanin[x][GRN], scanin[x][BLU]); + } + + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRadiance::Writer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeRadiance::Writer:: +Writer(PNMFileType *type, FILE *file, bool owns_file) : + PNMWriter(type, file, owns_file) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRadiance::Writer::supports_write_row +// Access: Public, Virtual +// Description: Returns true if this particular PNMWriter supports a +// streaming interface to writing the data: that is, it +// is capable of writing the image one row at a time, +// via repeated calls to write_row(). Returns false if +// the only way to write from this file is all at once, +// via write_data(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeRadiance::Writer:: +supports_write_row() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRadiance::Writer::write_header +// Access: Public, Virtual +// Description: If supports_write_row(), above, returns true, this +// function may be called to write out the image header +// in preparation to writing out the image data one row +// at a time. Returns true if the header is +// successfully written, false if there is an error. +// +// It is the user's responsibility to fill in the header +// data via calls to set_x_size(), set_num_channels(), +// etc., or copy_header_from(), before calling +// write_header(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeRadiance::Writer:: +write_header() { + setcolrgam(radiance_gamma_correction); + + /* put our header */ + newheader("RADIANCE", _file); + fputs("Generated via pnmimage\n", _file); + fputformat(COLRFMT, _file); + putc('\n', _file); + fprtresolu(_x_size, _y_size, _file); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeRadiance::Writer::write_row +// Access: Public, Virtual +// Description: If supports_write_row(), above, returns true, this +// function may be called repeatedly to write the image, +// one horizontal row at a time, beginning from the top. +// Returns true if the row is successfully written, +// false if there is an error. +// +// You must first call write_header() before writing the +// individual rows. It is also important to delete the +// PNMWriter class after successfully writing the last +// row. Failing to do this may result in some data not +// getting flushed! +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeRadiance::Writer:: +write_row(xel *row_data, xelval *) { + /* convert file */ + bool grayscale = is_grayscale(); + + COLR *scanout; + int x; + + /* allocate scanline */ + scanout = (COLR *)alloca(_x_size * sizeof(COLR)); + + /* convert image */ + for (x = 0; x < _x_size; x++) { + if (grayscale) { + scanout[x][RED] = + scanout[x][GRN] = + scanout[x][BLU] = (BYTE)((int)COLR_MAX * PPM_GETB(row_data[x]) / _maxval); + } else { + scanout[x][RED] = (BYTE)((int)COLR_MAX * PPM_GETR(row_data[x]) / _maxval); + scanout[x][GRN] = (BYTE)((int)COLR_MAX * PPM_GETG(row_data[x]) / _maxval); + scanout[x][BLU] = (BYTE)((int)COLR_MAX * PPM_GETB(row_data[x]) / _maxval); + } + } + + /* undo gamma */ + gambs_colrs(scanout, _x_size); + if (radiance_brightness_adjustment) + shiftcolrs(scanout, _x_size, radiance_brightness_adjustment); + if (fwritecolrs(scanout, _x_size, _file) < 0) + return false; + + return true; +} diff --git a/panda/src/pnmimagetypes/pnmFileTypeRadiance.h b/panda/src/pnmimagetypes/pnmFileTypeRadiance.h new file mode 100644 index 0000000000..18bb493b30 --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeRadiance.h @@ -0,0 +1,75 @@ +// Filename: pnmFileTypeRadiance.h +// Created by: drose (17Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMFILETYPERADIANCE_H +#define PNMFILETYPERADIANCE_H + +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : PNMFileTypeRadiance +// Description : For reading and Radiance native image files. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PNMFileTypeRadiance : public PNMFileType { +public: + PNMFileTypeRadiance(); + + virtual string get_name() const; + + virtual int get_num_extensions() const; + virtual string get_extension(int n) const; + virtual string get_suggested_extension() const; + + virtual bool has_magic_number() const; + virtual bool matches_magic_number(const string &magic_number) const; + + virtual PNMReader *make_reader(FILE *file, bool owns_file = true, + const string &magic_number = string()); + virtual PNMWriter *make_writer(FILE *file, bool owns_file = true); + +public: + class Reader : public PNMReader { + public: + Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number); + + virtual bool supports_read_row() const; + virtual bool read_row(xel *array, xelval *alpha); + }; + + class Writer : public PNMWriter { + public: + Writer(PNMFileType *type, FILE *file, bool owns_file); + + virtual bool supports_write_row() const; + virtual bool write_header(); + virtual bool write_row(xel *array, xelval *alpha); + }; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PNMFileType::init_type(); + register_type(_type_handle, "PNMFileTypeRadiance", + PNMFileType::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif + + diff --git a/panda/src/pnmimagetypes/pnmFileTypeSGI.cxx b/panda/src/pnmimagetypes/pnmFileTypeSGI.cxx new file mode 100644 index 0000000000..435e57c045 --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeSGI.cxx @@ -0,0 +1,122 @@ +// Filename: pnmFileTypeSGI.cxx +// Created by: drose (17Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmFileTypeSGI.h" +#include "config_pnmimagetypes.h" +#include "sgi.h" + +static const char * const extensions[] = { + "rgb", "rgba", "sgi" +}; +static const int num_extensions = sizeof(extensions) / sizeof(const char *); + +TypeHandle PNMFileTypeSGI::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeSGI:: +PNMFileTypeSGI() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::get_name +// Access: Public, Virtual +// Description: Returns a few words describing the file type. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeSGI:: +get_name() const { + return "SGI RGB"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::get_num_extensions +// Access: Public, Virtual +// Description: Returns the number of different possible filename +// extensions associated with this particular file type. +//////////////////////////////////////////////////////////////////// +int PNMFileTypeSGI:: +get_num_extensions() const { + return num_extensions; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::get_extension +// Access: Public, Virtual +// Description: Returns the nth possible filename extension +// associated with this particular file type, without a +// leading dot. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeSGI:: +get_extension(int n) const { + nassertr(n >= 0 && n < num_extensions, string()); + return extensions[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::get_suggested_extension +// Access: Public, Virtual +// Description: Returns a suitable filename extension (without a +// leading dot) to suggest for files of this type, or +// empty string if no suggestions are available. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeSGI:: +get_suggested_extension() const { + return "rgb"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::has_magic_number +// Access: Public, Virtual +// Description: Returns true if this particular file type uses a +// magic number to identify it, false otherwise. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeSGI:: +has_magic_number() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::matches_magic_number +// Access: Public, Virtual +// Description: Returns true if the indicated "magic number" byte +// stream (the initial few bytes read from the file) +// matches this particular file type, false otherwise. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeSGI:: +matches_magic_number(const string &magic_number) const { + nassertr(magic_number.size() >= 2, false); + int mn = + ((unsigned char)magic_number[0] << 8) | + ((unsigned char)magic_number[1]); + return (mn == SGI_MAGIC); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::make_reader +// Access: Public, Virtual +// Description: Allocates and returns a new PNMReader suitable for +// reading from this file type, if possible. If reading +// from this file type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMReader *PNMFileTypeSGI:: +make_reader(FILE *file, bool owns_file, const string &magic_number) { + return new Reader(this, file, owns_file, magic_number); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::make_writer +// Access: Public, Virtual +// Description: Allocates and returns a new PNMWriter suitable for +// reading from this file type, if possible. If writing +// files of this type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMWriter *PNMFileTypeSGI:: +make_writer(FILE *file, bool owns_file) { + return new Writer(this, file, owns_file); +} + diff --git a/panda/src/pnmimagetypes/pnmFileTypeSGI.h b/panda/src/pnmimagetypes/pnmFileTypeSGI.h new file mode 100644 index 0000000000..a70e11b2d1 --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeSGI.h @@ -0,0 +1,120 @@ +// Filename: pnmFileTypeSGI.h +// Created by: drose (17Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMFILETYPESGI_H +#define PNMFILETYPESGI_H + +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : PNMFileTypeSGI +// Description : For reading and writing SGI RGB files. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PNMFileTypeSGI : public PNMFileType { +public: + PNMFileTypeSGI(); + + virtual string get_name() const; + + virtual int get_num_extensions() const; + virtual string get_extension(int n) const; + virtual string get_suggested_extension() const; + + virtual bool has_magic_number() const; + virtual bool matches_magic_number(const string &magic_number) const; + + virtual PNMReader *make_reader(FILE *file, bool owns_file = true, + const string &magic_number = string()); + virtual PNMWriter *make_writer(FILE *file, bool owns_file = true); + +public: + class Reader : public PNMReader { + public: + Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number); + virtual ~Reader(); + + virtual bool supports_read_row() const; + virtual bool read_row(xel *array, xelval *alpha); + + typedef struct { + long start; /* offset in file */ + long length; /* length of compressed scanline */ + } TabEntry; + + private: + TabEntry *table; + long table_start; + int current_row; + int bpc; + }; + + class Writer : public PNMWriter { + public: + Writer(PNMFileType *type, FILE *file, bool owns_file); + virtual ~Writer(); + + virtual bool supports_write_row() const; + virtual bool write_header(); + virtual bool write_row(xel *array, xelval *alpha); + + typedef struct { + long start; /* offset in file */ + long length; /* length of compressed scanline */ + } TabEntry; + + typedef short ScanElem; + typedef struct { + ScanElem * data; + long length; + } ScanLine; + + private: + TabEntry &Table(int chan) { + return table[chan * _y_size + current_row]; + } + + void write_rgb_header(const char *imagename); + void write_table(); + void write_channels(ScanLine channel[], void (*put)(FILE *, short)); + void build_scanline(ScanLine output[], xel *row_data, xelval *alpha_data); + ScanElem *compress(ScanElem *temp, ScanLine &output); + int rle_compress(ScanElem *inbuf, int size); + + TabEntry *table; + long table_start; + int current_row; + int bpc; + int dimensions; + int new_maxval; + + ScanElem *rletemp; + }; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PNMFileType::init_type(); + register_type(_type_handle, "PNMFileTypeSGI", + PNMFileType::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif + + diff --git a/panda/src/pnmimagetypes/pnmFileTypeSGIReader.cxx b/panda/src/pnmimagetypes/pnmFileTypeSGIReader.cxx new file mode 100644 index 0000000000..5c61c9cbfa --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeSGIReader.cxx @@ -0,0 +1,465 @@ +// Filename: pnmFileTypeSGIReader.cxx +// Created by: drose (17Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmFileTypeSGI.h" +#include "config_pnmimagetypes.h" +#include "sgi.h" + +#include +#include + +#include + +// Much code in this file is borrowed from Netpbm, specifically sgitopnm.c. + +/* sgitopnm.c - read an SGI image and and produce a portable anymap +** +** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) +** +** Based on the SGI image description v0.9 by Paul Haeberli (paul@sgi.comp) +** Available via ftp from sgi.com:graphics/SGIIMAGESPEC +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +** +** 29Jan94: first version +** 08Feb94: minor bugfix +*/ + +/* entry in RLE offset table */ +typedef PNMFileTypeSGI::Reader::TabEntry TabEntry; + +typedef short ScanElem; +typedef ScanElem * ScanLine; + +/* prototypes */ +static unsigned char get_byte ( FILE* f ); +static long get_big_long (FILE *f); +static short get_big_short (FILE *f); +static short get_byte_as_short (FILE *f); +static int readerr (FILE *f); +static void * xmalloc (int bytes); +#define MALLOC(n, type) (type *)xmalloc((n) * sizeof(type)) +static char * compression_name (char compr); +static void read_bytes (FILE *ifp, int n, char *buf); +static void read_header(FILE *ifp, Header *head, const string &magic_number); +static TabEntry * read_table (FILE *ifp, int tablen); +static void read_channel (FILE *ifp, int xsize, int ysize, + int zsize, int bpc, TabEntry *table, + ScanElem *channel_data, long table_start, + int channel, int row); +static void rle_decompress (ScanElem *src, long srclen, ScanElem *dest, long destlen); + +#define WORSTCOMPR(x) (2*(x) + 2) + +#define MAXVAL_BYTE 255 +#define MAXVAL_WORD 65535 + +// This flag shouldn't really be a static global, but it's a little +// tricky to fix and it doesn't do any harm, since it only controls +// whether an error message is repeated. +static bool eof_err = false; + + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::Reader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeSGI::Reader:: +Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number) : + PNMReader(type, file, owns_file) +{ + eof_err = false; + table = NULL; + + if (!read_magic_number(_file, magic_number, 4)) { + // No magic number. No image. + if (pnmimage_sgi_cat.is_debug()) { + pnmimage_sgi_cat.debug() + << "RGB file appears to be empty.\n"; + } + _is_valid = false; + return; + } + + Header head; + + ::read_header(file, &head, magic_number); + + long pixmax = (head.bpc == 1) ? MAXVAL_BYTE : MAXVAL_WORD; + if( pixmax > PNM_MAXMAXVAL ) { + pnmimage_sgi_cat.error() + << "Cannot read RGB image with maxval of " << pixmax + << "--largest allowable maxval is currently " << PNM_MAXMAXVAL << "\n"; + _is_valid = false; + return; + } + + _maxval = (xelval)pixmax; + + table_start = ftell(file); + if( head.storage != STORAGE_VERBATIM ) + table = read_table(file, head.ysize * head.zsize); + + _x_size = head.xsize; + _y_size = head.ysize; + _num_channels = head.zsize; + bpc = head.bpc; + + current_row = _y_size - 1; + + if (pnmimage_sgi_cat.is_debug()) { + head.name[79] = '\0'; /* just to be safe */ + pnmimage_sgi_cat.debug() + << "Read RGB image:\n" + << " raster size " << head.xsize << " x " << head.ysize + << ", " << head.zsize << " channels\n" + << " compression: " << (int)head.storage << " = " + << compression_name(head.storage) << "\n" + << " image name: " << head.name << "\n" + << " bpc: " << (int)head.bpc << " dimension: " << head.dimension << "\n" + << " pixmin: " << head.pixmin << " pixmax: " << head.pixmax + << " colormap: " << head.colormap << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::Reader::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeSGI::Reader:: +~Reader() { + if (table != NULL) { + free(table); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::Reader::supports_read_row +// Access: Public, Virtual +// Description: Returns true if this particular PNMReader supports a +// streaming interface to reading the data: that is, it +// is capable of returning the data one row at a time, +// via repeated calls to read_row(). Returns false if +// the only way to read from this file is all at once, +// via read_data(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeSGI::Reader:: +supports_read_row() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::Reader::read_row +// Access: Public, Virtual +// Description: If supports_read_row(), above, returns true, this +// function may be called repeatedly to read the image, +// one horizontal row at a time, beginning from the top. +// Returns true if the row is successfully read, false +// if there is an error or end of file. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeSGI::Reader:: +read_row(xel *row_data, xelval *alpha_data) { + if (!is_valid()) { + return false; + } + nassertr(current_row >= 0, false); + + ScanElem *red = (ScanElem *)alloca(_x_size * sizeof(ScanElem)); + ScanElem *grn = (ScanElem *)alloca(_x_size * sizeof(ScanElem)); + ScanElem *blu = (ScanElem *)alloca(_x_size * sizeof(ScanElem)); + ScanElem *alpha = (ScanElem *)alloca(_x_size * sizeof(ScanElem)); + + read_channel(_file, _x_size, _y_size, _num_channels, bpc, table, red, + table_start, 0, current_row); + + if (!is_grayscale()) { + read_channel(_file, _x_size, _y_size, _num_channels, bpc, table, grn, + table_start, 1, current_row); + read_channel(_file, _x_size, _y_size, _num_channels, bpc, table, blu, + table_start, 2, current_row); + } + + if (has_alpha()) { + read_channel(_file, _x_size, _y_size, _num_channels, bpc, table, alpha, + table_start, _num_channels - 1, current_row); + } + + for (int x = 0; x < _x_size; x++) { + if (is_grayscale()) { + PPM_PUTB(row_data[x], (xelval)red[x]); + } else { + xelval r, g, b; + r = (xelval)red[x]; + g = (xelval)grn[x]; + b = (xelval)blu[x]; + PPM_ASSIGN(row_data[x], r, g, b); + } + + if (has_alpha()) { + alpha_data[x] = (xelval)alpha[x]; + } + } + current_row--; + return true; +} + + + +static void +read_header(FILE *ifp, Header *head, const string &magic_number) { + nassertv(magic_number.size() == 4); + head->magic = + ((unsigned char)magic_number[0] << 8) | + ((unsigned char)magic_number[1]); + head->storage = (unsigned char)magic_number[2]; + head->bpc = (unsigned char)magic_number[3]; + head->dimension = get_big_short(ifp); + head->xsize = get_big_short(ifp); + head->ysize = get_big_short(ifp); + head->zsize = get_big_short(ifp); + head->pixmin = get_big_long(ifp); + head->pixmax = get_big_long(ifp); + read_bytes(ifp, 4, head->dummy1); + read_bytes(ifp, 80, head->name); + head->colormap = get_big_long(ifp); + read_bytes(ifp, 404, head->dummy2); + + if( head->magic != SGI_MAGIC ) + pm_error("bad magic number - not an SGI image"); + if( head->storage != 0 && head->storage != 1 ) + pm_error("unknown compression type"); + if( head->bpc < 1 || head->bpc > 2 ) + pm_error("illegal precision value %d (only 1-2 allowed)", head->bpc ); + if( head->colormap != CMAP_NORMAL ) + pm_message("unsupported non-normal pixel data (%d)", + head->colormap); + + /* adjust ysize/zsize to dimension, just to be sure */ + switch( head->dimension ) { + case 1: + head->ysize = 1; + break; + case 2: + head->zsize = 1; + break; + case 3: + switch( head->zsize ) { + case 1: + case 2: + head->dimension = 2; + break; + case 3: + case 4: + break; + + default: + pm_message("%d-channel image, using only first 4 channels", head->zsize); + head->zsize = 4; + break; + } + break; + default: + pm_error("illegal dimension value %d (only 1-3 allowed)", head->dimension); + } +} + + +static TabEntry * +read_table(FILE *ifp, int tablen) { + TabEntry *table; + int i; + + table = MALLOC(tablen, TabEntry); + + for( i = 0; i < tablen; i++ ) + table[i].start = get_big_long(ifp); + for( i = 0; i < tablen; i++ ) + table[i].length = get_big_long(ifp); + + return table; +} + + + +static void +read_channel(FILE *ifp, + int xsize, int ysize, int, int bpc, + TabEntry *table, + ScanElem *channel_data, long table_start, + int channel, int row) { + ScanElem *temp; + int sgi_index, i; + long offset, length; + + short (*func)(FILE *); + func = (bpc==1) ? get_byte_as_short : get_big_short; + + if ( table ) + temp = (ScanElem *)alloca(WORSTCOMPR(xsize) * sizeof(ScanElem)); + + sgi_index = channel * ysize + row; + if( table ) { + offset = table[sgi_index].start; + length = table[sgi_index].length; + if( bpc == 2 ) + length /= 2; /* doc says length is in bytes, we are reading words */ + if( fseek(ifp, offset, SEEK_SET) != 0 ) + pm_error("seek error for offset %ld", offset); + + for( i = 0; i < length; i++ ) + temp[i] = (*func)(ifp); + + rle_decompress(temp, length, channel_data, xsize); + } + else { + offset = sgi_index * xsize + table_start; + if( fseek(ifp, offset, SEEK_SET) != 0 ) + pm_error("seek error for offset %ld", offset); + for( i = 0; i < xsize; i++ ) + channel_data[i] = (*func)(ifp); + } +} + + + +static void +rle_decompress(ScanElem *src, + long srcleft, + ScanElem *dest, + long destleft) { + int count; + unsigned char el; + + while( srcleft ) { + el = (unsigned char)(*src++ & 0xff); + --srcleft; + count = (int)(el & 0x7f); + + if( count == 0 ) + return; + if( destleft < count ) + pm_error("RLE error: too much input data (space left %d, need %d)", destleft, count); + destleft -= count; + if( el & 0x80 ) { + if( srcleft < count ) + pm_error("RLE error: not enough data for literal run (data left %d, need %d)", srcleft, count); + srcleft -= count; + while( count-- ) + *dest++ = *src++; + } + else { + if( srcleft == 0 ) + pm_error("RLE error: not enough data for replicate run"); + while( count-- ) + *dest++ = *src; + ++src; + --srcleft; + } + } + pm_error("RLE error: no terminating 0-byte"); +} + + +/* basic I/O functions, taken from ilbmtoppm.c */ + +static short +get_big_short(FILE *ifp) { + short s; + + if( pm_readbigshort(ifp, &s) == -1 ) + s = readerr(ifp); + + return s; +} + +static long +get_big_long(FILE *ifp) { + long l; + + if( pm_readbiglong(ifp, &l) == -1 ) + l = readerr(ifp); + + return l; +} + +static unsigned char +get_byte(FILE *ifp) { + int i; + + i = getc(ifp); + if( i == EOF ) + i = readerr(ifp); + + return (unsigned char) i; +} + + +static int +readerr(FILE *f) { + // This will return only if the error is EOF. + if( ferror(f) ) + pm_error("read error"); + + if (!eof_err) { + fprintf(stderr, "Warning: premature EOF on file\n"); + eof_err = true; + } + + return 0; +} + + +static void +read_bytes(FILE *ifp, + int n, + char *buf) { + int r; + + r = fread((void *)buf, 1, n, ifp); + if( r != n ) { + readerr(ifp); + memset(buf+r, 0, n-r); + } +} + + +static short +get_byte_as_short(FILE *ifp) { + return (short)get_byte(ifp); +} + + +static void * +xmalloc(int bytes) { + void *mem; + + if( bytes == 0 ) + return NULL; + + mem = malloc(bytes); + if( mem == NULL ) + pm_error("out of memory allocating %d bytes", bytes); + return mem; +} + +static char * +compression_name(char compr) { + switch( compr ) { + case STORAGE_VERBATIM: + return "none"; + case STORAGE_RLE: + return "RLE"; + default: + return "unknown"; + } +} + diff --git a/panda/src/pnmimagetypes/pnmFileTypeSGIWriter.cxx b/panda/src/pnmimagetypes/pnmFileTypeSGIWriter.cxx new file mode 100644 index 0000000000..46cb66d8a4 --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeSGIWriter.cxx @@ -0,0 +1,363 @@ +// Filename: pnmFileTypeSGIWriter.cxx +// Created by: drose (17Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmFileTypeSGI.h" +#include "config_pnmimagetypes.h" +#include "sgi.h" + +#include +#include + +// Much code in this file originally came from from Netpbm, +// specifically pnmtosgi.c. It has since been fairly heavily +// modified. + +/* pnmtosgi.c - convert portable anymap to SGI image +** +** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) +** +** Based on the SGI image description v0.9 by Paul Haeberli (paul@sgi.comp) +** Available via ftp from sgi.com:graphics/SGIIMAGESPEC +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +** +** 29Jan94: first version +*/ + + + +#define WORSTCOMPR(x) (2*(x) + 2) + + +#define MAXVAL_BYTE 255 +#define MAXVAL_WORD 65535 + +inline void +put_byte(FILE *out_file, unsigned char b) { + putc(b, out_file); +} + +static void +put_big_short(FILE *out_file, short s) { + if ( pm_writebigshort( out_file, s ) == -1 ) + pm_error( "write error" ); +} + + +static void +put_big_long(FILE *out_file, long l) { + if ( pm_writebiglong( out_file, l ) == -1 ) + pm_error( "write error" ); +} + + +static void +put_short_as_byte(FILE *out_file, short s) { + put_byte(out_file, (unsigned char)s); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::Writer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeSGI::Writer:: +Writer(PNMFileType *type, FILE *file, bool owns_file) : + PNMWriter(type, file, owns_file) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::Writer::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeSGI::Writer:: +~Writer() { + if (table!=NULL) { + // Rewrite the table with the correct values in it. + fseek(_file, table_start, SEEK_SET); + write_table(); + delete[] table; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::Writer::supports_write_row +// Access: Public, Virtual +// Description: Returns true if this particular PNMWriter supports a +// streaming interface to writing the data: that is, it +// is capable of writing the image one row at a time, +// via repeated calls to write_row(). Returns false if +// the only way to write from this file is all at once, +// via write_data(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeSGI::Writer:: +supports_write_row() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::Writer::write_header +// Access: Public, Virtual +// Description: If supports_write_row(), above, returns true, this +// function may be called to write out the image header +// in preparation to writing out the image data one row +// at a time. Returns true if the header is +// successfully written, false if there is an error. +// +// It is the user's responsibility to fill in the header +// data via calls to set_x_size(), set_num_channels(), +// etc., or copy_header_from(), before calling +// write_header(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeSGI::Writer:: +write_header() { + table = NULL; + + switch (_num_channels) { + case 1: + case 2: + dimensions = 2; + break; + + case 3: + case 4: + dimensions = 3; + dimensions = 3; + break; + + default: + nassertr(false, false); + } + + // For some reason, we have problems with SGI image files whose pixmax value + // is not 255 or 65535. So, we'll round up when writing. + if( _maxval <= MAXVAL_BYTE ) { + bpc = 1; + new_maxval = MAXVAL_BYTE; + } else if( _maxval <= MAXVAL_WORD ) { + bpc = 2; + new_maxval = MAXVAL_WORD; + } else { + return false; + } + + if( sgi_storage_type != STORAGE_VERBATIM ) { + table = new TabEntry[_num_channels * _x_size]; + memset(table, 0, _num_channels * _x_size * sizeof(TabEntry)); + } + + write_rgb_header(sgi_imagename.c_str()); + + if (table!=NULL) { + table_start = ftell(_file); + + // The first time we write the table, it has zeroes. We'll correct + // this later. + write_table(); + } + + current_row = _y_size - 1; + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSGI::Writer::write_row +// Access: Public, Virtual +// Description: If supports_write_row(), above, returns true, this +// function may be called repeatedly to write the image, +// one horizontal row at a time, beginning from the top. +// Returns true if the row is successfully written, +// false if there is an error. +// +// You must first call write_header() before writing the +// individual rows. It is also important to delete the +// PNMWriter class after successfully writing the last +// row. Failing to do this may result in some data not +// getting flushed! +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeSGI::Writer:: +write_row(xel *row_data, xelval *alpha_data) { + ScanLine channel[4]; + + build_scanline(channel, row_data, alpha_data); + + if( bpc == 1 ) + write_channels(channel, put_short_as_byte); + else + write_channels(channel, put_big_short); + + for (int i = 0; i < _num_channels; i++) { + delete[] channel[i].data; + } + + current_row--; + return true; +} + + +void PNMFileTypeSGI::Writer:: +write_rgb_header(const char *imagename) { + int i; + + put_big_short(_file, SGI_MAGIC); + put_byte(_file, sgi_storage_type); + put_byte(_file, (char)bpc); + put_big_short(_file, dimensions); + put_big_short(_file, _x_size); + put_big_short(_file, _y_size); + put_big_short(_file, _num_channels); + put_big_long(_file, 0); /* PIXMIN */ + put_big_long(_file, new_maxval); /* PIXMAX */ + for( i = 0; i < 4; i++ ) + put_byte(_file, 0); + for( i = 0; i < 79 && imagename[i] != '\0'; i++ ) + put_byte(_file, imagename[i]); + for(; i < 80; i++ ) + put_byte(_file, 0); + put_big_long(_file, CMAP_NORMAL); + for( i = 0; i < 404; i++ ) + put_byte(_file, 0); +} + + +void PNMFileTypeSGI::Writer:: +write_table() { + int i; + int tabsize = _y_size*_num_channels; + + for( i = 0; i < tabsize; i++ ) { + put_big_long(_file, table[i].start); + } + for( i = 0; i < tabsize; i++ ) + put_big_long(_file, table[i].length); +} + + +void PNMFileTypeSGI::Writer:: +write_channels(ScanLine channel[], void (*put)(FILE *, short)) { + int i, col; + + for( i = 0; i < _num_channels; i++ ) { + Table(i).start = ftell(_file); + Table(i).length = channel[i].length * bpc; + + for( col = 0; col < channel[i].length; col++ ) { + (*put)(_file, channel[i].data[col]); + } + } +} + + +void PNMFileTypeSGI::Writer:: +build_scanline(ScanLine output[], xel *row_data, xelval *alpha_data) { + int col; + ScanElem *temp; + + if( sgi_storage_type != STORAGE_VERBATIM ) { + rletemp = (ScanElem *)alloca(WORSTCOMPR(_x_size) * sizeof(ScanElem)); + } + temp = new ScanElem[_x_size]; + + if( _num_channels <= 2 ) { + for( col = 0; col < _x_size; col++ ) + temp[col] = (ScanElem) + (new_maxval * PPM_GETB(row_data[col]) / _maxval); + temp = compress(temp, output[0]); + + if (_num_channels == 2) { + for( col = 0; col < _x_size; col++ ) + temp[col] = (ScanElem) + (new_maxval * alpha_data[col] / _maxval); + temp = compress(temp, output[1]); + } + + } else { + for( col = 0; col < _x_size; col++ ) + temp[col] = (ScanElem) + (new_maxval * PPM_GETR(row_data[col]) / _maxval); + temp = compress(temp, output[0]); + for( col = 0; col < _x_size; col++ ) + temp[col] = (ScanElem) + (new_maxval * PPM_GETG(row_data[col]) / _maxval); + temp = compress(temp, output[1]); + for( col = 0; col < _x_size; col++ ) + temp[col] = (ScanElem) + (new_maxval * PPM_GETB(row_data[col]) / _maxval); + temp = compress(temp, output[2]); + if (_num_channels == 4) { + for( col = 0; col < _x_size; col++ ) + temp[col] = (ScanElem) + (new_maxval * alpha_data[col] / _maxval); + temp = compress(temp, output[3]); + } + } + + delete[] temp; +} + + +PNMFileTypeSGI::Writer::ScanElem *PNMFileTypeSGI::Writer:: +compress(ScanElem *temp, ScanLine &output) { + int len; + + switch( sgi_storage_type ) { + case STORAGE_VERBATIM: + output.length = _x_size; + output.data = temp; + temp = new ScanElem[_x_size]; + break; + case STORAGE_RLE: + len = rle_compress(temp, _x_size); /* writes result into rletemp */ + output.length = len; + output.data = new ScanElem[len]; + memcpy(output.data, rletemp, len * sizeof(ScanElem)); + break; + default: + pm_error("unknown storage type - can\'t happen"); + } + return temp; +} + + +/* +slightly modified RLE algorithm from ppmtoilbm.c +written by Robert A. Knop (rknop@mop.caltech.edu) +*/ +int PNMFileTypeSGI::Writer:: +rle_compress(ScanElem *inbuf, int size) { + int in, out, hold, count; + ScanElem *outbuf = rletemp; + + in=out=0; + while( in=size-2)&&(in=127 ) + break; + } + outbuf[hold]=(ScanElem)(count | 0x80); + } + } + outbuf[out++] = (ScanElem)0; /* terminator */ + return(out); +} + diff --git a/panda/src/pnmimagetypes/pnmFileTypeSoftImage.cxx b/panda/src/pnmimagetypes/pnmFileTypeSoftImage.cxx new file mode 100644 index 0000000000..8a3c7c8cf2 --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeSoftImage.cxx @@ -0,0 +1,742 @@ +// Filename: pnmFileTypeSoftImage.cxx +// Created by: drose (17Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmFileTypeSoftImage.h" +#include "config_pnmimagetypes.h" + +static const float imageVersionNumber = 3.0; +static const int imageCommentLength = 80; +static const char imageComment[imageCommentLength+1] = + "Written by pnmimage."; + +// Values to indicate compressed/uncompressed types +#define UNCOMPRESSED 0x00 +#define MIXED_RUN_LENGTH 0x02 + +// Bits to indicate channel type +#define RGB_CHANNEL 0xe0 +#define ALPHA_CHANNEL 0x10 + +// SoftImage magic number: high word, low word +#define SOFTIMAGE_MAGIC1 0x5380 +#define SOFTIMAGE_MAGIC2 0xf634 + +static const char * const extensions[] = { + "pic", "soft" +}; +static const int num_extensions = sizeof(extensions) / sizeof(const char *); + +TypeHandle PNMFileTypeSoftImage::_type_handle; + +inline float +read_float(FILE *file) { + long l; + + if (pm_readbiglong(file, &l)==0) { + return *(float *)&l; + } else { + return 0.0; + } +} + +inline unsigned short +read_ushort(FILE *file) { + unsigned short x; + return pm_readbigshort(file, (short *)&x)==0 ? x : 0; +} + +inline unsigned char +read_uchar(FILE *file) { + int x; + x = getc(file); + return (x!=EOF) ? (unsigned char)x : 0; +} + +inline void +write_ushort(FILE *file, unsigned short x) { + pm_writebigshort(file, (short)x); +} + +inline void +write_uchar(FILE *file, unsigned char x) { + putc(x, file); +} + +inline void +write_float(FILE *file, float x) { + pm_writebiglong(file, *(long *)&x); +} + +static int +read_channel_pkt(FILE *file, + int &chained, int &size, int &type, int &channel) { + chained = read_uchar(file); + size = read_uchar(file); + type = read_uchar(file); + channel = read_uchar(file); + + if (feof(file)) { + return false; + } + + if (size!=8) { + pnmimage_soft_cat.error() + << "Don't know how to interpret " << size << " bits per pixel!\n"; + return false; + } + + return true; +} + +static void +read_rgb(xel *row_data, xelval *, FILE *file, int x, int repeat) { + xelval red, grn, blu; + red = read_uchar(file); + grn = read_uchar(file); + blu = read_uchar(file); + + while (repeat>0) { + PPM_ASSIGN(row_data[x], red, grn, blu); + x++; + repeat--; + } +} + +static void +read_alpha(xel *, xelval *alpha_data, FILE *file, int x, int repeat) { + xelval alpha = read_uchar(file); + + while (repeat>0) { + alpha_data[x] = alpha; + x++; + repeat--; + } +} + +static void +read_rgba(xel *row_data, xelval *alpha_data, FILE *file, int x, int repeat) { + xelval red, grn, blu, alpha; + red = read_uchar(file); + grn = read_uchar(file); + blu = read_uchar(file); + alpha = read_uchar(file); + + while (repeat>0) { + PPM_ASSIGN(row_data[x], red, grn, blu); + alpha_data[x] = alpha; + x++; + repeat--; + } +} + + +static int +read_scanline(xel *row_data, xelval *alpha_data, int cols, FILE *file, + void (*read_data)(xel *row_data, xelval *alpha_data, FILE *file, + int x, int repeat), + int ctype) { + if (ctype==UNCOMPRESSED) { + for (int x = 0; x cols) { + return false; + } + while (num>0) { + read_data(row_data, alpha_data, file, x, 1); + if (feof(file)) { + return false; + } + x++; + num--; + } + } else { + // Sequence of repeated values. + if (num==128) { + num = read_ushort(file); + } else { + num -= 127; + } + if (x+num > cols) { + return false; + } + read_data(row_data, alpha_data, file, x, num); + if (feof(file)) { + return false; + } + x += num; + } + } + + return (x==cols); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSoftImage::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeSoftImage:: +PNMFileTypeSoftImage() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSoftImage::get_name +// Access: Public, Virtual +// Description: Returns a few words describing the file type. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeSoftImage:: +get_name() const { + return "SoftImage"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSoftImage::get_num_extensions +// Access: Public, Virtual +// Description: Returns the number of different possible filename +// extensions associated with this particular file type. +//////////////////////////////////////////////////////////////////// +int PNMFileTypeSoftImage:: +get_num_extensions() const { + return num_extensions; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSoftImage::get_extension +// Access: Public, Virtual +// Description: Returns the nth possible filename extension +// associated with this particular file type, without a +// leading dot. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeSoftImage:: +get_extension(int n) const { + nassertr(n >= 0 && n < num_extensions, string()); + return extensions[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSoftImage::get_suggested_extension +// Access: Public, Virtual +// Description: Returns a suitable filename extension (without a +// leading dot) to suggest for files of this type, or +// empty string if no suggestions are available. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeSoftImage:: +get_suggested_extension() const { + return "pic"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSoftImage::has_magic_number +// Access: Public, Virtual +// Description: Returns true if this particular file type uses a +// magic number to identify it, false otherwise. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeSoftImage:: +has_magic_number() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSoftImage::matches_magic_number +// Access: Public, Virtual +// Description: Returns true if the indicated "magic number" byte +// stream (the initial few bytes read from the file) +// matches this particular file type, false otherwise. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeSoftImage:: +matches_magic_number(const string &magic_number) const { + nassertr(magic_number.size() >= 2, false); + int mn = + ((unsigned char)magic_number[0] << 8) | + ((unsigned char)magic_number[1]); + return (mn == SOFTIMAGE_MAGIC1); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSoftImage::make_reader +// Access: Public, Virtual +// Description: Allocates and returns a new PNMReader suitable for +// reading from this file type, if possible. If reading +// from this file type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMReader *PNMFileTypeSoftImage:: +make_reader(FILE *file, bool owns_file, const string &magic_number) { + return new Reader(this, file, owns_file, magic_number); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSoftImage::make_writer +// Access: Public, Virtual +// Description: Allocates and returns a new PNMWriter suitable for +// reading from this file type, if possible. If writing +// files of this type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMWriter *PNMFileTypeSoftImage:: +make_writer(FILE *file, bool owns_file) { + return new Writer(this, file, owns_file); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSoftImage::Reader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeSoftImage::Reader:: +Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number) : + PNMReader(type, file, owns_file) +{ + if (!read_magic_number(_file, magic_number, 4)) { + // No magic number, no image. + if (pnmimage_soft_cat.is_debug()) { + pnmimage_soft_cat.debug() + << "SoftImage image file appears to be empty.\n"; + } + _is_valid = false; + return; + } + + int magic1 = + ((unsigned char)magic_number[0] << 8) | + ((unsigned char)magic_number[1]); + int magic2 = + ((unsigned char)magic_number[2] << 8) | + ((unsigned char)magic_number[3]); + + if (magic1 != SOFTIMAGE_MAGIC1 || magic2 != SOFTIMAGE_MAGIC2) { + _is_valid = false; + return; + } + + // skip version number + read_float(_file); + + // Skip comment + fseek(_file, imageCommentLength, SEEK_CUR); + + char pict_id[4]; + if (fread(pict_id, 1, 4, _file) < 4) { + _is_valid = false; + return; + } + + if (memcmp(pict_id, "PICT", 4)!=0) { + _is_valid = false; + return; + } + + _x_size = read_ushort(_file); + _y_size = read_ushort(_file); + + float ratio = read_float(_file); + int fields = read_ushort(_file); + read_ushort(_file); + + int chained, size, channel; + if (!read_channel_pkt(_file, chained, size, rgb_ctype, channel)) { + _is_valid = false; + return; + } + + soft_color = unknown; + + if (channel == (RGB_CHANNEL | ALPHA_CHANNEL)) { + // Four components in the first part: RGBA. + soft_color = rgba; + + } else if (channel == RGB_CHANNEL) { + // Three components in the first part: RGB. + soft_color = rgb; + + if (chained) { + if (!read_channel_pkt(_file, chained, size, alpha_ctype, channel)) { + _is_valid = false; + return; + } + + if (channel == ALPHA_CHANNEL) { + // Alpha component in the second part: RGBA. + soft_color = rgb_a; + } + } + } + + switch (soft_color) { + case rgb: + _num_channels = 3; + break; + + case rgba: + case rgb_a: + _num_channels = 4; + break; + + default: + pnmimage_soft_cat.error() + << "Image is not RGB or RGBA!\n"; + _is_valid = false; + return; + } + + if (chained) { + pnmimage_soft_cat.error() + << "Unexpected additional channels in image file.\n"; + _is_valid = false; + return; + } + + _maxval = 255; + + if (pnmimage_soft_cat.is_debug()) { + pnmimage_soft_cat.debug() + << "Reading SoftImage " << *this << "\n"; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSoftImage::Reader::supports_read_row +// Access: Public, Virtual +// Description: Returns true if this particular PNMReader supports a +// streaming interface to reading the data: that is, it +// is capable of returning the data one row at a time, +// via repeated calls to read_row(). Returns false if +// the only way to read from this file is all at once, +// via read_data(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeSoftImage::Reader:: +supports_read_row() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSoftImage::Reader::read_row +// Access: Public, Virtual +// Description: If supports_read_row(), above, returns true, this +// function may be called repeatedly to read the image, +// one horizontal row at a time, beginning from the top. +// Returns true if the row is successfully read, false +// if there is an error or end of file. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeSoftImage::Reader:: +read_row(xel *row_data, xelval *alpha_data) { + if (!is_valid()) { + return false; + } + switch (soft_color) { + case rgb: + if (!read_scanline(row_data, alpha_data, _x_size, _file, + read_rgb, rgb_ctype)) { + return false; + } + break; + + case rgba: + if (!read_scanline(row_data, alpha_data, _x_size, _file, + read_rgba, rgb_ctype)) { + return false; + } + break; + + case rgb_a: + if (!read_scanline(row_data, alpha_data, _x_size, _file, + read_rgb, rgb_ctype)) { + return false; + } + if (!read_scanline(row_data, alpha_data, _x_size, _file, + read_alpha, alpha_ctype)) { + return false; + } + break; + } + + return true; +} + + +static void +write_channel_pkt(FILE *file, + int chained, int size, int type, int channel) { + write_uchar(file, chained); + write_uchar(file, size); + write_uchar(file, type); + write_uchar(file, channel); +} + +static void +write_rgb(xel *row_data, xelval *, FILE *file, int x) { + write_uchar(file, PPM_GETR(row_data[x])); + write_uchar(file, PPM_GETG(row_data[x])); + write_uchar(file, PPM_GETB(row_data[x])); +} + +static int +compare_rgb(xel *row_data, xelval *, int x1, int x2) { + return PPM_EQUAL(row_data[x1], row_data[x2]); +} + +static void +write_gray(xel *row_data, xelval *, FILE *file, int x) { + write_uchar(file, PPM_GETB(row_data[x])); + write_uchar(file, PPM_GETB(row_data[x])); + write_uchar(file, PPM_GETB(row_data[x])); +} + +static int +compare_gray(xel *row_data, xelval *, int x1, int x2) { + return (PPM_GETB(row_data[x1])==PPM_GETB(row_data[x2])); +} + +static void +write_alpha(xel *, xelval *alpha_data, FILE *file, int x) { + write_uchar(file, alpha_data[x]); +} + +static int +compare_alpha(xel *, xelval *alpha_data, int x1, int x2) { + return (alpha_data[x1]==alpha_data[x2]); +} + +static void +write_diff(xel *row_data, xelval *alpha_data, FILE *file, + void (*write_data)(xel *row_data, xelval *alpha_data, FILE *file, + int x), + int tox, int length) { + if (length>0) { + nassertv(length<=128); + + write_uchar(file, length-1); + while (length>0) { + length--; + write_data(row_data, alpha_data, file, tox-length); + } + } +} + +static void +write_same(xel *row_data, xelval *alpha_data, FILE *file, + void (*write_data)(xel *row_data, xelval *alpha_data, FILE *file, + int x), + int tox, int length) { + if (length==1) { + write_diff(row_data, alpha_data, file, write_data, tox, length); + + } else if (length>0) { + if (length<128) { + write_uchar(file, length+127); + } else { + write_uchar(file, 128); + write_ushort(file, length); + } + write_data(row_data, alpha_data, file, tox); + } +} + + +static void +write_scanline(xel *row_data, xelval *alpha_data, int cols, FILE *file, + int (*compare_data)(xel *row_data, xelval *alpha_data, + int x1, int x2), + void (*write_data)(xel *row_data, xelval *alpha_data, + FILE *file, int x)) { + int run_length = 0; + + int x = 0; + int same = true; + + // Go through each value in the scanline, from beginning to end, looking + // for runs of identical values. + while (x < cols) { + + if (same) { + + // We have been scanning past a run of identical values. In this case, + // the run is the sequence of values from x-run_length to x-1. + + if (!compare_data(row_data, alpha_data, x, x-run_length)) { + // Oops, the end of a run. + + if (run_length <= 1) { + // If run_length is only 1, no big deal--this is actually the + // beginning of a different-valued run. + + same = false; + + } else { + // Write out the old run and begin a new one. We'll be optimistic + // and hope the new run will also represent a sequence of identical + // values (until we find otherwise). + + write_same(row_data, alpha_data, file, write_data, x-1, run_length); + same = true; + run_length = 0; + } + } + + } else { // !same + + // We have been scanning past a run of different values. In this case, + // the run is the sequence of values from x-run_length to x-1. + + if (run_length>128) { + // We can't have different runs of more than 128 characters. Close + // off the old run. + + int excess = run_length - 128; + write_diff(row_data, alpha_data, file, write_data, x-excess-1, 128); + run_length = excess; + + } else if (run_length > 2 && + compare_data(row_data, alpha_data, x, x-1) && + compare_data(row_data, alpha_data, x, x-2)) { + + // If the last three values have been the same, then it's time to + // begin a new run of similar values. Close off the old run. + + write_diff(row_data, alpha_data, file, write_data, x-3, run_length-2); + same = true; + run_length = 2; + } + } + + x++; + run_length++; + } + + // We made it all the way to the end. Flush out the last run. + + if (run_length>0) { + if (same) { + write_same(row_data, alpha_data, file, write_data, cols-1, run_length); + } else { + + // Mighty unlikely, but we might have just run over the + // 128-pixel limit. + if (run_length>128) { + int excess = run_length - 128; + write_diff(row_data, alpha_data, file, write_data, cols-excess-1, 128); + run_length = excess; + } + + write_diff(row_data, alpha_data, file, write_data, cols-1, run_length); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSoftImage::Writer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeSoftImage::Writer:: +Writer(PNMFileType *type, FILE *file, bool owns_file) : + PNMWriter(type, file, owns_file) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSoftImage::Writer::supports_write_row +// Access: Public, Virtual +// Description: Returns true if this particular PNMWriter supports a +// streaming interface to writing the data: that is, it +// is capable of writing the image one row at a time, +// via repeated calls to write_row(). Returns false if +// the only way to write from this file is all at once, +// via write_data(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeSoftImage::Writer:: +supports_write_row() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSoftImage::Writer::write_header +// Access: Public, Virtual +// Description: If supports_write_row(), above, returns true, this +// function may be called to write out the image header +// in preparation to writing out the image data one row +// at a time. Returns true if the header is +// successfully written, false if there is an error. +// +// It is the user's responsibility to fill in the header +// data via calls to set_x_size(), set_num_channels(), +// etc., or copy_header_from(), before calling +// write_header(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeSoftImage::Writer:: +write_header() { + write_ushort(_file, SOFTIMAGE_MAGIC1); + write_ushort(_file, SOFTIMAGE_MAGIC2); + write_float(_file, imageVersionNumber); + + fwrite(imageComment, 1, imageCommentLength, _file); + fwrite("PICT", 1, 4, _file); + + write_ushort(_file, _x_size); + write_ushort(_file, _y_size); + + write_float(_file, 1.0); // pixel aspect ratio; we don't know. + write_ushort(_file, 3); // fields value; we don't really know either. + write_ushort(_file, 0); // padding + + // There doesn't seem to be a variation on SoftImage image formats for + // grayscale images. We'll write out grayscale as a 3-channel image. + + if (has_alpha()) { + write_channel_pkt(_file, 1, 8, MIXED_RUN_LENGTH, RGB_CHANNEL); + write_channel_pkt(_file, 0, 8, MIXED_RUN_LENGTH, ALPHA_CHANNEL); + } else { + write_channel_pkt(_file, 0, 8, MIXED_RUN_LENGTH, RGB_CHANNEL); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeSoftImage::Writer::write_row +// Access: Public, Virtual +// Description: If supports_write_row(), above, returns true, this +// function may be called repeatedly to write the image, +// one horizontal row at a time, beginning from the top. +// Returns true if the row is successfully written, +// false if there is an error. +// +// You must first call write_header() before writing the +// individual rows. It is also important to delete the +// PNMWriter class after successfully writing the last +// row. Failing to do this may result in some data not +// getting flushed! +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeSoftImage::Writer:: +write_row(xel *row_data, xelval *alpha_data) { + if (is_grayscale()) { + write_scanline(row_data, alpha_data, _x_size, _file, compare_gray, write_gray); + + } else { + write_scanline(row_data, alpha_data, _x_size, _file, compare_rgb, write_rgb); + } + + if (has_alpha()) { + write_scanline(row_data, alpha_data, _x_size, _file, compare_alpha, write_alpha); + } + + return !ferror(_file); +} + + diff --git a/panda/src/pnmimagetypes/pnmFileTypeSoftImage.h b/panda/src/pnmimagetypes/pnmFileTypeSoftImage.h new file mode 100644 index 0000000000..be65db6b5f --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeSoftImage.h @@ -0,0 +1,79 @@ +// Filename: pnmFileTypeSoftImage.h +// Created by: drose (17Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMFILETYPESOFTIMAGE_H +#define PNMFILETYPESOFTIMAGE_H + +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : PNMFileTypeSoftImage +// Description : For reading and SoftImage native image files. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PNMFileTypeSoftImage : public PNMFileType { +public: + PNMFileTypeSoftImage(); + + virtual string get_name() const; + + virtual int get_num_extensions() const; + virtual string get_extension(int n) const; + virtual string get_suggested_extension() const; + + virtual bool has_magic_number() const; + virtual bool matches_magic_number(const string &magic_number) const; + + virtual PNMReader *make_reader(FILE *file, bool owns_file = true, + const string &magic_number = string()); + virtual PNMWriter *make_writer(FILE *file, bool owns_file = true); + +public: + class Reader : public PNMReader { + public: + Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number); + + virtual bool supports_read_row() const; + virtual bool read_row(xel *array, xelval *alpha); + + private: + enum { unknown, rgb, rgba, rgb_a } soft_color; + int rgb_ctype, alpha_ctype; + }; + + class Writer : public PNMWriter { + public: + Writer(PNMFileType *type, FILE *file, bool owns_file); + + virtual bool supports_write_row() const; + virtual bool write_header(); + virtual bool write_row(xel *array, xelval *alpha); + }; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PNMFileType::init_type(); + register_type(_type_handle, "PNMFileTypeSoftImage", + PNMFileType::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif + + diff --git a/panda/src/pnmimagetypes/pnmFileTypeTIFF.cxx b/panda/src/pnmimagetypes/pnmFileTypeTIFF.cxx new file mode 100644 index 0000000000..9d24f2e7ad --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeTIFF.cxx @@ -0,0 +1,771 @@ +// Filename: pnmFileTypeTIFF.cxx +// Created by: drose (19Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "pnmFileTypeTIFF.h" +#include "config_pnmimagetypes.h" + +extern "C" { +#include "../pnm/ppmcmap.h" +#include "../tiff/tiff.h" +#include "../tiff/tiffio.h" +} + +static const char * const extensions[] = { + "tiff", "tif" +}; +static const int num_extensions = sizeof(extensions) / sizeof(const char *); + +// These are configurable parameters to specify TIFF details on output. +// See tools/drr/../pnm/libtiff/tiff.h or type man pnmtotiff for a better +// explanation of options. + +unsigned short tiff_compression = COMPRESSION_LZW; +/* One of: + COMPRESSION_NONE + COMPRESSION_CCITTRLE + COMPRESSION_CCITTFAX3 + COMPRESSION_CCITTFAX4 + COMPRESSION_LZW + COMPRESSION_JPEG + COMPRESSION_NEXT + COMPRESSION_CCITTRLEW + COMPRESSION_PACKBITS + COMPRESSION_THUNDERSCAN + */ + +long tiff_g3options = 0; +/* One or more of: + GROUP3OPT_2DENCODING + GROUP3OPT_FILLBITS + + meaningful when tiff_compression == COMPRESSION_CCITTFAX3. + */ + +unsigned short tiff_fillorder = FILLORDER_MSB2LSB; +/* One of: + FILLORDER_MSB2LSB + FILLORDER_LSB2MSB + */ + +short tiff_predictor = 0; +/* 0, 1, or 2; meaningful when tiff_compression == COMPRESSION_LZW. */ + + +long tiff_rowsperstrip = 0; +/* 0 or any positive number */ + +#ifndef PHOTOMETRIC_DEPTH +#define PHOTOMETRIC_DEPTH 32768 +#endif + +// Here's a number of functions to support the stdio-FILE interface +// via the TIFF library. +static tsize_t +StdioReadProc(thandle_t fd, tdata_t buf, tsize_t size) +{ + return ((tsize_t)fread((void *)buf, 1, (size_t) size, (FILE *)fd)); +} + +static tsize_t +StdioWriteProc(thandle_t fd, tdata_t buf, tsize_t size) +{ + return ((tsize_t)fwrite((void *)buf, 1, (size_t) size, (FILE *)fd)); +} + +static toff_t +StdioSeekProc(thandle_t fd, off_t off, int whence) +{ + fseek((FILE *)fd, (long)off, whence); + return (toff_t)ftell((FILE *)fd); +} + +static int +StdioCloseProc(thandle_t) +{ + // We don't actually close the file; we'll leave that to PNMReader. + return true; +} + +static toff_t +StdioSizeProc(thandle_t fd) +{ + fseek((FILE *)fd, 0, SEEK_END); + return (toff_t)ftell((FILE *)fd); +} + +static int +StdioMapProc(thandle_t, tdata_t*, toff_t*) +{ + return (0); +} + +static void +StdioUnmapProc(thandle_t, tdata_t, toff_t) +{ +} + +TypeHandle PNMFileTypeTIFF::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeTIFF:: +PNMFileTypeTIFF() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::get_name +// Access: Public, Virtual +// Description: Returns a few words describing the file type. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeTIFF:: +get_name() const { + return "TIFF"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::get_num_extensions +// Access: Public, Virtual +// Description: Returns the number of different possible filename +// extensions associated with this particular file type. +//////////////////////////////////////////////////////////////////// +int PNMFileTypeTIFF:: +get_num_extensions() const { + return num_extensions; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::get_extension +// Access: Public, Virtual +// Description: Returns the nth possible filename extension +// associated with this particular file type, without a +// leading dot. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeTIFF:: +get_extension(int n) const { + nassertr(n >= 0 && n < num_extensions, string()); + return extensions[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::get_suggested_extension +// Access: Public, Virtual +// Description: Returns a suitable filename extension (without a +// leading dot) to suggest for files of this type, or +// empty string if no suggestions are available. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeTIFF:: +get_suggested_extension() const { + return "tiff"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::has_magic_number +// Access: Public, Virtual +// Description: Returns true if this particular file type uses a +// magic number to identify it, false otherwise. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeTIFF:: +has_magic_number() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::matches_magic_number +// Access: Public, Virtual +// Description: Returns true if the indicated "magic number" byte +// stream (the initial few bytes read from the file) +// matches this particular file type, false otherwise. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeTIFF:: +matches_magic_number(const string &magic_number) const { + nassertr(magic_number.size() >= 2, false); + int mn = + ((unsigned char)magic_number[0] << 8) | + ((unsigned char)magic_number[1]); + return (mn == TIFF_BIGENDIAN || mn == TIFF_LITTLEENDIAN); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::make_reader +// Access: Public, Virtual +// Description: Allocates and returns a new PNMReader suitable for +// reading from this file type, if possible. If reading +// from this file type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMReader *PNMFileTypeTIFF:: +make_reader(FILE *file, bool owns_file, const string &magic_number) { + return new Reader(this, file, owns_file, magic_number); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::make_writer +// Access: Public, Virtual +// Description: Allocates and returns a new PNMWriter suitable for +// reading from this file type, if possible. If writing +// files of this type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMWriter *PNMFileTypeTIFF:: +make_writer(FILE *file, bool owns_file) { + return new Writer(this, file, owns_file); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::Reader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeTIFF::Reader:: +Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number) : + PNMReader(type, file, owns_file) +{ + bool grayscale; + int numcolors; + int i; + unsigned short* redcolormap; + unsigned short* greencolormap; + unsigned short* bluecolormap; + + // Hope we can ungetc() more than one character. + for (string::reverse_iterator mi = magic_number.rbegin(); + mi != magic_number.rend(); + mi++) { + ungetc(*mi, _file); + } + + tif = TIFFClientOpen("TIFF file", "r", + (thandle_t) _file, + StdioReadProc, StdioWriteProc, + (TIFFSeekProc)StdioSeekProc, + StdioCloseProc, StdioSizeProc, + StdioMapProc, StdioUnmapProc); + + if ( tif == NULL ) { + _is_valid = false; + } + + if (_is_valid) { + if ( ! TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &bps ) ) + bps = 1; + if ( ! TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &spp ) ) + spp = 1; + + if ( ! TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photomet ) ) { + pnmimage_tiff_cat.error() + << "Error getting photometric from TIFF file.\n"; + _is_valid = false; + } + } + + if (_is_valid) { + if (spp >= 1 && spp <= 4) { + _num_channels = spp; + } else { + pnmimage_tiff_cat.error() + << "Cannot handle " << spp << "-channel image.\n"; + _is_valid = false; + } + } + + if (_is_valid) { + (void) TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &_x_size ); + (void) TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &_y_size ); + + if (pnmimage_tiff_cat.is_debug()) { + pnmimage_tiff_cat.debug() + << "Reading TIFF image: " << _x_size << " x " << _y_size << "\n" + << bps << " bits/sample, " << spp << " samples/pixel\n"; + } + + _maxval = ( 1 << bps ) - 1; + if ( _maxval == 1 && spp == 1 ) { + if (pnmimage_tiff_cat.is_debug()) { + pnmimage_tiff_cat.debug(false) + << "monochrome\n"; + } + grayscale = true; + } else { + switch ( photomet ) { + case PHOTOMETRIC_MINISBLACK: + if (pnmimage_tiff_cat.is_debug()) { + pnmimage_tiff_cat.debug(false) + << _maxval + 1 << " graylevels (min is black)\n"; + } + grayscale = true; + break; + + case PHOTOMETRIC_MINISWHITE: + if (pnmimage_tiff_cat.is_debug()) { + pnmimage_tiff_cat.debug(false) + << _maxval + 1 << " graylevels (min is white)\n"; + } + grayscale = true; + break; + + case PHOTOMETRIC_PALETTE: + if (pnmimage_tiff_cat.is_debug()) { + pnmimage_tiff_cat.debug(false) + << " colormapped\n"; + } + if ( ! TIFFGetField( tif, TIFFTAG_COLORMAP, &redcolormap, &greencolormap, &bluecolormap ) ) { + pnmimage_tiff_cat.error() + << "Error getting colormap from TIFF file.\n"; + _is_valid = false; + } else { + numcolors = _maxval + 1; + if ( numcolors > TIFF_COLORMAP_MAXCOLORS ) { + pnmimage_tiff_cat.error() + << "Cannot read TIFF file with " << numcolors + << " in colormap; max supported is " << TIFF_COLORMAP_MAXCOLORS << "\n"; + _is_valid = false; + } else { + _maxval = PNM_MAXMAXVAL; + grayscale = false; + for ( i = 0; i < numcolors; ++i ) { + xelval r, g, b; + r = (xelval)(_maxval * (double)(redcolormap[i] / 65535.0)); + g = (xelval)(_maxval * (double)(greencolormap[i] / 65535.0)); + b = (xelval)(_maxval * (double)(bluecolormap[i] / 65535.0)); + PPM_ASSIGN( colormap[i], r, g, b ); + } + } + } + break; + + case PHOTOMETRIC_RGB: + if (pnmimage_tiff_cat.is_debug()) { + pnmimage_tiff_cat.debug(false) + << "truecolor\n"; + } + grayscale = false; + break; + + case PHOTOMETRIC_MASK: + pnmimage_tiff_cat.error() + << "Don't know how to handle TIFF image with PHOTOMETRIC_MASK.\n"; + _is_valid = false; + break; + + case PHOTOMETRIC_DEPTH: + pnmimage_tiff_cat.error() + << "Don't know how to handle TIFF image with PHOTOMETRIC_DEPTH.\n"; + _is_valid = false; + break; + + default: + pnmimage_tiff_cat.error() + << "Unknown photometric " << photomet << " in TIFF image.\n"; + _is_valid = false; + break; + } + } + } + + if (_is_valid ) { + if ( _maxval > PNM_MAXMAXVAL ) { + pnmimage_tiff_cat.error() + << "Cannot read TIFF file with maxval of " << _maxval << "\n"; + _is_valid = false; + } + } + + if (_is_valid) { + if (grayscale && !is_grayscale()) { + _num_channels = (has_alpha()) ? 2 : 1; + } else if (!grayscale && is_grayscale()) { + _num_channels = (has_alpha()) ? 4 : 3; + } + + current_row = 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::Reader::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeTIFF::Reader:: +~Reader() { + TIFFClose( tif ); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::Reader::supports_read_row +// Access: Public, Virtual +// Description: Returns true if this particular PNMReader supports a +// streaming interface to reading the data: that is, it +// is capable of returning the data one row at a time, +// via repeated calls to read_row(). Returns false if +// the only way to read from this file is all at once, +// via read_data(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeTIFF::Reader:: +supports_read_row() const { + return true; +} + +#define NEXTSAMPLE \ + { \ + if ( bitsleft == 0 ) \ + { \ + ++inP; \ + bitsleft = 8; \ + } \ + bitsleft -= bps; \ + sample = ( *inP >> bitsleft ) & _maxval; \ + } + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::Reader::read_row +// Access: Public, Virtual +// Description: If supports_read_row(), above, returns true, this +// function may be called repeatedly to read the image, +// one horizontal row at a time, beginning from the top. +// Returns true if the row is successfully read, false +// if there is an error or end of file. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeTIFF::Reader:: +read_row(xel *row_data, xelval *alpha_data) { + if (!is_valid()) { + return false; + } + + unsigned char *buf = (unsigned char*) alloca((size_t)TIFFScanlineSize(tif)); + int col; + unsigned char sample; + + if ( TIFFReadScanline( tif, buf, current_row, 0 ) < 0 ) { + pnmimage_tiff_cat.error() + << "Bad data read on line " << current_row << " of TIFF image.\n"; + return false; + } + + unsigned char *inP = buf; + int bitsleft = 8; + + switch ( photomet ) { + case PHOTOMETRIC_MINISBLACK: + for ( col = 0; col < _x_size; ++col ) + { + NEXTSAMPLE; + PPM_PUTB(row_data[col], sample); + if ( spp == 2 ) { + NEXTSAMPLE; // Alpha channel + alpha_data[col] = sample; + } + } + break; + + case PHOTOMETRIC_MINISWHITE: + for ( col = 0; col < _x_size; ++col ) + { + NEXTSAMPLE; + sample = _maxval - sample; + PPM_PUTB(row_data[col], sample); + if ( spp == 2 ) { + NEXTSAMPLE; // Alpha channel + alpha_data[col] = sample; + } + } + break; + + case PHOTOMETRIC_PALETTE: + for ( col = 0; col < _x_size; ++col ) + { + NEXTSAMPLE; + row_data[col] = colormap[sample]; + if ( spp == 2 ) { + NEXTSAMPLE; // Alpha channel + alpha_data[col] = sample; + } + } + break; + + case PHOTOMETRIC_RGB: + for ( col = 0; col < _x_size; ++col ) { + xelval r, g, b; + + NEXTSAMPLE; + r = sample; + NEXTSAMPLE; + g = sample; + NEXTSAMPLE; + b = sample; + PPM_ASSIGN(row_data[col], r, g, b); + if ( spp == 4 ) { + NEXTSAMPLE; // Alpha channel + alpha_data[col] = sample; + } + } + break; + + default: + pnmimage_tiff_cat.error() + << "Internal error: unsupported photometric " << photomet << "\n"; + return false; + } + + current_row++; + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::Writer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeTIFF::Writer:: +Writer(PNMFileType *type, FILE *file, bool owns_file) : + PNMWriter(type, file, owns_file) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeTIFF::Writer::write_data +// Access: Public, Virtual +// Description: Writes out an entire image all at once, including the +// header, based on the image data stored in the given +// _x_size * _y_size array and alpha pointers. (If the +// image type has no alpha channel, alpha is ignored.) +// Returns the number of rows correctly written. +// +// It is the user's responsibility to fill in the header +// data via calls to set_x_size(), set_num_channels(), +// etc., or copy_header_from(), before calling +// write_data(). +// +// It is important to delete the PNMWriter class after +// successfully writing the data. Failing to do this +// may result in some data not getting flushed! +// +// Derived classes need not override this if they +// instead provide supports_streaming() and write_row(), +// below. +//////////////////////////////////////////////////////////////////// +int PNMFileTypeTIFF::Writer:: +write_data(xel *array, xelval *alpha) { + colorhist_vector chv; + colorhash_table cht; + unsigned short + red[TIFF_COLORMAP_MAXCOLORS], + grn[TIFF_COLORMAP_MAXCOLORS], + blu[TIFF_COLORMAP_MAXCOLORS]; + int row, colors, i; + register int col; + int grayscale; + struct tiff * tif; + short photometric; + short samplesperpixel; + short bitspersample; + int bytesperrow; + unsigned char* buf; + unsigned char* tP; + + switch ( get_color_type() ) { + case CT_color: + // This call is a bit of fakery to convert our proper 2-d array of + // xels to an indirect 2-d array of pixels. We make it look like a + // single row of _x_size * _y_size pixels. + chv = ppm_computecolorhist( (pixel **)&array, _x_size * _y_size, 1, + TIFF_COLORMAP_MAXCOLORS, &colors ); + if ( chv == (colorhist_vector) 0 ) { + pnmimage_tiff_cat.debug() + << colors << " colors found; too many for a palette.\n" + << "Writing a 24-bit RGB file.\n"; + grayscale = false; + } else { + pnmimage_tiff_cat.debug() + << colors << " colors found; writing an 8-bit palette file.\n"; + grayscale = true; + for ( i = 0; i < colors; ++i ) { + register xelval r, g, b; + + r = PPM_GETR( chv[i].color ); + g = PPM_GETG( chv[i].color ); + b = PPM_GETB( chv[i].color ); + if ( r != g || g != b ) { + grayscale = false; + break; + } + } + } + break; + + case CT_two_channel: // We don't yet support two-channel output for TIFF's. + case CT_four_channel: + chv = (colorhist_vector) 0; + grayscale = false; + break; + + case CT_grayscale: + chv = (colorhist_vector) 0; + grayscale = true; + break; + } + + /* Open output file. */ + tif = TIFFClientOpen("TIFF file", "w", + (thandle_t) _file, + StdioReadProc, StdioWriteProc, + (TIFFSeekProc)StdioSeekProc, + StdioCloseProc, StdioSizeProc, + StdioMapProc, StdioUnmapProc); + if ( tif == NULL ) { + return false; + } + + /* Figure out TIFF parameters. */ + switch ( get_color_type() ) { + case CT_color: + case CT_four_channel: + if ( chv == (colorhist_vector) 0 ) { + samplesperpixel = _num_channels; + bitspersample = 8; + photometric = PHOTOMETRIC_RGB; + bytesperrow = _x_size * samplesperpixel; + } else if ( grayscale ) { + samplesperpixel = 1; + bitspersample = pm_maxvaltobits( _maxval ); + photometric = PHOTOMETRIC_MINISBLACK; + bytesperrow = ( _x_size + i - 1 ) / i; + } else { + samplesperpixel = 1; + bitspersample = 8; + photometric = PHOTOMETRIC_PALETTE; + bytesperrow = _x_size; + } + break; + + case CT_grayscale: + case CT_two_channel: + samplesperpixel = _num_channels; + bitspersample = pm_maxvaltobits( _maxval ); + photometric = PHOTOMETRIC_MINISBLACK; + i = 8 / bitspersample; + bytesperrow = ( _x_size + i - 1 ) / i; + break; + } + + if ( tiff_rowsperstrip == 0 ) + tiff_rowsperstrip = ( 8 * 1024 ) / bytesperrow; + buf = (unsigned char*) malloc( bytesperrow ); + if ( buf == (unsigned char*) 0 ) { + pnmimage_tiff_cat.error() + << "Can't allocate memory for row buffer\n"; + return 0; + } + + /* Set TIFF parameters. */ + TIFFSetField( tif, TIFFTAG_IMAGEWIDTH, _x_size ); + TIFFSetField( tif, TIFFTAG_IMAGELENGTH, _y_size ); + TIFFSetField( tif, TIFFTAG_BITSPERSAMPLE, bitspersample ); + TIFFSetField( tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT ); + TIFFSetField( tif, TIFFTAG_COMPRESSION, tiff_compression ); + if ( tiff_compression == COMPRESSION_CCITTFAX3 && tiff_g3options != 0 ) + TIFFSetField( tif, TIFFTAG_GROUP3OPTIONS, tiff_g3options ); + if ( tiff_compression == COMPRESSION_LZW && tiff_predictor != 0 ) + TIFFSetField( tif, TIFFTAG_PREDICTOR, tiff_predictor ); + TIFFSetField( tif, TIFFTAG_PHOTOMETRIC, photometric ); + TIFFSetField( tif, TIFFTAG_FILLORDER, tiff_fillorder ); + //TIFFSetField( tif, TIFFTAG_DOCUMENTNAME, "TIFF Image File"); + TIFFSetField( tif, TIFFTAG_IMAGEDESCRIPTION, + "Generated via pnmimage.\n" ); + TIFFSetField( tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel ); + TIFFSetField( tif, TIFFTAG_ROWSPERSTRIP, tiff_rowsperstrip ); + /* TIFFSetField( tif, TIFFTAG_STRIPBYTECOUNTS, _y_size / tiff_rowsperstrip ); */ + TIFFSetField( tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG ); + + if ( chv == (colorhist_vector) 0 ) { + cht = (colorhash_table) 0; + } else { + /* Make TIFF colormap. */ + for ( i = 0; i < colors; ++i ) { + red[i] = (unsigned short) (PPM_GETR( chv[i].color ) * 65535L / _maxval); + grn[i] = (unsigned short) (PPM_GETG( chv[i].color ) * 65535L / _maxval); + blu[i] = (unsigned short) (PPM_GETB( chv[i].color ) * 65535L / _maxval); + } + TIFFSetField( tif, TIFFTAG_COLORMAP, red, grn, blu ); + + /* Convert color vector to color hash table, for fast lookup. */ + cht = ppm_colorhisttocolorhash( chv, colors ); + ppm_freecolorhist( chv ); + } + + /* Now write the TIFF data. */ + for ( row = 0; row < _y_size; ++row ) { + xel *row_data = array + row*_x_size; + xelval *alpha_data = alpha + row*_x_size; + + if ( !is_grayscale() && ! grayscale ) { + if ( cht == (colorhash_table) 0 ) { + tP = buf; + for ( col = 0; col < _x_size; ++col ) { + *tP++ = (unsigned char)(255 * PPM_GETR(row_data[col]) / _maxval); + *tP++ = (unsigned char)(255 * PPM_GETG(row_data[col]) / _maxval); + *tP++ = (unsigned char)(255 * PPM_GETB(row_data[col]) / _maxval); + if (samplesperpixel==4) { + *tP++ = (unsigned char)(255 * alpha_data[col] / _maxval); + } + } + } else { + tP = buf; + for ( col = 0; col < _x_size; ++col ) { + register int s; + + s = ppm_lookupcolor( cht, (pixel *)(&row_data[col]) ); + if ( s == -1 ) { + pnmimage_tiff_cat.error() + << "Internal error: color not found?!? row=" << row + << " col=" << col << "\n"; + return 0; + } + *tP++ = (unsigned char) s; + if (samplesperpixel==2) { + *tP++ = (unsigned char)(255 * alpha_data[col] / _maxval); + } + } + } + } else { + register xelval bigger_maxval; + register int bitshift; + register unsigned char byte; + register xelval s; + + bigger_maxval = pm_bitstomaxval( bitspersample ); + bitshift = 8 - bitspersample; + byte = 0; + tP = buf; + for ( col = 0; col < _x_size; ++col ) { + s = PPM_GETB(row_data[col]); + if ( _maxval != bigger_maxval ) + s = (xelval)((long) s * bigger_maxval / _maxval); + byte |= s << bitshift; + bitshift -= bitspersample; + if ( bitshift < 0 ) { + *tP++ = byte; + bitshift = 8 - bitspersample; + byte = 0; + } + } + if ( bitshift != 8 - bitspersample ) + *tP++ = byte; + } + + if ( TIFFWriteScanline( tif, buf, row, 0 ) < 0 ) { + pnmimage_tiff_cat.error() + << "failed a scanline write on row " << row << "\n"; + return row; + } + } + TIFFFlushData( tif ); + TIFFClose( tif ); + + return _y_size; +} diff --git a/panda/src/pnmimagetypes/pnmFileTypeTIFF.h b/panda/src/pnmimagetypes/pnmFileTypeTIFF.h new file mode 100644 index 0000000000..0430826141 --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeTIFF.h @@ -0,0 +1,84 @@ +// Filename: pnmFileTypeTIFF.h +// Created by: drose (17Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMFILETYPETIFF_H +#define PNMFILETYPETIFF_H + +#include + +#include +#include +#include + +#define TIFF_COLORMAP_MAXCOLORS 1024 + +//////////////////////////////////////////////////////////////////// +// Class : PNMFileTypeTIFF +// Description : For reading and writing TIFF files. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PNMFileTypeTIFF : public PNMFileType { +public: + PNMFileTypeTIFF(); + + virtual string get_name() const; + + virtual int get_num_extensions() const; + virtual string get_extension(int n) const; + virtual string get_suggested_extension() const; + + virtual bool has_magic_number() const; + virtual bool matches_magic_number(const string &magic_number) const; + + virtual PNMReader *make_reader(FILE *file, bool owns_file = true, + const string &magic_number = string()); + virtual PNMWriter *make_writer(FILE *file, bool owns_file = true); + +public: + class Reader : public PNMReader { + public: + Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number); + virtual ~Reader(); + + virtual bool supports_read_row() const; + virtual bool read_row(xel *array, xelval *alpha); + + private: + unsigned short photomet; + unsigned short bps, spp; + xel colormap[TIFF_COLORMAP_MAXCOLORS]; + + int current_row; + struct tiff *tif; + }; + + class Writer : public PNMWriter { + public: + Writer(PNMFileType *type, FILE *file, bool owns_file); + + virtual int write_data(xel *array, xelval *alpha); + }; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PNMFileType::init_type(); + register_type(_type_handle, "PNMFileTypeTIFF", + PNMFileType::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif + + diff --git a/panda/src/pnmimagetypes/pnmFileTypeYUV.cxx b/panda/src/pnmimagetypes/pnmFileTypeYUV.cxx new file mode 100644 index 0000000000..d4b2725fd5 --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeYUV.cxx @@ -0,0 +1,381 @@ +// Filename: pnmFileTypeYUV.cxx +// Created by: drose (19Jun00) +// +//////////////////////////////////////////////////////////////////// + + +// Much code in this file is borrowed from Netpbm, specifically yuvtoppm.c +// and ppmtoyuv.c. + +/* yuvtoppm.c - convert Abekas YUV bytes into a portable pixmap +** +** by Marc Boucher +** Internet: marc@PostImage.cxxOM +** +** Based on Example Conversion Program, A60/A64 Digital Video Interface +** Manual, page 69 +** +** Uses integer arithmetic rather than floating point for better performance +** +** Copyright (C) 1991 by DHD PostImage Inc. +** Copyright (C) 1987 by Abekas Video Systems Inc. +** Copyright (C) 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +/* ppmtoyuv.c - convert a portable pixmap into an Abekas YUV file +** +** by Marc Boucher +** Internet: marc@PostImage.cxxOM +** +** Based on Example Conversion Program, A60/A64 Digital Video Interface +** Manual, page 69. +** +** Copyright (C) 1991 by DHD PostImage Inc. +** Copyright (C) 1987 by Abekas Video Systems Inc. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "pnmFileTypeYUV.h" +#include "config_pnmimagetypes.h" + +/* x must be signed for the following to work correctly */ +#define limit(x) (xelval)(((x>0xffffff)?0xff0000:((x<=0xffff)?0:x&0xff0000))>>16) + +static const char * const extensions[] = { + "yuv" +}; +static const int num_extensions = sizeof(extensions) / sizeof(const char *); + +TypeHandle PNMFileTypeYUV::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeYUV::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeYUV:: +PNMFileTypeYUV() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeYUV::get_name +// Access: Public, Virtual +// Description: Returns a few words describing the file type. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeYUV:: +get_name() const { + return "Abekas YUV"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeYUV::get_num_extensions +// Access: Public, Virtual +// Description: Returns the number of different possible filename +// extensions associated with this particular file type. +//////////////////////////////////////////////////////////////////// +int PNMFileTypeYUV:: +get_num_extensions() const { + return num_extensions; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeYUV::get_extension +// Access: Public, Virtual +// Description: Returns the nth possible filename extension +// associated with this particular file type, without a +// leading dot. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeYUV:: +get_extension(int n) const { + nassertr(n >= 0 && n < num_extensions, string()); + return extensions[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeYUV::get_suggested_extension +// Access: Public, Virtual +// Description: Returns a suitable filename extension (without a +// leading dot) to suggest for files of this type, or +// empty string if no suggestions are available. +//////////////////////////////////////////////////////////////////// +string PNMFileTypeYUV:: +get_suggested_extension() const { + return "yuv"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeYUV::make_reader +// Access: Public, Virtual +// Description: Allocates and returns a new PNMReader suitable for +// reading from this file type, if possible. If reading +// from this file type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMReader *PNMFileTypeYUV:: +make_reader(FILE *file, bool owns_file, const string &magic_number) { + return new Reader(this, file, owns_file, magic_number); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeYUV::make_writer +// Access: Public, Virtual +// Description: Allocates and returns a new PNMWriter suitable for +// reading from this file type, if possible. If writing +// files of this type is not supported, returns NULL. +//////////////////////////////////////////////////////////////////// +PNMWriter *PNMFileTypeYUV:: +make_writer(FILE *file, bool owns_file) { + return new Writer(this, file, owns_file); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeYUV::Reader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeYUV::Reader:: +Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number) : + PNMReader(type, file, owns_file) +{ + yuvbuf = NULL; + + // Hope we can ungetc() more than one character. + for (string::reverse_iterator mi = magic_number.rbegin(); + mi != magic_number.rend(); + mi++) { + ungetc(*mi, _file); + } + + _x_size = yuv_xsize; + _y_size = yuv_ysize; + _num_channels = 3; + + if (_x_size <= 0 || _y_size <= 0) { + _is_valid = false; + return; + } + + nassertv(255 <= PGM_MAXMAXVAL); + + yuvbuf = (unsigned char *) pm_allocrow(_x_size, 2); + + _maxval = 255; + + if (pnmimage_yuv_cat.is_debug()) { + pnmimage_yuv_cat.debug() + << "Reading YUV " << *this << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeYUV::Reader::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeYUV::Reader:: +~Reader() { + if (yuvbuf!=NULL) { + pm_freerow((char *)yuvbuf); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeYUV::Reader::supports_read_row +// Access: Public, Virtual +// Description: Returns true if this particular PNMReader supports a +// streaming interface to reading the data: that is, it +// is capable of returning the data one row at a time, +// via repeated calls to read_row(). Returns false if +// the only way to read from this file is all at once, +// via read_data(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeYUV::Reader:: +supports_read_row() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeYUV::Reader::read_row +// Access: Public, Virtual +// Description: If supports_read_row(), above, returns true, this +// function may be called repeatedly to read the image, +// one horizontal row at a time, beginning from the top. +// Returns true if the row is successfully read, false +// if there is an error or end of file. +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeYUV::Reader:: +read_row(xel *row_data, xelval *) { + long tmp, y, u, v, y1, r, g, b; + unsigned char *yuvptr; + int col; + + if (fread(yuvbuf, _x_size * 2, 1, _file) != 1) { + // Short file--perhaps it's just a field instead of a full frame. + // Since the YUV format does not include a length designation, we'll + // have to assume this is not a problem and just truncate here. + return false; + } + + yuvptr = yuvbuf; + for (col = 0; col < _x_size; col += 2) { + u = (int)yuvptr[0] - 128; + y = (int)yuvptr[1] - 16; + if (y < 0) y = 0; + + v = (int)yuvptr[2] - 128; + y1 = (int)yuvptr[3] - 16; + if (y1 < 0) y1 = 0; + + r = 104635 * v; + g = -25690 * u + -53294 * v; + b = 132278 * u; + + y*=76310; y1*=76310; + + PPM_ASSIGN(row_data[col], limit(r+y), limit(g+y), limit(b+y)); + PPM_ASSIGN(row_data[col+1], limit(r+y1), limit(g+y1), limit(b+y1)); + + yuvptr += 4; + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeYUV::Writer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeYUV::Writer:: +Writer(PNMFileType *type, FILE *file, bool owns_file) : + PNMWriter(type, file, owns_file) +{ + yuvbuf = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeYUV::Writer::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PNMFileTypeYUV::Writer:: +~Writer() { + if (yuvbuf!=NULL) { + pm_freerow((char *)yuvbuf); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeYUV::Writer::supports_write_row +// Access: Public, Virtual +// Description: Returns true if this particular PNMWriter supports a +// streaming interface to writing the data: that is, it +// is capable of writing the image one row at a time, +// via repeated calls to write_row(). Returns false if +// the only way to write from this file is all at once, +// via write_data(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeYUV::Writer:: +supports_write_row() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeYUV::Writer::write_header +// Access: Public, Virtual +// Description: If supports_write_row(), above, returns true, this +// function may be called to write out the image header +// in preparation to writing out the image data one row +// at a time. Returns true if the header is +// successfully written, false if there is an error. +// +// It is the user's responsibility to fill in the header +// data via calls to set_x_size(), set_num_channels(), +// etc., or copy_header_from(), before calling +// write_header(). +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeYUV::Writer:: +write_header() { + if (yuvbuf!=NULL) { + pm_freerow((char *)yuvbuf); + } + + yuvbuf = (unsigned char *) pm_allocrow( _x_size, 2 ); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMFileTypeYUV::Writer::write_row +// Access: Public, Virtual +// Description: If supports_write_row(), above, returns true, this +// function may be called repeatedly to write the image, +// one horizontal row at a time, beginning from the top. +// Returns true if the row is successfully written, +// false if there is an error. +// +// You must first call write_header() before writing the +// individual rows. It is also important to delete the +// PNMWriter class after successfully writing the last +// row. Failing to do this may result in some data not +// getting flushed! +//////////////////////////////////////////////////////////////////// +bool PNMFileTypeYUV::Writer:: +write_row(xel *row_data, xelval *) { + int col; + unsigned long y1, y2=0, u=0, v=0, u0=0, u1, u2, v0=0, v1, v2; + static const int max_byte = 255; + + unsigned char *yuvptr; + + for (col = 0, yuvptr=yuvbuf; col < _x_size; col += 2) { + pixval r, g, b; + + /* first pixel gives Y and 0.5 of chroma */ + r = (pixval)(max_byte * PPM_GETR(row_data[col])/_maxval); + g = (pixval)(max_byte * PPM_GETG(row_data[col])/_maxval); + b = (pixval)(max_byte * PPM_GETB(row_data[col])/_maxval); + + y1 = 16829 * r + 33039 * g + 6416 * b + (0xffff & y2); + u1 = -4853 * r - 9530 * g + 14383 * b; + v1 = 14386 * r - 12046 * g - 2340 * b; + + /* second pixel just yields a Y and 0.25 U, 0.25 V */ + r = (pixval)(max_byte * PPM_GETR(row_data[col])/_maxval); + g = (pixval)(max_byte * PPM_GETG(row_data[col])/_maxval); + b = (pixval)(max_byte * PPM_GETB(row_data[col])/_maxval); + + y2 = 16829 * r + 33039 * g + 6416 * b + (0xffff & y1); + u2 = -2426 * r - 4765 * g + 7191 * b; + v2 = 7193 * r - 6023 * g - 1170 * b; + + /* filter the chroma */ + u = u0 + u1 + u2 + (0xffff & u); + v = v0 + v1 + v2 + (0xffff & v); + + u0 = u2; + v0 = v2; + + *yuvptr++ = (unsigned char)((u >> 16) + 128); + *yuvptr++ = (unsigned char)((y1 >> 16) + 16); + *yuvptr++ = (unsigned char)((v >> 16) + 128); + *yuvptr++ = (unsigned char)((y2 >> 16) + 16); + } + fwrite(yuvbuf, _x_size*2, 1, _file); + + return true; +} + + diff --git a/panda/src/pnmimagetypes/pnmFileTypeYUV.h b/panda/src/pnmimagetypes/pnmFileTypeYUV.h new file mode 100644 index 0000000000..ae5051b7cb --- /dev/null +++ b/panda/src/pnmimagetypes/pnmFileTypeYUV.h @@ -0,0 +1,80 @@ +// Filename: pnmFileTypeYUV.h +// Created by: drose (17Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PNMFILETYPEYUV_H +#define PNMFILETYPEYUV_H + +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : PNMFileTypeYUV +// Description : For reading and Abekas YUV files. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PNMFileTypeYUV : public PNMFileType { +public: + PNMFileTypeYUV(); + + virtual string get_name() const; + + virtual int get_num_extensions() const; + virtual string get_extension(int n) const; + virtual string get_suggested_extension() const; + + virtual PNMReader *make_reader(FILE *file, bool owns_file = true, + const string &magic_number = string()); + virtual PNMWriter *make_writer(FILE *file, bool owns_file = true); + +public: + class Reader : public PNMReader { + public: + Reader(PNMFileType *type, FILE *file, bool owns_file, string magic_number); + virtual ~Reader(); + + virtual bool supports_read_row() const; + virtual bool read_row(xel *array, xelval *alpha); + + private: + unsigned char *yuvbuf; + }; + + class Writer : public PNMWriter { + public: + Writer(PNMFileType *type, FILE *file, bool owns_file); + virtual ~Writer(); + + virtual bool supports_write_row() const; + virtual bool write_header(); + virtual bool write_row(xel *array, xelval *alpha); + + private: + unsigned char *yuvbuf; + }; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PNMFileType::init_type(); + register_type(_type_handle, "PNMFileTypeYUV", + PNMFileType::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif + + diff --git a/panda/src/pnmimagetypes/resolu.c b/panda/src/pnmimagetypes/resolu.c new file mode 100644 index 0000000000..586a968af8 --- /dev/null +++ b/panda/src/pnmimagetypes/resolu.c @@ -0,0 +1,95 @@ +/* Copyright (c) 1991 Regents of the University of California */ + +#ifndef lint +static char SCCSid[] = "@(#)resolu.c 2.2 11/28/91 LBL"; +#endif + +/* + * Read and write image resolutions. + */ + +#include +#include + +#include "resolu.h" + + +char resolu_buf[RESOLU_BUFLEN]; /* resolution line buffer */ + +int str2resolu(register RESOLU *, char *); +char *resolu2str(char *, register RESOLU *); + +void +fputresolu(int ord, int sl, int ns, FILE *fp) /* put out picture dimensions */ +{ + RESOLU rs; + + if ((rs.or = ord) & YMAJOR) { + rs.xr = sl; + rs.yr = ns; + } else { + rs.xr = ns; + rs.yr = sl; + } + fputsresolu(&rs, fp); +} + + +int +fgetresolu(int *sl, int *ns, FILE *fp) /* get picture dimensions */ +{ + RESOLU rs; + + if (!fgetsresolu(&rs, fp)) + return(-1); + if (rs.or & YMAJOR) { + *sl = rs.xr; + *ns = rs.yr; + } else { + *sl = rs.yr; + *ns = rs.xr; + } + return(rs.or); +} + + +char * +resolu2str(char *buf, register RESOLU *rp) /* convert resolution struct to line */ +{ + if (rp->or&YMAJOR) + sprintf(buf, "%cY %d %cX %d\n", + rp->or&YDECR ? '-' : '+', rp->yr, + rp->or&XDECR ? '-' : '+', rp->xr); + else + sprintf(buf, "%cX %d %cY %d\n", + rp->or&XDECR ? '-' : '+', rp->xr, + rp->or&YDECR ? '-' : '+', rp->yr); + return(buf); +} + + +int str2resolu(register RESOLU *rp, char *buf) /* convert resolution line to struct */ +{ + register char *xndx, *yndx; + register char *cp; + + if (buf == NULL) + return(0); + xndx = yndx = NULL; + for (cp = buf; *cp; cp++) + if (*cp == 'X') + xndx = cp; + else if (*cp == 'Y') + yndx = cp; + if (xndx == NULL || yndx == NULL) + return(0); + rp->or = 0; + if (xndx > yndx) rp->or |= YMAJOR; + if (xndx[-1] == '-') rp->or |= XDECR; + if (yndx[-1] == '-') rp->or |= YDECR; + if ((rp->xr = atoi(xndx+1)) <= 0) + return(0); + if ((rp->yr = atoi(yndx+1)) <= 0) + return(0); + return(1); +} diff --git a/panda/src/pnmimagetypes/resolu.h b/panda/src/pnmimagetypes/resolu.h new file mode 100644 index 0000000000..f06e7425de --- /dev/null +++ b/panda/src/pnmimagetypes/resolu.h @@ -0,0 +1,50 @@ +/* Copyright (c) 1991 Regents of the University of California */ + +/* SCCSid "@(#)resolu.h 2.2 6/4/93 LBL" */ + +/* + * Definitions for resolution line in image file. + * + * True image orientation is defined by an xy coordinate system + * whose origin is at the lower left corner of the image, with + * x increasing to the right and y increasing in the upward direction. + * This true orientation is independent of how the pixels are actually + * ordered in the file, which is indicated by the resolution line. + * This line is of the form "{+-}{XY} xyres {+-}{YX} yxres\n". + * A typical line for a 1024x600 image might be "-Y 600 +X 1024\n", + * indicating that the scanlines are in English text order (PIXSTANDARD). + */ + + /* flags for scanline ordering */ +#define XDECR 1 +#define YDECR 2 +#define YMAJOR 4 + + /* standard scanline ordering */ +#define PIXSTANDARD (YMAJOR|YDECR) +#define PIXSTDFMT "-Y %d +X %d\n" + + /* structure for image dimensions */ +typedef struct { + int or; /* orientation (from flags above) */ + int xr, yr; /* x and y resolution */ +} RESOLU; + + /* macros to get scanline length and number */ +#define scanlen(rs) ((rs)->or & YMAJOR ? (rs)->xr : (rs)->yr) +#define numscans(rs) ((rs)->or & YMAJOR ? (rs)->yr : (rs)->xr) + + /* resolution string buffer and its size */ +#define RESOLU_BUFLEN 32 +extern char resolu_buf[RESOLU_BUFLEN]; + + /* macros for reading/writing resolution struct */ +#define fputsresolu(rs,fp) fputs(resolu2str(resolu_buf,rs),fp) +#define fgetsresolu(rs,fp) str2resolu(rs, \ + fgets(resolu_buf,RESOLU_BUFLEN,fp)) + + /* reading/writing of standard ordering */ +#define fprtresolu(sl,ns,fp) fprintf(fp,PIXSTDFMT,ns,sl) +#define fscnresolu(sl,ns,fp) (fscanf(fp,PIXSTDFMT,ns,sl)==2) + +extern char *resolu2str(); diff --git a/panda/src/pnmimagetypes/sgi.h b/panda/src/pnmimagetypes/sgi.h new file mode 100644 index 0000000000..6ba09d8483 --- /dev/null +++ b/panda/src/pnmimagetypes/sgi.h @@ -0,0 +1,29 @@ +/* sgi.h - header file for sgitopnm.c and pnmtosgi.c */ + +typedef struct { + short magic; + char storage; + char bpc; /* pixel size: 1 = bytes, 2 = shorts */ + unsigned short dimension; /* 1 = single row, 2 = B/W, 3 = RGB */ + unsigned short xsize, /* width in pixels */ + ysize, /* height in pixels */ + zsize; /* # of channels; B/W=1, RGB=3, RGBA=4 */ + long pixmin, pixmax; /* min/max pixel values */ + char dummy1[4]; + char name[80]; + long colormap; + char dummy2[404]; +} Header; +#define HeaderSize 512 + +#define SGI_MAGIC (short)474 + +#define STORAGE_VERBATIM 0 +#define STORAGE_RLE 1 + +#define CMAP_NORMAL 0 +#define CMAP_DITHERED 1 /* not supported */ +#define CMAP_SCREEN 2 /* not supported */ +#define CMAP_COLORMAP 3 /* not supported */ + + diff --git a/panda/src/ps2display/Sources.pp b/panda/src/ps2display/Sources.pp new file mode 100644 index 0000000000..a2066212db --- /dev/null +++ b/panda/src/ps2display/Sources.pp @@ -0,0 +1,14 @@ +#define DIRECTORY_IF_PS2 yes + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET ps2display + + #define SOURCES \ + config_ps2display.cxx ps2GraphicsPipe.cxx ps2GraphicsWindow.cxx + + #define INSTALL_HEADERS \ + +#end lib_target + diff --git a/panda/src/ps2display/config_ps2display.cxx b/panda/src/ps2display/config_ps2display.cxx new file mode 100644 index 0000000000..f5e6b64b90 --- /dev/null +++ b/panda/src/ps2display/config_ps2display.cxx @@ -0,0 +1,4 @@ +// Filename: config_ps2display.cxx +// Created by: charles (01Jun00) +// +//////////////////////////////////////////////////////////////////// diff --git a/panda/src/ps2display/ps2GraphicsPipe.cxx b/panda/src/ps2display/ps2GraphicsPipe.cxx new file mode 100644 index 0000000000..7d0d3fe083 --- /dev/null +++ b/panda/src/ps2display/ps2GraphicsPipe.cxx @@ -0,0 +1,4 @@ +// Filename: ps2GraphicsPipe.cxx +// Created by: charles (01Jun00) +// +//////////////////////////////////////////////////////////////////// diff --git a/panda/src/ps2display/ps2GraphicsWindow.cxx b/panda/src/ps2display/ps2GraphicsWindow.cxx new file mode 100644 index 0000000000..215436f89f --- /dev/null +++ b/panda/src/ps2display/ps2GraphicsWindow.cxx @@ -0,0 +1,4 @@ +// Filename: ps2GraphicsWindow.cxx +// Created by: charles (01Jun00) +// +//////////////////////////////////////////////////////////////////// diff --git a/panda/src/ps2gsg/Sources.pp b/panda/src/ps2gsg/Sources.pp new file mode 100644 index 0000000000..9b2c000ba7 --- /dev/null +++ b/panda/src/ps2gsg/Sources.pp @@ -0,0 +1,18 @@ +#define DIRECTORY_IF_PS2 yes + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET ps2gsg + #define LOCAL_LIBS \ + cull gsgmisc gsgbase gobj sgattrib sgraphutil graph display light \ + putil linmath sgraph mathutil pnmimage + + #define SOURCES \ + config_ps2gsg.cxx ps2GraphicsStateGuardian.cxx \ + ps2SavedFrameBuffer.cxx ps2TextureContext.cxx + + #define INSTALL_HEADERS \ + +#end lib_target + diff --git a/panda/src/ps2gsg/config_ps2gsg.cxx b/panda/src/ps2gsg/config_ps2gsg.cxx new file mode 100644 index 0000000000..a9f8429974 --- /dev/null +++ b/panda/src/ps2gsg/config_ps2gsg.cxx @@ -0,0 +1,4 @@ +// Filename: config_ps2gsg.cxx +// Created by: charles (01Jun00) +// +//////////////////////////////////////////////////////////////////// diff --git a/panda/src/ps2gsg/ps2GraphicsStateGuardian.cxx b/panda/src/ps2gsg/ps2GraphicsStateGuardian.cxx new file mode 100644 index 0000000000..780f0ef84d --- /dev/null +++ b/panda/src/ps2gsg/ps2GraphicsStateGuardian.cxx @@ -0,0 +1,4 @@ +// Filename: ps2GraphicsStateGuardian.cxx +// Created by: charles (01Jun00) +// +//////////////////////////////////////////////////////////////////// diff --git a/panda/src/ps2gsg/ps2SavedFrameBuffer.cxx b/panda/src/ps2gsg/ps2SavedFrameBuffer.cxx new file mode 100644 index 0000000000..4d28d2e26f --- /dev/null +++ b/panda/src/ps2gsg/ps2SavedFrameBuffer.cxx @@ -0,0 +1,4 @@ +// Filename: ps2SavedFrameBuffer.cxx +// Created by: charles (01Jun00) +// +//////////////////////////////////////////////////////////////////// diff --git a/panda/src/ps2gsg/ps2TextureContext.cxx b/panda/src/ps2gsg/ps2TextureContext.cxx new file mode 100644 index 0000000000..52fa2eec46 --- /dev/null +++ b/panda/src/ps2gsg/ps2TextureContext.cxx @@ -0,0 +1,4 @@ +// Filename: ps2TextureContext.cxx +// Created by: charles (01Jun00) +// +//////////////////////////////////////////////////////////////////// diff --git a/panda/src/pstatclient/Sources.pp b/panda/src/pstatclient/Sources.pp new file mode 100644 index 0000000000..2042b2f1ea --- /dev/null +++ b/panda/src/pstatclient/Sources.pp @@ -0,0 +1,34 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET pstatclient + #define LOCAL_LIBS \ + net linmath putil + + #define SOURCES \ + config_pstats.cxx config_pstats.h pStatClient.I pStatClient.cxx \ + pStatClient.h pStatClientControlMessage.cxx \ + pStatClientControlMessage.h pStatCollectorDef.cxx \ + pStatCollectorDef.h pStatFrameData.I pStatFrameData.cxx \ + pStatFrameData.h pStatServerControlMessage.cxx \ + pStatServerControlMessage.h + + #define INSTALL_HEADERS \ + config_pstats.h pStatClient.I pStatClient.h \ + pStatClientControlMessage.h pStatCollector.I pStatCollector.h \ + pStatCollectorDef.h pStatFrameData.I pStatFrameData.h \ + pStatServerControlMessage.h pStatThread.I pStatThread.h \ + pStatTimer.I pStatTimer.h + + #define IGATESCAN all + +#end lib_target + +#begin test_bin_target + #define TARGET test_client + + #define SOURCES \ + test_client.cxx + +#end test_bin_target + diff --git a/panda/src/pstatclient/config_pstats.cxx b/panda/src/pstatclient/config_pstats.cxx new file mode 100644 index 0000000000..4d4d73606c --- /dev/null +++ b/panda/src/pstatclient/config_pstats.cxx @@ -0,0 +1,31 @@ +// Filename: config_pstats.cxx +// Created by: drose (09Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_pstats.h" + +#include + +Configure(config_pstats); +NotifyCategoryDef(pstats, ""); + +ConfigureFn(config_pstats) { +} + +string get_pstats_name() { + return config_pstats.GetString("pstats-name", "Panda Stats"); +} + +double get_pstats_max_rate() { + return config_pstats.GetDouble("pstats-max-rate", 30.0); +} + +const string pstats_host = config_pstats.GetString("pstats-host", "localhost"); +const int pstats_port = config_pstats.GetInt("pstats-port", 5180); +const double pstats_target_frame_rate = config_pstats.GetDouble("pstats-target-frame-rate", 30.0); + +// The rest are different in that they directly control the server, +// not the client. +const bool pstats_scroll_mode = config_pstats.GetBool("pstats-scroll-mode", true); +const double pstats_history = config_pstats.GetDouble("pstats-history", 30.0); diff --git a/panda/src/pstatclient/config_pstats.h b/panda/src/pstatclient/config_pstats.h new file mode 100644 index 0000000000..79c4c51d2d --- /dev/null +++ b/panda/src/pstatclient/config_pstats.h @@ -0,0 +1,26 @@ +// Filename: config_pstats.h +// Created by: drose (09Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_PSTATS_H +#define CONFIG_PSTATS_H + +#include + +#include + +// Configure variables for pstats package. + +NotifyCategoryDecl(pstats, EXPCL_PANDA, EXPTP_PANDA); + +extern EXPCL_PANDA string get_pstats_name(); +extern EXPCL_PANDA double get_pstats_max_rate(); +extern EXPCL_PANDA const string pstats_host; +extern EXPCL_PANDA const int pstats_port; +extern EXPCL_PANDA const double pstats_target_frame_rate; + +extern EXPCL_PANDA const bool pstats_scroll_mode; +extern EXPCL_PANDA const double pstats_history; + +#endif diff --git a/panda/src/pstatclient/pStatClient.I b/panda/src/pstatclient/pStatClient.I new file mode 100644 index 0000000000..9a0a97ce52 --- /dev/null +++ b/panda/src/pstatclient/pStatClient.I @@ -0,0 +1,59 @@ +// Filename: pStatClient.I +// Created by: drose (16Jul00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::set_client_name +// Access: Public +// Description: Sets the name of the client. This is reported to the +// PStatsServer, and will presumably be written in the +// title bar or something. +//////////////////////////////////////////////////////////////////// +INLINE void PStatClient:: +set_client_name(const string &name) { + _client_name = name; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::get_client_name +// Access: Public +// Description: Retrieves the name of the client as set. +//////////////////////////////////////////////////////////////////// +INLINE string PStatClient:: +get_client_name() const { + return _client_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::set_max_rate +// Access: Public +// Description: Controls the number of packets that will be sent to +// the server. Normally, one packet is sent per frame, +// but this can flood the server with more packets than +// it can handle if the frame rate is especially good +// (e.g. if nothing is onscreen at the moment). Set +// this parameter to a reasonable number to prevent this +// from happening. +// +// This number specifies the maximum number of packets +// that will be sent to the server per second, per +// thread. +//////////////////////////////////////////////////////////////////// +INLINE void PStatClient:: +set_max_rate(double rate) { + _max_rate = rate; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::get_max_rate +// Access: Public +// Description: Returns the maximum number of packets that will be +// sent to the server per second, per thread. See +// set_max_rate(). +//////////////////////////////////////////////////////////////////// +INLINE double PStatClient:: +get_max_rate() const { + return _max_rate; +} diff --git a/panda/src/pstatclient/pStatClient.cxx b/panda/src/pstatclient/pStatClient.cxx new file mode 100644 index 0000000000..d1a3870393 --- /dev/null +++ b/panda/src/pstatclient/pStatClient.cxx @@ -0,0 +1,755 @@ +// Filename: pStatClient.cxx +// Created by: drose (09Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "pStatClient.h" +#include "pStatClientControlMessage.h" +#include "pStatServerControlMessage.h" +#include "pStatCollector.h" +#include "pStatThread.h" +#include "config_pstats.h" + +#include + +#ifdef WIN32_VC +#define WINDOWS_LEAN_AND_MEAN +#include +#undef WINDOWS_LEAN_AND_MEAN +#endif + +PStatClient *PStatClient::_global_pstats = NULL; + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PStatClient:: +PStatClient() : + _reader(this, 0), + _writer(this, 0) +{ + _is_connected = false; + _got_udp_port = false; + _collectors_reported = 0; + _threads_reported = 0; + + // We always have a collector at index 0 named "Frame". This tracks + // the total frame time and is the root of all other collectors. We + // have to make this one by hand since it's the root. + Collector collector; + collector._def = new PStatCollectorDef(0, "Frame"); + collector._def->_parent_index = 0; + collector._def->_suggested_color.set(0.5, 0.5, 0.5); + _collectors.push_back(collector); + + // We also always have a thread at index 0 named "Main". + make_thread("Main"); + + _client_name = get_pstats_name(); + _max_rate = get_pstats_max_rate(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PStatClient:: +~PStatClient() { + disconnect(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::get_num_collectors +// Access: Public +// Description: Returns the total number of collectors the Client +// knows about. +//////////////////////////////////////////////////////////////////// +int PStatClient:: +get_num_collectors() const { + return _collectors.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::get_collector +// Access: Public +// Description: Returns the nth collector. +//////////////////////////////////////////////////////////////////// +PStatCollector PStatClient:: +get_collector(int index) const { + nassertr(index >= 0 && index < (int)_collectors.size(), PStatCollector()); + return PStatCollector((PStatClient *)this, index); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::get_collector_def +// Access: Public +// Description: Returns the definition body of the nth collector. +//////////////////////////////////////////////////////////////////// +const PStatCollectorDef &PStatClient:: +get_collector_def(int index) const { +#ifndef NDEBUG + static PStatCollectorDef bogus; + nassertr(index >= 0 && index < (int)_collectors.size(), bogus); +#endif + + return *_collectors[index]._def; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::get_collector_name +// Access: Public +// Description: Returns the name of the indicated collector. +//////////////////////////////////////////////////////////////////// +string PStatClient:: +get_collector_name(int index) const { + nassertr(index >= 0 && index < (int)_collectors.size(), string()); + + const PStatCollectorDef *def = _collectors[index]._def; + return def->_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::get_collector_fullname +// Access: Public +// Description: Returns the "full name" of the indicated collector. +// This will be the concatenation of all of the +// collector's parents' names (except Frame) and the +// collector's own name. +//////////////////////////////////////////////////////////////////// +string PStatClient:: +get_collector_fullname(int index) const { + nassertr(index >= 0 && index < (int)_collectors.size(), string()); + + const PStatCollectorDef *def = _collectors[index]._def; + if (def->_parent_index == 0) { + return def->_name; + } else { + return get_collector_fullname(def->_parent_index) + ":" + def->_name; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::get_num_threads +// Access: Public +// Description: Returns the total number of threads the Client +// knows about. +//////////////////////////////////////////////////////////////////// +int PStatClient:: +get_num_threads() const { + return _threads.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::get_thread +// Access: Public +// Description: Returns the nth thread. +//////////////////////////////////////////////////////////////////// +PStatThread PStatClient:: +get_thread(int index) const { + nassertr(index >= 0 && index < (int)_threads.size(), PStatThread()); + return PStatThread((PStatClient *)this, index); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::get_thread_name +// Access: Public +// Description: Returns the name of the indicated thread. +//////////////////////////////////////////////////////////////////// +string PStatClient:: +get_thread_name(int index) const { + nassertr(index >= 0 && index < (int)_threads.size(), string()); + return _threads[index]._name; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::connect +// Access: Public +// Description: Attempts to establish a connection to the indicated +// PStatServer. Returns true if successful, false on +// failure. +//////////////////////////////////////////////////////////////////// +bool PStatClient:: +connect(string hostname, int port) { + disconnect(); + + if (hostname.empty()) { + hostname = pstats_host; + } + if (port < 0) { + port = pstats_port; + } + + if (!_server.set_host(hostname, port)) { + pstats_cat.error() + << "Unknown host: " << hostname << "\n"; + return false; + } + + _tcp_connection = open_TCP_client_connection(_server, 5000); + + if (_tcp_connection.is_null()) { + pstats_cat.error() + << "Couldn't connect to PStatServer at " << hostname << ":" + << port << "\n"; + return false; + } + + _reader.add_connection(_tcp_connection); + _is_connected = true; + + _udp_connection = open_UDP_connection(); + + send_hello(); + + return _is_connected; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::disconnect +// Access: Public +// Description: Closes the connection previously established. +//////////////////////////////////////////////////////////////////// +void PStatClient:: +disconnect() { + if (_is_connected) { + _reader.remove_connection(_tcp_connection); + close_connection(_tcp_connection); + close_connection(_udp_connection); + } + + _tcp_connection.clear(); + _udp_connection.clear(); + + _is_connected = false; + _got_udp_port = false; + + _collectors_reported = 0; + _threads_reported = 0; + + Threads::iterator ti; + for (ti = _threads.begin(); ti != _threads.end(); ++ti) { + (*ti)._frame_number = 0; + (*ti)._is_active = false; + (*ti)._last_packet = 0.0; + (*ti)._frame_data.clear(); + } + + Collectors::iterator ci; + for (ci = _collectors.begin(); ci != _collectors.end(); ++ci) { + vector_int::iterator ii; + for (ii = (*ci)._nested_count.begin(); + ii != (*ci)._nested_count.end(); + ++ii) { + (*ii) = 0; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::is_connected +// Access: Public +// Description: Returns true if the client believes it is connected +// to a working PStatServer, false otherwise. +//////////////////////////////////////////////////////////////////// +bool PStatClient:: +is_connected() const { + return _is_connected; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::get_clock +// Access: Public +// Description: Returns a reference to the PStatClient's clock +// object. It keeps its own clock, instead of using the +// global clock object, so the stats won't get mucked up +// if you put the global clock in non-real-time mode or +// something. +//////////////////////////////////////////////////////////////////// +const ClockObject &PStatClient:: +get_clock() const { + return _clock; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::get_main_thread +// Access: Public +// Description: Returns a handle to the client's "Main", or default, +// thread. This is where collectors will be started and +// stopped if they don't specify otherwise. +//////////////////////////////////////////////////////////////////// +PStatThread PStatClient:: +get_main_thread() const { + return PStatThread((PStatClient *)this, 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::make_collector +// Access: Private +// Description: Returns a PStatCollector suitable for measuring +// categories with the indicated name. This is normally +// called by a PStatCollector constructor. +//////////////////////////////////////////////////////////////////// +PStatCollector PStatClient:: +make_collector(int parent_index, const string &fullname) { + // Skip any colons at the beginning of the name. + size_t start = 0; + while (start < fullname.size() && fullname[start] == ':') { + start++; + } + + // If the name contains a colon (after the initial colon), it means + // we are making a nested collector. + size_t colon = fullname.find(':', start); + if (colon != string::npos) { + string parent_name = fullname.substr(start, colon - start); + PStatCollector parent_collector = + make_collector(parent_index, parent_name); + return make_collector(parent_collector._index, fullname.substr(colon + 1)); + } + + string name = fullname.substr(start); + + nassertr(parent_index >= 0 && parent_index < _collectors.size(), + PStatCollector()); + + Collector &parent = _collectors[parent_index]; + + // A special case: if we asked for a child the same name as its + // parent, we really meant the parent. That is, "Frame:Frame" is + // really the same collector as "Frame". + if (parent._def->_name == name) { + return PStatCollector(this, parent_index); + } + + ThingsByName::const_iterator ni = parent._children.find(name); + + if (ni != parent._children.end()) { + // We already had a collector by this name; return it. + int index = (*ni).second; + nassertr(index >= 0 && index < _collectors.size(), PStatCollector()); + return PStatCollector(this, (*ni).second); + } + + // Create a new collector for this name. + int new_index = _collectors.size(); + parent._children.insert(ThingsByName::value_type(name, new_index)); + + Collector collector; + collector._def = new PStatCollectorDef(new_index, name); + collector._def->_parent_index = parent_index; + + // We need one nested_count for each thread. + while (collector._nested_count.size() < _threads.size()) { + collector._nested_count.push_back(0); + } + + _collectors.push_back(collector); + + return PStatCollector(this, new_index); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::make_collector +// Access: Private +// Description: This flavor of make_collector will make a new +// collector and automatically set up some of its +// properties. +//////////////////////////////////////////////////////////////////// +PStatCollector PStatClient:: +make_collector(int parent_index, const string &fullname, + const RGBColorf &suggested_color, int sort) { + PStatCollector c = make_collector(parent_index, fullname); + nassertr(c._client == this, PStatCollector()); + nassertr(c._index >= 0 && c._index < _collectors.size(), PStatCollector()); + + PStatCollectorDef *def = _collectors[c._index]._def; + nassertr(def != (PStatCollectorDef *)NULL, PStatCollector()); + + if (suggested_color != RGBColorf::zero() && + def->_suggested_color != suggested_color) { + // We need to change the suggested color. + def->_suggested_color = suggested_color; + _collectors_reported = min(_collectors_reported, c._index); + } + + if (sort != -1 && def->_sort != sort) { + // We need to change the sort. + def->_sort = sort; + _collectors_reported = min(_collectors_reported, c._index); + } + + return c; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::make_thread +// Access: Private +// Description: Returns a PStatThread with the indicated name +// suitable for grouping collectors. This is normally +// called by a PStatThread constructor. +//////////////////////////////////////////////////////////////////// +PStatThread PStatClient:: +make_thread(const string &name) { + ThingsByName::const_iterator ni = + _threads_by_name.find(name); + + if (ni != _threads_by_name.end()) { + // We already had a thread by this name; return it. + int index = (*ni).second; + nassertr(index >= 0 && index < _threads.size(), PStatThread()); + return PStatThread(this, (*ni).second); + } + + // Create a new thread for this name. + int new_index = _threads.size(); + _threads_by_name.insert(ThingsByName::value_type(name, new_index)); + + Thread thread; + thread._name = name; + thread._is_active = false; + thread._last_packet = 0.0; + thread._frame_number = 0; + + _threads.push_back(thread); + + // We need an additional nested_count for this thread in all of the + // collectors. + Collectors::iterator ci; + for (ci = _collectors.begin(); ci != _collectors.end(); ++ci) { + (*ci)._nested_count.push_back(0); + nassertr((*ci)._nested_count.size() == _threads.size(), PStatThread()); + } + + return PStatThread(this, new_index); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::get_global_pstats +// Access: Public, Static +// Description: Returns a pointer to the global PStatClient object. +// It's legal to declare your own PStatClient locally, +// but it's also convenient to have a global one that +// everyone can register with. This is the global one. +//////////////////////////////////////////////////////////////////// +PStatClient *PStatClient:: +get_global_pstats() { + if (_global_pstats == (PStatClient *)NULL) { + _global_pstats = new PStatClient; + } + return _global_pstats; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::main_tick +// Access: Public, Static +// Description: A convenience function to call new_frame() on the +// global PStatClient's main thread. +//////////////////////////////////////////////////////////////////// +void PStatClient:: +main_tick() { + get_global_pstats()->get_main_thread().new_frame(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::start_collector +// Access: Private +// Description: Marks the indicated collector index as started. +// Normally you would not use this interface directly; +// instead, call pStatCollector::start(). +//////////////////////////////////////////////////////////////////// +void PStatClient:: +start(int collector_index, int thread_index, double as_of) { + nassertv(collector_index >= 0 && collector_index < (int)_collectors.size()); + nassertv(thread_index >= 0 && thread_index < (int)_threads.size()); + + if (_threads[thread_index]._is_active) { + if (_collectors[collector_index]._nested_count[thread_index] == 0) { + // This collector wasn't already started in this thread; record + // a new data point. + _threads[thread_index]._frame_data.add_start(collector_index, as_of); + } + _collectors[collector_index]._nested_count[thread_index]++; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::start_collector +// Access: Private +// Description: Marks the indicated collector index as stopped. +// Normally you would not use this interface directly; +// instead, call pStatCollector::stop(). +//////////////////////////////////////////////////////////////////// +void PStatClient:: +stop(int collector_index, int thread_index, double as_of) { + nassertv(collector_index >= 0 && collector_index < _collectors.size()); + nassertv(thread_index >= 0 && thread_index < (int)_threads.size()); + + if (_threads[thread_index]._is_active) { + if (_collectors[collector_index]._nested_count[thread_index] == 0) { + pstats_cat.warning() + << "Collector " << get_collector_fullname(collector_index) + << " was already stopped in thread " << get_thread_name(thread_index) + << "!\n"; + return; + } + + _collectors[collector_index]._nested_count[thread_index]--; + + if (_collectors[collector_index]._nested_count[thread_index] == 0) { + // This collector has now been completely stopped; record a new + // data point. + _threads[thread_index]._frame_data.add_stop(collector_index, as_of); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::new_frame +// Access: Private +// Description: Called by the PStatThread interface at the beginning +// of every frame, for each thread. This resets the +// clocks for the new frame and transmits the data for +// the previous frame. +//////////////////////////////////////////////////////////////////// +void PStatClient:: +new_frame(int thread_index) { + nassertv(thread_index >= 0 && thread_index < (int)_threads.size()); + + Thread &thread = _threads[thread_index]; + + // If we're the main thread, we should exchange control packets with + // the server. + if (thread_index == 0) { + transmit_control_data(); + } + + // If we've got the UDP port by the time the frame starts, it's + // time to become active and start actually tracking data. + if (_got_udp_port) { + thread._is_active = true; + } + + if (!thread._is_active) { + return; + } + + double frame_start = _clock.get_real_time(); + + if (!thread._frame_data.is_empty()) { + // Collector 0 is the whole frame. + stop(0, thread_index, frame_start); + + /* + We don't need to do this, since the stats server will do it. + Why should we waste our time? + + // Make sure all of our Collectors have turned themselves off now + // at the end of the frame. + Collectors::iterator ci; + for (ci = _collectors.begin(); ci != _collectors.end(); ++ci) { + if ((*ci)._nested_count > 0) { + pstats_cat.warning() + << "Collector " << get_collector_fullname((*ci)._def->_index) + << " wasn't stopped!\n"; + (*ci)._nested_count = 0; + } + } */ + + transmit_frame_data(thread_index); + } + + thread._frame_data.clear(); + thread._frame_number++; + start(0, thread_index, frame_start); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::transmit_frame_data +// Access: Private +// Description: Should be called once per frame per thread to +// transmit the latest data to the PStatServer. +//////////////////////////////////////////////////////////////////// +void PStatClient:: +transmit_frame_data(int thread_index) { + nassertv(thread_index >= 0 && thread_index < (int)_threads.size()); + if (_is_connected && _threads[thread_index]._is_active) { + + // We don't want to send too many packets in a hurry and flood the + // server. In fact, we don't want to send more than + // _max_rate packets per second, per thread. + double min_packet_delay = 1.0 / _max_rate; + double now = _clock.get_real_time(); + + if (now - _threads[thread_index]._last_packet > min_packet_delay) { + nassertv(_got_udp_port); + + // Send new data. + NetDatagram datagram; + datagram.add_uint16(thread_index); + datagram.add_uint32(_threads[thread_index]._frame_number); + _threads[thread_index]._frame_data.write_datagram(datagram); + _writer.send(datagram, _udp_connection, _server); + _threads[thread_index]._last_packet = now; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::transmit_control_data +// Access: Private +// Description: Should be called once a frame to exchange control +// information with the server. +//////////////////////////////////////////////////////////////////// +void PStatClient:: +transmit_control_data() { + // Check for new messages from the server. + while (_is_connected && _reader.data_available()) { + NetDatagram datagram; + + if (_reader.get_data(datagram)) { + PStatServerControlMessage message; + if (message.decode(datagram)) { + handle_server_control_message(message); + + } else { + pstats_cat.error() + << "Got unexpected message from server.\n"; + } + } + } + + if (_is_connected) { + report_new_collectors(); + report_new_threads(); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::get_hostname +// Access: Private +// Description: Returns the current machine's hostname. +//////////////////////////////////////////////////////////////////// +string PStatClient:: +get_hostname() { + if (_hostname.empty()) { + char temp_buff[1024]; + if (gethostname(temp_buff, 1024) == 0) { + _hostname = temp_buff; + } else { + _hostname = "unknown"; + } + } + return _hostname; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::send_hello +// Access: Private +// Description: Sends the initial greeting message to the server. +//////////////////////////////////////////////////////////////////// +void PStatClient:: +send_hello() { + nassertv(_is_connected); + + PStatClientControlMessage message; + message._type = PStatClientControlMessage::T_hello; + message._client_hostname = get_hostname(); + message._client_progname = _client_name; + + Datagram datagram; + message.encode(datagram); + _writer.send(datagram, _tcp_connection); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::report_new_collectors +// Access: Private +// Description: Sends over any information about new Collectors that +// the user code might have recently created. +//////////////////////////////////////////////////////////////////// +void PStatClient:: +report_new_collectors() { + nassertv(_is_connected); + + if (_collectors_reported < (int)_collectors.size()) { + PStatClientControlMessage message; + message._type = PStatClientControlMessage::T_define_collectors; + while (_collectors_reported < (int)_collectors.size()) { + message._collectors.push_back(_collectors[_collectors_reported]._def); + _collectors_reported++; + } + + Datagram datagram; + message.encode(datagram); + _writer.send(datagram, _tcp_connection); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::report_new_threads +// Access: Private +// Description: Sends over any information about new Threads that +// the user code might have recently created. +//////////////////////////////////////////////////////////////////// +void PStatClient:: +report_new_threads() { + nassertv(_is_connected); + + if (_threads_reported < (int)_threads.size()) { + PStatClientControlMessage message; + message._type = PStatClientControlMessage::T_define_threads; + message._first_thread_index = _threads_reported; + while (_threads_reported < (int)_threads.size()) { + message._names.push_back(_threads[_threads_reported]._name); + _threads_reported++; + } + + Datagram datagram; + message.encode(datagram); + _writer.send(datagram, _tcp_connection); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::handle_server_control_message +// Access: Private +// Description: Called when a control message has been received by +// the server over the TCP connection. +//////////////////////////////////////////////////////////////////// +void PStatClient:: +handle_server_control_message(const PStatServerControlMessage &message) { + switch (message._type) { + case PStatServerControlMessage::T_hello: + pstats_cat.info() + << "Connected to " << message._server_progname << " on " + << message._server_hostname << "\n"; + + _server.set_port(message._udp_port); + _got_udp_port = true; + break; + + default: + pstats_cat.error() + << "Invalid control message received from server.\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::connection_reset +// Access: Private, Virtual +// Description: Called by the internal net code when the connection +// has been lost. +//////////////////////////////////////////////////////////////////// +void PStatClient:: +connection_reset(const PT(Connection) &connection) { + if (connection == _tcp_connection) { + disconnect(); + } else { + pstats_cat.warning() + << "Ignoring spurious connection_reset() message\n"; + } +} diff --git a/panda/src/pstatclient/pStatClient.h b/panda/src/pstatclient/pStatClient.h new file mode 100644 index 0000000000..c26a2db4ed --- /dev/null +++ b/panda/src/pstatclient/pStatClient.h @@ -0,0 +1,136 @@ +// Filename: pStatClient.h +// Created by: drose (09Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PSTATCLIENT_H +#define PSTATCLIENT_H + +#include + +#include "pStatFrameData.h" + +#include +#include +#include +#include +#include +#include +#include + +class PStatServerControlMessage; +class PStatCollector; +class PStatCollectorDef; +class PStatThread; + +//////////////////////////////////////////////////////////////////// +// Class : PStatClient +// Description : Manages the communications to report statistics via a +// network connection to a remote PStatServer. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PStatClient : public ConnectionManager { +public: + PStatClient(); + ~PStatClient(); + + INLINE void set_client_name(const string &name); + INLINE string get_client_name() const; + INLINE void set_max_rate(double rate); + INLINE double get_max_rate() const; + + int get_num_collectors() const; + PStatCollector get_collector(int index) const; + const PStatCollectorDef &get_collector_def(int index) const; + string get_collector_name(int index) const; + string get_collector_fullname(int index) const; + + int get_num_threads() const; + PStatThread get_thread(int index) const; + string get_thread_name(int index) const; + + bool connect(string hostname = string(), int port = -1); + void disconnect(); + + bool is_connected() const; + const ClockObject &get_clock() const; + PStatThread get_main_thread() const; + + static PStatClient *get_global_pstats(); + static void main_tick(); + +private: + PStatCollector make_collector(int parent_index, const string &fullname); + PStatCollector make_collector(int parent_index, const string &fullname, + const RGBColorf &suggested_color, int sort); + PStatThread make_thread(const string &name); + + void start(int collector_index, int thread_index, double as_of); + void stop(int collector_index, int thread_index, double as_of); + + void new_frame(int thread_index); + void transmit_frame_data(int thread_index); + + void transmit_control_data(); + + // Stats collecting stuff + ClockObject _clock; + + typedef map ThingsByName; + ThingsByName _threads_by_name; + + class Collector { + public: + PStatCollectorDef *_def; + vector_int _nested_count; + ThingsByName _children; + }; + typedef vector Collectors; + Collectors _collectors; + + class Thread { + public: + string _name; + PStatFrameData _frame_data; + bool _is_active; + int _frame_number; + double _last_packet; + }; + + typedef vector Threads; + Threads _threads; + +private: + // Networking stuff + string get_hostname(); + void send_hello(); + void report_new_collectors(); + void report_new_threads(); + void handle_server_control_message(const PStatServerControlMessage &message); + + virtual void connection_reset(const PT(Connection) &connection); + + bool _is_connected; + bool _got_udp_port; + + NetAddress _server; + QueuedConnectionReader _reader; + ConnectionWriter _writer; + + PT(Connection) _tcp_connection; + PT(Connection) _udp_connection; + + int _collectors_reported; + int _threads_reported; + + string _hostname; + string _client_name; + double _max_rate; + + static PStatClient *_global_pstats; + friend class PStatCollector; + friend class PStatThread; +}; + +#include "pStatClient.I" + +#endif diff --git a/panda/src/pstatclient/pStatClientControlMessage.cxx b/panda/src/pstatclient/pStatClientControlMessage.cxx new file mode 100644 index 0000000000..b377cbc535 --- /dev/null +++ b/panda/src/pstatclient/pStatClientControlMessage.cxx @@ -0,0 +1,110 @@ +// Filename: pStatClientControlMessage.cxx +// Created by: drose (09Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_pstats.h" +#include "pStatClientControlMessage.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: PStatClientControlMessage::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PStatClientControlMessage:: +PStatClientControlMessage() { + _type = T_invalid; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClientControlMessage::encode +// Access: Public +// Description: Writes the message into the indicated datagram. +//////////////////////////////////////////////////////////////////// +void PStatClientControlMessage:: +encode(Datagram &datagram) const { + datagram.clear(); + datagram.add_uint8(_type); + switch (_type) { + case T_hello: + datagram.add_string(_client_hostname); + datagram.add_string(_client_progname); + break; + + case T_define_collectors: + { + datagram.add_uint16(_collectors.size()); + for (int i = 0; i < (int)_collectors.size(); i++) { + _collectors[i]->write_datagram(datagram); + } + } + break; + + case T_define_threads: + { + datagram.add_uint16(_first_thread_index); + datagram.add_uint16(_names.size()); + for (int i = 0; i < (int)_names.size(); i++) { + datagram.add_string(_names[i]); + } + } + break; + + default: + pstats_cat.error() + << "Invalid PStatClientControlMessage::Type " << (int)_type << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClientControlMessage::decode +// Access: Public +// Description: Extracts the message from the indicated datagram. +// Returns true on success, false on error. +//////////////////////////////////////////////////////////////////// +bool PStatClientControlMessage:: +decode(const Datagram &datagram) { + DatagramIterator source(datagram); + _type = (Type)source.get_uint8(); + + switch (_type) { + case T_hello: + _client_hostname = source.get_string(); + _client_progname = source.get_string(); + break; + + case T_define_collectors: + { + int num = source.get_uint16(); + _collectors.clear(); + for (int i = 0; i < num; i++) { + PStatCollectorDef *def = new PStatCollectorDef; + def->read_datagram(source); + _collectors.push_back(def); + } + } + break; + + case T_define_threads: + { + _first_thread_index = source.get_uint16(); + int num = source.get_uint16(); + _names.clear(); + for (int i = 0; i < num; i++) { + _names.push_back(source.get_string()); + } + } + break; + + default: + pstats_cat.error() + << "Read invalid PStatClientControlMessage type: " << (int)_type << "\n"; + _type = T_invalid; + return false; + } + + return true; +} diff --git a/panda/src/pstatclient/pStatClientControlMessage.h b/panda/src/pstatclient/pStatClientControlMessage.h new file mode 100644 index 0000000000..a208910068 --- /dev/null +++ b/panda/src/pstatclient/pStatClientControlMessage.h @@ -0,0 +1,53 @@ +// Filename: pStatClientControlMessage.h +// Created by: drose (09Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PSTATCLIENTCONTROLMESSAGE_H +#define PSTATCLIENTCONTROLMESSAGE_H + +#include + +#include "pStatCollectorDef.h" + +#include + +class Datagram; + +//////////////////////////////////////////////////////////////////// +// Class : PStatClientControlMessage +// Description : This kind of message is sent from the client to the +// server on the TCP socket to establish critical +// control information. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PStatClientControlMessage { +public: + PStatClientControlMessage(); + + void encode(Datagram &datagram) const; + bool decode(const Datagram &datagram); + + enum Type { + T_invalid, + T_hello, + T_define_collectors, + T_define_threads, + }; + + Type _type; + + // Used for T_hello + string _client_hostname; + string _client_progname; + + // Used for T_define_collectors + vector _collectors; + + // Used for T_define_threads + int _first_thread_index; + vector _names; +}; + + +#endif + diff --git a/panda/src/pstatclient/pStatCollector.I b/panda/src/pstatclient/pStatCollector.I new file mode 100644 index 0000000000..abc06c24c0 --- /dev/null +++ b/panda/src/pstatclient/pStatCollector.I @@ -0,0 +1,185 @@ +// Filename: pStatCollector.I +// Created by: drose (10Jul00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PStatCollector::Default Constructor +// Access: Private +// Description: Normally, this constructor is called only from +// PStatClient. Use one of the constructors below to +// create your own Collector. +//////////////////////////////////////////////////////////////////// +INLINE PStatCollector:: +PStatCollector() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatCollector::Constructor +// Access: Private +// Description: Normally, this constructor is called only from +// PStatClient. Use one of the constructors below to +// create your own Collector. +//////////////////////////////////////////////////////////////////// +INLINE PStatCollector:: +PStatCollector(PStatClient *client, int index) : + _client(client), + _index(index) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatCollector::Constructor +// Access: Public +// Description: Creates a new PStatCollector, ready to start +// accumulating data. The name of the collector +// uniquely identifies it among the other collectors; if +// two collectors share the same name then they are +// really the same collector. +// +// The name may also be a compound name, something like +// "Cull:Sort", which indicates that this is a collector +// named "Sort", a child of the collector named "Cull". +// The parent may also be named explicitly by reference +// in the other flavor of the constructor; see further +// comments on this for that constructor. +// +// If the client pointer is non-null, it specifies a +// particular client to register the collector with; +// otherwise, the global client is used. +//////////////////////////////////////////////////////////////////// +INLINE PStatCollector:: +PStatCollector(const string &name, const RGBColorf &suggested_color, + int sort, PStatClient *client) { + if (client == (PStatClient *)NULL) { + client = PStatClient::get_global_pstats(); + } + PStatCollector collector(client->make_collector(0, name, suggested_color, sort)); + (*this) = collector; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatCollector::Constructor +// Access: Public +// Description: Creates a new PStatCollector, ready to start +// accumulating data. The name of the collector +// uniquely identifies it among the other collectors; if +// two collectors share the same name then they are +// really the same collector. +// +// The parent is the collector that conceptually +// includes all of the time measured for this collector. +// For instance, a particular character's animation time +// is owned by the "Animation" collector, which is in +// turn owned by the "Frame" collector. It is not +// strictly necessary that all of the time spent in a +// particular collector is completely nested within time +// spent in its parent's collector. If parent is the +// empty string, the collector is owned by "Frame". +// +// This constructor does not take a client pointer; it +// always creates the new collector on the same client +// as its parent. +//////////////////////////////////////////////////////////////////// +INLINE PStatCollector:: +PStatCollector(const PStatCollector &parent, const string &name, + const RGBColorf &suggested_color, int sort) { + nassertv(parent._client != (PStatClient *)NULL); + PStatCollector collector(parent._client->make_collector + (parent._index, name, suggested_color, sort)); + (*this) = collector; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatCollector::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PStatCollector:: +PStatCollector(const PStatCollector ©) : + _client(copy._client), + _index(copy._index) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatCollector::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PStatCollector:: +operator = (const PStatCollector ©) { + _client = copy._client; + _index = copy._index; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatCollector::start +// Access: Public +// Description: Starts this particular timer ticking. This should be +// called before the code you want to measure. +//////////////////////////////////////////////////////////////////// +INLINE void PStatCollector:: +start() { + _client->start(_index, 0, _client->_clock.get_real_time()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatCollector::start +// Access: Public +// Description: Starts this timer ticking within a particular thread. +//////////////////////////////////////////////////////////////////// +INLINE void PStatCollector:: +start(const PStatThread &thread) { + _client->start(_index, thread._index, _client->_clock.get_real_time()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatCollector::start +// Access: Public +// Description: Marks that the timer should have been started as of +// the indicated time. This must be a time based on the +// PStatClient's clock (see PStatClient::get_clock()), +// and care should be taken that all such calls exhibit +// a monotonically increasing series of time values. +//////////////////////////////////////////////////////////////////// +INLINE void PStatCollector:: +start(const PStatThread &thread, double as_of) { + _client->start(_index, thread._index, as_of); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatCollector::stop +// Access: Public +// Description: Stops this timer. This should be called after the +// code you want to measure. +//////////////////////////////////////////////////////////////////// +INLINE void PStatCollector:: +stop() { + _client->stop(_index, 0, _client->_clock.get_real_time()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatCollector::stop +// Access: Public +// Description: Stops this timer within a particular thread. +//////////////////////////////////////////////////////////////////// +INLINE void PStatCollector:: +stop(const PStatThread &thread) { + _client->stop(_index, thread._index, _client->_clock.get_real_time()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatCollector::stop +// Access: Public +// Description: Marks that the timer should have been stopped as of +// the indicated time. This must be a time based on the +// PStatClient's clock (see PStatClient::get_clock()), +// and care should be taken that all such calls exhibit +// a monotonically increasing series of time values. +//////////////////////////////////////////////////////////////////// +INLINE void PStatCollector:: +stop(const PStatThread &thread, double as_of) { + _client->stop(_index, thread._index, as_of); +} diff --git a/panda/src/pstatclient/pStatCollector.h b/panda/src/pstatclient/pStatCollector.h new file mode 100644 index 0000000000..2b5c809a94 --- /dev/null +++ b/panda/src/pstatclient/pStatCollector.h @@ -0,0 +1,58 @@ +// Filename: pStatCollector.h +// Created by: drose (10Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PSTATCOLLECTOR_H +#define PSTATCOLLECTOR_H + +#include + +#include "pStatThread.h" +#include "pStatClient.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PStatCollector +// Description : A lightweight class that represents a single element +// that may be timed via stats. Bracket the code to be +// timed with calls to start() and stop(). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PStatCollector { +private: + INLINE PStatCollector(); + INLINE PStatCollector(PStatClient *client, int index); + +public: + INLINE PStatCollector(const string &name, + const RGBColorf &suggested_color = RGBColorf::zero(), + int sort = -1, + PStatClient *client = NULL); + INLINE PStatCollector(const PStatCollector &parent, + const string &name, + const RGBColorf &suggested_color = RGBColorf::zero(), + int sort = -1); + + INLINE PStatCollector(const PStatCollector ©); + INLINE void operator = (const PStatCollector ©); + + INLINE void start(); + INLINE void start(const PStatThread &thread); + INLINE void start(const PStatThread &thread, double as_of); + + INLINE void stop(); + INLINE void stop(const PStatThread &thread); + INLINE void stop(const PStatThread &thread, double as_of); + +private: + PStatClient *_client; + int _index; + +friend class PStatClient; +}; + +#include "pStatCollector.I" + +#endif + diff --git a/panda/src/pstatclient/pStatCollectorDef.cxx b/panda/src/pstatclient/pStatCollectorDef.cxx new file mode 100644 index 0000000000..cd6af8caaf --- /dev/null +++ b/panda/src/pstatclient/pStatCollectorDef.cxx @@ -0,0 +1,71 @@ +// Filename: pStatCollectorDef.cxx +// Created by: drose (09Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "pStatCollectorDef.h" + +#include +#include + + +//////////////////////////////////////////////////////////////////// +// Function: PStatCollectorDef::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PStatCollectorDef:: +PStatCollectorDef() { + _index = 0; + _parent_index = 0; + _suggested_color.set(0.0, 0.0, 0.0); + _sort = -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatCollectorDef::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PStatCollectorDef:: +PStatCollectorDef(int index, const string &name) : + _index(index), + _name(name) +{ + _parent_index = 0; + _suggested_color.set(0.0, 0.0, 0.0); + _sort = -1; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PStatCollectorDef::write_datagram +// Access: Public +// Description: Writes the definition of the collectorDef to the +// datagram. +//////////////////////////////////////////////////////////////////// +void PStatCollectorDef:: +write_datagram(Datagram &destination) const { + destination.add_int16(_index); + destination.add_string(_name); + destination.add_int16(_parent_index); + _suggested_color.write_datagram(destination); + destination.add_int16(_sort); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatCollectorDef::read_datagram +// Access: Public +// Description: Extracts the collectorDef definition from the datagram. +//////////////////////////////////////////////////////////////////// +void PStatCollectorDef:: +read_datagram(DatagramIterator &source) { + _index = source.get_int16(); + _name = source.get_string(); + _parent_index = source.get_int16(); + _suggested_color.read_datagram(source); + + if (source.get_remaining_size() > 0) { + _sort = source.get_int16(); + } +} diff --git a/panda/src/pstatclient/pStatCollectorDef.h b/panda/src/pstatclient/pStatCollectorDef.h new file mode 100644 index 0000000000..b933b272c8 --- /dev/null +++ b/panda/src/pstatclient/pStatCollectorDef.h @@ -0,0 +1,38 @@ +// Filename: pStatCollectorDef.h +// Created by: drose (09Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PSTATCOLLECTORDEF_H +#define PSTATCOLLECTORDEF_H + +#include + +#include + +class Datagram; +class DatagramIterator; +class PStatClient; + +//////////////////////////////////////////////////////////////////// +// Class : PStatCollectorDef +// Description : Defines the details about the Collectors: the name, +// the suggested color, etc. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PStatCollectorDef { +public: + PStatCollectorDef(); + PStatCollectorDef(int index, const string &name); + + void write_datagram(Datagram &destination) const; + void read_datagram(DatagramIterator &source); + + int _index; + string _name; + int _parent_index; + RGBColorf _suggested_color; + int _sort; +}; + +#endif + diff --git a/panda/src/pstatclient/pStatFrameData.I b/panda/src/pstatclient/pStatFrameData.I new file mode 100644 index 0000000000..d726e0e528 --- /dev/null +++ b/panda/src/pstatclient/pStatFrameData.I @@ -0,0 +1,136 @@ +// Filename: pStatFrameData.I +// Created by: drose (10Jul00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PStatFrameData::is_empty +// Access: Public +// Description: Returns true if there are no data points in the frame +// data, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool PStatFrameData:: +is_empty() const { + return _data.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatFrameData::clear +// Access: Public +// Description: Removes all the data points from the frame data, in +// preparation for building up a new frame's worth. +//////////////////////////////////////////////////////////////////// +INLINE void PStatFrameData:: +clear() { + _data.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatFrameData::add_start +// Access: Public +// Description: Adds a 'start collector' data point to the frame +// data. +//////////////////////////////////////////////////////////////////// +INLINE void PStatFrameData:: +add_start(int index, double time) { + DataPoint dp; + dp._index = index; + dp._time = time; + _data.push_back(dp); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatFrameData::add_stop +// Access: Public +// Description: Adds a 'stop collector' data point to the frame +// data. +//////////////////////////////////////////////////////////////////// +INLINE void PStatFrameData:: +add_stop(int index, double time) { + DataPoint dp; + dp._index = index | 0x8000; + dp._time = time; + _data.push_back(dp); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatFrameData::get_start +// Access: Public +// Description: Returns the time of the first data point in the frame +// data. This will generally be the time of the start +// of the frame. +//////////////////////////////////////////////////////////////////// +INLINE double PStatFrameData:: +get_start() const { + nassertr(!is_empty(), 0.0); + + return _data.front()._time; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatFrameData::get_end +// Access: Public +// Description: Returns the time of the last data point in the frame +// data. This will generally be the time of the end +// of the frame. +//////////////////////////////////////////////////////////////////// +INLINE double PStatFrameData:: +get_end() const { + nassertr(!is_empty(), 0.0); + + return _data.back()._time; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatFrameData::get_net_time +// Access: Public +// Description: Returns the total time elapsed for the frame. +//////////////////////////////////////////////////////////////////// +INLINE double PStatFrameData:: +get_net_time() const { + nassertr(!is_empty(), 0.0); + + return _data.back()._time - _data.front()._time; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatFrameData::get_num_events +// Access: Public +// Description: Returns the number of individual events stored in the +// FrameData. +//////////////////////////////////////////////////////////////////// +INLINE int PStatFrameData:: +get_num_events() const { + return _data.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatFrameData::get_collector +// Access: Public +// Description: Returns the index of the collector associated with +// the nth event. If this is the first time the +// collector appears in the event list, it indicates the +// collector has started; if it is the second time, it +// indicates the collector has stopped. Similarly for +// repeated appearances of the same collector. +//////////////////////////////////////////////////////////////////// +INLINE int PStatFrameData:: +get_collector(int n) const { + nassertr(n >= 0 && n < (int)_data.size(), 0); + return _data[n]._index; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatFrameData::get_time +// Access: Public +// Description: Returns the timestamp of the nth event, in seconds +// elapsed since some undefined epoch (which is +// guaranteed to be shared among all events returned +// from a given client). +//////////////////////////////////////////////////////////////////// +INLINE double PStatFrameData:: +get_time(int n) const { + nassertr(n >= 0 && n < (int)_data.size(), 0); + return _data[n]._time; +} diff --git a/panda/src/pstatclient/pStatFrameData.cxx b/panda/src/pstatclient/pStatFrameData.cxx new file mode 100644 index 0000000000..fc60e602af --- /dev/null +++ b/panda/src/pstatclient/pStatFrameData.cxx @@ -0,0 +1,41 @@ +// Filename: pStatFrameData.cxx +// Created by: drose (10Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "pStatFrameData.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: PStatFrameData::write_datagram +// Access: Public +// Description: Writes the definition of the FrameData to the +// datagram. +//////////////////////////////////////////////////////////////////// +void PStatFrameData:: +write_datagram(Datagram &destination) const { + Data::const_iterator di; + for (di = _data.begin(); di != _data.end(); ++di) { + destination.add_uint16((*di)._index); + destination.add_float64((*di)._time); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatFrameData::read_datagram +// Access: Public +// Description: Extracts the FrameData definition from the datagram. +//////////////////////////////////////////////////////////////////// +void PStatFrameData:: +read_datagram(DatagramIterator &source) { + clear(); + while (source.get_remaining_size() > 0) { + DataPoint dp; + dp._index = source.get_uint16(); + dp._time = source.get_float64(); + _data.push_back(dp); + } +} + diff --git a/panda/src/pstatclient/pStatFrameData.h b/panda/src/pstatclient/pStatFrameData.h new file mode 100644 index 0000000000..b33bd5e62f --- /dev/null +++ b/panda/src/pstatclient/pStatFrameData.h @@ -0,0 +1,54 @@ +// Filename: pStatFrameData.h +// Created by: drose (10Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PSTATFRAMEDATA_H +#define PSTATFRAMEDATA_H + +#include + +#include + +#include + +class Datagram; +class DatagramIterator; + +//////////////////////////////////////////////////////////////////// +// Class : PStatFrameData +// Description : Defines the raw timing data for a single frame. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PStatFrameData { +public: + INLINE bool is_empty() const; + INLINE void clear(); + INLINE void add_start(int index, double time); + INLINE void add_stop(int index, double time); + + INLINE double get_start() const; + INLINE double get_end() const; + INLINE double get_net_time() const; + + INLINE int get_num_events() const; + INLINE int get_collector(int n) const; + INLINE double get_time(int n) const; + + void write_datagram(Datagram &destination) const; + void read_datagram(DatagramIterator &source); + +private: + class DataPoint { + public: + int _index; + double _time; + }; + typedef vector Data; + + Data _data; +}; + +#include "pStatFrameData.I" + +#endif + diff --git a/panda/src/pstatclient/pStatServerControlMessage.cxx b/panda/src/pstatclient/pStatServerControlMessage.cxx new file mode 100644 index 0000000000..bc7be69e54 --- /dev/null +++ b/panda/src/pstatclient/pStatServerControlMessage.cxx @@ -0,0 +1,70 @@ +// Filename: pStatServerControlMessage.cxx +// Created by: drose (09Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_pstats.h" +#include "pStatServerControlMessage.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: PStatServerControlMessage::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PStatServerControlMessage:: +PStatServerControlMessage() { + _type = T_invalid; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatServerControlMessage::encode +// Access: Public +// Description: Writes the message into the indicated datagram. +//////////////////////////////////////////////////////////////////// +void PStatServerControlMessage:: +encode(Datagram &datagram) const { + datagram.clear(); + datagram.add_uint8(_type); + switch (_type) { + case T_hello: + datagram.add_string(_server_hostname); + datagram.add_string(_server_progname); + datagram.add_uint16(_udp_port); + break; + + default: + pstats_cat.error() + << "Invalid PStatServerControlMessage::Type " << (int)_type << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatServerControlMessage::decode +// Access: Public +// Description: Extracts the message from the indicated datagram. +// Returns true on success, false on error. +//////////////////////////////////////////////////////////////////// +bool PStatServerControlMessage:: +decode(const Datagram &datagram) { + DatagramIterator source(datagram); + _type = (Type)source.get_uint8(); + + switch (_type) { + case T_hello: + _server_hostname = source.get_string(); + _server_progname = source.get_string(); + _udp_port = source.get_uint16(); + break; + + default: + pstats_cat.error() + << "Read invalid PStatServerControlMessage type: " << (int)_type << "\n"; + _type = T_invalid; + return false; + } + + return true; +} diff --git a/panda/src/pstatclient/pStatServerControlMessage.h b/panda/src/pstatclient/pStatServerControlMessage.h new file mode 100644 index 0000000000..8ca0291974 --- /dev/null +++ b/panda/src/pstatclient/pStatServerControlMessage.h @@ -0,0 +1,43 @@ +// Filename: pStatServerControlMessage.h +// Created by: drose (09Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PSTATSERVERCONTROLMESSAGE_H +#define PSTATSERVERCONTROLMESSAGE_H + +#include + +#include + +class Datagram; + +//////////////////////////////////////////////////////////////////// +// Class : PStatServerControlMessage +// Description : This kind of message is sent from the server to the +// client on the TCP socket to establish critical +// control information. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PStatServerControlMessage { +public: + PStatServerControlMessage(); + + void encode(Datagram &datagram) const; + bool decode(const Datagram &datagram); + + enum Type { + T_invalid, + T_hello, + }; + + Type _type; + + // Used for T_hello + string _server_hostname; + string _server_progname; + int _udp_port; +}; + + +#endif + diff --git a/panda/src/pstatclient/pStatThread.I b/panda/src/pstatclient/pStatThread.I new file mode 100644 index 0000000000..d0f75ab05f --- /dev/null +++ b/panda/src/pstatclient/pStatThread.I @@ -0,0 +1,84 @@ +// Filename: pStatThread.I +// Created by: drose (11Jul00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PStatThread::Default Constructor +// Access: Private +// Description: Normally, this constructor is called only from +// PStatClient. Use one of the constructors below to +// create your own Thread. +//////////////////////////////////////////////////////////////////// +INLINE PStatThread:: +PStatThread() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatThread::Constructor +// Access: Private +// Description: Normally, this constructor is called only from +// PStatClient. Use one of the constructors below to +// create your own Thread. +//////////////////////////////////////////////////////////////////// +INLINE PStatThread:: +PStatThread(PStatClient *client, int index) : + _client(client), + _index(index) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatThread::Constructor +// Access: Public +// Description: Creates a new named thread. This will be used to +// unify tasks that share a common thread, and +// differentiate tasks that occur in different threads. +// If any two different PStatThread objects share the +// same name, then they are really the same thread. +//////////////////////////////////////////////////////////////////// +INLINE PStatThread:: +PStatThread(const string &name, PStatClient *client) { + if (client == (PStatClient *)NULL) { + client = PStatClient::get_global_pstats(); + } + PStatThread thread(client->make_thread(name)); + (*this) = thread; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatThread::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PStatThread:: +PStatThread(const PStatThread ©) : + _client(copy._client), + _index(copy._index) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatThread::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PStatThread:: +operator = (const PStatThread ©) { + _client = copy._client; + _index = copy._index; +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatThread::new_frame +// Access: Public +// Description: This must be called at the start of every "frame", +// whatever a frame may be deemed to be, to accumulate +// all the stats that have collected so far for the +// thread and ship them off to the server. +//////////////////////////////////////////////////////////////////// +INLINE void PStatThread:: +new_frame() { + _client->new_frame(_index); +} diff --git a/panda/src/pstatclient/pStatThread.h b/panda/src/pstatclient/pStatThread.h new file mode 100644 index 0000000000..0e26b37daf --- /dev/null +++ b/panda/src/pstatclient/pStatThread.h @@ -0,0 +1,47 @@ +// Filename: pStatThread.h +// Created by: drose (11Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PSTATTHREAD_H +#define PSTATTHREAD_H + +#include + +#include "pStatClient.h" + +//////////////////////////////////////////////////////////////////// +// Class : PStatThread +// Description : A lightweight class that represents a single thread +// of execution to PStats. It doesn't have any real +// connection to any actual threads, but it's used to +// differentiate tasks which run in different threads, +// so we can measure them properly. It is presently the +// user's responsibility to correctly differentiate the +// various threads. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PStatThread { +private: + INLINE PStatThread(); + INLINE PStatThread(PStatClient *client, int index); + +public: + INLINE PStatThread(const string &name, PStatClient *client = NULL); + + INLINE PStatThread(const PStatThread ©); + INLINE void operator = (const PStatThread ©); + + INLINE void new_frame(); + +private: + PStatClient *_client; + int _index; + +friend class PStatClient; +friend class PStatCollector; +}; + +#include "pStatThread.I" + +#endif + diff --git a/panda/src/pstatclient/pStatTimer.I b/panda/src/pstatclient/pStatTimer.I new file mode 100644 index 0000000000..169ec1f649 --- /dev/null +++ b/panda/src/pstatclient/pStatTimer.I @@ -0,0 +1,28 @@ +// Filename: pStatTimer.I +// Created by: drose (11Jul00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PStatTimer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PStatTimer:: +PStatTimer(PStatCollector &collector) : + _collector(collector) +{ + _collector.start(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatTimer::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PStatTimer:: +~PStatTimer() { + _collector.stop(); +} + diff --git a/panda/src/pstatclient/pStatTimer.h b/panda/src/pstatclient/pStatTimer.h new file mode 100644 index 0000000000..a52cda140a --- /dev/null +++ b/panda/src/pstatclient/pStatTimer.h @@ -0,0 +1,35 @@ +// Filename: pStatTimer.h +// Created by: drose (11Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PSTATTIMER_H +#define PSTATTIMER_H + +#include + +#include "pStatCollector.h" + +//////////////////////////////////////////////////////////////////// +// Class : PStatTimer +// Description : A lightweight class that can be used to automatically +// start and stop a PStatCollector around a section of +// code. It's intended to be used in the following way: +// create a local PStatTimer variable to start the +// Collector, and when the PStatTimer variable goes out +// of scope (for instance, at the end of the function), +// it will automatically stop the Collector. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PStatTimer { +public: + INLINE PStatTimer(PStatCollector &collector); + INLINE ~PStatTimer(); + +private: + PStatCollector &_collector; +}; + +#include "pStatTimer.I" + +#endif + diff --git a/panda/src/pstatclient/test_client.cxx b/panda/src/pstatclient/test_client.cxx new file mode 100644 index 0000000000..c259c606ff --- /dev/null +++ b/panda/src/pstatclient/test_client.cxx @@ -0,0 +1,183 @@ +// Filename: test_client.cxx +// Created by: drose (09Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_pstats.h" +#include "pStatClient.h" +#include "pStatCollector.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static bool user_interrupted = false; + +// Define a signal handler so we can clean up on control-C. +void signal_handler(int) { + user_interrupted = true; +} + +struct SampleData { + const char *category; + RGBColorf color; + int min_ms; + int max_ms; +}; + +SampleData dataset_zero[] = { + { "Draw", RGBColorf(0,0,1), 10, 10 }, + { "Cull", RGBColorf(0,1,1), 5, 6 }, + { "App", RGBColorf(1,0,0), 0, 5 }, + { NULL }, +}; + +SampleData dataset_one[] = { + { "Draw", RGBColorf(0,0,1), 10, 12 }, + { "Squeak", RGBColorf(1,1,0), 25, 30 }, + { NULL }, +}; + +SampleData dataset_two[] = { + { "Squeak", RGBColorf(1,1,0), 40, 45 }, + { "Cull", RGBColorf(0,1,1), 20, 22 }, + { "Draw", RGBColorf(0,0,1), 10, 20 }, + { "Animation", RGBColorf(0.5,1,0.5), 0, 0 }, + { "Animation:mickey", RGBColorf(0,0,0), 5, 6 }, + { "Animation:donald", RGBColorf(0,0,0), 5, 6 }, + { "Animation:goofy", RGBColorf(0,0,0), 5, 6 }, + { "Animation:pluto", RGBColorf(0,0,0), 5, 6 }, + { NULL }, +}; + +#define NUM_DATASETS 3 +SampleData *datasets[NUM_DATASETS] = { + dataset_zero, dataset_one, dataset_two +}; + + +class WaitRequest { +public: + double _time; + int _index; + bool _start; + + bool operator < (const WaitRequest &other) const { + return _time < other._time; + } +}; + +int +main(int argc, char *argv[]) { + string hostname = "localhost"; + int port = pstats_port; + + if (argc > 1) { + hostname = argv[1]; + } + if (argc > 2) { + port = atoi(argv[2]); + if (port == 0) { + nout << "Invalid port number: " << argv[2] << "\n"; + exit(1); + } + } + if (argc > 4) { + nout << "test_client host port [dataset]\n"; + exit(1); + } + + signal(SIGINT, &signal_handler); + + PStatClient *client = PStatClient::get_global_pstats(); + client->set_client_name("Bogus Stats"); + + if (!client->connect(hostname, port)) { + nout << "Couldn't connect.\n"; + exit(1); + } + + srand(time(NULL)); + + int ds_index; + if (argc > 3) { + ds_index = atoi(argv[3]); + } else { + // Pick a random Dataset. + ds_index = (int)((double)NUM_DATASETS * rand() / (RAND_MAX + 1.0)); + } + if (ds_index < 0 || ds_index >= NUM_DATASETS) { + nout << "Invalid datasets; choose a number in the range 0 to " + << NUM_DATASETS - 1 << "\n"; + exit(1); + } + + SampleData *ds = datasets[ds_index]; + + vector _collectors; + int i = 0; + while (ds[i].category != (const char *)NULL) { + _collectors.push_back(PStatCollector(ds[i].category, ds[i].color, i)); + i++; + } + + while (!user_interrupted && client->is_connected()) { + client->get_main_thread().new_frame(); + + double total_ms = 0.0; + double now = client->get_clock().get_real_time(); + + typedef vector Wait; + Wait wait; + + // Make up some random intervals to "wait". + for (i = 0; i < _collectors.size(); i++) { + // A bit of random jitter so the collectors might overlap some. + double jitter_ms = (5.0 * rand() / (RAND_MAX + 1.0)); + + WaitRequest wr; + wr._time = now + jitter_ms / 1000.0; + wr._index = i; + wr._start = true; + wait.push_back(wr); + + int ms_range = ds[i].max_ms - ds[i].min_ms; + double ms = (double)ds[i].min_ms + + ((double)ms_range * rand() / (RAND_MAX + 1.0)); + now += ms / 1000.0; + total_ms += ms; + + wr._time = now + jitter_ms / 1000.0; + wr._start = false; + wait.push_back(wr); + } + + // Put the wait requests in order, to allow for the jitter, and + // invoke them. + sort(wait.begin(), wait.end()); + Wait::const_iterator wi; + for (wi = wait.begin(); wi != wait.end(); ++wi) { + const WaitRequest &wr = (*wi); + if (wr._start) { + _collectors[wr._index].start(client->get_main_thread(), wr._time); + } else { + _collectors[wr._index].stop(client->get_main_thread(), wr._time); + } + } + + // Now actually wait some approximation of the time we said we + // did. + PRIntervalTime sleep_timeout = + PR_MillisecondsToInterval((int)total_ms + 5); + PR_Sleep(sleep_timeout); + } + + return (0); +} diff --git a/panda/src/putil/Sources.pp b/panda/src/putil/Sources.pp new file mode 100644 index 0000000000..8a4f87f0b1 --- /dev/null +++ b/panda/src/putil/Sources.pp @@ -0,0 +1,120 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET putil + #define LOCAL_LIBS \ + express pandabase + + #define SOURCES \ + bamReader.I bamReader.N bamReader.cxx bamReader.h bamReaderParam.I \ + bamReaderParam.cxx bamReaderParam.h bamWriter.I bamWriter.cxx \ + bamWriter.h bitMask.I bitMask.cxx bitMask.h buttonEvent.I \ + buttonEvent.cxx buttonEvent.h buttonHandle.I buttonHandle.cxx \ + buttonHandle.h buttonRegistry.I buttonRegistry.cxx buttonRegistry.h \ + config_util.N config_util.cxx config_util.h configurable.cxx \ + configurable.h factoryBase.I factoryBase.cxx factoryBase.h \ + factoryParam.I factoryParam.cxx factoryParam.h factoryParams.I \ + factoryParams.cxx factoryParams.h globPattern.I globPattern.cxx \ + globPattern.h globalPointerRegistry.I globalPointerRegistry.cxx \ + globalPointerRegistry.h ioPtaDatagramFloat.cxx ioPtaDatagramFloat.h \ + ioPtaDatagramInt.cxx ioPtaDatagramInt.h ioPtaDatagramShort.cxx \ + ioPtaDatagramShort.h keyboardButton.cxx keyboardButton.h \ + lineStream.I lineStream.cxx lineStream.h lineStreamBuf.I \ + lineStreamBuf.cxx lineStreamBuf.h modifierButtons.I \ + modifierButtons.cxx modifierButtons.h \ + mouseButton.cxx mouseButton.h mouseData.cxx mouseData.h \ + nameUniquifier.I nameUniquifier.cxx nameUniquifier.h pta_double.cxx \ + pta_double.h pta_float.cxx pta_float.h pta_int.cxx pta_int.h \ + pta_uchar.cxx pta_uchar.h pta_ushort.cxx pta_ushort.h \ + string_utils.I string_utils.N string_utils.cxx string_utils.h \ + timedCycle.I timedCycle.cxx timedCycle.h typedWriteable.I \ + typedWriteable.cxx typedWriteable.h typedWriteableReferenceCount.I \ + typedWriteableReferenceCount.cxx typedWriteableReferenceCount.h \ + updateSeq.I updateSeq.cxx updateSeq.h vector_double.cxx \ + vector_double.h vector_float.cxx vector_float.h \ + vector_typedWriteable.cxx vector_typedWriteable.h vector_uchar.cxx \ + vector_uchar.h vector_ushort.cxx vector_ushort.h \ + vector_writeable.cxx vector_writeable.h writeable.I writeable.cxx \ + writeable.h writeableConfigurable.cxx writeableConfigurable.h \ + writeableParam.I writeableParam.cxx writeableParam.h + + #define INSTALL_HEADERS \ + bam.h bamReader.I bamReader.h bamReaderParam.I bamReaderParam.h \ + bamWriter.I bamWriter.h bitMask.I bitMask.h buttonEvent.I \ + buttonEvent.h buttonHandle.I buttonHandle.h buttonRegistry.I \ + buttonRegistry.h config_util.h configurable.h factory.I factory.h \ + factoryBase.I factoryBase.h factoryParam.I factoryParam.h \ + factoryParams.I factoryParams.h globPattern.I globPattern.h \ + globalPointerRegistry.I globalPointerRegistry.h indirectCompareTo.I \ + indirectCompareTo.h ioPtaDatagramFloat.h ioPtaDatagramInt.h \ + ioPtaDatagramShort.h iterator_types.h keyboardButton.h lineStream.I \ + lineStream.h lineStreamBuf.I lineStreamBuf.h modifierButtons.I \ + modifierButtons.h mouseButton.h mouseData.h nameUniquifier.I \ + nameUniquifier.h pointerToArray.I pointerToArray.h pta_double.h \ + pta_float.h pta_int.h pta_uchar.h pta_ushort.h string_utils.I \ + string_utils.h timedCycle.I timedCycle.h typedWriteable.I \ + typedWriteable.h typedWriteableReferenceCount.I \ + typedWriteableReferenceCount.h updateSeq.I updateSeq.h \ + vector_double.h vector_float.h vector_typedWriteable.h \ + vector_uchar.h vector_ushort.h vector_writeable.h writeable.I \ + writeable.h writeableConfigurable.h writeableParam.I \ + writeableParam.h + + #define IGATESCAN all + +#end lib_target + +#begin test_bin_target + #define TARGET test_bamRead + #define LOCAL_LIBS \ + putil graph + + #define SOURCES \ + test_bam.cxx test_bam.h test_bamRead.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET test_bamWrite + #define LOCAL_LIBS \ + putil graph + + #define SOURCES \ + test_bam.cxx test_bam.h test_bamWrite.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET test_filename + + #define SOURCES \ + test_filename.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET test_glob + + #define SOURCES \ + globPattern.I globPattern.cxx globPattern.h test_glob.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET test_linestream + + #define SOURCES \ + test_linestream.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET test_types + #define LOCAL_LIBS \ + putil + + #define SOURCES \ + test_types.cxx + +#end test_bin_target + diff --git a/panda/src/putil/bam.h b/panda/src/putil/bam.h new file mode 100644 index 0000000000..d0e0ce6268 --- /dev/null +++ b/panda/src/putil/bam.h @@ -0,0 +1,23 @@ +// Filename: bam.h +// Created by: jason (27Jun00) +// +//////////////////////////////////////////////////////////////////// + +//This file just holds the Magic Number, Major and Minor version +//numbers that are common to both BamWriter and BamReader. + +#ifndef _BAM_H +#define _BAM_H + +//The magic number for a BAM file and a carriage return and newline +//for detecting files damaged due to ASCII/Binary conversion +static const string _bam_header = string("pbj\0\n\r", 6); + +static const unsigned short _bam_major_ver = 2; +// Bumped to major version 2 on 7/6/00 due to major changes in Character. +static const unsigned short _bam_minor_ver = 2; +// Bumped to minor version 1 on 7/19/00 to quantize channel files. +// Bumped to minor version 2 on 8/21/00 for CollisionNode::_collide_geom. + + +#endif diff --git a/panda/src/putil/bamReader.I b/panda/src/putil/bamReader.I new file mode 100644 index 0000000000..2538415940 --- /dev/null +++ b/panda/src/putil/bamReader.I @@ -0,0 +1,161 @@ +// Filename: bamReader.I +// Created by: jason (12Jun00) +// + +#include + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BamReader:: +BamReader(DatagramGenerator *generator) + : _source(generator) +{ +} + + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::create_factory +// Access: Private, Static +// Description: Creates a new WriteableFactory for generating +// TypedWriteable objects +//////////////////////////////////////////////////////////////////// +INLINE void BamReader:: +create_factory(void) +{ + _factory = new WriteableFactory; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::get_factory +// Access: Public, Static +// Description: Returns the global WriteableFactory for generating +// TypedWriteable objects +//////////////////////////////////////////////////////////////////// +INLINE WriteableFactory* BamReader:: +get_factory(void) +{ + if (_factory == NullFactory) + create_factory(); + return _factory; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::get_file_major_ver +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int BamReader:: +get_file_major_ver(void) +{ + return _file_major; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::get_file_minor_ver +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int BamReader:: +get_file_minor_ver(void) +{ + return _file_minor; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::get_current_major_ver +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int BamReader:: +get_current_major_ver(void) +{ + return _cur_major; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::get_current_minor_ver +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int BamReader:: +get_current_minor_ver(void) +{ + return _cur_minor; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::queue +// Access: Private +// Description: queues the objId of the object needing to be created +//////////////////////////////////////////////////////////////////// +INLINE void BamReader:: +queue(PN_uint16 objId) +{ + if (!binary_search(_deferred_reads.begin(), _deferred_reads.end(), objId)) + _deferred_reads.push_back(objId); +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::empty_queue +// Access: Private +// Description: For every objId in the queue, reads an object +// from the datagram source +//////////////////////////////////////////////////////////////////// +INLINE void BamReader:: +empty_queue(void) +{ + while(!_deferred_reads.empty()) + { + _deferred_reads.pop_front(); + (void)read_object(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::clear_queue +// Access: Private +// Description: Clears all pending object reads +//////////////////////////////////////////////////////////////////// +INLINE void BamReader:: +clear_queue(void) +{ + _deferred_reads.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::empty +// Access: Public +// Description: Returns if BamReader has read all that it can from +// the given DatagramGenerator +//////////////////////////////////////////////////////////////////// +INLINE void BamReader:: +empty(void) +{ + _source->empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: parse_params +// Access: Private, Static +// Description: Takes in a FactoryParams, passed from a WriteableFactory +// into any TypedWriteable's make function, and parses +// out the datagram that contatins the data it needs to +// construct itself, and parses out the pointer to the +// managing BamReader object +//////////////////////////////////////////////////////////////////// +INLINE void +parse_params(const FactoryParams ¶ms, BamReader* &manager, Datagram &packet) +{ + WriteableParam *datagram_param = DCAST(WriteableParam, params.get_param(0)); + BamReaderParam *manager_param = DCAST(BamReaderParam, params.get_param(1)); + + Datagram temp_packet = datagram_param->get_datagram(); + DatagramIterator scan(temp_packet); + string mesg = scan.get_remaining_bytes(); + packet.append_data(mesg.data(), mesg.size()); + + manager = manager_param->get_manager(); +} diff --git a/panda/src/putil/bamReader.N b/panda/src/putil/bamReader.N new file mode 100644 index 0000000000..51d6505b97 --- /dev/null +++ b/panda/src/putil/bamReader.N @@ -0,0 +1,5 @@ +ignoremember _factory +ignoremember get_factory + +ignoretype FactoryBase +ignoretype Factory diff --git a/panda/src/putil/bamReader.cxx b/panda/src/putil/bamReader.cxx new file mode 100644 index 0000000000..9ccba23119 --- /dev/null +++ b/panda/src/putil/bamReader.cxx @@ -0,0 +1,470 @@ +// Filename: bamReader.cxx +// Created by: jason (12Jun00) +// + +#include +#include +#include + +#include "bam.h" +#include "bamReader.h" +#include +#include "config_util.h" + +WriteableFactory *BamReader::_factory = (WriteableFactory*)0L; +BamReader* const BamReader::Null = (BamReader*)0L; +WriteableFactory* const BamReader::NullFactory = (WriteableFactory*)0L; + +const int BamReader::_cur_major = _bam_major_ver; +const int BamReader::_cur_minor = _bam_minor_ver; + + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +BamReader:: +~BamReader(void) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::init +// Access: Public +// Description: Does all initialization necessary for BamReader to work +// Current this means reading in the Type Map stored as +// the header of the file. This TypeMap contains the +// name of all types written out and their type indeces +// as set when the file was created. This map is used +// for later determing the current TypeHandle for each +// object written. Each Object is identified in the file +// with the old index for space reason. +// +// This returns true if the BamReader successfully +// initialized, false otherwise. +//////////////////////////////////////////////////////////////////// +bool BamReader:: +init(void) +{ + Datagram header; + + if (!_source->is_valid()) + { + return false; + } + + if (!_source->get_datagram(header)) { + util_cat->error() + << "Unable to read Bam header.\n"; + return false; + } + + DatagramIterator scan(header); + + _file_major = scan.get_uint16(); + _file_minor = scan.get_uint16(); + + // If the major version is different, or the minor version is + // *newer*, we can't safely load the file. + if (_file_major != _bam_major_ver || _file_minor > _bam_minor_ver) + { + util_cat->error() + << "Bam file is version " << _file_major << "." << _file_minor + << ".\n"; + + if (_bam_minor_ver == 0) { + util_cat.error() + << "This program can only load version " + << _bam_major_ver << ".0 bams.\n"; + } else { + util_cat.error() + << "This program can only load version " + << _bam_major_ver << ".0 through " + << _bam_major_ver << "." << _bam_minor_ver << " bams.\n"; + } + + return false; + } + + if (util_cat->is_debug()) { + util_cat->debug() + << "Bam file is version " << _file_major << "." << _file_minor + << ".\n"; + if (_file_minor != _bam_minor_ver) { + util_cat.debug() + << "(Current version is " << _bam_major_ver << "." << _bam_minor_ver + << ".)\n"; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::read_handle +// Access: Public +// Description: Reads a TypeHandle out of the Datagram. If the Type +// has not been registered yet, BamReader will register +// the Type with TypeRegistry +//////////////////////////////////////////////////////////////////// +TypeHandle BamReader:: +read_handle(DatagramIterator& scan) +{ + int id = scan.get_uint16(); + + if (id == 0) + { + //This indicates an object that should have already been read in, + //so return TypeHandle::none() to indicate this. + return TypeHandle::none(); + } + + if (_index_map.find(id) == _index_map.end()) + { + //Have not loaded this type before + string name = scan.get_string(); + TypeHandle type = TypeRegistry::ptr()->find_type(name); + bool new_type = false; + + if (type == TypeHandle::none()) + { + //This is a new type we've never heard of before so register it + //with the type registry + type = TypeRegistry::ptr()->register_dynamic_type(name); + util_cat.warning() + << "Bam file contains objects of unknown type: " << type << "\n"; + new_type = true; + } + _index_map[id] = type; + + // Now pick up the derivation information. + int num_parent_classes = scan.get_uint8(); + for (int i = 0; i < num_parent_classes; i++) { + TypeHandle parent_type = read_handle(scan); + if (new_type) { + TypeRegistry::ptr()->record_derivation(type, parent_type); + } else { + if (type.get_parent_towards(parent_type) != parent_type) { + util_cat.warning() + << "Bam file indicates a derivation of " << type + << " from " << parent_type << " which is no longer true.\n"; + } + } + } + + } + + return _index_map[id]; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::read_object +// Access: Public +// Description: Reads an object definition from the DatagramGenerator +// and generates an object of the correct type, and returns +// a pointer to that object. +//////////////////////////////////////////////////////////////////// +TypedWriteable* BamReader:: +read_object(void) +{ + Datagram packet, body; + + //if (_source->empty()) //If the source is empty, then we merely have + //{ //back reference read's queue, so clear the queue + // clear_queue(); + // return TypedWriteable::Null; + //} + + if (!_source->is_valid()) + { + return TypedWriteable::Null; + } + + if (!_source->get_datagram(packet)) { + // The datagram source is empty. + return TypedWriteable::Null; + } + + //Now pull out the type ID and Object ID from the + //datagram and pass the remaining data in that + //datagram, the body to the WriteableParam for + //creation of the object + DatagramIterator scan(packet); + TypeHandle type = read_handle(scan); + PN_uint16 objId = scan.get_uint16(); + + //Before anything else, check to see if type is none + //If that is the case, then we SHOULD have already read + //in and created the object (although pointers in that object + //may not be fully instanciated yet), so just return a pointer + //to that already created obj and don't try to re-create it + if (type != TypeHandle::none()) + { + string message = scan.get_remaining_bytes(); + body.append_data(message.data(), message.size()); + + //Generate FactoryParams object to pass to the factory for + //creating the object + FactoryParams list; + list.add_param(new WriteableParam(body)); + list.add_param(new BamReaderParam(this)); + + //Due to the recursive nature of reading in objects (and their pointers) + //this line is to ensure that objId gets into the map _created_objs + //before we call make_instance. It works due to the nature of map. + //What happens is that a pair consistenting of objId and some random + //pointer is put into the map. But this is okay, since in the + //recursion we only refer to this objId for purposes of whether + //it is there or not and we immediately override it with a good + //value otherwise + _created_objs[objId]; + _created_objs[objId] = _factory->make_instance_more_general(type, list); + //Just some sanity checks + if (_created_objs[objId] == (TypedWriteable *)NULL) + { + util_cat->error() << "Failed to create a " << type.get_name() << endl; + } + else if (_created_objs[objId]->get_type() != type) + { + util_cat->warning() << "Attempted to create a " << type.get_name() \ + << " but a " << _created_objs[objId]->get_type().get_name() \ + << " was created instead." << endl; + } + } + + empty_queue(); + return _created_objs[objId]; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::read_pointer +// Access: Public +// Description: Utility function provided to correctly read in objects +// from the Datagram source, that any other object contains +// pointers to. Will correctly handle all circular references +// Any caller of this function should pass a pointer to itself +// because the pointer request will be stored and completed +// at a later pointer when the object is actually generated +//////////////////////////////////////////////////////////////////// +void BamReader:: +read_pointer(DatagramIterator& scan, TypedWriteable* forWhom) +{ + PN_uint16 objId = scan.get_uint16(); + _deferred_pointers[forWhom].push_back(objId); + //This is safe since we have already read the full datagram + //for the object requesting this object. So there can be + //no collision on that front. + //IMPORTANT NOTE: This does make the assumption that + //that objects are requested by other objects in the same + //order that they wrote them originally. + + //Don't queue a read of a null pointer + //or if the object has already been read + if ((objId != 0) && (_created_objs.find(objId) == _created_objs.end())) + queue(objId); +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::register_finalize +// Access: Public +// Description: Register for later finalization +//////////////////////////////////////////////////////////////////// +void BamReader:: +register_finalize(TypedWriteable *whom) +{ + if (whom == TypedWriteable::Null) + { + util_cat->error() << "Can't register a null pointer to finalize!" << endl; + return; + } + _finalize_list.insert(whom); +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::finalize_now +// Access: Public +// Description: Force finalization of a particular object +//////////////////////////////////////////////////////////////////// +void BamReader:: +finalize_now(TypedWriteable *whom) +{ + if (whom == TypedWriteable::Null) + { + util_cat->error() << "Can't finalize null pointer!" << endl; + return; + } + if (_finalize_list.find(whom) != _finalize_list.end()) + { + whom->finalize(); + _finalize_list.erase(whom); + } + else + { + util_cat->warning() << "Request to finalize object of type " + << whom->get_type().get_name() << " failed" + << endl; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::finalize_this +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void BamReader:: +finalize_this(TypedWriteable *whom) +{ + whom->finalize(); + _finalize_list.erase(whom); +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::finalize +// Access: Private +// Description: Call finalize_this on all objects stored +//////////////////////////////////////////////////////////////////// +void BamReader:: +finalize(void) +{ + Finalize::iterator fi = _finalize_list.begin(); + while(fi != _finalize_list.end()) + { + if (*fi == TypedWriteable::Null) + { + util_cat->error() << "Spun off into the weeds. " + << "somehow a null was registered to be " + << "finalized." << endl; + _finalize_list.erase(fi); + } + else + { + finalize_this(*fi); + } + fi = _finalize_list.begin(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::get_pta +// Access: Public +// Description: Requests an already read PTA from BamReader. If +// the PTA has not already been read and registered, +// returns a NULL +//////////////////////////////////////////////////////////////////// +void* BamReader:: +get_pta(DatagramIterator &scan) +{ + int _id = scan.get_uint16(); + + if (_id == 0) + { + _pta_id = -1; + return (void*)NULL; + } + + if (_ptamap.find(_id) == _ptamap.end()) + { + _pta_id = _id; + return (void*)NULL; + } + else + { + //Just to enforce that register_pta should do nothing if + //get_pta returns a good value or a NULL was what was + //actually written + _pta_id = -1; + } + + return _ptamap[_id]; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::register_pta +// Access: Public +// Description: Utility function to be called by the objects reading +// themselves from a Datagram. Registers a PTA with +// BamReader, so that if there are any shared ptr references, +// BamReader can detect and resolve those. To be used +// in conjunction with get_pta +//////////////////////////////////////////////////////////////////// +void BamReader:: +register_pta(void *ptr) +{ + //Just a sanity check to make sure that register_pta + //does nothing if get_pta has not been called first + if (_pta_id != -1) + { + _ptamap[_pta_id] = ptr; + } + _pta_id = -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::resolve +// Access: Public +// Description: Calling this function, asks BamReader to go ahead +// and resolve all pointer requests that can be resolved +//////////////////////////////////////////////////////////////////// +bool BamReader:: +resolve(void) +{ + Requests::iterator whom, old; + vector_ushort::iterator request; + bool objReferencesComplete; + vector_typedWriteable references; + + //Loop through the list of all objects who registered themselves as wanting + //a pointer to another object to be completed + whom = _deferred_pointers.begin(); + while(whom != _deferred_pointers.end()) + { + //For each of those objects, loop through its list of objects that it is + //interested in. If any one of those is not complete, then do nothing + //for that object, as when it is passed the list of pointers, it will + //expect them in a certain order and in totality. + objReferencesComplete = true; + references.clear(); + for(request = (*whom).second.begin(); request != (*whom).second.end(); request++) + { + //Check and see if the request was for a null pointer + if (*request == 0) + { + references.push_back(TypedWriteable::Null); + } + else + { + //Test to see if the object requested has been created or not yet + if (_created_objs.find((*request)) == _created_objs.end()) + { + objReferencesComplete = false; + break; + } + references.push_back(_created_objs[(*request)]); + } + } + if (objReferencesComplete) + { + (*whom).first->complete_pointers(references, this); + //The request list has been completed, so delete the + //list froms storage + //Annoying STL design flaw forces this delete trick + old = whom; + whom++; + _deferred_pointers.erase(old); + } + else + { + // nassertv(objReferencesComplete); + util_cat.warning() + << "Unable to complete " << (*whom).first->get_type() << "\n"; + whom++; + } + } + + finalize(); + + return true; +} + + diff --git a/panda/src/putil/bamReader.h b/panda/src/putil/bamReader.h new file mode 100644 index 0000000000..425113c427 --- /dev/null +++ b/panda/src/putil/bamReader.h @@ -0,0 +1,156 @@ +// Filename: bamReader.h +// Created by: jason (12Jun00) +// + +#ifndef __BAM_READER_ +#define __BAM_READER_ + +#include +#include +#include + +#include "typedWriteable.h" +#include "datagramGenerator.h" +#include "datagramIterator.h" +#include "writeableParam.h" +#include "bamReaderParam.h" +#include "factory.h" +#include "vector_ushort.h" +#include + +//Useful define for reading pta's +#define READ_PTA(Manager, source, Read_func, array) \ +{ \ + void *t; \ + if ((t = Manager->get_pta(source)) == (void*)NULL) \ + { \ + array = Read_func(source); \ + Manager->register_pta(array.get_void_ptr()); \ + } \ + else \ + { \ + array.set_void_ptr(t); \ + } \ +} + +//////////////////////////////////////////////////////////////////// +// Class : BamReader +// Description : This class manages all aspects of reading the data +// structure from a DatagramGenerator Requests are made to +// read an object out of the source. This class will then +// read in all the necessary data and pass that data into +// a factory object for creating TypedWriteable objects. +// The factory, will call the particular "make" function +// of the class of the object being created. +// If that object has pointers to any objects that are +// also stored in the binary source, then that object will +// make a request to BamReader for that object to be +// read and register itself with BamReader. Once BamReader +// has read in the objects requested, resolve can be called +// to go back and complete the pointer requests. BamReader's +// interface back into the functions is complete_pointers, +// which is defined in TypedWriteable +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA BamReader { +public: + typedef Factory WriteableFactory; + static BamReader* const Null; + static WriteableFactory* const NullFactory; + + INLINE BamReader(DatagramGenerator *generator); + ~BamReader(void); + + INLINE void empty(void); + + bool init(void); + TypedWriteable* read_object(void); + //When a client class asks BamReader to read out an object that it + //points to, it needs to pass a reference to itself, that BamReader + //can register in itself for later "fixing" + void read_pointer(DatagramIterator &scan, TypedWriteable* forWhom); + + //At any time you can call this function to try and resolve all + //outstanding pointer requests. Will resolve all requests that + //it can, and ignores those it can't. This allows for immediate + //use of part of a full definition if that is wanted. + bool resolve(void); + + //There are some classes that need to perform certain tasks before they + //are useable, but those taks can not be performed before all pointer + //references have been completed. So this function is provided as an + //interface to allow each class to register itself to be finalized + void register_finalize(TypedWriteable *whom); + + //Since there may be an ordering dependency on who gets finalized first, + //either the parent or the children. So this function is provided as an + //interface to force finalization of an object + void finalize_now(TypedWriteable *whom); + + void *get_pta(DatagramIterator &scan); + void register_pta(void *ptr); + + TypeHandle read_handle(DatagramIterator &scan); + + + //Version access functions + INLINE int get_file_major_ver(void); + INLINE int get_file_minor_ver(void); + + INLINE int get_current_major_ver(void); + INLINE int get_current_minor_ver(void); +public: + INLINE static WriteableFactory* get_factory(void); +private: + INLINE static void create_factory(void); + +private: + INLINE void queue(PN_uint16); + INLINE void empty_queue(void); + INLINE void clear_queue(void); + void finalize_this(TypedWriteable *whom); + void finalize(void); + +private: + typedef map Created; + typedef map Requests; + typedef set Finalize; + + static WriteableFactory *_factory; + + DatagramGenerator *_source; + //Map of old TypeHandle index number to current TypeHandle + map _index_map; + //Map of unique object ID in Binary structure to created object + Created _created_objs; + //Map of Objects that need pointers completed, to the + //object ID of the objects they need + Requests _deferred_pointers; + deque _deferred_reads; + + //Keep track of all objects needing to be finalized + Finalize _finalize_list; + + //Map of unique PTA ID's to the PTA's + map _ptamap; + + //When register_pta is called, this is the unique + //PTA ID that was stored during the call to + //get_pta that failed. + int _pta_id; + + int _file_major, _file_minor; + static const int _cur_major; + static const int _cur_minor; +}; + +typedef BamReader::WriteableFactory WriteableFactory; + +//Useful function for taking apart the Factory Params +//in the static functions that need to be defined in each +//writeable class that will be generated by a factory. Sets a +//datagram and a BamReader +INLINE void parse_params(const FactoryParams &, BamReader* &, Datagram &); + +#include "bamReader.I" + +#endif diff --git a/panda/src/putil/bamReaderParam.I b/panda/src/putil/bamReaderParam.I new file mode 100644 index 0000000000..5af31358eb --- /dev/null +++ b/panda/src/putil/bamReaderParam.I @@ -0,0 +1,58 @@ +// Filename: bamReaderParam.I +// Created by: jason (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: BamReaderParam::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BamReaderParam:: +BamReaderParam(BamReader *manager) : + _manager(manager) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReaderParam::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BamReaderParam:: +BamReaderParam(const BamReaderParam ©) : + FactoryParam(copy), + _manager(copy._manager) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReaderParam::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void BamReaderParam:: +operator = (const BamReaderParam ©) { + FactoryParam::operator = (copy); + _manager = copy._manager; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReaderParam::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BamReaderParam:: +~BamReaderParam(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReaderParam::get_datagram +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BamReader* BamReaderParam:: +get_manager(void) { + return _manager; +} + diff --git a/panda/src/putil/bamReaderParam.cxx b/panda/src/putil/bamReaderParam.cxx new file mode 100644 index 0000000000..e123d2d8b8 --- /dev/null +++ b/panda/src/putil/bamReaderParam.cxx @@ -0,0 +1,8 @@ +// Filename: bamReaderParam.cxx +// Created by: jason (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "bamReaderParam.h" + +TypeHandle BamReaderParam::_type_handle; diff --git a/panda/src/putil/bamReaderParam.h b/panda/src/putil/bamReaderParam.h new file mode 100644 index 0000000000..ada791221f --- /dev/null +++ b/panda/src/putil/bamReaderParam.h @@ -0,0 +1,59 @@ +// Filename: bamReaderParam.h +// Created by: jason (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BAMREADERPARAM_H +#define BAMREADERPARAM_H + +#include + +#include "factoryParam.h" +#include "datagram.h" + +#include + +class BamReader; + +//////////////////////////////////////////////////////////////////// +// Class : BamReaderParam +// Description : The specific derivation of FactoryParam that +// contains the information needed by a TypedWriteable +// object. Simply contains a pointer to the managing +// BamReader +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA BamReaderParam : public FactoryParam { +public: + INLINE BamReader *get_manager(void); + +private: + BamReader *_manager; + +public: + INLINE BamReaderParam(BamReader *manager); + INLINE BamReaderParam(const BamReaderParam &other); + INLINE void operator = (const BamReaderParam &other); + INLINE ~BamReaderParam(); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "BamReaderParam", + FactoryParam::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "bamReaderParam.I" + +#endif + diff --git a/panda/src/putil/bamWriter.I b/panda/src/putil/bamWriter.I new file mode 100644 index 0000000000..12687ae0e4 --- /dev/null +++ b/panda/src/putil/bamWriter.I @@ -0,0 +1,17 @@ +// Filename: bamWriter.I +// Created by: jason (08Jun00) +// + +//////////////////////////////////////////////////////////////////// +// Function: BamWriter::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BamWriter:: +BamWriter(DatagramSink *sink) : + //_current needs to start from 1, as a value of 0 + //for a unique object ID will be used to indicate a null pointer + _target(sink), _writing(false), _current(1), + _current_pta(1), _emptying(false) +{ +} diff --git a/panda/src/putil/bamWriter.cxx b/panda/src/putil/bamWriter.cxx new file mode 100644 index 0000000000..af91d1685d --- /dev/null +++ b/panda/src/putil/bamWriter.cxx @@ -0,0 +1,284 @@ +// Filename: bamWriter.cxx +// Created by: jason (08Jun00) +// + +#include +#include + +#include "config_util.h" +#include "bam.h" +#include "bamWriter.h" + +//////////////////////////////////////////////////////////////////// +// Function: BamWriter::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +BamWriter::~BamWriter(void) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: BamWriter::write_handle +// Access: Public +// Description: Writes a type handle into the file. If the handle +// has already been encountered then a number +// identifying that handle is written instead. +//////////////////////////////////////////////////////////////////// +void BamWriter:: +write_handle(Datagram &packet, TypeHandle type) +{ + //No class should have a type handle index of 0 + nassertv(type.get_index() != 0); + + packet.add_uint16(type.get_index()); + if (_type_map.find(type.get_index()) == _type_map.end()) + { + // This is the first time this TypeHandle has been written, so + // also write out its name. + _type_map.insert(type.get_index()); + packet.add_string(type.get_name()); + + // We also need to write the derivation of the TypeHandle, in case + // the program reading this file later has never heard of this + // type before. + int num_parent_classes = type.get_num_parent_classes(); + nassertv(num_parent_classes <= 255); // Good grief! + packet.add_uint8(num_parent_classes); + for (int i = 0; i < num_parent_classes; i++) { + write_handle(packet, type.get_parent_class(i)); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: BamWriter::init +// Access: Public +// Description: This function initializes the BamWriter, setting +// up whatever needs to be set up before the BamWriter +// is ready to write out objects. It returns true if +// successful, false on failure. +//////////////////////////////////////////////////////////////////// +bool BamWriter:: +init(void) +{ + //Write out the current major and minor BAM file version numbers + Datagram header; + + header.add_uint16(_bam_major_ver); + header.add_uint16(_bam_minor_ver); + + if (!_target->put_datagram(header)) { + util_cat.error() + << "Unable to write Bam header.\n"; + return false; + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamWriter::write_object +// Access: Public +// Description: Main function for performing the processing of +// writing an object to some Binary target. Combined +// with write_pointer this function can correctly handle +// and break circular references, while still writing out +// the necessary information for re-constructing those +// references when the objects are read back in. +// +// Returns true if the object is successfully written, +// false otherwise. +//////////////////////////////////////////////////////////////////// +bool BamWriter:: +write_object(TypedWriteable* obj) +{ + Datagram objData; + bool backreference = false; + + nassertr(obj != TypedWriteable::Null, false); + + //No object should ever be written out that is not + //registered as a child of TypedWriteable. The + //only way this can get in here and have that true is + //if someone forgot to set the classes init_type correctly. + //So nassert on that to make it easy for them to track + //down the error, because that will be an error, but one + //that won't show up on the writing, it will show up in + //reading, so could be potentially difficult to track down + nassertr(obj->is_of_type(TypedWriteable::get_class_type()), false); + + //Need to keep track of the state of any objects written + //or queued. So every time write_object is called, + //check to see if has been encountered before, if not + //insert it into a map with a Key of the pointer to + //the object and the value being a class that contains + //the unigue object ID for that object and a flag of + //whether it has been written yet or no + if (_statemap.find(obj) == _statemap.end()) + { + //If the object is not already in the map, then + //add it and assign it and unique object number + _statemap[obj].objId = _current; + _current++; + //We are making the assumption that there will never + //be more than the max of an unsigned short in objects + //in one file, but just in case, this nassert will make + //it easy to find that possible error + nassertr(_current != 0, false); + } + + bool okflag = true; + + if (_writing) + { + enqueue(obj); + } + else + { + _writing = true; + + if (!_statemap[obj].written) + { + //Write the type handle of the object + write_handle(objData, obj->get_type()); + //Write the unique ID of the object into the datagram + //so that when it is read back, and there is a back + //reference to this object, we know what indices correspond + //to what objects + objData.add_uint16(_statemap[obj].objId); + obj->write_datagram(this, objData); + _statemap[obj].written = true; + } + else + { + //If it is in the map, then we don't want to try + //and write it again, so write the unique ID of the + //object into the datagram so that when we read in + //later, we can resolve back references. Write + //0 for the type handle to identify back references + objData.add_uint16(0); + objData.add_uint16(_statemap[obj].objId); + } + + if (!_target->put_datagram(objData)) { + util_cat.error() + << "Unable to write datagram to file.\n"; + okflag = false; + } + _writing = false; + if (!_emptying) + { + if (!empty_queue()) { + okflag = false; + } + } + } + + return okflag; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamWriter::write_pointer +// Access: Public +// Description: Utility function to be called by the objects writing +// themselves to a Datagram. Basically amounts to a +// request to BamWriter to queue an object to be written +// and to write into the Datagram the necessary information +// to reference that object +//////////////////////////////////////////////////////////////////// +void BamWriter:: +write_pointer(Datagram &packet , TypedWriteable *dest) +{ + //Write a zero for the object ID if the pointer is null + if (dest == TypedWriteable::Null) + { + packet.add_uint16(0); + } + else + { + if (_statemap.find(dest) == _statemap.end()) + { + write_object(dest); + } + + packet.add_uint16(_statemap[dest].objId); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: BamWriter::register_pta +// Access: Public +// Description: Utility function to be called by the objects writing +// themselves to a Datagram. Registers a PTA to be written +// into the Datagram, and allows for shared references +// to be captured in the datagram +//////////////////////////////////////////////////////////////////// +bool BamWriter:: +register_pta(Datagram &packet, void *ptr) +{ + if (ptr != (void*)NULL) + { + if (_ptamap.find(ptr) == _ptamap.end()) + { + _ptamap[ptr] = _current_pta; + packet.add_uint16(_current_pta); + _current_pta++; + return false; + } + + packet.add_uint16(_ptamap[ptr]); + } + else + { + //A zero for the PTA ID indicates a NULL ptr + packet.add_uint16(0); + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: BamWriter::enqueue +// Access: Private +// Description: Queue an object to be written +//////////////////////////////////////////////////////////////////// +void BamWriter:: +enqueue(TypedWriteable *obj) +{ + _deferred.push_back(obj); +} + +//////////////////////////////////////////////////////////////////// +// Function: BamWriter::empty_queue +// Access: Private +// Description: For each object in the queue call write_object on it. +// Returns true of all write_object calls returned true, +// false otherwise. +//////////////////////////////////////////////////////////////////// +bool BamWriter:: +empty_queue(void) +{ + _emptying = true; + bool okflag = true; + while(!_deferred.empty()) + { + if (!write_object(_deferred.front())) { + okflag = false; + } + _deferred.pop_front(); + } + _emptying = false; + return okflag; +} + + + + + + + + + + + diff --git a/panda/src/putil/bamWriter.h b/panda/src/putil/bamWriter.h new file mode 100644 index 0000000000..9237dc8ff7 --- /dev/null +++ b/panda/src/putil/bamWriter.h @@ -0,0 +1,84 @@ +// Filename: bamWriter.h +// Created by: jason (08Jun00) +// + +#ifndef __BAM_WRITER_ +#define __BAM_WRITER_ + +#include +#include +#include + +#include "typedWriteable.h" +#include "datagramSink.h" +#include + +#define WRITE_PTA(Manager, dest, Write_func, array) \ + if (!Manager->register_pta(dest, array.p())) \ + { \ + Write_func(dest, array); \ + } \ + + +//////////////////////////////////////////////////////////////////// +// Class : BamWriter +// Description : This class manages all aspects of writing data +// structures to some Binary form. It writes to a +// a DatagramSink which is an abstraction that could +// be a file, the net, etc... The two basic functions +// used are write_object and write_pointer. write_object +// is called to write any TypedWriteable object out. +// BamWriter actually asks the object passed to write +// itself out to a datagram, and then it writes that +// datagram to the DatagramSink. write_pointer is called +// by the objects themselves to resolve writing out +// pointers to other objects. BamWriter will handle all +// circular references correctly +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA BamWriter{ +public: + INLINE BamWriter(DatagramSink *sink); + ~BamWriter(void); + + bool init(void); + bool write_object(TypedWriteable *obj); + void write_pointer(Datagram &packet, TypedWriteable *dest); + + //This function is provided for writing out shared pointers + //to PTA's. You should pass in a pointer to the PTA you want + //to write. If BamWriter has not already been asked to register + //this pointer, it will register it, write the appropriate info + //into the Datagram and return false. If it has, it will write + //the reference into the Datagram and return true. If false is + //returned, then the class registering the PTA should write out + //the info into the Datagram itself + bool register_pta(Datagram &packet, void* ptr); + + void write_handle(Datagram &packet, TypeHandle type); +private: + class storeState { + public: + PN_uint16 objId; + bool written; + + storeState(void) : objId(0), written(false) {} + ~storeState(void) {} + }; + + void enqueue(TypedWriteable *obj); + bool empty_queue(void); + + DatagramSink *_target; + bool _writing; + deque _deferred; + set _type_map; + map _statemap; + map _ptamap; + PN_uint16 _current, _current_pta; + bool _emptying; +}; + +#include "bamWriter.I" + +#endif + diff --git a/panda/src/putil/bitMask.I b/panda/src/putil/bitMask.I new file mode 100644 index 0000000000..730bcc1c4c --- /dev/null +++ b/panda/src/putil/bitMask.I @@ -0,0 +1,537 @@ +// Filename: bitMask.I +// Created by: drose (08Jun00) +// +//////////////////////////////////////////////////////////////////// + +template +TypeHandle BitMask::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BitMask:: +BitMask() : + _word(0) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BitMask:: +BitMask(WordType init_value) : + _word(init_value) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BitMask:: +BitMask(const BitMask ©) : + _word(copy._word) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMask:: +operator = (const BitMask ©) { + _word = copy._word; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::Named all_on constructor +// Access: Public, Static +// Description: Returns a BitMask whose bits are all on. +//////////////////////////////////////////////////////////////////// +template +INLINE BitMask BitMask:: +all_on() { + BitMask result; + result._word = ~0; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::Named all_on constructor +// Access: Public, Static +// Description: Returns a BitMask whose bits are all off. +//////////////////////////////////////////////////////////////////// +template +INLINE BitMask BitMask:: +all_off() { + BitMask result; + result._word = 0; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::Named lower_on constructor +// Access: Public, Static +// Description: Returns a BitMask whose lower num_bits bits are on. +//////////////////////////////////////////////////////////////////// +template +INLINE BitMask BitMask:: +lower_on(int on_bits) { + if (on_bits <= 0) { + return all_off(); + } else if (on_bits >= num_bits) { + return all_on(); + } + BitMask result; + result._word = ((WordType)1 << on_bits) - 1; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::Named bit constructor +// Access: Public, Static +// Description: Returns a BitMask with only the indicated bit on. +//////////////////////////////////////////////////////////////////// +template +INLINE BitMask BitMask:: +bit(int index) { + BitMask result; + result.set_bit(index); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BitMask:: +~BitMask() { +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::get_num_bits +// Access: Public +// Description: Returns the number of bits available to set in the +// bitmask. +//////////////////////////////////////////////////////////////////// +template +INLINE int BitMask:: +get_num_bits() const { + return num_bits; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::get_bit +// Access: Public +// Description: Returns true if the nth bit is set, false if it is +// cleared. index must be in the range [0, +// get_num_bits). +//////////////////////////////////////////////////////////////////// +template +INLINE bool BitMask:: +get_bit(int index) const { + nassertr(index >= 0 && index < num_bits, false); + return (_word & ((WordType)1 << index)) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::set_bit +// Access: Public +// Description: Sets the nth bit on. index must be in the range +// [0, get_num_bits). +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMask:: +set_bit(int index) { + nassertv(index >= 0 && index < num_bits); + _word |= ((WordType)1 << index); +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::clear_bit +// Access: Public +// Description: Sets the nth bit off. index must be in the range +// [0, get_num_bits). +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMask:: +clear_bit(int index) { + nassertv(index >= 0 && index < num_bits); + _word &= ~((WordType)1 << index); +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::set_bit_to +// Access: Public +// Description: Sets the nth bit either on or off, according to the +// indicated bool value. index must be in the range [0, +// get_num_bits). +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMask:: +set_bit_to(int index, bool value) { + if (value) { + set_bit(index); + } else { + clear_bit(index); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::extract +// Access: Public +// Description: Returns a word that represents only the indicated +// range of bits within this BitMask, shifted to the +// least-significant position. +//////////////////////////////////////////////////////////////////// +template +INLINE WordType BitMask:: +extract(int low_bit, int size) const { + return (_word >> low_bit) & + BitMask::lower_on(size)._word; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::store +// Access: Public +// Description: Stores the indicated word into the indicated range of +// bits with this BitMask. +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMask:: +store(WordType value, int low_bit, int size) { + WordType mask = + BitMask::lower_on(size)._word << low_bit; + _word = (_word & ~mask) | ((value << low_bit) & mask); +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::get_word +// Access: Public +// Description: Returns the entire BitMask as a single word. +//////////////////////////////////////////////////////////////////// +template +INLINE WordType BitMask:: +get_word() const { + return _word; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::set_word +// Access: Public +// Description: Sets the entire BitMask to the value indicated by the +// given word. +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMask:: +set_word(WordType value) { + _word = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::invert_in_place +// Access: Public +// Description: Inverts all the bits in the BitMask. +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMask:: +invert_in_place() { + _word = ~_word; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::clear +// Access: Public +// Description: Sets all the bits in the BitMask off. +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMask:: +clear() { + _word = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::output +// Access: Public +// Description: Writes the BitMask out as a binary or a hex number, +// according to the number of bits. +//////////////////////////////////////////////////////////////////// +template +void BitMask:: +output(ostream &out) const { + if (num_bits >= 40) { + output_hex(out); + } else { + output_binary(out); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::output_binary +// Access: Public +// Description: Writes the BitMask out as a binary number, with +// spaces every four bits. +//////////////////////////////////////////////////////////////////// +template +void BitMask:: +output_binary(ostream &out, int spaces_every) const { + for (int i = num_bits - 1; i >= 0; i--) { + if (spaces_every != 0 && ((i % spaces_every) == spaces_every - 1)) { + out << ' '; + } + out << (get_bit(i) ? '1' : '0'); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::output_hex +// Access: Public +// Description: Writes the BitMask out as a hexadecimal number, with +// spaces every four digits. +//////////////////////////////////////////////////////////////////// +template +void BitMask:: +output_hex(ostream &out, int spaces_every) const { + int num_digits = (num_bits + 3) / 4; + + for (int i = num_digits - 1; i >= 0; i--) { + WordType digit = extract(i * 4, 4); + if (spaces_every != 0 && ((i % spaces_every) == spaces_every - 1)) { + out << ' '; + } + if (digit > 9) { + out << (char)(digit - 10 + 'a'); + } else { + out << (char)(digit + '0'); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::write +// Access: Public +// Description: Writes the BitMask out as a binary or a hex number, +// according to the number of bits. +//////////////////////////////////////////////////////////////////// +template +void BitMask:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << *this << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::operator == +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool BitMask:: +operator == (const BitMask &other) const { + return _word == other._word; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::operator != +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool BitMask:: +operator != (const BitMask &other) const { + return _word != other._word; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::operator < +// Access: Public +// Description: The ordering operator is of limited usefulness with a +// BitMask, however, it has a definition which places +// all unique BitMasks into a unique ordering. It may +// be useful when defining ordered STL containers of +// BitMasks, for instance; and it's required in order to +// export any STL container (ordered or unordered) of +// BitMask under Windows. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BitMask:: +operator < (const BitMask &other) const { + return _word < other._word; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::compare_to +// Access: Public +// Description: Returns a number less than zero if this BitMask sorts +// before the indicated other BitMask, greater than zero +// if it sorts after, or 0 if they are equivalent. This +// is based on the same ordering defined by operator <. +//////////////////////////////////////////////////////////////////// +template +INLINE int BitMask:: +compare_to(const BitMask &other) const { + if ((*this) < other) { + return -1; + } else if (other < (*this)) { + return 1; + } else { + return 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::operator & +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BitMask BitMask:: +operator & (const BitMask &other) const { + BitMask result(*this); + result &= other; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::operator | +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BitMask BitMask:: +operator | (const BitMask &other) const { + BitMask result(*this); + result |= other; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::operator ^ +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BitMask BitMask:: +operator ^ (const BitMask &other) const { + BitMask result(*this); + result ^= other; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::operator ~ +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BitMask BitMask:: +operator ~ () const { + BitMask result(*this); + result._word = ~result._word; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::operator << +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BitMask BitMask:: +operator << (int shift) const { + BitMask result(*this); + result <<= shift; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::operator >> +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE BitMask BitMask:: +operator >> (int shift) const { + BitMask result(*this); + result >>= shift; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::operator &= +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMask:: +operator &= (const BitMask &other) { + _word &= other._word; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::operator |= +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMask:: +operator |= (const BitMask &other) { + _word |= other._word; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::operator ^= +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMask:: +operator ^= (const BitMask &other) { + _word ^= other._word; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::operator <<= +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMask:: +operator <<= (int shift) { + _word <<= shift; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::operator >>= +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void BitMask:: +operator >>= (int shift) { + _word >>= shift; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::init_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +void BitMask:: +init_type() { + ostringstream str; + str << "BitMask" << num_bits; + register_type(_type_handle, str.str()); +} diff --git a/panda/src/putil/bitMask.cxx b/panda/src/putil/bitMask.cxx new file mode 100644 index 0000000000..749ec616d8 --- /dev/null +++ b/panda/src/putil/bitMask.cxx @@ -0,0 +1,12 @@ +// Filename: bitMask.cxx +// Created by: drose (08Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "bitMask.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif + diff --git a/panda/src/putil/bitMask.h b/panda/src/putil/bitMask.h new file mode 100644 index 0000000000..5f3ecea2da --- /dev/null +++ b/panda/src/putil/bitMask.h @@ -0,0 +1,118 @@ +// Filename: bitMask.h +// Created by: drose (08Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BITMASK_H +#define BITMASK_H + +#include + +#include "numeric_types.h" +#include "typeHandle.h" +#include "indent.h" + + +//////////////////////////////////////////////////////////////////// +// Class : BitMask +// Description : A general bitmask class. This stores an array of +// bits of some length that must fit within a given word +// of the indicated type. See also BitArray. +//////////////////////////////////////////////////////////////////// +template +class BitMask { +public: + INLINE BitMask(); + INLINE BitMask(WordType init_value); + INLINE BitMask(const BitMask ©); + INLINE void operator = (const BitMask ©); + + INLINE static BitMask all_on(); + INLINE static BitMask all_off(); + INLINE static BitMask lower_on(int on_bits); + INLINE static BitMask bit(int index); + + INLINE ~BitMask(); + + INLINE int get_num_bits() const; + INLINE bool get_bit(int index) const; + INLINE void set_bit(int index); + INLINE void clear_bit(int index); + INLINE void set_bit_to(int index, bool value); + + INLINE WordType extract(int low_bit, int size) const; + INLINE void store(WordType value, int low_bit, int size); + INLINE WordType get_word() const; + INLINE void set_word(WordType value); + + INLINE void invert_in_place(); + INLINE void clear(); + + void output(ostream &out) const; + void output_binary(ostream &out, int spaces_every = 4) const; + void output_hex(ostream &out, int spaces_every = 4) const; + void write(ostream &out, int indent_level = 0) const; + + INLINE bool operator == (const BitMask &other) const; + INLINE bool operator != (const BitMask &other) const; + INLINE bool operator < (const BitMask &other) const; + INLINE int compare_to(const BitMask &other) const; + + INLINE BitMask + operator & (const BitMask &other) const; + + INLINE BitMask + operator | (const BitMask &other) const; + + INLINE BitMask + operator ^ (const BitMask &other) const; + + INLINE BitMask + operator ~ () const; + + INLINE BitMask + operator << (int shift) const; + + INLINE BitMask + operator >> (int shift) const; + + INLINE void operator &= (const BitMask &other); + INLINE void operator |= (const BitMask &other); + INLINE void operator ^= (const BitMask &other); + INLINE void operator <<= (int shift); + INLINE void operator >>= (int shift); + +private: + WordType _word; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +#include "bitMask.I" + +template +INLINE ostream &operator << (ostream &out, const BitMask &bitmask) { + bitmask.output(out); + return out; +} + +// We need to define this temporary macro so we can pass a parameter +// containing a comma through the macro. +#define BITMASK32_DEF BitMask +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, BITMASK32_DEF); + +typedef BitMask BitMask32; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/putil/buttonEvent.I b/panda/src/putil/buttonEvent.I new file mode 100644 index 0000000000..7bb9c6f72a --- /dev/null +++ b/panda/src/putil/buttonEvent.I @@ -0,0 +1,107 @@ +// Filename: buttonEvent.I +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEvent::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ButtonEvent:: +ButtonEvent() : + _button(ButtonHandle::none()), + _down(false) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEvent::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ButtonEvent:: +ButtonEvent(ButtonHandle button, bool down) : + _button(button), + _down(down) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEvent::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ButtonEvent:: +ButtonEvent(ButtonHandle button, bool down, ModifierButtons mods) : + _button(button), + _down(down), + _mods(mods) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEvent::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ButtonEvent:: +ButtonEvent(const ButtonEvent ©) : + _button(copy._button), + _down(copy._down), + _mods(copy._mods) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEvent::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ButtonEvent:: +operator = (const ButtonEvent ©) { + _button = copy._button; + _down = copy._down; + ((ModifierButtons &)_mods) = copy._mods; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEvent::Equality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ButtonEvent:: +operator == (const ButtonEvent &other) const { + return (_button == other._button && + _down == other._down && + _mods == other._mods); +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEvent::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ButtonEvent:: +operator != (const ButtonEvent &other) const { + return !operator == (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEvent::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ButtonEvent:: +operator < (const ButtonEvent &other) const { + if (_button != other._button) { + return _button < other._button; + } + + if (_down != other._down) { + return _down < other._down; + } + + return _mods < other._mods; +} diff --git a/panda/src/putil/buttonEvent.cxx b/panda/src/putil/buttonEvent.cxx new file mode 100644 index 0000000000..e0eca2c1ba --- /dev/null +++ b/panda/src/putil/buttonEvent.cxx @@ -0,0 +1,22 @@ +// Filename: buttonEvent.cxx +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "buttonEvent.h" + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEvent::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ButtonEvent:: +output(ostream &out) const { + out << "button " << _button; + if (_down) { + out << " down"; + } else { + out << " up"; + } + out << " with " << _mods; +} diff --git a/panda/src/putil/buttonEvent.h b/panda/src/putil/buttonEvent.h new file mode 100644 index 0000000000..264d2c7340 --- /dev/null +++ b/panda/src/putil/buttonEvent.h @@ -0,0 +1,47 @@ +// Filename: buttonEvent.h +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BUTTONEVENT_H +#define BUTTONEVENT_H + +#include + +#include "buttonHandle.h" +#include "modifierButtons.h" + +//////////////////////////////////////////////////////////////////// +// Class : ButtonEvent +// Description : Records a transition of one button from up to down or +// vice-versa, as well as an optional recording of some +// ModifierButtons that were being tracked at the time. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ButtonEvent { +public: + INLINE ButtonEvent(); + INLINE ButtonEvent(ButtonHandle button, bool down); + INLINE ButtonEvent(ButtonHandle button, bool down, ModifierButtons mods); + INLINE ButtonEvent(const ButtonEvent ©); + INLINE void operator = (const ButtonEvent ©); + + INLINE bool operator == (const ButtonEvent &other) const; + INLINE bool operator != (const ButtonEvent &other) const; + INLINE bool operator < (const ButtonEvent &other) const; + + void output(ostream &out) const; + + ButtonHandle _button; + bool _down; + const ModifierButtons _mods; +}; + +INLINE ostream &operator << (ostream &out, const ButtonEvent &be) { + be.output(out); + return out; +} + +#include "buttonEvent.I" + +#endif + diff --git a/panda/src/putil/buttonHandle.I b/panda/src/putil/buttonHandle.I new file mode 100644 index 0000000000..b6c29ae567 --- /dev/null +++ b/panda/src/putil/buttonHandle.I @@ -0,0 +1,91 @@ +// Filename: buttonHandle.I +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: ButtonHandle::Constructor +// Access: Public +// Description: The default constructor must do nothing, because we +// can't guarantee ordering of static initializers. If +// the constructor tried to initialize its value, it +// might happen after the value had already been set +// previously by another static initializer! +//////////////////////////////////////////////////////////////////// +INLINE ButtonHandle:: +ButtonHandle() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonHandle::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ButtonHandle:: +ButtonHandle(const ButtonHandle ©) : _index(copy._index) { +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonHandle::Equality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ButtonHandle:: +operator == (const ButtonHandle &other) const { + return (_index == other._index); +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonHandle::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ButtonHandle:: +operator != (const ButtonHandle &other) const { + return (_index != other._index); +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonHandle::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ButtonHandle:: +operator < (const ButtonHandle &other) const { + return (_index < other._index); +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonHandle::has_ascii_equivalent +// Access: Public +// Description: Returns true if the button was created with an ASCII +// equivalent code (e.g. for a standard keyboard +// button). +//////////////////////////////////////////////////////////////////// +INLINE bool ButtonHandle:: +has_ascii_equivalent() const { + return (_index > 0 && _index < 128); +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonHandle::get_ascii_equivalent +// Access: Public +// Description: Returns the character code associated with the +// button, or '\0' if no ASCII code was associated. +//////////////////////////////////////////////////////////////////// +INLINE char ButtonHandle:: +get_ascii_equivalent() const { + return has_ascii_equivalent() ? (char)_index : '\0'; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonHandle::none +// Access: Public, Static +// Description: Returns a special zero-valued ButtonHandle that is +// used to indicate no button. +//////////////////////////////////////////////////////////////////// +INLINE ButtonHandle ButtonHandle:: +none() { + return _none; +} diff --git a/panda/src/putil/buttonHandle.cxx b/panda/src/putil/buttonHandle.cxx new file mode 100644 index 0000000000..a5d1cac1d5 --- /dev/null +++ b/panda/src/putil/buttonHandle.cxx @@ -0,0 +1,25 @@ +// Filename: buttonHandle.cxx +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "buttonHandle.h" +#include "buttonRegistry.h" + +// This is initialized to zero by static initialization. +ButtonHandle ButtonHandle::_none; + + +//////////////////////////////////////////////////////////////////// +// Function: ButtonHandle::get_name +// Access: Public +// Description: Returns the name of the button. +//////////////////////////////////////////////////////////////////// +string ButtonHandle:: +get_name() const { + if ((*this) == ButtonHandle::none()) { + return "none"; + } else { + return ButtonRegistry::ptr()->get_name(*this); + } +} diff --git a/panda/src/putil/buttonHandle.h b/panda/src/putil/buttonHandle.h new file mode 100644 index 0000000000..9c6d88f370 --- /dev/null +++ b/panda/src/putil/buttonHandle.h @@ -0,0 +1,48 @@ +// Filename: buttonHandle.h +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BUTTONHANDLE_H +#define BUTTONHANDLE_H + +#include + +//////////////////////////////////////////////////////////////////// +// Class : ButtonHandle +// Description : A ButtonHandle represents a single button from any +// device, including keyboard buttons and mouse buttons +// (but see KeyboardButton and MouseButton). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ButtonHandle { +public: + INLINE ButtonHandle(); + INLINE ButtonHandle(const ButtonHandle ©); + + INLINE bool operator == (const ButtonHandle &other) const; + INLINE bool operator != (const ButtonHandle &other) const; + INLINE bool operator < (const ButtonHandle &other) const; + + string get_name() const; + INLINE bool has_ascii_equivalent() const; + INLINE char get_ascii_equivalent() const; + + INLINE static ButtonHandle none(); + +private: + int _index; + static ButtonHandle _none; + +friend class ButtonRegistry; +}; + +// It's handy to be able to output a ButtonHandle directly, and see the +// button name. +INLINE ostream &operator << (ostream &out, ButtonHandle button) { + return out << button.get_name(); +} + +#include "buttonHandle.I" + +#endif + diff --git a/panda/src/putil/buttonRegistry.I b/panda/src/putil/buttonRegistry.I new file mode 100644 index 0000000000..cead64476e --- /dev/null +++ b/panda/src/putil/buttonRegistry.I @@ -0,0 +1,42 @@ +// Filename: buttonRegistry.I +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////// +// Function: ButtonRegistry::RegistryNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ButtonRegistry::RegistryNode:: +RegistryNode(ButtonHandle handle, const string &name) : + _handle(handle), _name(name) { +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonRegistry::get_name +// Access: Public +// Description: Returns the name of the indicated button. +//////////////////////////////////////////////////////////////////// +INLINE string ButtonRegistry:: +get_name(ButtonHandle button) const { + RegistryNode *rnode = look_up(button); + nassertr(rnode != (RegistryNode *)NULL, ""); + return rnode->_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonRegistry::ptr +// Access: Public, Static +// Description: Returns the pointer to the global ButtonRegistry +// object. +//////////////////////////////////////////////////////////////////// +INLINE ButtonRegistry *ButtonRegistry:: +ptr() { + if (_global_pointer == (ButtonRegistry *)NULL) { + init_global_pointer(); + } + return _global_pointer; +} diff --git a/panda/src/putil/buttonRegistry.cxx b/panda/src/putil/buttonRegistry.cxx new file mode 100644 index 0000000000..3d43977359 --- /dev/null +++ b/panda/src/putil/buttonRegistry.cxx @@ -0,0 +1,189 @@ +// Filename: buttonRegistry.cxx +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "buttonRegistry.h" +#include "config_util.h" + +#include + +// In general, we use the util_cat->info() syntax in this file +// (instead of util_cat.info()), because much of this work is done at +// static init time, and we must use the arrow syntax to force +// initialization of the util_cat category. + +ButtonRegistry *ButtonRegistry::_global_pointer = NULL; + + +//////////////////////////////////////////////////////////////////// +// Function: ButtonRegistry::register_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool ButtonRegistry:: +register_button(ButtonHandle &button_handle, const string &name, + char ascii_equivalent) { + NameRegistry::iterator ri; + ri = _name_registry.find(name); + + if (ri == _name_registry.end()) { + // The name was not already used; this is the first time this + // button has been defined. + + int index = -1; + if (ascii_equivalent != '\0') { + if (_handle_registry[ascii_equivalent] == (RegistryNode *)NULL) { + index = ascii_equivalent; + } else { + util_cat->error() + << "Attempt to register multiple buttons under ASCII equivalent " + << ascii_equivalent << "\n"; + } + } + + if (util_cat->is_spam()) { + util_cat->spam() + << "Registering button " << name << "\n"; + } + + if (index == -1) { + // It's not an ASCII equivalent; make up a new number. + index = _handle_registry.size(); + _handle_registry.push_back(NULL); + } + + ButtonHandle new_handle; + new_handle._index = index; + + RegistryNode *rnode = new RegistryNode(new_handle, name); + _handle_registry[index] = rnode; + _name_registry[name] = rnode; + + button_handle = new_handle; + return true; + } + + RegistryNode *rnode = (*ri).second; + nassertr(rnode->_name == (*ri).first, false); + nassertr(rnode->_handle._index >= 0 && + rnode->_handle._index < _handle_registry.size(), false); + nassertr(_handle_registry[rnode->_handle._index] == rnode, false); + nassertr(rnode->_handle._index != 0, false); + + if (button_handle != rnode->_handle) { + // Hmm, we seem to have a contradictory button registration! + util_cat->warning() + << "Attempt to register button " << name << " more than once!\n"; + + button_handle = rnode->_handle; + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonRegistry::find_button +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ButtonHandle ButtonRegistry:: +find_button(const string &name) const { + NameRegistry::const_iterator ri; + ri = _name_registry.find(name); + if (ri == _name_registry.end()) { + return ButtonHandle::none(); + } else { + return (*ri).second->_handle; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonRegistry::find_button +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ButtonHandle ButtonRegistry:: +find_button(char ascii_equivalent) const { + if (_handle_registry[ascii_equivalent] == (RegistryNode *)NULL) { + return ButtonHandle::none(); + } + return _handle_registry[ascii_equivalent]->_handle; +} + + +//////////////////////////////////////////////////////////////////// +// Function: ButtonRegistry::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ButtonRegistry:: +write(ostream &out) const { + out << "ASCII equivalents:\n"; + for (int i = 1; i < 128; i++) { + if (_handle_registry[i] != (RegistryNode *)NULL) { + char hex[12]; + sprintf(hex, "%02x", (unsigned int)i); + nassertv(strlen(hex) < 12); + + out << " " << hex << " " << _handle_registry[i]->_name << "\n"; + } + } + + out << "\nOther buttons:\n"; + NameRegistry::const_iterator ri; + for (ri = _name_registry.begin(); ri != _name_registry.end(); ++ri) { + if (!(*ri).second->_handle.has_ascii_equivalent()) { + out << " " << (*ri).second->_name << "\n"; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonRegistry::Constructor +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +ButtonRegistry:: +ButtonRegistry() { + // We'll start by filling up the handle_registry with 128 entries + // for ButtonHandle::none(), as well as for all the ASCII + // equivalents. + + _handle_registry.reserve(128); + int i; + for (i = 0; i < 128; i++) { + _handle_registry.push_back(NULL); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonRegistry::init_global_pointer +// Access: Private, Static +// Description: Constructs the ButtonRegistry object for the first +// time. +//////////////////////////////////////////////////////////////////// +void ButtonRegistry:: +init_global_pointer() { + _global_pointer = new ButtonRegistry; +} + + +//////////////////////////////////////////////////////////////////// +// Function: ButtonRegistry::look_up +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +ButtonRegistry::RegistryNode *ButtonRegistry:: +look_up(ButtonHandle handle) const { + nassertr(handle._index != 0, NULL); + + if (handle._index < 0 || + handle._index >= _handle_registry.size()) { + util_cat->fatal() + << "Invalid ButtonHandle index " << handle._index + << "! Is memory corrupt?\n"; + return (RegistryNode *)NULL; + } + + return _handle_registry[handle._index]; +} diff --git a/panda/src/putil/buttonRegistry.h b/panda/src/putil/buttonRegistry.h new file mode 100644 index 0000000000..f27fa4147a --- /dev/null +++ b/panda/src/putil/buttonRegistry.h @@ -0,0 +1,68 @@ +// Filename: buttonRegistry.h +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BUTTONREGISTRY_H +#define BUTTONREGISTRY_H + +#include + +#include "buttonHandle.h" + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : ButtonRegistry +// Description : The ButtonRegistry class maintains all the assigned +// ButtonHandles in a given system. There should be only +// one ButtonRegistry class during the lifetime of the +// application. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ButtonRegistry { +protected: + class EXPCL_PANDA RegistryNode { + public: + INLINE RegistryNode(ButtonHandle handle, const string &name); + + ButtonHandle _handle; + string _name; + }; + +public: + bool register_button(ButtonHandle &button_handle, const string &name, + char ascii_equivalent = '\0'); + + ButtonHandle find_button(const string &name) const; + ButtonHandle find_button(char ascii_equivalent) const; + + INLINE string get_name(ButtonHandle button) const; + + void write(ostream &out) const; + + // ptr() returns the pointer to the global ButtonRegistry object. + INLINE static ButtonRegistry *ptr(); + +private: + // The ButtonRegistry class should never be constructed by user code. + // There is only one in the universe, and it constructs itself! + ButtonRegistry(); + + static void init_global_pointer(); + + RegistryNode *look_up(ButtonHandle button) const; + + typedef vector HandleRegistry; + HandleRegistry _handle_registry; + + typedef map NameRegistry; + NameRegistry _name_registry; + + static ButtonRegistry *_global_pointer; +}; + +#include "buttonRegistry.I" + +#endif diff --git a/panda/src/putil/config_util.N b/panda/src/putil/config_util.N new file mode 100644 index 0000000000..4506d37b79 --- /dev/null +++ b/panda/src/putil/config_util.N @@ -0,0 +1,2 @@ +forcetype DSearchPath + diff --git a/panda/src/putil/config_util.cxx b/panda/src/putil/config_util.cxx new file mode 100644 index 0000000000..8c3a2c4a21 --- /dev/null +++ b/panda/src/putil/config_util.cxx @@ -0,0 +1,76 @@ +// Filename: config_util.cxx +// Created by: cary (04Jan00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_util.h" +#include "clockObject.h" +#include "typeHandle.h" +#include "configurable.h" +#include "namable.h" +#include "referenceCount.h" +#include "typedReferenceCount.h" +#include "keyboardButton.h" +#include "mouseButton.h" +#include "factoryParam.h" +#include "datagram.h" +#include "writeable.h" +#include "typedWriteable.h" +#include "typedWriteableReferenceCount.h" +#include "writeableParam.h" +#include "bamReaderParam.h" +#include "writeableConfigurable.h" +#include "get_config_path.h" + +#include + +ConfigureDef(config_util); +NotifyCategoryDef(util, ""); + +ConfigureFn(config_util) { +// ClockObject::init_ptr(); + TypedObject::init_type(); + Configurable::init_type(); + Namable::init_type(); + ReferenceCount::init_type(); + TypedReferenceCount::init_type(); + KeyboardButton::init_keyboard_buttons(); + MouseButton::init_mouse_buttons(); + FactoryParam::init_type(); + Datagram::init_type(); + Writeable::init_type(); + TypedWriteable::init_type(); + WriteableParam::init_type(); + BamReaderParam::init_type(); + TypedWriteableReferenceCount::init_type(); + WriteableConfigurable::init_type(); +} + + +// Set this true to enable tracking of ReferenceCount pointer +// allocation/deallcation via the MemoryUsage object. This is +// primarily useful for detecting memory leaks. It has no effect when +// compiling in NDEBUG mode. + +// This variable is no longer defined here; instead, it's a member of +// MemoryUsage. + +//const bool track_memory_usage = config_util.GetBool("track-memory-usage", false); + +const DSearchPath & +get_model_path() { + static DSearchPath *model_path = NULL; + return get_config_path("model-path", model_path); +} + +const DSearchPath & +get_texture_path() { + static DSearchPath *texture_path = NULL; + return get_config_path("texture-path", texture_path); +} + +const DSearchPath & +get_sound_path() { + static DSearchPath *sound_path = NULL; + return get_config_path("sound-path", sound_path); +} diff --git a/panda/src/putil/config_util.h b/panda/src/putil/config_util.h new file mode 100644 index 0000000000..62cdaeaf73 --- /dev/null +++ b/panda/src/putil/config_util.h @@ -0,0 +1,33 @@ +// Filename: config_util.h +// Created by: cary (04Jan00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __CONFIG_UTIL_H__ +#define __CONFIG_UTIL_H__ + +#include +#include +#include + +class DSearchPath; + +ConfigureDecl(config_util, EXPCL_PANDA, EXPTP_PANDA); +NotifyCategoryDecl(util, EXPCL_PANDA, EXPTP_PANDA); + +// Actually, we can't determine this config variable the normal way, +// because we must be able to access it at static init time. Instead +// of declaring it a global constant, we'll make it a member of +// MemoryUsage. + +//extern EXPCL_PANDA const bool track_memory_usage; + +// These are functions instead of constant variables because they are +// computed based on the concatenation of all appearances of the +// corresponding variable in the config files. + +EXPCL_PANDA const DSearchPath &get_model_path(); +EXPCL_PANDA const DSearchPath &get_texture_path(); +EXPCL_PANDA const DSearchPath &get_sound_path(); + +#endif /* __CONFIG_UTIL_H__ */ diff --git a/panda/src/putil/configurable.cxx b/panda/src/putil/configurable.cxx new file mode 100644 index 0000000000..3effc4d1de --- /dev/null +++ b/panda/src/putil/configurable.cxx @@ -0,0 +1,9 @@ +// Filename: configurable.cxx +// Created by: drose (15Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "configurable.h" + + +TypeHandle Configurable::_type_handle; diff --git a/panda/src/putil/configurable.h b/panda/src/putil/configurable.h new file mode 100644 index 0000000000..5264cbdeea --- /dev/null +++ b/panda/src/putil/configurable.h @@ -0,0 +1,72 @@ +// Filename: configurable.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef CONFIGURABLE_H +#define CONFIGURABLE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// + +#include + +#include "typeHandle.h" + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : Configurable +// Description : An object that has data or parameters that are set +// less frequently (at least occasionally) than every +// frame. We can cache the configuration info by +// by using the "dirty" flag. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Configurable : public TypedObject { +public: + + Configurable( void ) { make_dirty(); } + virtual void config( void ) { _dirty = false; } + INLINE void check_config() const { + if (_dirty) { + // This is a sneaky trick to allow check_config() to be called + // from a const member function. Even though we will be calling + // config(), a non-const function that modifies the class + // object, in some sense it's not really modifying the class + // object--it's just updating a few internal settings for + // consistency. + ((Configurable *)this)->config(); + } + } + + INLINE bool is_dirty(void) const { return _dirty; } + INLINE void make_dirty(void) { _dirty = true; } + +private: + + bool _dirty; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedObject::init_type(); + register_type(_type_handle, "Configurable", + TypedObject::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/putil/factory.I b/panda/src/putil/factory.I new file mode 100644 index 0000000000..52ee44beda --- /dev/null +++ b/panda/src/putil/factory.I @@ -0,0 +1,83 @@ +// Filename: factory.I +// Created by: drose (08May00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Factory::make_instance +// Access: Public +// Description: Attempts to create a new instance of some class of +// the indicated type, or some derivative if necessary. +// If an instance of the exact type cannot be created, +// the specified preferred will specify which derived +// class will be preferred. +//////////////////////////////////////////////////////////////////// +template +INLINE Type *Factory:: +make_instance(TypeHandle handle, const FactoryParams ¶ms) { + return (Type *)FactoryBase::make_instance(handle, params); +} + +//////////////////////////////////////////////////////////////////// +// Function: Factory::make_instance +// Access: Public +// Description: Attempts to create a new instance of some class of +// the indicated type, or some derivative if necessary. +// If an instance of the exact type cannot be created, +// the specified preferred will specify which derived +// class will be preferred. +// +// This flavor of make_instance() accepts a string name +// that indicates the desired type. It must be the name +// of some already-registered type. +//////////////////////////////////////////////////////////////////// +template +INLINE Type *Factory:: +make_instance(const string &type_name, const FactoryParams ¶ms) { + return (Type *)FactoryBase::make_instance(type_name, params); +} + +//////////////////////////////////////////////////////////////////// +// Function: Factory::make_instance_more_general +// Access: Public +// Description: Attempts to create an instance of the type requested, +// or some base type of the type requested. Returns the +// new instance created, or NULL if the instance could +// not be created. +//////////////////////////////////////////////////////////////////// +template +INLINE Type *Factory:: +make_instance_more_general(TypeHandle handle, const FactoryParams ¶ms) { + return (Type *)FactoryBase::make_instance_more_general(handle, params); +} + +//////////////////////////////////////////////////////////////////// +// Function: Factory::make_instance_more_general +// Access: Public +// Description: Attempts to create an instance of the type requested, +// or some base type of the type requested. Returns the +// new instance created, or NULL if the instance could +// not be created. +// +// This flavor of make_instance_more_general() accepts a +// string name that indicates the desired type. It must +// be the name of some already-registered type. +//////////////////////////////////////////////////////////////////// +template +INLINE Type *Factory:: +make_instance_more_general(const string &type_name, + const FactoryParams ¶ms) { + return (Type *)FactoryBase::make_instance_more_general(type_name, params); +} + +//////////////////////////////////////////////////////////////////// +// Function: Factory::register_factory +// Access: Public +// Description: Registers a new kind of thing the Factory will be +// able to create. +//////////////////////////////////////////////////////////////////// +template +INLINE void Factory:: +register_factory(TypeHandle handle, CreateFunc *func) { + FactoryBase::register_factory(handle, (BaseCreateFunc *)func); +} diff --git a/panda/src/putil/factory.h b/panda/src/putil/factory.h new file mode 100644 index 0000000000..ea22316339 --- /dev/null +++ b/panda/src/putil/factory.h @@ -0,0 +1,56 @@ +// Filename: factory.h +// Created by: drose (08May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FACTORY_H +#define FACTORY_H + +#include + +#include "factoryBase.h" + +//////////////////////////////////////////////////////////////////// +// Class : Factory +// Description : A Factory can be used to create an instance of a +// particular subclass of some general base class. Each +// subclass registers itself with the Factory, supplying +// a function that will construct an instance of that +// subclass; the Factory can later choose a suitable +// subclass and return a newly-constructed pointer to an +// object of that type on the user's demand. This is +// used, for instance, to manage the set of +// GraphicsPipes available to the user. +// +// This is a thin template wrapper around FactoryBase. +// All it does is ensure the types are correctly cast. +// All of its methods are inline, and it has no data +// members, so it is not necessary to export the class +// from the DLL. +//////////////////////////////////////////////////////////////////// +template +class Factory : public FactoryBase { +public: + typedef Type *CreateFunc(const FactoryParams ¶ms); + + INLINE Type *make_instance(TypeHandle handle, + const FactoryParams ¶ms = FactoryParams()); + + INLINE Type *make_instance(const string &type_name, + const FactoryParams ¶ms = FactoryParams()); + + INLINE Type * + make_instance_more_general(TypeHandle handle, + const FactoryParams ¶ms = FactoryParams()); + + INLINE Type * + make_instance_more_general(const string &type_name, + const FactoryParams ¶ms = FactoryParams()); + + INLINE void register_factory(TypeHandle handle, CreateFunc *func); +}; + +#include "factory.I" + +#endif + diff --git a/panda/src/putil/factoryBase.I b/panda/src/putil/factoryBase.I new file mode 100644 index 0000000000..f603c5e9e8 --- /dev/null +++ b/panda/src/putil/factoryBase.I @@ -0,0 +1,49 @@ +// Filename: factory.I +// Created by: drose (08May00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::make_instance +// Access: Public +// Description: Attempts to create a new instance of some class of +// the indicated type, or some derivative if necessary. +// If an instance of the exact type cannot be created, +// the specified priorities will specify which derived +// class will be preferred. +// +// This flavor of make_instance() accepts a string name +// that indicates the desired type. It must be the name +// of some already-registered type. +//////////////////////////////////////////////////////////////////// +INLINE TypedObject *FactoryBase:: +make_instance(const string &type_name, const FactoryParams ¶ms) { + TypeHandle handle = TypeRegistry::ptr()->find_type(type_name); + nassertr(handle != TypeHandle::none(), NULL); + + return make_instance(handle, params); +} + + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::make_instance_more_general +// Access: Public +// Description: Attempts to create an instance of the type requested, +// or some base type of the type requested. Returns the +// new instance created, or NULL if the instance could +// not be created. +// +// This flavor of make_instance_more_general() accepts a +// string name that indicates the desired type. It must +// be the name of some already-registered type. +//////////////////////////////////////////////////////////////////// +INLINE TypedObject *FactoryBase:: +make_instance_more_general(const string &type_name, + const FactoryParams ¶ms) { + TypeHandle handle = TypeRegistry::ptr()->find_type(type_name); + nassertr(handle != TypeHandle::none(), NULL); + + return make_instance_more_general(handle, params); +} + diff --git a/panda/src/putil/factoryBase.cxx b/panda/src/putil/factoryBase.cxx new file mode 100644 index 0000000000..d9ad42592d --- /dev/null +++ b/panda/src/putil/factoryBase.cxx @@ -0,0 +1,265 @@ +// Filename: factory.cxx +// Created by: drose (08May00) +// +//////////////////////////////////////////////////////////////////// + +#include "factoryBase.h" +#include "indent.h" + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +FactoryBase:: +FactoryBase() { +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +FactoryBase:: +~FactoryBase() { +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::make_instance +// Access: Public +// Description: Attempts to create a new instance of some class of +// the indicated type, or some derivative if necessary. +// If an instance of the exact type cannot be created, +// the specified preferred will specify which derived +// class will be preferred. +//////////////////////////////////////////////////////////////////// +TypedObject *FactoryBase:: +make_instance(TypeHandle handle, const FactoryParams ¶ms) { + TypedObject *instance = (TypedObject *)NULL; + + instance = make_instance_exact(handle, params); + if (instance == (TypedObject *)NULL) { + // Can't create an exact instance; try for a derived type. + instance = make_instance_more_specific(handle, params); + } + return instance; +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::make_instance_more_general +// Access: Public +// Description: Attempts to create an instance of the type requested, +// or some base type of the type requested. Returns the +// new instance created, or NULL if the instance could +// not be created. +//////////////////////////////////////////////////////////////////// +TypedObject *FactoryBase:: +make_instance_more_general(TypeHandle handle, const FactoryParams ¶ms) { + // Walk up the left side of the inheritance tree until we find + // something we know about. + TypedObject *object = make_instance_exact(handle, params); + while (object == (TypedObject *)NULL) { + if (handle.get_num_parent_classes() == 0) { + return NULL; + } + handle = handle.get_parent_class(0); + object = make_instance_exact(handle, params); + } + + return object; +} + + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::register_factory +// Access: Public +// Description: Registers a new kind of thing the Factory will be +// able to create. +//////////////////////////////////////////////////////////////////// +void FactoryBase:: +register_factory(TypeHandle handle, BaseCreateFunc *func) { + nassertv(handle != TypeHandle::none()); + nassertv(func != (BaseCreateFunc *)NULL); + _creators[handle] = func; +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::get_num_types +// Access: Public +// Description: Returns the number of different types the Factory +// knows how to create. +//////////////////////////////////////////////////////////////////// +int FactoryBase:: +get_num_types() const { + return _creators.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::get_type +// Access: Public +// Description: Returns the nth type the Factory knows how to create. +// This is not a terribly efficient function; it's +// included primarily for debugging output. Normally +// you wouldn't need to traverse the list of the +// Factory's types. +//////////////////////////////////////////////////////////////////// +TypeHandle FactoryBase:: +get_type(int n) const { + nassertr(n >= 0 && n < get_num_types(), TypeHandle::none()); + Creators::const_iterator ci; + for (ci = _creators.begin(); ci != _creators.end(); ++ci) { + if (n == 0) { + return (*ci).first; + } + n--; + } + + // We shouldn't get here. + nassertr(false, TypeHandle::none()); + return TypeHandle::none(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::clear_preferred +// Access: Public +// Description: Empties the list of preferred types. +//////////////////////////////////////////////////////////////////// +void FactoryBase:: +clear_preferred() { + _preferred.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::add_preferred +// Access: Public +// Description: Adds the indicated type to the end of the list of +// preferred types. On the next call to +// make_instance(), if the exact type requested cannot +// be created, the preferred types are first tried in +// the order specified. +//////////////////////////////////////////////////////////////////// +void FactoryBase:: +add_preferred(TypeHandle handle) { + nassertv(handle != TypeHandle::none()); + _preferred.push_back(handle); +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::get_num_preferred +// Access: Public +// Description: Returns the number of types added to the +// preferred-type list. +//////////////////////////////////////////////////////////////////// +int FactoryBase:: +get_num_preferred() const { + return _preferred.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::get_preferred +// Access: Public +// Description: Returns the nth type added to the preferred-type +// list. +//////////////////////////////////////////////////////////////////// +TypeHandle FactoryBase:: +get_preferred(int n) const { + nassertr(n >= 0 && n < get_num_preferred(), TypeHandle::none()); + return _preferred[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::write_types +// Access: Public +// Description: Writes a list of all known types the Factory can +// create to the indicated output stream, one per line. +//////////////////////////////////////////////////////////////////// +void FactoryBase:: +write_types(ostream &out, int indent_level) const { + Creators::const_iterator ci; + for (ci = _creators.begin(); ci != _creators.end(); ++ci) { + indent(out, indent_level) << (*ci).first << "\n"; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::Copy Constructor +// Access: Private +// Description: Don't copy Factories. +//////////////////////////////////////////////////////////////////// +FactoryBase:: +FactoryBase(const FactoryBase &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::Copy Assignment Operator +// Access: Private +// Description: Don't copy Factories. +//////////////////////////////////////////////////////////////////// +void FactoryBase:: +operator = (const FactoryBase &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::make_instance_exact +// Access: Private +// Description: Attempts to create an instance of the exact type +// requested by the given handle. Returns the new +// instance created, or NULL if the instance could not +// be created. +//////////////////////////////////////////////////////////////////// +TypedObject *FactoryBase:: +make_instance_exact(TypeHandle handle, const FactoryParams ¶ms) { + Creators::const_iterator ci = _creators.find(handle); + if (ci == _creators.end()) { + return NULL; + } + + BaseCreateFunc *func = (BaseCreateFunc *)((*ci).second); + nassertr(func != (BaseCreateFunc *)NULL, NULL); + return (*func)(params); +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryBase::make_instance_more_specific +// Access: Private +// Description: Attempts to create an instance of some derived type +// of the type requested by the given handle. Returns +// the new instance created, or NULL if the instance +// could not be created. +//////////////////////////////////////////////////////////////////// +TypedObject *FactoryBase:: +make_instance_more_specific(TypeHandle handle, const FactoryParams ¶ms) { + // First, walk through the established preferred list. Maybe one + // of these qualifies. + + Preferred::const_iterator pi; + for (pi = _preferred.begin(); pi != _preferred.end(); ++pi) { + TypeHandle ptype = (*pi); + if (ptype.is_derived_from(handle)) { + TypedObject *object = make_instance_exact(ptype, params); + if (object != (TypedObject *)NULL) { + return object; + } + } + } + + // No, we couldn't create anything on the preferred list, so create + // the first thing we know about that derives from the indicated + // type. + Creators::const_iterator ci; + for (ci = _creators.begin(); ci != _creators.end(); ++ci) { + TypeHandle ctype = (*ci).first; + if (ctype.is_derived_from(handle)) { + BaseCreateFunc *func = (BaseCreateFunc *)((*ci).second); + nassertr(func != (BaseCreateFunc *)NULL, NULL); + TypedObject *object = (*func)(params); + if (object != (TypedObject *)NULL) { + return object; + } + } + } + + return NULL; +} + diff --git a/panda/src/putil/factoryBase.h b/panda/src/putil/factoryBase.h new file mode 100644 index 0000000000..4b588e9e10 --- /dev/null +++ b/panda/src/putil/factoryBase.h @@ -0,0 +1,94 @@ +// Filename: factory.h +// Created by: cary (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FACTORYBASE_H +#define FACTORYBASE_H + +#include + +#include "typeHandle.h" +#include "typedReferenceCount.h" +#include "factoryParams.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : FactoryBase +// Description : A Factory can be used to create an instance of a +// particular subclass of some general base class. Each +// subclass registers itself with the Factory, supplying +// a function that will construct an instance of that +// subclass; the Factory can later choose a suitable +// subclass and return a newly-constructed pointer to an +// object of that type on the user's demand. This is +// used, for instance, to manage the set of +// GraphicsPipes available to the user. +// +// FactoryBase is the main definition of the thin +// template class Factory. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA FactoryBase { +public: + typedef TypedObject *BaseCreateFunc(const FactoryParams ¶ms); + + // public interface +public: + FactoryBase(); + ~FactoryBase(); + + TypedObject *make_instance(TypeHandle handle, + const FactoryParams ¶ms); + + INLINE TypedObject *make_instance(const string &type_name, + const FactoryParams ¶ms); + + TypedObject *make_instance_more_general(TypeHandle handle, + const FactoryParams ¶ms); + + INLINE TypedObject *make_instance_more_general(const string &type_name, + const FactoryParams ¶ms); + + void register_factory(TypeHandle handle, BaseCreateFunc *func); + + int get_num_types() const; + TypeHandle get_type(int n) const; + + void clear_preferred(); + void add_preferred(TypeHandle handle); + int get_num_preferred() const; + TypeHandle get_preferred(int n) const; + + void write_types(ostream &out, int indent_level = 0) const; + +private: + // These are private; we shouldn't be copy-constructing Factories. + FactoryBase(const FactoryBase ©); + void operator = (const FactoryBase ©); + + // internal utility functions + TypedObject *make_instance_exact(TypeHandle handle, + const FactoryParams ¶ms); + TypedObject *make_instance_more_specific(TypeHandle handle, + const FactoryParams ¶ms); + +private: + // internal mechanics and bookkeeping +#ifdef WIN32_VC + // Visual C++ seems to have a problem with building a map based on + // BaseCreateFunc. We'll have to typecast it on the way out. + typedef map Creators; +#else + typedef map Creators; +#endif + + Creators _creators; + + typedef vector Preferred; + Preferred _preferred; +}; + +#include "factoryBase.I" + +#endif /* FACTORY_H */ diff --git a/panda/src/putil/factoryParam.I b/panda/src/putil/factoryParam.I new file mode 100644 index 0000000000..707cc67aaf --- /dev/null +++ b/panda/src/putil/factoryParam.I @@ -0,0 +1,40 @@ +// Filename: factoryParam.I +// Created by: drose (08May00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: FactoryParams::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FactoryParam:: +FactoryParam() { +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryParams::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FactoryParam:: +FactoryParam(const FactoryParam &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryParams::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void FactoryParam:: +operator = (const FactoryParam &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryParams::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FactoryParam:: +~FactoryParam() { +} diff --git a/panda/src/putil/factoryParam.cxx b/panda/src/putil/factoryParam.cxx new file mode 100644 index 0000000000..c2995c91bc --- /dev/null +++ b/panda/src/putil/factoryParam.cxx @@ -0,0 +1,8 @@ +// Filename: factoryParam.cxx +// Created by: drose (08May00) +// +//////////////////////////////////////////////////////////////////// + +#include "factoryParam.h" + +TypeHandle FactoryParam::_type_handle; diff --git a/panda/src/putil/factoryParam.h b/panda/src/putil/factoryParam.h new file mode 100644 index 0000000000..3cd223c4bd --- /dev/null +++ b/panda/src/putil/factoryParam.h @@ -0,0 +1,53 @@ +// Filename: factoryParam.h +// Created by: drose (08May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FACTORYPARAM_H +#define FACTORYPARAM_H + +#include + +#include "typedReferenceCount.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : FactoryParam +// Description : The base class of any number of specific pieces of +// parameter information that might be passed to a +// Factory's CreateFunc to control what kind of instance +// is created. This class is empty and contains no +// data, but different kinds of factories may expect +// parameters of various types that derive from +// FactoryParam (and do contain data). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA FactoryParam : public TypedReferenceCount { +public: + INLINE FactoryParam(); + INLINE FactoryParam(const FactoryParam &other); + INLINE void operator = (const FactoryParam &other); + INLINE ~FactoryParam(); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "FactoryParam", + TypedReferenceCount::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "factoryParam.I" + +#endif + diff --git a/panda/src/putil/factoryParams.I b/panda/src/putil/factoryParams.I new file mode 100644 index 0000000000..3d5da43022 --- /dev/null +++ b/panda/src/putil/factoryParams.I @@ -0,0 +1,27 @@ +// Filename: factoryParams.I +// Created by: drose (08May00) +// +//////////////////////////////////////////////////////////////////// + +#include + + +//////////////////////////////////////////////////////////////////// +// Function: get_param_into +// Description: A handy convenience template function that extracts a +// parameter of the indicated type from the +// FactoryParams list. If the parameter type is found, +// it fills the pointer and returns true; otherwise, it +// sets the pointer to NULL and returns false. +//////////////////////////////////////////////////////////////////// +template +bool get_param_into(ParamType *&pointer, const FactoryParams ¶ms) { + FactoryParam *param = + params.get_param_of_type(ParamType::get_class_type()); + if (param == (FactoryParam *)NULL) { + pointer = NULL; + return false; + } + DCAST_INTO_R(pointer, param, false); + return true; +} diff --git a/panda/src/putil/factoryParams.cxx b/panda/src/putil/factoryParams.cxx new file mode 100644 index 0000000000..0ea669c310 --- /dev/null +++ b/panda/src/putil/factoryParams.cxx @@ -0,0 +1,105 @@ +// Filename: factoryParams.cxx +// Created by: drose (08May00) +// +//////////////////////////////////////////////////////////////////// + +#include "factoryParams.h" + +//////////////////////////////////////////////////////////////////// +// Function: FactoryParams::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +FactoryParams:: +FactoryParams() { +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryParams::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +FactoryParams:: +~FactoryParams() { +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryParams::add_param +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void FactoryParams:: +add_param(FactoryParam *param) { + nassertv(param != (FactoryParam *)NULL); + _params.push_back(param); +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryParams::clear +// Access: Public +// Description: Removes all parameters from the set. +//////////////////////////////////////////////////////////////////// +void FactoryParams:: +clear() { + _params.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryParams::get_num_params +// Access: Public +// Description: Returns the number of parameters that have been added +// to the set. +//////////////////////////////////////////////////////////////////// +int FactoryParams:: +get_num_params() const { + return _params.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryParams::get_param +// Access: Public +// Description: Returns the nth parameter that has been added to the +// set. +//////////////////////////////////////////////////////////////////// +FactoryParam *FactoryParams:: +get_param(int n) const { + nassertr(n >= 0 && n < _params.size(), NULL); + return DCAST(FactoryParam, _params[n]); +} + +//////////////////////////////////////////////////////////////////// +// Function: FactoryParams::get_param_of_type +// Access: Public +// Description: Returns the first parameter that matches exactly the +// indicated type, or if there are no exact matches, +// returns the first one that derives from the indicated +// type. If no parameters match at all, returns NULL. +//////////////////////////////////////////////////////////////////// +FactoryParam *FactoryParams:: +get_param_of_type(TypeHandle type) const { + Params::const_iterator pi; + + // First, search for the exact match. + for (pi = _params.begin(); pi != _params.end(); ++pi) { + FactoryParam *param; + DCAST_INTO_R(param, *pi, NULL); + nassertr(param != (FactoryParam *)NULL, NULL); + + if (param->is_exact_type(type)) { + return param; + } + } + + // Now, search for a derived match. + for (pi = _params.begin(); pi != _params.end(); ++pi) { + FactoryParam *param; + DCAST_INTO_R(param, *pi, NULL); + nassertr(param != (FactoryParam *)NULL, NULL); + + if (param->is_of_type(type)) { + return param; + } + } + + return NULL; +} diff --git a/panda/src/putil/factoryParams.h b/panda/src/putil/factoryParams.h new file mode 100644 index 0000000000..36532cb2bd --- /dev/null +++ b/panda/src/putil/factoryParams.h @@ -0,0 +1,54 @@ +// Filename: factoryParams.h +// Created by: drose (08May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FACTORYPARAMS_H +#define FACTORYPARAMS_H + +#include + +#include "typeHandle.h" +#include "typedReferenceCount.h" +#include "pointerTo.h" +#include "factoryParam.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : FactoryParams +// Description : An instance of this class is passed to the Factory +// when requesting it to do its business and construct a +// new something. It can be filled with optional +// parameters to the CreateFunc for the particular +// subclass the Factory will be creating. +// +// This is just a vector of pointers to *something*; it +// will be up to the individual CreateFuncs to interpret +// this meaningfully. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA FactoryParams { +public: + FactoryParams(); + ~FactoryParams(); + + void add_param(FactoryParam *param); + void clear(); + + int get_num_params() const; + FactoryParam *get_param(int n) const; + + FactoryParam *get_param_of_type(TypeHandle type) const; + +private: + typedef vector Params; + + Params _params; +}; + +template +INLINE bool get_param_into(ParamType *&pointer, const FactoryParams ¶ms); + +#include "factoryParams.I" + +#endif diff --git a/panda/src/putil/globPattern.I b/panda/src/putil/globPattern.I new file mode 100644 index 0000000000..6f791df2b2 --- /dev/null +++ b/panda/src/putil/globPattern.I @@ -0,0 +1,49 @@ +// Filename: globPattern.I +// Created by: drose (30May00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: GlobPattern::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GlobPattern:: +GlobPattern(const string &pattern) : _pattern(pattern) { +} + +//////////////////////////////////////////////////////////////////// +// Function: GlobPattern::set_pattern +// Access: Public +// Description: Changes the pattern string that the GlobPattern +// object matches. +//////////////////////////////////////////////////////////////////// +INLINE void GlobPattern:: +set_pattern(const string &pattern) { + _pattern = pattern; +} + +//////////////////////////////////////////////////////////////////// +// Function: GlobPattern::get_pattern +// Access: Public +// Description: Returns the pattern string that the GlobPattern +// object matches. +//////////////////////////////////////////////////////////////////// +INLINE const string &GlobPattern:: +get_pattern() const { + return _pattern; +} + +//////////////////////////////////////////////////////////////////// +// Function: GlobPattern::matches +// Access: Public +// Description: Returns true if the candidate string matches the +// pattern, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool GlobPattern:: +matches(const string &candidate) const { + return matches_substr(_pattern.begin(), _pattern.end(), + candidate.begin(), candidate.end()); +} + diff --git a/panda/src/putil/globPattern.cxx b/panda/src/putil/globPattern.cxx new file mode 100644 index 0000000000..c593ca0ee3 --- /dev/null +++ b/panda/src/putil/globPattern.cxx @@ -0,0 +1,148 @@ +// Filename: globPattern.cxx +// Created by: drose (30May00) +// +//////////////////////////////////////////////////////////////////// + +#include "globPattern.h" + +//////////////////////////////////////////////////////////////////// +// Function: GlobPattern::matches_substr +// Access: Private +// Description: The recursive implementation of matches(). This +// returns true if the pattern substring [pi, pend) +// matches the candidate substring [ci, cend), false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool GlobPattern:: +matches_substr(string::const_iterator pi, string::const_iterator pend, + string::const_iterator ci, string::const_iterator cend) const { + // If we run out of pattern or candidate string, it's a match only + // if they both ran out at the same time. + if (pi == pend || ci == cend) { + // A special exception: we allow ci to reach the end before pi, + // only if pi is one character before the end and that last + // character is '*'. + if ((ci == cend) && (pi + 1 == pend) && (*pi) == '*') { + return true; + } + return (pi == pend && ci == cend); + } + + switch (*pi) { + + case '*': + // A '*' in the pattern string means to match any sequence of zero + // or more characters in the candidate string. This means we have + // to recurse twice: either consume one character of the candidate + // string and continue to try matching the *, or stop trying to + // match the * here. + return + matches_substr(pi, pend, ci + 1, cend) || + matches_substr(pi + 1, pend, ci, cend); + + case '?': + // A '?' in the pattern string means to match exactly one + // character in the candidate string. That's easy. + return matches_substr(pi + 1, pend, ci + 1, cend); + + case '[': + // An open square bracket begins a set. + ++pi; + if ((*pi) == '!') { + ++pi; + if (matches_set(pi, pend, *ci)) { + return false; + } + } else { + if (!matches_set(pi, pend, *ci)) { + return false; + } + } + if (pi == pend) { + // Oops, there wasn't a closing square bracket. + return false; + } + return matches_substr(pi + 1, pend, ci + 1, cend); + + case '\\': + // A backslash escapes the next special character. + ++pi; + if (pi == pend) { + return false; + } + // fall through. + + default: + // Anything else means to match exactly that. + if ((*pi) != (*ci)) { + return false; + } + return matches_substr(pi + 1, pend, ci + 1, cend); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: GlobPattern::matches_set +// Access: Private +// Description: Called when an unescaped open square bracked is +// scanned, this is called with pi positioned after the +// opening square bracket, scans the set sequence, +// leaving pi positioned on the closing square bracket, +// and returns true if the indicated character matches +// the set of characters indicated, false otherwise. +//////////////////////////////////////////////////////////////////// +bool GlobPattern:: +matches_set(string::const_iterator &pi, string::const_iterator pend, + char ch) const { + bool matched = false; + + while (pi != pend && (*pi) != ']') { + if ((*pi) == '\\') { + // Backslash escapes the next character. + ++pi; + if (pi == pend) { + return false; + } + } + + if (ch == (*pi)) { + matched = true; + } + + // Maybe it's an a-z style range? + char start = (*pi); + ++pi; + if (pi != pend && (*pi) == '-') { + ++pi; + if (pi != pend && (*pi) != ']') { + // Yes, we have a range: start-end. + + if ((*pi) == '\\') { + // Backslash escapes. + ++pi; + if (pi == pend) { + return false; + } + } + + char end = (*pi); + ++pi; + + if (ch >= start && ch <= end) { + matched = true; + } + } else { + // This was a - at the end of the string. + if (ch == '-') { + matched = true; + } + } + } + } + + return matched; +} + + + diff --git a/panda/src/putil/globPattern.h b/panda/src/putil/globPattern.h new file mode 100644 index 0000000000..b618e9bcef --- /dev/null +++ b/panda/src/putil/globPattern.h @@ -0,0 +1,50 @@ +// Filename: globPattern.h +// Created by: drose (30May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GLOBPATTERN_H +#define GLOBPATTERN_H + +#include + +//////////////////////////////////////////////////////////////////// +// Class : GlobPattern +// Description : This class can be used to test for string matches +// against standard Unix-shell filename globbing +// conventions. It serves as a portable standin for the +// Posix fnmatch() call. +// +// A GlobPattern is given a pattern string, which can +// contain operators like *, ?, and []. Then it can be +// tested against any number of candidate strings; for +// each candidate, it will indicate whether the string +// matches the pattern or not. It can be used, for +// example, to scan a directory for all files matching a +// particular pattern. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GlobPattern { +public: + INLINE GlobPattern(const string &pattern = string()); + + INLINE void set_pattern(const string &pattern); + INLINE const string &get_pattern() const; + + INLINE bool matches(const string &candidate) const; + +private: + bool matches_substr(string::const_iterator pi, + string::const_iterator pend, + string::const_iterator ci, + string::const_iterator cend) const; + + bool matches_set(string::const_iterator &pi, + string::const_iterator pend, + char ch) const; + + string _pattern; +}; + +#include "globPattern.I" + +#endif diff --git a/panda/src/putil/globalPointerRegistry.I b/panda/src/putil/globalPointerRegistry.I new file mode 100644 index 0000000000..170e826616 --- /dev/null +++ b/panda/src/putil/globalPointerRegistry.I @@ -0,0 +1,63 @@ +// Filename: globalPointerRegistry.I +// Created by: drose (03Feb00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: GlobalPointerRegistry::get_pointer +// Access: Public, Static +// Description: Returns the pointer associated with the indicated +// TypeHandle, if any. If no pointer has yet been +// associated, returns NULL. +//////////////////////////////////////////////////////////////////// +INLINE void *GlobalPointerRegistry:: +get_pointer(TypeHandle type) { + return get_global_ptr()->ns_get_pointer(type); +} + +//////////////////////////////////////////////////////////////////// +// Function: GlobalPointerRegistry::store_pointer +// Access: Public, Static +// Description: Associates the given pointer with the indicated +// TypeHandle. It is an error to call this with a NULL +// pointer, or to call this function more than once with +// a given TypeHandle (without first calling +// clear_pointer). +//////////////////////////////////////////////////////////////////// +INLINE void GlobalPointerRegistry:: +store_pointer(TypeHandle type, void *ptr) { + get_global_ptr()->ns_store_pointer(type, ptr); +} + +//////////////////////////////////////////////////////////////////// +// Function: GlobalPointerRegistry::clear_pointer +// Access: Public, Static +// Description: Removes the association of the given pointer with the +// indicated TypeHandle. Subsequent calls to +// get_pointer() with this TypeHandle will return NULL, +// until another call to store_pointer() is made. +//////////////////////////////////////////////////////////////////// +INLINE void GlobalPointerRegistry:: +clear_pointer(TypeHandle type) { + get_global_ptr()->ns_clear_pointer(type); +} + + +//////////////////////////////////////////////////////////////////// +// Function: GlobalPointerRegistry::get_global_pointer +// Access: Private, Static +// Description: Returns a pointer to the single GlobalPointerRegistry +// object. If the object does not yet exist, creates +// it. This indirection is used instead of making all +// the data members of GlobalPointerRegistry static, so +// that we don't have to worry about order dependency +// during static init time. +//////////////////////////////////////////////////////////////////// +INLINE GlobalPointerRegistry *GlobalPointerRegistry:: +get_global_ptr() { + if (_global_ptr == (GlobalPointerRegistry *)NULL) { + _global_ptr = new GlobalPointerRegistry; + } + return _global_ptr; +} diff --git a/panda/src/putil/globalPointerRegistry.cxx b/panda/src/putil/globalPointerRegistry.cxx new file mode 100644 index 0000000000..6fa1761fb2 --- /dev/null +++ b/panda/src/putil/globalPointerRegistry.cxx @@ -0,0 +1,95 @@ +// Filename: globalPointerRegistry.cxx +// Created by: drose (03Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "globalPointerRegistry.h" +#include "config_util.h" + +// In general, we use the util_cat->info() syntax in this file +// (instead of util_cat.info()), because much of this work is done at +// static init time, and we must use the arrow syntax to force +// initialization of the util_cat category. + +GlobalPointerRegistry *GlobalPointerRegistry::_global_ptr; + +//////////////////////////////////////////////////////////////////// +// Function: GlobalPointerRegistry::ns_get_pointer +// Access: Private +// Description: Returns the pointer associated with the indicated +// TypeHandle, if any. If no pointer has yet been +// associated, returns NULL. +//////////////////////////////////////////////////////////////////// +void *GlobalPointerRegistry:: +ns_get_pointer(TypeHandle type) const { + if (type == TypeHandle::none()) { + util_cat->error() + << "GlobalPointerRegistry::get_pointer() called on empty TypeHandle\n"; + } + Pointers::const_iterator pi; + pi = _pointers.find(type); + if (pi == _pointers.end()) { + return (void *)NULL; + } + + return (*pi).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: GlobalPointerRegistry::ns_store_pointer +// Access: Private +// Description: Associates the given pointer with the indicated +// TypeHandle. It is an error to call this with a NULL +// pointer, or to call this function more than once with +// a given TypeHandle (without first calling +// clear_pointer). +//////////////////////////////////////////////////////////////////// +void GlobalPointerRegistry:: +ns_store_pointer(TypeHandle type, void *ptr) { + if (type == TypeHandle::none()) { + util_cat->error() + << "GlobalPointerRegistry::store_pointer() called on empty TypeHandle\n"; + } + if (ptr == (void *)NULL) { + util_cat->error() + << "Invalid attempt to store a NULL pointer for " << type << "\n"; + clear_pointer(type); + return; + } + pair result = + _pointers.insert(Pointers::value_type(type, ptr)); + + if (!result.second) { + // There was already a pointer in the map. + if ((*result.first).second == ptr) { + util_cat->error() + << "Invalid attempt to store pointer " << ptr + << " twice for " << type << "\n"; + } else { + util_cat->error() + << "Invalid attempt to store additional pointer " << ptr + << " for " << type << "; " << (*result.first).second + << " stored previously.\n"; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GlobalPointerRegistry::ns_clear_pointer +// Access: Private +// Description: Removes the association of the given pointer with the +// indicated TypeHandle. Subsequent calls to +// get_pointer() with this TypeHandle will return NULL, +// until another call to store_pointer() is made. +//////////////////////////////////////////////////////////////////// +void GlobalPointerRegistry:: +ns_clear_pointer(TypeHandle type) { + if (type == TypeHandle::none()) { + util_cat->error() + << "GlobalPointerRegistry::clear_pointer() called on empty TypeHandle\n"; + } + + // It's not an error to clear_pointer() if it was already cleared. + // Don't bother checking that. + _pointers.erase(type); +} diff --git a/panda/src/putil/globalPointerRegistry.h b/panda/src/putil/globalPointerRegistry.h new file mode 100644 index 0000000000..718a4fc09a --- /dev/null +++ b/panda/src/putil/globalPointerRegistry.h @@ -0,0 +1,76 @@ +// Filename: globalPointerRegistry.h +// Created by: drose (03Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GLOBALPOINTERREGISTRY_H +#define GLOBALPOINTERREGISTRY_H + +#include + +#include "typeHandle.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : GlobalPointerRegistry +// Description : This class maintains a one-to-one mapping from +// TypeHandle to a void * pointer. Its purpose is to +// store a pointer to some class data for a given class. +// +// Normally, one would simply use a static data member +// to store class data. However, when the static data +// is associated with a template class, the dynamic +// loader may have difficulty in properly resolving the +// statics. +// +// Consider: class foo defines a static member, _a. +// There should be only one instance of _a shared +// between all instances of foo, and there will be +// a different instance of _a shared between all +// instances of foo. +// +// Now suppose that two different shared libraries +// instantiate foo. In each .so, there exists a +// different foo::_a. It is the loader's job to +// recognize this and collapse them together when both +// libraries are loaded. This usually works, but +// sometimes it doesn't, and you end up with two +// different instances of foo::_a; some functions +// see one instance, while others see the other. We +// have particularly seen this problem occur under Linux +// with gcc. +// +// This class attempts to circumvent the problem by +// managing pointers to data based on TypeHandle. Since +// the TypeHandle will already be unique based on the +// string name supplied to the init_type() function, it +// can be used to differentiate foo from +// foo, while allowing different instances of +// foo to guarantee that they share the same static +// data. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GlobalPointerRegistry { +public: + INLINE static void *get_pointer(TypeHandle type); + INLINE static void store_pointer(TypeHandle type, void *ptr); + INLINE static void clear_pointer(TypeHandle type); + +private: + // Nonstatic implementations of the above functions. + void *ns_get_pointer(TypeHandle type) const; + void ns_store_pointer(TypeHandle type, void *ptr); + void ns_clear_pointer(TypeHandle type); + + INLINE static GlobalPointerRegistry *get_global_ptr(); + static GlobalPointerRegistry *_global_ptr; + +private: + typedef map Pointers; + Pointers _pointers; +}; + +#include "globalPointerRegistry.I" + +#endif + diff --git a/panda/src/putil/indirectCompareTo.I b/panda/src/putil/indirectCompareTo.I new file mode 100644 index 0000000000..9b597df6da --- /dev/null +++ b/panda/src/putil/indirectCompareTo.I @@ -0,0 +1,15 @@ +// Filename: indirectCompareTo.I +// Created by: drose (04Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: IndirectCompareTo::operator () +// Access: Public +// Description: Returns true if a sorts before b, false otherwise. +//////////////////////////////////////////////////////////////////// +template +INLINE bool IndirectCompareTo:: +operator () (const ObjectType *a, const ObjectType *b) const { + return (a != b && a->compare_to(*b) < 0); +} diff --git a/panda/src/putil/indirectCompareTo.h b/panda/src/putil/indirectCompareTo.h new file mode 100644 index 0000000000..dae79765ab --- /dev/null +++ b/panda/src/putil/indirectCompareTo.h @@ -0,0 +1,27 @@ +// Filename: indirectCompareTo.h +// Created by: drose (04Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INDIRECTCOMPARETO_H +#define INDIRECTCOMPARETO_H + +#include + +//////////////////////////////////////////////////////////////////// +// Class : IndirectCompareTo +// Description : An STL function object class, this is intended to be +// used on any ordered collection of pointers to classes +// that contain a compare_to() method. It defines the +// order of the pointers via compare_to(). +//////////////////////////////////////////////////////////////////// +template +class IndirectCompareTo { +public: + INLINE bool operator () (const ObjectType *a, const ObjectType *b) const; +}; + +#include "indirectCompareTo.I" + +#endif + diff --git a/panda/src/putil/ioPtaDatagramFloat.cxx b/panda/src/putil/ioPtaDatagramFloat.cxx new file mode 100644 index 0000000000..ce725c7997 --- /dev/null +++ b/panda/src/putil/ioPtaDatagramFloat.cxx @@ -0,0 +1,42 @@ +// Filename: ioPtaDatagramFloat.cxx +// Created by: charles (10Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "ioPtaDatagramFloat.h" +#include "datagram.h" +#include "datagramIterator.h" + +//////////////////////////////////////////////////////////////////// +// Function: IoPtaDatagramFloat::write_datagram +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +void IoPtaDatagramFloat:: +write_datagram(Datagram &dest, CPTA_float array) +{ + dest.add_uint32(array.size()); + for (int i = 0; i < array.size(); i++) { + dest.add_float64(array[i]); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: IoPtaDatagramFloat::read_datagram +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +PTA_float IoPtaDatagramFloat:: +read_datagram(DatagramIterator &source) +{ + PTA_float array; + + int size = source.get_uint32(); + for (int i = 0; i < size; i++) { + array.push_back(source.get_float64()); + } + + return array; +} diff --git a/panda/src/putil/ioPtaDatagramFloat.h b/panda/src/putil/ioPtaDatagramFloat.h new file mode 100644 index 0000000000..677ee6f02f --- /dev/null +++ b/panda/src/putil/ioPtaDatagramFloat.h @@ -0,0 +1,33 @@ +// Filename: ioPtaDatagramFloat.h +// Created by: charles (10Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef _IO_PTA_DATAGRAM_FLOAT +#define _IO_PTA_DATAGRAM_FLOAT + +#include + +#include "pointerToArray.h" +#include "pta_float.h" + +class Datagram; +class DatagramIterator; + +/////////////////////////////////////////////////////////////////// +// Class : IoPtaDatagramFloat +// Description : This class is used to read and write a PTA_float +// from a Datagram, in support of Bam. It's not +// intended to be constructed; it's just a convenient +// place to scope these static methods which should be +// called directly. +//////////////////////////////////////////////////////////////////// +class IoPtaDatagramFloat { +public: + static void write_datagram(Datagram &dest, CPTA_float array); + static PTA_float read_datagram(DatagramIterator &source); +}; + +typedef IoPtaDatagramFloat IPD_float; + +#endif // _IO_PTA_DATAGRAM_FLOAT diff --git a/panda/src/putil/ioPtaDatagramInt.cxx b/panda/src/putil/ioPtaDatagramInt.cxx new file mode 100644 index 0000000000..2d1b67f114 --- /dev/null +++ b/panda/src/putil/ioPtaDatagramInt.cxx @@ -0,0 +1,45 @@ +// Filename: ioPtaDatagramInt.cxx +// Created by: jason (26Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "ioPtaDatagramInt.h" +#include "datagram.h" +#include "datagramIterator.h" + +//////////////////////////////////////////////////////////////////// +// Function: IoPtaDatagramInt::write_datagram +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +void IoPtaDatagramInt:: +write_datagram(Datagram &dest, CPTA_int array) +{ + dest.add_uint32(array.size()); + for(int i = 0; i < array.size(); i++) + { + dest.add_uint32(array[i]); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: IoPtaDatagramInt::read_datagram +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +PTA_int IoPtaDatagramInt:: +read_datagram(DatagramIterator &source) +{ + PTA_int array; + + int size = source.get_uint32(); + for(int i = 0; i < size; i++) + { + array.push_back(source.get_uint32()); + } + + return array; +} + diff --git a/panda/src/putil/ioPtaDatagramInt.h b/panda/src/putil/ioPtaDatagramInt.h new file mode 100644 index 0000000000..d509adcaba --- /dev/null +++ b/panda/src/putil/ioPtaDatagramInt.h @@ -0,0 +1,33 @@ +// Filename: ioPtaDatagramInt.h +// Created by: jason (26Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef _IO_PTA_DATAGRAM_INT +#define _IO_PTA_DATAGRAM_INT + +#include + +#include "pointerToArray.h" +#include "pta_int.h" + +class Datagram; +class DatagramIterator; + +/////////////////////////////////////////////////////////////////// +// Class : IoPtaDatagramInt +// Description : This class is used to read and write a PTA_int from a +// Datagram, in support of Bam. It's not intended to be +// constructed; it's just a convenient place to scope +// these static methods which should be called directly. +//////////////////////////////////////////////////////////////////// +class IoPtaDatagramInt { +public: + static void write_datagram(Datagram &dest, CPTA_int array); + static PTA_int read_datagram(DatagramIterator &source); +}; + +typedef IoPtaDatagramInt IPD_int; + +#endif + diff --git a/panda/src/putil/ioPtaDatagramShort.cxx b/panda/src/putil/ioPtaDatagramShort.cxx new file mode 100644 index 0000000000..eb4c19d8e9 --- /dev/null +++ b/panda/src/putil/ioPtaDatagramShort.cxx @@ -0,0 +1,44 @@ +// Filename: ioPtaDatagramShort.cxx +// Created by: jason (26Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "ioPtaDatagramShort.h" +#include "datagram.h" +#include "datagramIterator.h" + +//////////////////////////////////////////////////////////////////// +// Function: IoPtaDatagramShort::write_datagram +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +void IoPtaDatagramShort:: +write_datagram(Datagram &dest, CPTA_ushort array) +{ + dest.add_uint32(array.size()); + for(int i = 0; i < array.size(); i++) + { + dest.add_uint16(array[i]); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: IoPtaDatagramShort::read_datagram +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +PTA_ushort IoPtaDatagramShort:: +read_datagram(DatagramIterator &source) +{ + PTA_ushort array; + + int size = source.get_uint32(); + for(int i = 0; i < size; i++) + { + array.push_back(source.get_uint16()); + } + + return array; +} diff --git a/panda/src/putil/ioPtaDatagramShort.h b/panda/src/putil/ioPtaDatagramShort.h new file mode 100644 index 0000000000..f378e6384d --- /dev/null +++ b/panda/src/putil/ioPtaDatagramShort.h @@ -0,0 +1,33 @@ +// Filename: ioPtaDatagramShort.h +// Created by: jason (26Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef _IO_PTA_DATAGRAM_SHORT +#define _IO_PTA_DATAGRAM_SHORT + +#include + +#include "pointerToArray.h" +#include "pta_ushort.h" + +class Datagram; +class DatagramIterator; + +/////////////////////////////////////////////////////////////////// +// Class : IoPtaDatagramShort +// Description : This class is used to read and write a PTA_ushort +// from a Datagram, in support of Bam. It's not +// intended to be constructed; it's just a convenient +// place to scope these static methods which should be +// called directly. +//////////////////////////////////////////////////////////////////// +class IoPtaDatagramShort { +public: + static void write_datagram(Datagram &dest, CPTA_ushort array); + static PTA_ushort read_datagram(DatagramIterator &source); +}; + +typedef IoPtaDatagramShort IPD_ushort; + +#endif diff --git a/panda/src/putil/iterator_types.h b/panda/src/putil/iterator_types.h new file mode 100644 index 0000000000..833492c842 --- /dev/null +++ b/panda/src/putil/iterator_types.h @@ -0,0 +1,72 @@ +// Filename: iterator_types.h +// Created by: drose (10Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ITERATOR_TYPES_H +#define ITERATOR_TYPES_H + + +//////////////////////////////////////////////////////////////////// +// Class : first_of_pair_iterator +// Description : This is an iterator adaptor that converts any +// iterator that returns a pair (e.g. a map iterator) +// into one that returns just the first component of +// that pair. +//////////////////////////////////////////////////////////////////// +template +class EXPCL_PANDA first_of_pair_iterator : public pair_iterator { +public: + typedef TYPENAME pair_iterator::value_type::first_type value_type; + + first_of_pair_iterator() { } + first_of_pair_iterator(const pair_iterator &init) : pair_iterator(init) { } + first_of_pair_iterator(const first_of_pair_iterator ©) : pair_iterator(copy) { } + + value_type operator *() { + return pair_iterator::operator *().first; + } +}; + +//////////////////////////////////////////////////////////////////// +// Class : second_of_pair_iterator +// Description : This is an iterator adaptor that converts any +// iterator that returns a pair (e.g. a map iterator) +// into one that returns just the second component of +// that pair. +//////////////////////////////////////////////////////////////////// +template +class EXPCL_PANDA second_of_pair_iterator : public pair_iterator { +public: + typedef TYPENAME pair_iterator::value_type::second_type value_type; + + second_of_pair_iterator() { } + second_of_pair_iterator(const pair_iterator &init) : pair_iterator(init) { } + second_of_pair_iterator(const second_of_pair_iterator ©) : pair_iterator(copy) { } + + value_type operator *() { + return pair_iterator::operator *().second; + } +}; + +//////////////////////////////////////////////////////////////////// +// Class : typecast_iterator +// Description : This is an iterator adaptor that explicitly typecasts +// each value returned by the base iterator to the +// indicated type. +//////////////////////////////////////////////////////////////////// +template +class EXPCL_PANDA typecast_iterator : public base_iterator { +public: + typedef new_type value_type; + + typecast_iterator() { } + typecast_iterator(const base_iterator &init) : base_iterator(init) { } + typecast_iterator(const typecast_iterator ©) : base_iterator(copy) { } + + value_type operator *() { + return (new_type)base_iterator::operator *(); + } +}; + +#endif diff --git a/panda/src/putil/keyboardButton.cxx b/panda/src/putil/keyboardButton.cxx new file mode 100644 index 0000000000..5bcd000567 --- /dev/null +++ b/panda/src/putil/keyboardButton.cxx @@ -0,0 +1,291 @@ +// Filename: keyboardButton.cxx +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "keyboardButton.h" +#include "buttonRegistry.h" + +#include + +static ButtonHandle _space; +static ButtonHandle _backspace; +static ButtonHandle _tab; +static ButtonHandle _enter; +static ButtonHandle _escape; + +static ButtonHandle _f1; +static ButtonHandle _f2; +static ButtonHandle _f3; +static ButtonHandle _f4; +static ButtonHandle _f5; +static ButtonHandle _f6; +static ButtonHandle _f7; +static ButtonHandle _f8; +static ButtonHandle _f9; +static ButtonHandle _f10; +static ButtonHandle _f11; +static ButtonHandle _f12; + +static ButtonHandle _left; +static ButtonHandle _right; +static ButtonHandle _up; +static ButtonHandle _down; +static ButtonHandle _page_up; +static ButtonHandle _page_down; +static ButtonHandle _home; +static ButtonHandle _end; +static ButtonHandle _insert; +static ButtonHandle _del; + +static ButtonHandle _shift; +static ButtonHandle _control; +static ButtonHandle _alt; +static ButtonHandle _meta; +static ButtonHandle _caps_lock; +static ButtonHandle _shift_lock; + + +//////////////////////////////////////////////////////////////////// +// Function: KeyboardButton::ascii_key +// Access: Public, Static +// Description: Returns the ButtonHandle associated with the +// particular ASCII character, if there is one, or +// ButtonHandle::none() if there is not. +//////////////////////////////////////////////////////////////////// +ButtonHandle KeyboardButton:: +ascii_key(char ascii_equivalent) { + return ButtonRegistry::ptr()->find_button(ascii_equivalent); +} + +//////////////////////////////////////////////////////////////////// +// Function: KeyboardButton::space +// Access: Public, Static +// Description: Returns the ButtonHandle associated with the +// Space bar. +//////////////////////////////////////////////////////////////////// +ButtonHandle KeyboardButton:: +space() { + return _space; +} + +//////////////////////////////////////////////////////////////////// +// Function: KeyboardButton::backspace +// Access: Public, Static +// Description: Returns the ButtonHandle associated with the +// Backspace key. Most of the remaining methods in this +// file are similar. +//////////////////////////////////////////////////////////////////// +ButtonHandle KeyboardButton:: +backspace() { + return _backspace; +} + +ButtonHandle KeyboardButton:: +tab() { + return _tab; +} + +ButtonHandle KeyboardButton:: +enter() { + return _enter; +} + +ButtonHandle KeyboardButton:: +escape() { + return _escape; +} + +ButtonHandle KeyboardButton:: +f1() { + return _f1; +} + +ButtonHandle KeyboardButton:: +f2() { + return _f2; +} + +ButtonHandle KeyboardButton:: +f3() { + return _f3; +} + +ButtonHandle KeyboardButton:: +f4() { + return _f4; +} + +ButtonHandle KeyboardButton:: +f5() { + return _f5; +} + +ButtonHandle KeyboardButton:: +f6() { + return _f6; +} + +ButtonHandle KeyboardButton:: +f7() { + return _f7; +} + +ButtonHandle KeyboardButton:: +f8() { + return _f8; +} + +ButtonHandle KeyboardButton:: +f9() { + return _f9; +} + +ButtonHandle KeyboardButton:: +f10() { + return _f10; +} + +ButtonHandle KeyboardButton:: +f11() { + return _f11; +} + +ButtonHandle KeyboardButton:: +f12() { + return _f12; +} + +ButtonHandle KeyboardButton:: +left() { + return _left; +} + +ButtonHandle KeyboardButton:: +right() { + return _right; +} + +ButtonHandle KeyboardButton:: +up() { + return _up; +} + +ButtonHandle KeyboardButton:: +down() { + return _down; +} + +ButtonHandle KeyboardButton:: +page_up() { + return _page_up; +} + +ButtonHandle KeyboardButton:: +page_down() { + return _page_down; +} + +ButtonHandle KeyboardButton:: +home() { + return _home; +} + +ButtonHandle KeyboardButton:: +end() { + return _end; +} + +ButtonHandle KeyboardButton:: +insert() { + return _insert; +} + +ButtonHandle KeyboardButton:: +del() { + return _del; +} + +ButtonHandle KeyboardButton:: +shift() { + return _shift; +} + +ButtonHandle KeyboardButton:: +control() { + return _control; +} + +ButtonHandle KeyboardButton:: +alt() { + return _alt; +} + +ButtonHandle KeyboardButton:: +meta() { + return _meta; +} + +ButtonHandle KeyboardButton:: +caps_lock() { + return _caps_lock; +} + +ButtonHandle KeyboardButton:: +shift_lock() { + return _shift_lock; +} + +//////////////////////////////////////////////////////////////////// +// Function: KeyboardButton::init_keyboard_buttons +// Access: Public, Static +// Description: This is intended to be called only once, by the +// static initialization performed in config_util.cxx. +//////////////////////////////////////////////////////////////////// +void KeyboardButton:: +init_keyboard_buttons() { + ButtonRegistry::ptr()->register_button(_space, "space", ' '); + ButtonRegistry::ptr()->register_button(_backspace, "backspace", '\x08'); + ButtonRegistry::ptr()->register_button(_tab, "tab", '\x09'); + ButtonRegistry::ptr()->register_button(_enter, "enter", '\x0d'); + ButtonRegistry::ptr()->register_button(_escape, "escape", '\x1b'); + ButtonRegistry::ptr()->register_button(_del, "delete", '\x7f'); + + ButtonRegistry::ptr()->register_button(_f1, "f1"); + ButtonRegistry::ptr()->register_button(_f2, "f2"); + ButtonRegistry::ptr()->register_button(_f3, "f3"); + ButtonRegistry::ptr()->register_button(_f4, "f4"); + ButtonRegistry::ptr()->register_button(_f5, "f5"); + ButtonRegistry::ptr()->register_button(_f6, "f6"); + ButtonRegistry::ptr()->register_button(_f7, "f7"); + ButtonRegistry::ptr()->register_button(_f8, "f8"); + ButtonRegistry::ptr()->register_button(_f9, "f9"); + ButtonRegistry::ptr()->register_button(_f10, "f10"); + ButtonRegistry::ptr()->register_button(_f11, "f11"); + ButtonRegistry::ptr()->register_button(_f12, "f12"); + + ButtonRegistry::ptr()->register_button(_left, "left"); + ButtonRegistry::ptr()->register_button(_right, "right"); + ButtonRegistry::ptr()->register_button(_up, "up"); + ButtonRegistry::ptr()->register_button(_down, "down"); + ButtonRegistry::ptr()->register_button(_page_up, "page_up"); + ButtonRegistry::ptr()->register_button(_page_down, "page_down"); + ButtonRegistry::ptr()->register_button(_home, "home"); + ButtonRegistry::ptr()->register_button(_end, "end"); + ButtonRegistry::ptr()->register_button(_insert, "insert"); + + ButtonRegistry::ptr()->register_button(_shift, "shift"); + ButtonRegistry::ptr()->register_button(_control, "control"); + ButtonRegistry::ptr()->register_button(_alt, "alt"); + ButtonRegistry::ptr()->register_button(_meta, "meta"); + ButtonRegistry::ptr()->register_button(_caps_lock, "caps_lock"); + ButtonRegistry::ptr()->register_button(_shift_lock, "shift_lock"); + + // Also register all of the visible ASCII characters. + for (int i = 32; i < 127; i++) { + if (isgraph(i)) { + ButtonHandle key; + ButtonRegistry::ptr()->register_button(key, string(1, (char)i), i); + } + } +} diff --git a/panda/src/putil/keyboardButton.h b/panda/src/putil/keyboardButton.h new file mode 100644 index 0000000000..5d25fb7eeb --- /dev/null +++ b/panda/src/putil/keyboardButton.h @@ -0,0 +1,63 @@ +// Filename: keyboardButton.h +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef KEYBOARDBUTTON_H +#define KEYBOARDBUTTON_H + +#include + +#include "buttonHandle.h" + +//////////////////////////////////////////////////////////////////// +// Class : KeyboardButton +// Description : This class is just used as a convenient namespace for +// grouping all of these handy functions that return +// buttons which map to standard keyboard keys. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA KeyboardButton { +public: + static ButtonHandle ascii_key(char ascii_equivalent); + + static ButtonHandle space(); + static ButtonHandle backspace(); + static ButtonHandle tab(); + static ButtonHandle enter(); + static ButtonHandle escape(); + + static ButtonHandle f1(); + static ButtonHandle f2(); + static ButtonHandle f3(); + static ButtonHandle f4(); + static ButtonHandle f5(); + static ButtonHandle f6(); + static ButtonHandle f7(); + static ButtonHandle f8(); + static ButtonHandle f9(); + static ButtonHandle f10(); + static ButtonHandle f11(); + static ButtonHandle f12(); + + static ButtonHandle left(); + static ButtonHandle right(); + static ButtonHandle up(); + static ButtonHandle down(); + static ButtonHandle page_up(); + static ButtonHandle page_down(); + static ButtonHandle home(); + static ButtonHandle end(); + static ButtonHandle insert(); + static ButtonHandle del(); // delete is a C++ keyword. + + static ButtonHandle shift(); + static ButtonHandle control(); + static ButtonHandle alt(); + static ButtonHandle meta(); + static ButtonHandle caps_lock(); + static ButtonHandle shift_lock(); + + static void init_keyboard_buttons(); +}; + +#endif diff --git a/panda/src/putil/lineStream.I b/panda/src/putil/lineStream.I new file mode 100644 index 0000000000..2ec93e8542 --- /dev/null +++ b/panda/src/putil/lineStream.I @@ -0,0 +1,53 @@ +// Filename: lineStream.I +// Created by: drose (26Feb00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: LineStream::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LineStream:: +LineStream() : ostream(&_lsb) { +} + +//////////////////////////////////////////////////////////////////// +// Function: LineStream::is_text_available +// Access: Public +// Description: Returns true if there is at least one line of text +// (or even a partial line) available in the LineStream +// object. If this returns true, the line may then be +// retrieved via get_line(). +//////////////////////////////////////////////////////////////////// +INLINE bool LineStream:: +is_text_available() const { + return _lsb.is_text_available(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LineStream::get_line +// Access: Public +// Description: Extracts and returns the next line (or partial line) +// of text available in the LineStream object. Once the +// line has been extracted, you may call has_newline() +// to determine whether or not there was an explicit +// newline character written following this line. +//////////////////////////////////////////////////////////////////// +INLINE string LineStream:: +get_line() { + return _lsb.get_line(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LineStream::has_newline +// Access: Public +// Description: Returns true if the line of text most recently +// returned by get_line() was written out with a +// terminating newline, or false if a newline character +// has not yet been written to the LineStream. +//////////////////////////////////////////////////////////////////// +INLINE bool LineStream:: +has_newline() const { + return _lsb.has_newline(); +} diff --git a/panda/src/putil/lineStream.cxx b/panda/src/putil/lineStream.cxx new file mode 100644 index 0000000000..c01acdd2b0 --- /dev/null +++ b/panda/src/putil/lineStream.cxx @@ -0,0 +1,6 @@ +// Filename: lineStream.cxx +// Created by: drose (26Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "lineStream.h" diff --git a/panda/src/putil/lineStream.h b/panda/src/putil/lineStream.h new file mode 100644 index 0000000000..3ee270feda --- /dev/null +++ b/panda/src/putil/lineStream.h @@ -0,0 +1,41 @@ +// Filename: lineStream.h +// Created by: drose (26Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINESTREAM_H +#define LINESTREAM_H + +#include + +#include "lineStreamBuf.h" + +//////////////////////////////////////////////////////////////////// +// Class : LineStream +// Description : This is a special ostream that writes to a memory +// buffer, like ostrstream. However, its contents can +// be continuously extracted as a sequence of lines of +// text. +// +// Unlike ostrstream, which can only be extracted from +// once (and then the buffer freezes and it can no +// longer be written to), the LineStream is not +// otherwise affected when a line of text is extracted. +// More text can still be written to it and continuously +// extracted. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA LineStream : public ostream { +public: + INLINE LineStream(); + + INLINE bool is_text_available() const; + INLINE string get_line(); + INLINE bool has_newline() const; + +private: + LineStreamBuf _lsb; +}; + +#include "lineStream.I" + +#endif diff --git a/panda/src/putil/lineStreamBuf.I b/panda/src/putil/lineStreamBuf.I new file mode 100644 index 0000000000..f64be57b34 --- /dev/null +++ b/panda/src/putil/lineStreamBuf.I @@ -0,0 +1,47 @@ +// Filename: lineStreamBuf.I +// Created by: drose (26Feb00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: LineStreamBuf::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LineStreamBuf:: +LineStreamBuf() { + _has_newline = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: LineStreamBuf::is_text_available +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool LineStreamBuf:: +is_text_available() const { + return !_data.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LineStreamBuf::has_newline +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool LineStreamBuf:: +has_newline() const { + return _has_newline; +} + +//////////////////////////////////////////////////////////////////// +// Function: LineStreamBuf::write_chars +// Access: Public +// Description: An internal function called by sync() and overflow() +// to store one or more characters written to the stream +// into the memory buffer. +//////////////////////////////////////////////////////////////////// +INLINE void LineStreamBuf:: +write_chars(const char *start, int length) { + _data += string(start, length); +} diff --git a/panda/src/putil/lineStreamBuf.cxx b/panda/src/putil/lineStreamBuf.cxx new file mode 100644 index 0000000000..bb9e42d0b2 --- /dev/null +++ b/panda/src/putil/lineStreamBuf.cxx @@ -0,0 +1,86 @@ +// Filename: lineStreamBuf.cxx +// Created by: drose (26Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "lineStreamBuf.h" + +#ifdef PENV_SGI +// SGI compiler doesn't seem to define this yet. +typedef int streamsize; +#endif + +//////////////////////////////////////////////////////////////////// +// Function: LineStreamBuf::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +LineStreamBuf:: +~LineStreamBuf() { + sync(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LineStreamBuf::get_line +// Access: Public +// Description: Extracts the next line of text from the +// LineStreamBuf, and sets the has_newline() flag +// according to whether this line had a trailing newline +// or not. +//////////////////////////////////////////////////////////////////// +string LineStreamBuf:: +get_line() { + // Extract the data up to, but not including, the next newline + // character. + size_t nl = _data.find('\n'); + if (nl == string::npos) { + // No trailing newline; return the remainder of the string. + _has_newline = false; + string result = _data; + _data = ""; + return result; + } + + _has_newline = true; + string result = _data.substr(0, nl); + _data = _data.substr(nl + 1); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: LineStreamBuf::sync +// Access: Public, Virtual +// Description: Called by the system ostream implementation when the +// buffer should be flushed to output (for instance, on +// destruction). +//////////////////////////////////////////////////////////////////// +int LineStreamBuf:: +sync() { + streamsize n = pptr() - pbase(); + write_chars(pbase(), n); + return 0; // EOF to indicate write full. +} + +//////////////////////////////////////////////////////////////////// +// Function: LineStreamBuf::overflow +// Access: Public, Virtual +// Description: Called by the system ostream implementation when its +// internal buffer is filled, plus one character. +//////////////////////////////////////////////////////////////////// +int LineStreamBuf:: +overflow(int ch) { + streamsize n = pptr() - pbase(); + + if (n != 0 && sync() != 0) { + return EOF; + } + + if (ch != EOF) { + // Write one more character. + char c = ch; + write_chars(&c, 1); + } + + pbump(-n); // Reset pptr(). + return 0; +} diff --git a/panda/src/putil/lineStreamBuf.h b/panda/src/putil/lineStreamBuf.h new file mode 100644 index 0000000000..b01d8fbe9c --- /dev/null +++ b/panda/src/putil/lineStreamBuf.h @@ -0,0 +1,42 @@ +// Filename: lineStreamBuf.h +// Created by: drose (26Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINESTREAMBUF_H +#define LINESTREAMBUF_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : LineStreamBuf +// Description : Used by LineStream to implement an ostream that +// writes to a memory buffer, whose contents can be +// continuously extracted as a sequence of lines of +// text. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA LineStreamBuf : public streambuf { +public: + INLINE LineStreamBuf(); + virtual ~LineStreamBuf(); + + INLINE bool is_text_available() const; + string get_line(); + INLINE bool has_newline() const; + +protected: + virtual int overflow(int c); + virtual int sync(); + +private: + INLINE void write_chars(const char *start, int length); + + string _data; + bool _has_newline; +}; + +#include "lineStreamBuf.I" + +#endif diff --git a/panda/src/putil/modifierButtons.I b/panda/src/putil/modifierButtons.I new file mode 100644 index 0000000000..a22a48c60b --- /dev/null +++ b/panda/src/putil/modifierButtons.I @@ -0,0 +1,88 @@ +// Filename: modifierButtons.I +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ModifierButtons:: +operator = (const ModifierButtons ©) { + _button_list = copy._button_list; + _state = copy._state; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::Equality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ModifierButtons:: +operator == (const ModifierButtons &other) const { + return (_button_list == other._button_list && + _state == other._state); +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ModifierButtons:: +operator != (const ModifierButtons &other) const { + return !operator == (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ModifierButtons:: +operator < (const ModifierButtons &other) const { + if (_button_list != other._button_list) { + return _button_list < other._button_list; + } + return _state < other._state; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::get_num_buttons +// Access: Public +// Description: Returns the number of buttons that the +// ModifierButtons object is monitoring (e.g. the number +// of buttons passed to add_button()). +//////////////////////////////////////////////////////////////////// +INLINE int ModifierButtons:: +get_num_buttons() const { + return _button_list.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::get_button +// Access: Public +// Description: Returns the nth button that the ModifierButtons +// object is monitoring (the nth button passed to +// add_button()). This must be in the range 0 <= index +// < get_num_buttons(). +//////////////////////////////////////////////////////////////////// +INLINE ButtonHandle ModifierButtons:: +get_button(int index) const { + nassertr(index >= 0 && index < (int)_button_list.size(), ButtonHandle::none()); + return _button_list[index]; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::is_down +// Access: Public +// Description: Returns true if the indicated button is known to be +// down, or false if it is known to be up. +//////////////////////////////////////////////////////////////////// +INLINE bool ModifierButtons:: +is_down(int index) const { + nassertr(index >= 0 && index < (int)_button_list.size(), false); + return ((_state & ((BitmaskType)1 << index)) != 0); +} diff --git a/panda/src/putil/modifierButtons.cxx b/panda/src/putil/modifierButtons.cxx new file mode 100644 index 0000000000..6d021e93d5 --- /dev/null +++ b/panda/src/putil/modifierButtons.cxx @@ -0,0 +1,242 @@ +// Filename: modifierButtons.cxx +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "modifierButtons.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ModifierButtons:: +ModifierButtons() : + _button_list(0), + _state(0) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ModifierButtons:: +ModifierButtons(const ModifierButtons ©) : + _button_list(copy._button_list), + _state(copy._state) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ModifierButtons:: +~ModifierButtons() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::add_button +// Access: Public +// Description: Adds the indicated button to the set of buttons that +// will be monitored for upness and downness. Returns +// true if the button was added, false if it was already +// being monitored or if too many buttons are currently +// being monitored. +//////////////////////////////////////////////////////////////////// +bool ModifierButtons:: +add_button(ButtonHandle button) { + nassertr(button != ButtonHandle::none(), false); + + static const int max_buttons = sizeof(BitmaskType) * 8; + + if (_button_list.size() >= max_buttons) { + return false; + } + + // First, check to see if the button is already being monitored. + if (has_button(button)) { + return false; + } + + // Ok, it's not; add it. + modify_button_list(); + _button_list.push_back(button); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::has_button +// Access: Public +// Description: Returns true if the indicated button is in the set of +// buttons being monitored, false otherwise. +//////////////////////////////////////////////////////////////////// +bool ModifierButtons:: +has_button(ButtonHandle button) const { + PTA(ButtonHandle)::const_iterator bi; + for (bi = _button_list.begin(); bi != _button_list.end(); ++bi) { + if (button == (*bi)) { + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::remove_button +// Access: Public +// Description: Removes the indicated button from the set of buttons +// being monitored. Returns true if the button was +// removed, false if it was not being monitored in the +// first place. +//////////////////////////////////////////////////////////////////// +bool ModifierButtons:: +remove_button(ButtonHandle button) { + // We use i instead of an iterator, because we need to call + // modify_button_list() just before we remove the button, and that + // may invalidate all of the iterators. + + for (int i = 0; i < _button_list.size(); i++) { + if (button == _button_list[i]) { + modify_button_list(); + _button_list.erase(_button_list.begin() + i); + + // Now remove the corresponding bit from the bitmask and shift + // all the bits above it down. + BitmaskType mask = ((BitmaskType)1 << i); + BitmaskType below = mask - 1; + BitmaskType above = (~below) & (~mask); + + _state = ((_state & above) >> 1) | (_state & below); + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::button_down +// Access: Public +// Description: Records that a particular button has been pressed. +// If the given button is one of the buttons that is +// currently being monitored, this will update the +// internal state appropriately; otherwise, it will do +// nothing. +//////////////////////////////////////////////////////////////////// +void ModifierButtons:: +button_down(ButtonHandle button) { + for (int i = 0; i < _button_list.size(); i++) { + if (button == _button_list[i]) { + _state |= ((BitmaskType)1 << i); + return; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::button_up +// Access: Public +// Description: Records that a particular button has been released. +// If the given button is one of the buttons that is +// currently being monitored, this will update the +// internal state appropriately; otherwise, it will do +// nothing. +//////////////////////////////////////////////////////////////////// +void ModifierButtons:: +button_up(ButtonHandle button) { + for (int i = 0; i < _button_list.size(); i++) { + if (button == _button_list[i]) { + _state &= ~((BitmaskType)1 << i); + return; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::is_down +// Access: Public +// Description: Returns true if the indicated button is known to be +// down, or false if it is known to be up or if it is +// not in the set of buttons being tracked. +//////////////////////////////////////////////////////////////////// +bool ModifierButtons:: +is_down(ButtonHandle button) const { + for (int i = 0; i < _button_list.size(); i++) { + if (button == _button_list[i]) { + return ((_state & ((BitmaskType)1 << i)) != 0); + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::output +// Access: Public +// Description: Writes a one-line summary of the buttons known to be +// down. +//////////////////////////////////////////////////////////////////// +void ModifierButtons:: +output(ostream &out) const { + out << "["; + for (int i = 0; i < _button_list.size(); i++) { + if ((_state & ((BitmaskType)1 << i)) != 0) { + out << " " << _button_list[i]; + } + } + out << " ]"; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::write +// Access: Public +// Description: Writes a multi-line summary including all of the +// buttons being monitored and which ones are known to +// be down. +//////////////////////////////////////////////////////////////////// +void ModifierButtons:: +write(ostream &out) const { + out << "ModifierButtons:\n"; + for (int i = 0; i < _button_list.size(); i++) { + out << " " << _button_list[i]; + if ((_state & ((BitmaskType)1 << i)) != 0) { + out << " (down)"; + } + out << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ModifierButtons::modify_button_list +// Access: Private +// Description: Implements a poor-man's copy-on-write for the +// ModifierButtons class. If any reference counts are +// held on our _button_list, besides ourselves, then +// allocates and copies a brand new copy of the +// _button_list. This should be done in preparation for +// any modifications to the _button_list, since multiple +// instances of the ModifierButtons object may share the +// same _button_list pointer. +//////////////////////////////////////////////////////////////////// +void ModifierButtons:: +modify_button_list() { + if (_button_list.get_count() > 1) { + PTA(ButtonHandle) old_list = _button_list; + _button_list = PTA(ButtonHandle)(0); + + // This forces a new allocation and memberwise copy, instead of + // just a reference-counting pointer copy. + _button_list.v() = old_list.v(); + } + + // Now we should be the only ones holding a count. + nassertv(_button_list.get_count() == 1); +} diff --git a/panda/src/putil/modifierButtons.h b/panda/src/putil/modifierButtons.h new file mode 100644 index 0000000000..9fa037d3fc --- /dev/null +++ b/panda/src/putil/modifierButtons.h @@ -0,0 +1,63 @@ +// Filename: modifierButtons.h +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MODIFIERBUTTONS_H +#define MODIFIERBUTTONS_H + +#include + +#include "buttonHandle.h" +#include "pointerToArray.h" + +//////////////////////////////////////////////////////////////////// +// Class : ModifierButtons +// Description : This class monitors the state of a number of +// individual buttons and tracks whether each button is +// known to be down or up. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ModifierButtons { +public: + ModifierButtons(); + ModifierButtons(const ModifierButtons ©); + ~ModifierButtons(); + INLINE void operator = (const ModifierButtons ©); + + INLINE bool operator == (const ModifierButtons &other) const; + INLINE bool operator != (const ModifierButtons &other) const; + INLINE bool operator < (const ModifierButtons &other) const; + + bool add_button(ButtonHandle button); + bool has_button(ButtonHandle button) const; + bool remove_button(ButtonHandle button); + + INLINE int get_num_buttons() const; + INLINE ButtonHandle get_button(int index) const; + + void button_down(ButtonHandle button); + void button_up(ButtonHandle button); + + bool is_down(ButtonHandle button) const; + INLINE bool is_down(int index) const; + + void output(ostream &out) const; + void write(ostream &out) const; + +private: + void modify_button_list(); + + PTA(ButtonHandle) _button_list; + typedef unsigned long BitmaskType; + BitmaskType _state; +}; + +INLINE ostream &operator << (ostream &out, const ModifierButtons &mb) { + mb.output(out); + return out; +} + +#include "modifierButtons.I" + +#endif + diff --git a/panda/src/putil/mouseButton.cxx b/panda/src/putil/mouseButton.cxx new file mode 100644 index 0000000000..d179954a82 --- /dev/null +++ b/panda/src/putil/mouseButton.cxx @@ -0,0 +1,81 @@ +// Filename: mouseButton.cxx +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "mouseButton.h" +#include "buttonRegistry.h" + +#include +#include + +static const int num_mouse_buttons = 3; + +static ButtonHandle _buttons[num_mouse_buttons]; + +//////////////////////////////////////////////////////////////////// +// Function: MouseButton::button +// Access: Public, Static +// Description: Returns the ButtonHandle associated with the +// particular numbered mouse button (zero-based), if +// there is one, or ButtonHandle::none() if there is +// not. +//////////////////////////////////////////////////////////////////// +ButtonHandle MouseButton:: +button(int button_number) { + if (button_number >= 0 && button_number < num_mouse_buttons) { + return _buttons[button_number]; + } + return ButtonHandle::none(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseButton::one +// Access: Public, Static +// Description: Returns the ButtonHandle associated with the +// first mouse button. +//////////////////////////////////////////////////////////////////// +ButtonHandle MouseButton:: +one() { + return _buttons[0]; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseButton::two +// Access: Public, Static +// Description: Returns the ButtonHandle associated with the +// second mouse button. +//////////////////////////////////////////////////////////////////// +ButtonHandle MouseButton:: +two() { + return _buttons[1]; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseButton::three +// Access: Public, Static +// Description: Returns the ButtonHandle associated with the +// third mouse button. +//////////////////////////////////////////////////////////////////// +ButtonHandle MouseButton:: +three() { + return _buttons[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseButton::init_mouse_buttons +// Access: Public, Static +// Description: This is intended to be called only once, by the +// static initialization performed in config_util.cxx. +//////////////////////////////////////////////////////////////////// +void MouseButton:: +init_mouse_buttons() { + char numstr[20]; + + for (int i = 0; i < num_mouse_buttons; i++) { + sprintf(numstr, "mouse%d", i + 1); + nassertv(strlen(numstr) < 20); + + ButtonRegistry::ptr()->register_button(_buttons[i], numstr); + } +} diff --git a/panda/src/putil/mouseButton.h b/panda/src/putil/mouseButton.h new file mode 100644 index 0000000000..30926f0c3b --- /dev/null +++ b/panda/src/putil/mouseButton.h @@ -0,0 +1,29 @@ +// Filename: mouseButton.h +// Created by: drose (01Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MOUSEBUTTON_H +#define MOUSEBUTTON_H + +#include + +#include "buttonHandle.h" + +//////////////////////////////////////////////////////////////////// +// Class : MouseButton +// Description : This class is just used as a convenient namespace for +// grouping all of these handy functions that return +// buttons which map to standard mouse buttons. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA MouseButton { +public: + static ButtonHandle button(int button_number); + static ButtonHandle one(); + static ButtonHandle two(); + static ButtonHandle three(); + + static void init_mouse_buttons(); +}; + +#endif diff --git a/panda/src/putil/mouseData.cxx b/panda/src/putil/mouseData.cxx new file mode 100644 index 0000000000..31f4046b65 --- /dev/null +++ b/panda/src/putil/mouseData.cxx @@ -0,0 +1,49 @@ +// Filename: mouseData.cxx +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "mouseData.h" + + +//////////////////////////////////////////////////////////////////// +// Function: MouseData::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +MouseData:: +MouseData() { + _in_window = false; + _xpos = 0; + _ypos = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseData::get_x +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int MouseData:: +get_x() const { + return _xpos; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseData::get_y +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int MouseData:: +get_y() const { + return _ypos; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseData::get_in_window +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool MouseData:: +get_in_window() const { + return _in_window; +} diff --git a/panda/src/putil/mouseData.h b/panda/src/putil/mouseData.h new file mode 100644 index 0000000000..f0cbb9e0e8 --- /dev/null +++ b/panda/src/putil/mouseData.h @@ -0,0 +1,34 @@ +// Filename: mouseData.h +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MOUSEDATA_H +#define MOUSEDATA_H + +#include + +#include "modifierButtons.h" + +//////////////////////////////////////////////////////////////////// +// Class : MouseData +// Description : Holds the data that might be generated by a 2-d +// pointer input device, such as the mouse in the +// GraphicsWindow. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA MouseData { +public: + MouseData(); + + int get_x() const; + int get_y() const; + bool get_in_window() const; + + bool _in_window; + int _xpos; + int _ypos; +}; + +#endif + + diff --git a/panda/src/putil/nameUniquifier.I b/panda/src/putil/nameUniquifier.I new file mode 100644 index 0000000000..3859855a4c --- /dev/null +++ b/panda/src/putil/nameUniquifier.I @@ -0,0 +1,76 @@ +// Filename: nameUniquifier.I +// Created by: drose (16Feb00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: NameUniquifier::Constructor +// Access: Public +// Description: Creates a new NameUniquifier. +// +// The separator string is used to separate the original +// name (or supplied prefix) and the generated number +// when a name must be generated. +// +// If the original name is empty, the empty string is +// used, followed by the generated number. +//////////////////////////////////////////////////////////////////// +INLINE NameUniquifier:: +NameUniquifier(const string &separator, + const string &empty) : + _separator(separator), + _empty(empty) +{ + _counter = 0; + + if (_empty.empty()) { + _empty = _separator; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NameUniquifier::add_name +// Access: Public +// Description: If name is nonempty and so far unique, returns it +// unchanged. +// +// Otherwise, generates and returns a new name according +// to the following rules: +// +// If the name is empty, the new name is the +// NameUniquifier's "empty" string followed by a number, +// or the "separator" string if the "empty" string is +// empty. +// +// If the name is nonempty, the new name is the original +// name, followed by the NameUniquifier's "separator" +// string, followed by a number. +//////////////////////////////////////////////////////////////////// +INLINE string NameUniquifier:: +add_name(const string &name) { + return add_name_body(name, name); +} + +//////////////////////////////////////////////////////////////////// +// Function: NameUniquifier::add_name +// Access: Public +// Description: If name is nonempty and so far unique, returns it +// unchanged. +// +// Otherwise, generates and returns a new name according +// to the following rules: +// +// If the prefix is empty, the new name is the +// NameUniquifier's "empty" string followed by a number, +// or the "separator" string if the "empty" string is +// empty. +// +// If the prefix is nonempty, the new name is the +// prefix, followed by the NameUniquifier's "separator" +// string, followed by a number. +//////////////////////////////////////////////////////////////////// +INLINE string NameUniquifier:: +add_name(const string &name, const string &prefix) { + return add_name_body(name, prefix); +} diff --git a/panda/src/putil/nameUniquifier.cxx b/panda/src/putil/nameUniquifier.cxx new file mode 100644 index 0000000000..d326886455 --- /dev/null +++ b/panda/src/putil/nameUniquifier.cxx @@ -0,0 +1,64 @@ +// Filename: nameUniquifier.cxx +// Created by: drose (16Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "nameUniquifier.h" + +#include + +#include + + +//////////////////////////////////////////////////////////////////// +// Function: NameUniquifier::add_name_body +// Access: Private +// Description: The actual implementation of the two flavors of +// add_name(). +// +// If name is nonempty and so far unique, returns it +// unchanged. +// +// Otherwise, generates and returns a new name according +// to the following rules: +// +// If the prefix is empty, the new name is the +// NameUniquifier's "empty" string followed by a number, +// or the "separator" string if the "empty" string is +// empty. +// +// If the prefix is nonempty, the new name is the +// prefix, followed by the NameUniquifier's "separator" +// string, followed by a number. +//////////////////////////////////////////////////////////////////// +string NameUniquifier:: +add_name_body(const string &name, const string &prefix) { + if (!name.empty()) { + if (_names.insert(name).second) { + // The name was successfully inserted into the set; therefore, + // it's unique. Return it. + return name; + } + } + + // The name was not successfully inserted; there must be another one + // already. Make up a new one. + + // Keep trying to make up names until we make one that's unique. + string temp_name; + do { + static const int max_len = 16; + char num_str[max_len]; + sprintf(num_str, "%d", ++_counter); + nassertr(strlen(num_str) <= max_len, ""); + + if (prefix.empty()) { + temp_name = _empty + num_str; + } else { + temp_name = prefix + _separator + num_str; + } + } while (!_names.insert(temp_name).second); + + return temp_name; +} + diff --git a/panda/src/putil/nameUniquifier.h b/panda/src/putil/nameUniquifier.h new file mode 100644 index 0000000000..007bb5a95d --- /dev/null +++ b/panda/src/putil/nameUniquifier.h @@ -0,0 +1,42 @@ +// Filename: nameUniquifier.h +// Created by: drose (16Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NAMEUNIQUIFIER_H +#define NAMEUNIQUIFIER_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : NameUniquifier +// Description : A handy class for converting a list of arbitrary +// names (strings) so that each name is guaranteed to be +// unique in the list. Useful for writing egg files +// with unique vertex pool names, or for file converters +// to file formats that require unique node names, etc. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NameUniquifier { +public: + INLINE NameUniquifier(const string &separator = string(), + const string &empty = string()); + + INLINE string add_name(const string &name); + INLINE string add_name(const string &name, const string &prefix); + +private: + string add_name_body(const string &name, const string &prefix); + + typedef set Names; + Names _names; + string _separator; + string _empty; + int _counter; +}; + +#include "nameUniquifier.I" + +#endif diff --git a/panda/src/putil/notify_utils.N b/panda/src/putil/notify_utils.N new file mode 100644 index 0000000000..00ce75c11a --- /dev/null +++ b/panda/src/putil/notify_utils.N @@ -0,0 +1,7 @@ +forcetype Notify +forcetype NotifyCategory +forcetype NotifySeverity + +ignoremember set_assert_handler +ignoremember get_assert_handler +ignoremember AssertHandler diff --git a/panda/src/putil/notify_utils.h b/panda/src/putil/notify_utils.h new file mode 100644 index 0000000000..f25676beb4 --- /dev/null +++ b/panda/src/putil/notify_utils.h @@ -0,0 +1,17 @@ +// Filename: notify_utils.h +// Created by: drose (24May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NOTIFY_UTILS_H +#define NOTIFY_UTILS_H + +#include + +#include + +// The main purpose of this file is to provide a hook for +// interrogating Notify. + +#endif + diff --git a/panda/src/putil/pointerToArray.I b/panda/src/putil/pointerToArray.I new file mode 100644 index 0000000000..2f6c5cde95 --- /dev/null +++ b/panda/src/putil/pointerToArray.I @@ -0,0 +1,783 @@ +// Filename: pointerToArray.I +// Created by: drose (07Jan00) +// +//////////////////////////////////////////////////////////////////// + +template +vector PointerToArray::_empty_array; + +template +vector ConstPointerToArray::_empty_array; + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray:: +PointerToArray() : + PointerToBase > >((RefCountObj > *)NULL) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray:: +PointerToArray(size_type n) : + PointerToBase > >(new RefCountObj >) { + _ptr->reserve(n); + insert(begin(), n, Element()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray:: +PointerToArray(size_type n, const Element &value) : + PointerToBase > >(new RefCountObj >) { + _ptr->reserve(n); + insert(begin(), n, value); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray:: +PointerToArray(const PointerToArray ©) : + PointerToBase > >(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::begin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray::iterator PointerToArray:: +begin() const { + if (_ptr == NULL) { + return _empty_array.begin(); + } + return _ptr->begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::end +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray::iterator PointerToArray:: +end() const { + if (_ptr == NULL) { + return _empty_array.begin(); + } + return _ptr->end(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::rbegin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray::reverse_iterator PointerToArray:: +rbegin() const { + if (_ptr == NULL) { + return _empty_array.rbegin(); + } + return _ptr->rbegin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::rend +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray::reverse_iterator PointerToArray:: +rend() const { + if (_ptr == NULL) { + return _empty_array.rbegin(); + } + return _ptr->rend(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray::size_type PointerToArray:: +size() const { + return (_ptr == NULL) ? 0 : _ptr->size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::max_size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray::size_type PointerToArray:: +max_size() const { + nassertd(_ptr != NULL) { + ((PointerToArray *)this)->reassign(new RefCountObj >); + } + return _ptr->max_size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::empty +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool PointerToArray:: +empty() const { + return (_ptr == NULL) ? true : _ptr->empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::reserve +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void PointerToArray:: +reserve(PointerToArray::size_type n) { + if (_ptr == NULL) { + reassign(new RefCountObj >); + } + _ptr->reserve(n); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::capacity +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray::size_type PointerToArray:: +capacity() const { + nassertr(_ptr != NULL, 0); + return _ptr->capacity(); +} + +#ifndef WIN32_VC +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::Indexing operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray::reference PointerToArray:: +operator[](size_type n) const { + nassertd(_ptr != NULL) { + ((PointerToArray *)this)->reassign(new RefCountObj >); + } + nassertd(!_ptr->empty()) { + _ptr->push_back(Element()); + } + nassertr(n < _ptr->size(), _ptr->operator[](0)); + return _ptr->operator[](n); +} +#endif + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::front +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray::reference PointerToArray:: +front() const { + nassertd(_ptr != NULL) { + ((PointerToArray *)this)->reassign(new RefCountObj >); + } + nassertd(!_ptr->empty()) { + _ptr->push_back(Element()); + } + return _ptr->front(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::back +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray::reference PointerToArray:: +back() const { + nassertd(_ptr != NULL) { + ((PointerToArray *)this)->reassign(new RefCountObj >); + } + nassertd(!_ptr->empty()) { + _ptr->push_back(Element()); + } + return _ptr->back(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::push_back +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void PointerToArray:: +push_back(const Element &x) { + if (_ptr == NULL) { + reassign(new RefCountObj >); + } + _ptr->push_back(x); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::insert +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray::iterator PointerToArray:: +insert(iterator position) const { + nassertr(_ptr != NULL, position); + nassertr(position >= _ptr->begin() && + position <= _ptr->end(), position); + return _ptr->insert(position); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::insert +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray::iterator PointerToArray:: +insert(iterator position, const Element &x) const { + nassertr(_ptr != NULL, position); + nassertr(position >= _ptr->begin() && + position <= _ptr->end(), position); + return _ptr->insert(position, x); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::insert +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void PointerToArray:: +insert(iterator position, size_type n, const Element &x) const { + nassertv(_ptr != NULL); + nassertv(position >= _ptr->begin() && + position <= _ptr->end()); + _ptr->insert(position, n, x); +} + +#ifdef HAVE_MEMBER_TEMPLATES +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::insert +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +template +INLINE void PointerToArray:: +insert(iterator position, InputIterator first, InputIterator last) const { + nassertv(_ptr != NULL); + nassertv(position >= _ptr->begin() && + position <= _ptr->end()); + _ptr->insert(position, first, last); +} +#else +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::insert +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void PointerToArray:: +insert(iterator position, const Element *first, const Element *last) const { + nassertv(_ptr != NULL); + nassertv(position >= _ptr->begin() && + position <= _ptr->end()); + _ptr->insert(position, first, last); +} +#endif + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::pop_back +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void PointerToArray:: +pop_back() const { + nassertd(_ptr != NULL) { + ((PointerToArray *)this)->reassign(new RefCountObj >); + } + nassertv(!_ptr->empty()); + _ptr->pop_back(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::erase +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void PointerToArray:: +erase(iterator position) const { + nassertd(_ptr != NULL) { + ((PointerToArray *)this)->reassign(new RefCountObj >); + } + nassertv(position >= _ptr->begin() && + position <= _ptr->end()); + _ptr->erase(position); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::erase +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void PointerToArray:: +erase(iterator first, iterator last) const { + nassertd(_ptr != NULL) { + ((PointerToArray *)this)->reassign(new RefCountObj >); + } + nassertv(first >= _ptr->begin() && first <= _ptr->end()); + nassertv(last >= _ptr->begin() && last <= _ptr->end()); + _ptr->erase(first, last); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::Typecast operator +// Access: Public +// Description: The pointer typecast operator is convenient for +// maintaining the fiction that we actually have a +// C-style array. It returns the address of the first +// element in the array, unless the pointer is +// unassigned, in which case it returns NULL. +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray:: +operator Element *() const { + return (_ptr == NULL) ? (Element *)NULL : &(_ptr->front()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::p +// Access: Public +// Description: Function p() is similar to the function from +// PointerTo. It does the same thing: it returns the +// same thing as the typecast operator, above. +//////////////////////////////////////////////////////////////////// +template +INLINE Element *PointerToArray:: +p() const { + return (_ptr == NULL) ? (Element *)NULL : &(_ptr->front()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::v +// Access: Public +// Description: To access the vector itself, for more direct fiddling +// with some of the vector's esoteric functionality. +//////////////////////////////////////////////////////////////////// +template +INLINE vector &PointerToArray:: +v() const { + nassertd(_ptr != NULL) { + ((PointerToArray *)this)->reassign(new RefCountObj >); + } + return *_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::get_void_ptr +// Access: Public +// Description: Returns the reference to memory where the vector +// is stored. To be used only with get_void_ptr +//////////////////////////////////////////////////////////////////// +template +INLINE void* PointerToArray:: +get_void_ptr() const +{ + return PointerToBase > >::_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::set_void_ptr +// Access: Public +// Description: Sets this PTA to point to the pointer passed in +//////////////////////////////////////////////////////////////////// +template +INLINE void PointerToArray:: +set_void_ptr(void* p) +{ + reassign((RefCountObj > *)p); +} +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::get_count +// Access: Public +// Description: Returns the reference count of the underlying vector. +//////////////////////////////////////////////////////////////////// +template +INLINE int PointerToArray:: +get_count() const { + return (_ptr == NULL) ? 0 : _ptr->get_count(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray &PointerToArray:: +operator = (RefCountObj > *ptr) { + reassign(ptr); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE PointerToArray &PointerToArray:: +operator = (const PointerToArray ©) { + reassign(copy); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::clear +// Access: Public +// Description: To empty the PTA, use the clear() method, since +// assignment to NULL is problematic (given the +// ambiguity of the pointer type of NULL). +//////////////////////////////////////////////////////////////////// +template +INLINE void PointerToArray:: +clear() { + reassign((RefCountObj > *)NULL); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerToArray:: +ConstPointerToArray() : + PointerToBase > >((RefCountObj > *)NULL) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerToArray:: +ConstPointerToArray(const PointerToArray ©) : + PointerToBase > >(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerToArray:: +ConstPointerToArray(const ConstPointerToArray ©) : + PointerToBase > >(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::begin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerToArray::iterator ConstPointerToArray:: +begin() const { + if (_ptr == NULL) { + return _empty_array.begin(); + } + return _ptr->begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::end +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerToArray::iterator ConstPointerToArray:: +end() const { + if (_ptr == NULL) { + return _empty_array.begin(); + } + return _ptr->end(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::rbegin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerToArray::reverse_iterator ConstPointerToArray:: +rbegin() const { + if (_ptr == NULL) { + return _empty_array.rbegin(); + } + return _ptr->rbegin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::rend +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerToArray::reverse_iterator ConstPointerToArray:: +rend() const { + if (_ptr == NULL) { + return _empty_array.rbegin(); + } + return _ptr->rend(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerToArray::size_type ConstPointerToArray:: +size() const { + return (_ptr == NULL) ? 0 : _ptr->size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::max_size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerToArray::size_type ConstPointerToArray:: +max_size() const { + nassertd(_ptr != NULL) { + ((ConstPointerToArray *)this)->reassign(new RefCountObj >); + } + return _ptr->max_size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::empty +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool ConstPointerToArray:: +empty() const { + return (_ptr == NULL) ? true : _ptr->empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::capacity +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerToArray::size_type ConstPointerToArray:: +capacity() const { + nassertd(_ptr != NULL) { + ((ConstPointerToArray *)this)->reassign(new RefCountObj >); + } + return _ptr->capacity(); +} + +#ifndef WIN32_VC +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::Indexing operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerToArray::reference ConstPointerToArray:: +operator[](size_type n) const { + nassertd(_ptr != NULL) { + ((ConstPointerToArray *)this)->reassign(new RefCountObj >); + } + nassertd(!_ptr->empty()) { + _ptr->push_back(Element()); + } + nassertr(n < _ptr->size(), _ptr->operator[](0)); + return _ptr->operator[](n); +} +#endif + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::front +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerToArray::reference ConstPointerToArray:: +front() const { + nassertd(_ptr != NULL) { + ((ConstPointerToArray *)this)->reassign(new RefCountObj >); + } + nassertd(!_ptr->empty()) { + _ptr->push_back(Element()); + } + return _ptr->front(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::back +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerToArray::reference ConstPointerToArray:: +back() const { + nassertd(_ptr != NULL) { + ((ConstPointerToArray *)this)->reassign(new RefCountObj >); + } + nassertd(!_ptr->empty()) { + _ptr->push_back(Element()); + } + return _ptr->back(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::Typecast operator +// Access: Public +// Description: The pointer typecast operator is convenient for +// maintaining the fiction that we actually have a +// C-style array. It returns the address of the first +// element in the array, unless the pointer is +// unassigned, in which case it returns NULL. +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerToArray:: +operator const Element *() const { + return (_ptr == NULL) ? (const Element *)NULL : &(_ptr->front()); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::p +// Access: Public +// Description: Function p() is similar to the function from +// ConstPointerTo. It does the same thing: it returns the +// same thing as the typecast operator, above. +//////////////////////////////////////////////////////////////////// +template +INLINE const Element *ConstPointerToArray:: +p() const { + return (_ptr == NULL) ? (const Element *)NULL : &(_ptr->front()); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::v +// Access: Public +// Description: To access the vector itself, for more direct fiddling +// with some of the vector's esoteric functionality. +//////////////////////////////////////////////////////////////////// +template +INLINE const vector &ConstPointerToArray:: +v() const { + nassertd(_ptr != NULL) { + ((ConstPointerToArray *)this)->reassign(new RefCountObj >); + } + return *_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::get_count +// Access: Public +// Description: Returns the reference count of the underlying vector. +//////////////////////////////////////////////////////////////////// +template +INLINE int ConstPointerToArray:: +get_count() const { + return (_ptr == NULL) ? 0 : _ptr->get_count(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerToArray &ConstPointerToArray:: +operator = (RefCountObj > *ptr) { + reassign(ptr); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerToArray &ConstPointerToArray:: +operator = (const PointerToArray ©) { + reassign(copy); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ConstPointerToArray &ConstPointerToArray:: +operator = (const ConstPointerToArray ©) { + reassign(copy); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: ConstPointerToArray::clear +// Access: Public +// Description: To empty the PTA, use the clear() method, since +// assignment to NULL is problematic (given the +// ambiguity of the pointer type of NULL). +//////////////////////////////////////////////////////////////////// +template +INLINE void ConstPointerToArray:: +clear() { + reassign((RefCountObj > *)NULL); +} + diff --git a/panda/src/putil/pointerToArray.h b/panda/src/putil/pointerToArray.h new file mode 100644 index 0000000000..003aa9a2a4 --- /dev/null +++ b/panda/src/putil/pointerToArray.h @@ -0,0 +1,245 @@ +// Filename: pointerToArray.h +// Created by: drose (14Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef POINTERTOARRAY_H +#define POINTERTOARRAY_H + + +//////////////////////////////////////////////////////////////////// +// +// This file defines the classes PointerToArray and +// ConstPointerToArray (and their abbreviations, PTA and CPTA), which +// are extensions to the PointerTo class that support +// reference-counted arrays. +// +// You may think of a PointerToArray as the same thing as a +// traditional C-style array. However, it actually stores a pointer +// to an STL vector, which is then reference-counted. Thus, most +// vector operations may be applied directly to a PointerToArray +// object, including dynamic resizing via push_back() and pop_back(). +// +// Unlike the PointerTo class, the PointerToArray may store pointers +// to any kind of object, not just those derived from ReferenceCount. +// +// Like PointerTo and ConstPointerTo, the macro abbreviations PTA and +// CPTA are defined for convenience. +// +// Some examples of syntax: instead of: +// +// PTA(int) array(10); int *array = new int[10]; +// memset(array, 0, sizeof(int) * 10); memset(array, 0, sizeof(int) * 10); +// array[i] = array[i+1]; array[i] = array[i+1]; +// num_elements = array.size(); (no equivalent) +// +// PTA(int) copy = array; int *copy = array; +// +// Note that in the above example, unlike an STL vector (but like a +// C-style array), assigning a PointerToArray object from another +// simply copies the pointer, and does not copy individual elements. +// (Of course, reference counts are adjusted appropriately.) If you +// actually wanted an element-by-element copy of the array, you would +// do this: +// +// PTA(int) copy(0); // Create a pointer to an empty vector. +// copy.v() = array.v(); // v() is the STL vector itself. +// +// The (0) parameter to the constructor in the above example is +// crucial. When a numeric length parameter, such as zero, is given +// to the constructor, it means to define a new STL vector with that +// number of elements initially in it. If no parameter is given, on +// the other hand, it means the PointerToArray should point to +// nothing--no STL vector is created. This is equivalent to a C array +// that points to NULL. +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "referenceCount.h" +#include "pointerTo.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PointerToArray +// Description : A special kind of PointerTo that stores an array of +// the indicated element type, instead of a single +// element. This is actually implemented as an STL +// vector, using the RefCountObj class to wrap it up +// with a reference count. +//////////////////////////////////////////////////////////////////// +template +class PointerToArray : public PointerToBase > > { +public: + typedef vector::value_type value_type; + typedef vector::reference reference; + typedef vector::const_reference const_reference; + typedef vector::iterator iterator; + typedef vector::const_iterator const_iterator; + typedef vector::reverse_iterator reverse_iterator; + typedef vector::const_reverse_iterator const_reverse_iterator; + typedef vector::difference_type difference_type; + typedef vector::size_type size_type; + + INLINE PointerToArray(); + INLINE PointerToArray(size_type n); + INLINE PointerToArray(size_type n, const Element &value); + INLINE PointerToArray(const PointerToArray ©); + + // Duplicating the interface of vector. The following member + // functions are all const, because they do not reassign the + // pointer--they operate only within the vector itself, which is + // non-const in this class. + + INLINE iterator begin() const; + INLINE iterator end() const; + INLINE PointerToArray::reverse_iterator rbegin() const; + INLINE PointerToArray::reverse_iterator rend() const; + + // Equality and comparison operators are pointerwise for + // PointerToArrays, not elementwise as in vector. + + INLINE size_type size() const; + INLINE size_type max_size() const; + INLINE bool empty() const; + + // Functions specific to vectors. + INLINE void reserve(size_type n); + INLINE size_type capacity() const; +#ifndef WIN32_VC + INLINE reference operator[](size_type n) const; +#endif + INLINE reference front() const; + INLINE reference back() const; + INLINE void push_back(const Element &x); + INLINE iterator insert(iterator position) const; + INLINE iterator insert(iterator position, const Element &x) const; + INLINE void insert(iterator position, size_type n, const Element &x) const; + +#ifdef HAVE_MEMBER_TEMPLATES + template + INLINE void insert(iterator position, InputIterator first, InputIterator last) const; +#else + INLINE void insert(iterator position, const Element *first, const Element *last) const; +#endif + + INLINE void pop_back() const; + INLINE void erase(iterator position) const; + INLINE void erase(iterator first, iterator last) const; + + INLINE operator Element *() const; + INLINE Element *p() const; + INLINE vector &v() const; + + //These functions are only to be used in Reading through BamReader. + //They are designed to work in pairs, so that you register what is + //returned by get_void_ptr with BamReader and when you are setting + //another PTA with what is returned by BamReader, you set it with + //set_void_ptr. If you used the provided macro of READ_PTA, this is + //done for you. So you should never call these functions directly + INLINE void *get_void_ptr() const; + INLINE void set_void_ptr(void* p); + + INLINE int get_count() const; + + // Reassignment is by pointer, not memberwise as with a vector. + INLINE PointerToArray & + operator = (RefCountObj > *ptr); + INLINE PointerToArray & + operator = (const PointerToArray ©); + INLINE void clear(); + +private: + // This static empty array is kept around just so we can return + // something meangful when begin() or end() is called and we have a + // NULL pointer. It might not be shared properly between different + // .so's, since it's a static member of a template class, but we + // don't really care. + static vector _empty_array; +}; + + +//////////////////////////////////////////////////////////////////// +// Class : ConstPointerToArray +// Description : Similar to PointerToArray, except that its contents +// may not be modified. +//////////////////////////////////////////////////////////////////// +template +class ConstPointerToArray : public PointerToBase > > { +public: + typedef vector::value_type value_type; + typedef vector::const_reference reference; + typedef vector::const_reference const_reference; + typedef vector::const_iterator iterator; + typedef vector::const_iterator const_iterator; +#ifdef WIN32_VC + // VC++ seems to break the const_reverse_iterator definition somehow. + typedef vector::reverse_iterator reverse_iterator; +#else + typedef vector::const_reverse_iterator reverse_iterator; +#endif + typedef vector::const_reverse_iterator const_reverse_iterator; + typedef vector::difference_type difference_type; + typedef vector::size_type size_type; + + INLINE ConstPointerToArray(); + INLINE ConstPointerToArray(const PointerToArray ©); + INLINE ConstPointerToArray(const ConstPointerToArray ©); + + // Duplicating the interface of vector. + + INLINE iterator begin() const; + INLINE iterator end() const; + INLINE ConstPointerToArray::reverse_iterator rbegin() const; + INLINE ConstPointerToArray::reverse_iterator rend() const; + + // Equality and comparison operators are pointerwise for + // PointerToArrays, not elementwise as in vector. + + INLINE size_type size() const; + INLINE size_type max_size() const; + INLINE bool empty() const; + + // Functions specific to vectors. + INLINE size_type capacity() const; +#ifndef WIN32_VC + INLINE reference operator[](size_type n) const; +#endif + INLINE reference front() const; + INLINE reference back() const; + + INLINE operator const Element *() const; + INLINE const Element *p() const; + INLINE const vector &v() const; + + INLINE int get_count() const; + + // Reassignment is by pointer, not memberwise as with a vector. + INLINE ConstPointerToArray & + operator = (RefCountObj > *ptr); + INLINE ConstPointerToArray & + operator = (const PointerToArray ©); + INLINE ConstPointerToArray & + operator = (const ConstPointerToArray ©); + INLINE void clear(); + +private: + // This static empty array is kept around just so we can return + // something meangful when begin() or end() is called and we have a + // NULL pointer. It might not be shared properly between different + // .so's, since it's a static member of a template class, but we + // don't really care. + static vector _empty_array; +}; + + +// And the brevity macros. + +#define PTA(type) PointerToArray< type > +#define CPTA(type) ConstPointerToArray< type > + +#include "pointerToArray.I" + +#endif diff --git a/panda/src/putil/pta_double.cxx b/panda/src/putil/pta_double.cxx new file mode 100644 index 0000000000..d624793ef7 --- /dev/null +++ b/panda/src/putil/pta_double.cxx @@ -0,0 +1,11 @@ +// Filename: pta_double.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "pta_double.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/putil/pta_double.h b/panda/src/putil/pta_double.h new file mode 100644 index 0000000000..49ce69006d --- /dev/null +++ b/panda/src/putil/pta_double.h @@ -0,0 +1,36 @@ +// Filename: pta_double.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PTA_DOUBLE_H +#define PTA_DOUBLE_H + +#include + +#include "pointerToArray.h" +#include "vector_double.h" + +//////////////////////////////////////////////////////////////////// +// Class : PTA_double +// Description : A pta of doubles. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a pta of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the pta again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, RefCountObj); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToBase >); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToArray) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, ConstPointerToArray) + +typedef PointerToArray PTA_double; +typedef ConstPointerToArray CPTA_double; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/putil/pta_float.cxx b/panda/src/putil/pta_float.cxx new file mode 100644 index 0000000000..80ced8a711 --- /dev/null +++ b/panda/src/putil/pta_float.cxx @@ -0,0 +1,11 @@ +// Filename: pta_float.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "pta_float.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/putil/pta_float.h b/panda/src/putil/pta_float.h new file mode 100644 index 0000000000..0d10440ac3 --- /dev/null +++ b/panda/src/putil/pta_float.h @@ -0,0 +1,36 @@ +// Filename: pta_float.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PTA_FLOAT_H +#define PTA_FLOAT_H + +#include + +#include "pointerToArray.h" +#include "vector_float.h" + +//////////////////////////////////////////////////////////////////// +// Class : PTA_float +// Description : A pta of floats. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a pta of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the pta again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, RefCountObj); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToBase >); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToArray) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, ConstPointerToArray) + +typedef PointerToArray PTA_float; +typedef ConstPointerToArray CPTA_float; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/putil/pta_int.cxx b/panda/src/putil/pta_int.cxx new file mode 100644 index 0000000000..c8032ed79e --- /dev/null +++ b/panda/src/putil/pta_int.cxx @@ -0,0 +1,11 @@ +// Filename: pta_int.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "pta_int.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/putil/pta_int.h b/panda/src/putil/pta_int.h new file mode 100644 index 0000000000..99f882bf26 --- /dev/null +++ b/panda/src/putil/pta_int.h @@ -0,0 +1,36 @@ +// Filename: pta_int.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PTA_INT_H +#define PTA_INT_H + +#include + +#include "pointerToArray.h" +#include "vector_int.h" + +//////////////////////////////////////////////////////////////////// +// Class : PTA_int +// Description : A pta of ints. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a pta of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the pta again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, RefCountObj); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToBase >); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToArray) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, ConstPointerToArray) + +typedef PointerToArray PTA_int; +typedef ConstPointerToArray CPTA_int; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/putil/pta_uchar.cxx b/panda/src/putil/pta_uchar.cxx new file mode 100644 index 0000000000..bcc2e86f3a --- /dev/null +++ b/panda/src/putil/pta_uchar.cxx @@ -0,0 +1,11 @@ +// Filename: pta_uchar.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "pta_uchar.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/putil/pta_uchar.h b/panda/src/putil/pta_uchar.h new file mode 100644 index 0000000000..5934042f3d --- /dev/null +++ b/panda/src/putil/pta_uchar.h @@ -0,0 +1,36 @@ +// Filename: pta_uchar.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PTA_UCHAR_H +#define PTA_UCHAR_H + +#include + +#include "pointerToArray.h" +#include "vector_uchar.h" + +//////////////////////////////////////////////////////////////////// +// Class : PTA_uchar +// Description : A pta of uchars. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a pta of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the pta again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, RefCountObj); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToBase >); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToArray) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, ConstPointerToArray) + +typedef PointerToArray PTA_uchar; +typedef ConstPointerToArray CPTA_uchar; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/putil/pta_ushort.cxx b/panda/src/putil/pta_ushort.cxx new file mode 100644 index 0000000000..2b71c1f8e6 --- /dev/null +++ b/panda/src/putil/pta_ushort.cxx @@ -0,0 +1,11 @@ +// Filename: pta_ushort.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "pta_ushort.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/putil/pta_ushort.h b/panda/src/putil/pta_ushort.h new file mode 100644 index 0000000000..199d71e74c --- /dev/null +++ b/panda/src/putil/pta_ushort.h @@ -0,0 +1,36 @@ +// Filename: pta_ushort.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PTA_USHORT_H +#define PTA_USHORT_H + +#include + +#include "pointerToArray.h" +#include "vector_ushort.h" + +//////////////////////////////////////////////////////////////////// +// Class : PTA_ushort +// Description : A pta of ushorts. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a pta of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the pta again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, RefCountObj); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToBase >); +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, PointerToArray) +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, ConstPointerToArray) + +typedef PointerToArray PTA_ushort; +typedef ConstPointerToArray CPTA_ushort; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/putil/string_utils.I b/panda/src/putil/string_utils.I new file mode 100644 index 0000000000..e8abcca96f --- /dev/null +++ b/panda/src/putil/string_utils.I @@ -0,0 +1,13 @@ +// Filename: string_utils.I +// Created by: drose (14Jul00) +// +//////////////////////////////////////////////////////////////////// + + +template +INLINE string +format_string(const Thing &thing) { + ostringstream str; + str << thing; + return str.str(); +} diff --git a/panda/src/putil/string_utils.N b/panda/src/putil/string_utils.N new file mode 100644 index 0000000000..6d9d5c3ee2 --- /dev/null +++ b/panda/src/putil/string_utils.N @@ -0,0 +1,5 @@ +forcetype string +ignoremember rbegin +ignoremember rend + +ignoreinvolved __reserve_t \ No newline at end of file diff --git a/panda/src/putil/string_utils.cxx b/panda/src/putil/string_utils.cxx new file mode 100644 index 0000000000..2fd930585f --- /dev/null +++ b/panda/src/putil/string_utils.cxx @@ -0,0 +1,63 @@ +// Filename: string_utils.cxx +// Created by: drose (18Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "string_utils.h" + +#include + +// Case-insensitive string comparison, from Stroustrup's C++ third edition. +// Works like strcmp(). +int +cmp_nocase(const string &s, const string &s2) { + string::const_iterator p = s.begin(); + string::const_iterator p2 = s2.begin(); + + while (p != s.end() && p2 != s2.end()) { + if (toupper(*p) != toupper(*p2)) { + return (toupper(*p) < toupper(*p2)) ? -1 : 1; + } + ++p; + ++p2; + } + + return (s2.size() == s.size()) ? 0 : + (s.size() < s2.size()) ? -1 : 1; // size is unsigned +} + +INLINE int +toupper_uh(int ch) { + return (ch == '_') ? '-' : toupper(ch); +} + + +int +cmp_nocase_uh(const string &s, const string &s2) { + string::const_iterator p = s.begin(); + string::const_iterator p2 = s2.begin(); + + while (p != s.end() && p2 != s2.end()) { + if (toupper_uh(*p) != toupper_uh(*p2)) { + return (toupper_uh(*p) < toupper_uh(*p2)) ? -1 : 1; + } + ++p; + ++p2; + } + + return (s2.size() == s.size()) ? 0 : + (s.size() < s2.size()) ? -1 : 1; // size is unsigned +} + + + +string +downcase(const string &s) { + string result; + string::const_iterator p; + for (p = s.begin(); p != s.end(); ++p) { + result += tolower(*p); + } + return result; +} + diff --git a/panda/src/putil/string_utils.h b/panda/src/putil/string_utils.h new file mode 100644 index 0000000000..2157d8ea6d --- /dev/null +++ b/panda/src/putil/string_utils.h @@ -0,0 +1,31 @@ +// Filename: string_utils.h +// Created by: drose (18Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef STRING_UTILS_H +#define STRING_UTILS_H + +#include + +#include + +// Case-insensitive string comparison, from Stroustrup's C++ third edition. +// Works like strcmp(). +EXPCL_PANDA int cmp_nocase(const string &s, const string &s2); + +// Similar, except it also accepts hyphen and underscore as equivalent. +EXPCL_PANDA int cmp_nocase_uh(const string &s, const string &s2); + +// Returns the string converted to lowercase. +EXPCL_PANDA string downcase(const string &s); + +// Convenience function to make a string from anything that has an +// ostream operator. +template +INLINE string format_string(const Thing &thing); + +#include "string_utils.I" + +#endif + diff --git a/panda/src/putil/test_bam.cxx b/panda/src/putil/test_bam.cxx new file mode 100644 index 0000000000..d9229a24e7 --- /dev/null +++ b/panda/src/putil/test_bam.cxx @@ -0,0 +1,209 @@ +// Filename: test_bam.cxx +// Created by: jason (13Jun00) +// + +#include +#include +#include + +#include "test_bam.h" + + +TypeHandle Person::_type_handle; +TypeHandle Parent::_type_handle; +TypeHandle Child::_type_handle; + +ConfigureFn(config_util) +{ + TypedObject::init_type(); + ReferenceCount::init_type(); + TypedReferenceCount::init_type(); + init_system_type_handles(); + FactoryParam::init_type(); + Datagram::init_type(); + TypedWriteable::init_type(); + WriteableParam::init_type(); + BamReaderParam::init_type(); + TypedWriteableReferenceCount::init_type(); + + Person::init_type(); + Parent::init_type(); + Child::init_type(); + + BamReader::get_factory()->register_factory(Person::get_class_type(), Person::make_person); + BamReader::get_factory()->register_factory(Parent::get_class_type(), Parent::make_parent); + BamReader::get_factory()->register_factory(Child::get_class_type(), Child::make_child); +} + +void Person:: +write_datagram(BamWriter* manager, Datagram &me) +{ + //Write out name + me.add_string(_name); + //Write out gender + me.add_uint8(myGender); + manager->write_pointer(me, _bro); + manager->write_pointer(me, _sis); +} + +TypedWriteable* Person:: +make_person(const FactoryParams ¶ms) +{ + Person *me = new Person; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(me, scan, manager); + return me; +} + +void Person:: +fillin(Person* me, DatagramIterator& scan, BamReader* manager) +{ + _name = scan.get_string(); + myGender = scan.get_uint8(); + manager->read_pointer(scan, this); + manager->read_pointer(scan, this); +} + +int Person:: +complete_pointers(vector_typedWriteable& plist, BamReader *) +{ + _bro = (plist[0] == TypedWriteable::Null) ? (Person*)NULL : DCAST(Person, plist[0]); + _sis = (plist[1] == TypedWriteable::Null) ? (Person*)NULL : DCAST(Person, plist[1]); + return 2; +} + +void Person:: +print_relationships(void){ + nout << "My name is " << _name << endl; + if (_bro != NULL) + nout << "My brother is " << _bro->name() << endl; + if (_sis != NULL) + nout << "My sister is " << _sis->name() << endl; +} + +void Parent:: +write_datagram(BamWriter* manager, Datagram &me) +{ + Person::write_datagram(manager, me); + manager->write_pointer(me, _son); + manager->write_pointer(me, _daughter); +} + +TypedWriteable* Parent:: +make_parent(const FactoryParams ¶ms) +{ + Parent *me = new Parent; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(me, scan, manager); + return me; +} + +void Parent:: +fillin(Parent* me, DatagramIterator& scan, BamReader* manager) +{ + Person::fillin(me, scan, manager); + manager->read_pointer(scan, this); + manager->read_pointer(scan, this); +} + +int Parent:: +complete_pointers(vector_typedWriteable& plist, BamReader *manager) +{ + int start = Person::complete_pointers(plist, manager); + _son = (plist[start] == TypedWriteable::Null) ? (Child*)NULL : DCAST(Child, plist[2]); + _daughter = (plist[start+1] == TypedWriteable::Null) ? (Child*)NULL : DCAST(Child, plist[3]); + return start+2; +} + +void Parent:: +setSon(Child* son) +{ + if (son->isMale()) _son = son; +} +void Parent:: +setDaughter(Child* daughter) +{ + if (!daughter->isMale()) _daughter = daughter; +} + + +void Parent:: +print_relationships(void){ + Person::print_relationships(); + if (_son != NULL) + nout << "My son is " << _son->name() << endl; + if (_daughter != NULL) + nout << "My daughter is " << _daughter->name() << endl; +} + +void Child:: +write_datagram(BamWriter* manager, Datagram &me) +{ + Person::write_datagram(manager, me); + manager->write_pointer(me, _dad); + manager->write_pointer(me, _mom); +} + +TypedWriteable* Child:: +make_child(const FactoryParams ¶ms) +{ + Child *me = new Child; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(me, scan, manager); + + return me; +} + +void Child:: +fillin(Child* me, DatagramIterator& scan, BamReader* manager) +{ + Person::fillin(me, scan, manager); + manager->read_pointer(scan, this); + manager->read_pointer(scan, this); +} + +int Child:: +complete_pointers(vector_typedWriteable& plist, BamReader *manager) +{ + int start = Person::complete_pointers(plist, manager); + _dad = (plist[start] == TypedWriteable::Null) ? (Parent*)NULL : DCAST(Parent, plist[2]); + _mom = (plist[start+1] == TypedWriteable::Null) ? (Parent*)NULL : DCAST(Parent, plist[3]); + return start+2; +} + + +void Child:: +setFather(Parent* dad) +{ + if (dad->isMale()) _dad = dad; +} + +void Child:: +setMother(Parent* mom) +{ + if (!mom->isMale()) _mom = mom; +} + +void Child:: +print_relationships(void){ + Person::print_relationships(); + if (_dad != NULL) + nout << "My dad is " << _dad->name() << endl; + if (_mom != NULL) + nout << "My mom is " << _mom->name() << endl; +} diff --git a/panda/src/putil/test_bam.h b/panda/src/putil/test_bam.h new file mode 100644 index 0000000000..45077587e0 --- /dev/null +++ b/panda/src/putil/test_bam.h @@ -0,0 +1,169 @@ +// Filename: test_bam.h +// Created by: jason (12Jun00) +// + + +#include +#include + +#include "factory.h" +#include "writeableParam.h" +#include "factoryParams.h" +#include "bamWriter.h" +#include "bamReader.h" +#include "pointerTo.h" + +#include "typedWriteableReferenceCount.h" + +#include + +class Child; + +class Person : public TypedWriteableReferenceCount { +public: + void write_datagram(BamWriter*, Datagram&); + + static TypedWriteable *make_person(const FactoryParams ¶ms); + virtual int complete_pointers(vector_typedWriteable &plist, + BamReader *manager); +protected: + void fillin(Person*,DatagramIterator&,BamReader*); +public: + enum sex { + MALE, + FEMALE + }; + + void setBrother(Person* bro) {if (bro->isMale()) _bro = bro;} + void setSister(Person* sis) {if (!sis->isMale()) _sis = sis;} + bool isMale(void) {return myGender == MALE;} + + void print_relationships(void); + string name(void) {return _name;} +private: + Person *_bro, *_sis; + sex myGender; + string _name; + +public: + Person(void) {} + Person(const string &name, const sex Gender) : + _name(name), myGender(Gender), _bro((Person*)NULL), _sis((Person*)NULL) { + + } + virtual ~Person() { + + } + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedWriteableReferenceCount::init_type(); + register_type(_type_handle, "Person", + TypedWriteableReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } + +private: + static TypeHandle _type_handle; +}; + +class Parent : public Person { +public: + void write_datagram(BamWriter*, Datagram&); + + static TypedWriteable *make_parent(const FactoryParams ¶ms); + virtual int complete_pointers(vector_typedWriteable &plist, + BamReader *manager); +protected: + void fillin(Parent*,DatagramIterator&,BamReader*); +public: + void setSon(Child*); + void setDaughter(Child*); + + void print_relationships(void); + +private: + Child *_son, *_daughter; + +public: + Parent(void) {} + Parent(const string &name, const sex Gender) : Person(name, Gender) { + + } + virtual ~Parent() { + + } + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Person::init_type(); + register_type(_type_handle, "Parent", + Person::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } + +private: + static TypeHandle _type_handle; +}; + +class Child : public Person { +public: + void write_datagram(BamWriter*, Datagram&); + + static TypedWriteable *make_child(const FactoryParams ¶ms); + virtual int complete_pointers(vector_typedWriteable &plist, + BamReader *manager); +protected: + void fillin(Child*,DatagramIterator&,BamReader*); +public: + void setFather(Parent*); + void setMother(Parent*); + + void print_relationships(void); + +private: + Parent *_dad, *_mom; + +public: + Child(void) {} + Child(const string &name, const sex Gender) : Person(name, Gender) { + + } + virtual ~Child() { + + } + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Person::init_type(); + register_type(_type_handle, "Child", Person::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } + +private: + static TypeHandle _type_handle; +}; + + + diff --git a/panda/src/putil/test_bamRead.cxx b/panda/src/putil/test_bamRead.cxx new file mode 100644 index 0000000000..6ee0280ff1 --- /dev/null +++ b/panda/src/putil/test_bamRead.cxx @@ -0,0 +1,38 @@ +// Filename: test_bamRead.cxx +// Created by: jason (13Jun00) +// + +#include +#include + +#include +#include "test_bam.h" +#include "bamReader.h" + +int main(int argc, char* argv[]) +{ + string test_file = "bamTest.out"; + datagram_file stream(test_file); + + stream.open(file::FILE_READ); + BamReader manager(&stream); + + manager.init(); + + PointerTo dad = DCAST(Parent, manager.read_object()); + PointerTo mom = DCAST(Parent, manager.read_object()); + PointerTo bro = DCAST(Child, manager.read_object()); + PointerTo sis = DCAST(Child, manager.read_object()); + + manager.resolve(); + + dad->print_relationships(); + nout << endl; + mom->print_relationships(); + nout << endl; + bro->print_relationships(); + nout << endl; + sis->print_relationships(); + + return 1; +} diff --git a/panda/src/putil/test_bamWrite.cxx b/panda/src/putil/test_bamWrite.cxx new file mode 100644 index 0000000000..e8ae32a84c --- /dev/null +++ b/panda/src/putil/test_bamWrite.cxx @@ -0,0 +1,51 @@ +// Filename: test_bamWrite.cxx +// Created by: jason (09Jun00) +// + +#include +#include + +#include "test_bam.h" + +int main(int argc, char* argv[]) +{ + string test_file("bamTest.out"); + datagram_file stream(test_file); + + BamWriter manager(&stream); + stream.open(file::FILE_WRITE); + + // This initialization would normally be done by a ConfigureFn + // block. + + PointerTo dad = new Parent("Attila", Person::MALE); + PointerTo mom = new Parent("Brunhilda", Person::FEMALE); + PointerTo bro = new Child("Bob", Person::MALE); + PointerTo sis = new Child("Mary Poppins", Person::FEMALE); + + //Set up relationships + dad->setSon(bro.p()); + dad->setDaughter(sis.p()); + + mom->setSon(bro.p()); + mom->setDaughter(sis.p()); + + bro->setMother(mom.p()); + bro->setFather(dad.p()); + bro->setSister(sis.p()); + + sis->setFather(dad.p()); + sis->setMother(mom.p()); + sis->setBrother(bro.p()); + + manager.init(); + + manager.write_object(dad.p()); + manager.write_object(mom.p()); + manager.write_object(bro.p()); + manager.write_object(sis.p()); + + stream.close(); + return 1; +} + diff --git a/panda/src/putil/test_filename.cxx b/panda/src/putil/test_filename.cxx new file mode 100644 index 0000000000..61778decef --- /dev/null +++ b/panda/src/putil/test_filename.cxx @@ -0,0 +1,106 @@ +// Filename: test_filename.cxx +// Created by: drose (18Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "filename.h" +#include "config_util.h" + +#include + +#include + +void + show_filename(const Filename &f) { + nout + << "get_fullpath = " << f.get_fullpath() << "\n" + /* + << " _dirname_end = " << f._dirname_end + << " _basename_start = " << f._basename_start << "\n" + << " _basename_end = " << f._basename_end + << " _extension_start = " << f._extension_start << "\n" + */ + + << "get_dirname = " << f.get_dirname() << "\n" + << "get_basename = " << f.get_basename() << "\n" + << "get_fullpath_wo_extension = " << f.get_fullpath_wo_extension() << "\n" + << "get_basename_wo_extension = " << f.get_basename_wo_extension() << "\n" + << "get_extension = " << f.get_extension() << "\n" + << "\n"; +} + + +int +main(int argc, char *argv[]) { + if (argc < 2) { + nout << "Specify filename on command line.\n"; + exit(1); + } + + Filename f(argv[1]); + + show_filename(f); + + if (argc >= 3) { + Filename t(argv[2]); + nout << "f is " << f << " exists = " << f.exists() << "\n" + << "t is " << t << " exists = " << t.exists() << "\n"; + + nout << "f.compare_timestamps(t, true, true) = " + << f.compare_timestamps(t, true, true) << "\n" + << "f.compare_timestamps(t, true, false) = " + << f.compare_timestamps(t, true, false) << "\n" + << "f.compare_timestamps(t, false, true) = " + << f.compare_timestamps(t, false, true) << "\n" + << "f.compare_timestamps(t, false, false) = " + << f.compare_timestamps(t, false, false) << "\n"; + } + /* + if (argc >= 3) { + nout << "\nRelative to: " << argv[2] << "\n"; + f.make_relative_to(argv[2]); + show_filename(f); + + } else { + nout << "Searching on model path: " << get_model_path() << "\n"; + if (f.resolve_filename(get_model_path())) { + nout << "Found: " << f << "\n"; + } else { + nout << "not found.\n"; + } + + int index = f.find_on_searchpath(get_model_path()); + nout << "Found at position " << index << ": " << f << "\n"; + } + */ + + /* + f.set_extension("new_extension"); + show_filename(f); + + f.set_extension(""); + show_filename(f); + + f.set_extension("ext"); + show_filename(f); + + f.set_dirname("/new/dirname"); + show_filename(f); + + f.set_dirname("/"); + show_filename(f); + + f.set_dirname(""); + show_filename(f); + + f.set_dirname("/dir/name/"); + show_filename(f); + + f.set_basename_wo_extension("base_name"); + show_filename(f); + */ + + return(0); +} + + diff --git a/panda/src/putil/test_glob.cxx b/panda/src/putil/test_glob.cxx new file mode 100644 index 0000000000..26be65b123 --- /dev/null +++ b/panda/src/putil/test_glob.cxx @@ -0,0 +1,68 @@ +// Filename: test_glob.cxx +// Created by: drose (30May00) +// +//////////////////////////////////////////////////////////////////// + +#include "globPattern.h" + +#ifdef WIN32 +#include +#else +#include +#include +#endif + +int +main(int argc, char *argv[]) { + if (argc != 2) { + cerr + << "test_glob \"pattern\"\n\n" + << "Attempts to match the pattern against each of the files in the\n" + << "current directory. Reports all of the matching files. This is,\n" + << "of course, exactly the normal behavior of the shell; this test\n" + << "program merely exercises the Panda GlobPattern object, which\n" + << "duplicates the shell functionality.\n\n"; + exit(1); + } + + GlobPattern pattern(argv[1]); + +#ifdef WIN32 + struct _finddata_t fd; + long dp = _findfirst("*.*", &fd); + + if (dp != 0) { + do { + if (pattern.matches(fd.name)) { + cout << "Matches: " << fd.name << "\n"; + } else { + cout << "Fails: " << fd.name << "\n"; + } + } while (_findnext(dp, &fd) == 0); + } + +#else // WIN32 + DIR *dp = opendir("."); + if (dp == (DIR *)NULL) { + cerr << "Unable to scan directory '.'\n"; + exit(1); + } + + struct dirent *ent; + ent = readdir(dp); + while (ent != (struct dirent *)NULL) { + if (pattern.matches(ent->d_name)) { + cout << "Matches: " << ent->d_name << "\n"; + } else { + cout << "Fails: " << ent->d_name << "\n"; + } + ent = readdir(dp); + } + + closedir(dp); +#endif // WIN32 + + return (0); +} + + diff --git a/panda/src/putil/test_linestream.cxx b/panda/src/putil/test_linestream.cxx new file mode 100644 index 0000000000..0995ce952e --- /dev/null +++ b/panda/src/putil/test_linestream.cxx @@ -0,0 +1,36 @@ +// Filename: test_linestream.cxx +// Created by: drose (26Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "lineStream.h" + +#include + +int +main(int argc, char *argv[]) { + LineStream ls; + + int i = 0; + while (ls) { + if ((i % 5) == 0) { + if (ls.is_text_available()) { + nout << "Got line: '" << ls.get_line() << "'"; + if (ls.has_newline()) { + nout << " (nl)"; + } + nout << "\n"; + } + } + + ls << "123456789 "; + if ((i % 6)==0) { + ls << "\n"; + } + i++; + } + + return 0; +} + + diff --git a/panda/src/putil/test_types.cxx b/panda/src/putil/test_types.cxx new file mode 100644 index 0000000000..f3d80a13ea --- /dev/null +++ b/panda/src/putil/test_types.cxx @@ -0,0 +1,260 @@ +// Filename: test_types.cxx +// Created by: drose (23Oct98) +// +//////////////////////////////////////////////////////////////////// + +#include "typeHandle.h" +#include "pointerTo.h" +#include "pointerToArray.h" +#include "referenceCount.h" + +#include + +class ThatThingie : public TypedObject, public ReferenceCount { +public: + ThatThingie(const string &name) : _name(name) { + nout << "Constructing ThatThingie " << _name << "\n"; + } + virtual ~ThatThingie() { + nout << "Destructing ThatThingie " << _name << "\n"; + } + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedObject::init_type(); + ReferenceCount::init_type(); + register_type(_type_handle, "ThatThingie", + ReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } + + string _name; + +private: + static TypeHandle _type_handle; +}; + +class ThisThingie : public ThatThingie { +public: + ThisThingie(const string &name) : ThatThingie(name) { + nout << "Constructing ThisThingie " << _name << "\n"; + } + virtual ~ThisThingie() { + nout << "Destructing ThisThingie " << _name << "\n"; + } + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ThatThingie::init_type(); + register_type(_type_handle, "ThisThingie", ThatThingie::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } + + void const_hello() const { + nout << "Const hello from " << _name << "\n"; + } + +private: + static TypeHandle _type_handle; +}; + +class TheOtherThingie { +public: + TheOtherThingie(const string &name) : _name(name) { + nout << "Constructing TheOtherThingie " << _name << "\n"; + } + virtual ~TheOtherThingie() { + nout << "Destructing TheOtherThingie " << _name << "\n"; + } + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + register_type(_type_handle, "TheOtherThingie"); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } + + void hello() { + nout << "Hello from " << _name << "\n"; + } + + string _name; + +private: + static TypeHandle _type_handle; +}; + +class WhatAThingie : public ThatThingie, public TheOtherThingie { +public: + WhatAThingie(const string &name) : ThatThingie(name), TheOtherThingie(name) { + nout << "Constructing WhatAThingie " << ThatThingie::_name << "\n"; + } + virtual ~WhatAThingie() { + nout << "Destructing WhatAThingie " << ThatThingie::_name << "\n"; + } + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ThatThingie::init_type(); + TheOtherThingie::init_type(); + register_type(_type_handle, "WhatAThingie", + ThatThingie::get_class_type(), + TheOtherThingie::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } + +private: + static TypeHandle _type_handle; +}; + +TypeHandle ThatThingie::_type_handle; +TypeHandle ThisThingie::_type_handle; +TypeHandle TheOtherThingie::_type_handle; +TypeHandle WhatAThingie::_type_handle; + +void show_derivation(TypeHandle type) { + nout << type << " derives from:"; + for (int i = 0; i < type.get_num_parent_classes(); i++) { + nout << " " << type.get_parent_class(i); + } + nout << "\n"; +} + +int +main() { + // This initialization would normally be done by a ConfigureFn + // block. + ThatThingie::init_type(); + ThisThingie::init_type(); + TheOtherThingie::init_type(); + WhatAThingie::init_type(); + + PointerTo thing_1 = new ThisThingie("thing_1"); + ConstPointerTo thing_2 = new ThatThingie("thing_2"); + PointerTo thing_3 = new WhatAThingie("thing_3"); + + nout << "\nthing_1 of type " << thing_1->get_type() + << "\nthing_2 of type " << thing_2->get_type() + << "\nthing_3 of type " << thing_3->get_type() + + << "\n\nthing_1 is ThisThingie : " + << thing_1->is_of_type(ThisThingie::get_class_type()) + << "\nthing_1 is ThatThingie : " + << thing_1->is_of_type(ThatThingie::get_class_type()) + << "\nthing_1 is TheOtherThingie : " + << thing_1->is_of_type(TheOtherThingie::get_class_type()) + << "\nthing_1 is WhatAThingie : " + << thing_1->is_of_type(WhatAThingie::get_class_type()) + + << "\n\nthing_2 is ThisThingie : " + << thing_2->is_of_type(ThisThingie::get_class_type()) + << "\nthing_2 is ThatThingie : " + << thing_2->is_of_type(ThatThingie::get_class_type()) + << "\nthing_2 is TheOtherThingie : " + << thing_2->is_of_type(TheOtherThingie::get_class_type()) + << "\nthing_2 is WhatAThingie : " + << thing_2->is_of_type(WhatAThingie::get_class_type()) + + << "\n\nthing_3 is ThisThingie : " + << thing_3->is_of_type(ThisThingie::get_class_type()) + << "\nthing_3 is ThatThingie : " + << thing_3->is_of_type(ThatThingie::get_class_type()) + << "\nthing_3 is TheOtherThingie : " + << thing_3->is_of_type(TheOtherThingie::get_class_type()) + << "\nthing_3 is WhatAThingie : " + << thing_3->is_of_type(WhatAThingie::get_class_type()) + << "\n\n"; + + show_derivation(ThatThingie::get_class_type()); + show_derivation(ThisThingie::get_class_type()); + show_derivation(TheOtherThingie::get_class_type()); + show_derivation(WhatAThingie::get_class_type()); + + nout << "WhatAThingie::get_parent_towards(ReferenceCount) = " + << WhatAThingie::get_class_type().get_parent_towards + (ReferenceCount::get_class_type()) << "\n" + << "WhatAThingie::get_parent_towards(ThatThingie) = " + << WhatAThingie::get_class_type().get_parent_towards + (ThatThingie::get_class_type()) << "\n" + << "WhatAThingie::get_parent_towards(ThisThingie) = " + << WhatAThingie::get_class_type().get_parent_towards + (ThisThingie::get_class_type()) << "\n" + << "WhatAThingie::get_parent_towards(TheOtherThingie) = " + << WhatAThingie::get_class_type().get_parent_towards + (TheOtherThingie::get_class_type()) << "\n" + << "WhatAThingie::get_parent_towards(WhatAThingie) = " + << WhatAThingie::get_class_type().get_parent_towards + (WhatAThingie::get_class_type()) << "\n" + << "\n"; + + { + ConstPointerTo dup_thing_1 = thing_1; + nout << "thing_1 is " << thing_1 << " and dup_thing_1 is " + << dup_thing_1 << "\n"; + } + nout << "thing_1 is now " << thing_1 << "\n"; + + nout << "\n"; + + { + ConstPointerTo new_thing = new ThisThingie("new_thing"); + ((const ThisThingie *)new_thing.p())->const_hello(); + } + + nout << "\n"; + + ((ThisThingie *)thing_1.p())->const_hello(); + ((WhatAThingie *)thing_3.p())->hello(); + + nout << "\n"; + + // Testing RefCountProxy. + + RefCountProxy x = 10; + x++; + RefCountProxy y; + y = x; + nout << "y is " << y << ", y's type is " << y.get_class_type() << "\n"; + + + // Testing PointerToArray. + + PointerToArray iarray(10); + memset(iarray, 0, sizeof(int)*10); + + for (int i = 0; i < 10; i++) { + iarray[i] = i; + } + + ConstPointerToArray jarray = iarray; + + // jarray[4] = jarray[6]; + + nout << "jarray[4] is " << jarray[4] << "\n"; + + return 0; +} diff --git a/panda/src/putil/timedCycle.I b/panda/src/putil/timedCycle.I new file mode 100644 index 0000000000..29c44d598a --- /dev/null +++ b/panda/src/putil/timedCycle.I @@ -0,0 +1,85 @@ +// Filename: timedCycle.I +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TimedCycle::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TimedCycle:: +TimedCycle() : + _next_switch(-1), _current_child(0), _cycle_time(30), + _inv_cycle_time(1./30), _element_count(0) +{ + _global_clock = ClockObject::get_global_clock(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TimedCycle::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TimedCycle:: +TimedCycle(float cycle_time, int element_count) : + _cycle_time(cycle_time), _current_child(0), + _element_count(element_count) +{ + nassertv(_cycle_time > 0); + _global_clock = ClockObject::get_global_clock(); + _next_switch = _global_clock->get_real_time() + _cycle_time; + _inv_cycle_time = 1. / _cycle_time; +} + +//////////////////////////////////////////////////////////////////// +// Function: TimedCycle::set_element_count +// Access: Public +// Description: Set the number of elements being cycled through +//////////////////////////////////////////////////////////////////// +INLINE void TimedCycle:: +set_element_count(int element_count) +{ + _element_count = element_count; +} + +//////////////////////////////////////////////////////////////////// +// Function: TimedCycle::set_cycle_time +// Access: Public +// Description: Set the number of elements being cycled through +//////////////////////////////////////////////////////////////////// +INLINE void TimedCycle:: +set_cycle_time(float cycle_time) +{ + nassertv(cycle_time > 0); + if (_next_switch == -1) + { + _next_switch = _global_clock->get_real_time() + cycle_time; + } + else + { + _next_switch = _next_switch - _cycle_time + cycle_time; + } + _cycle_time = cycle_time; + _inv_cycle_time = 1. / _cycle_time; +} + + +//////////////////////////////////////////////////////////////////// +// Function: TimedCycle::next_element +// Access: Public +// Description: Set the number of elements being cycled through +//////////////////////////////////////////////////////////////////// +INLINE int TimedCycle:: +next_element() +{ + double current_time = _global_clock->get_real_time(); + unsigned int increment = (unsigned int) ((current_time - _next_switch) + * _inv_cycle_time); + + _next_switch += _cycle_time * increment; + _current_child = (_current_child + increment) % _element_count; + + return _current_child; +} + diff --git a/panda/src/putil/timedCycle.cxx b/panda/src/putil/timedCycle.cxx new file mode 100644 index 0000000000..7fb44b1ccf --- /dev/null +++ b/panda/src/putil/timedCycle.cxx @@ -0,0 +1,38 @@ +// Filename: timedCycle.h +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// +#include +#include "timedCycle.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: TimedCycle::write_object +// Description: Writes the contents of this object to the datagram +// for shipping out to a Bam file. +//////////////////////////////////////////////////////////////////// +void TimedCycle:: +write_datagram(Datagram &me) { + me.add_float64(_cycle_time); + me.add_uint16(_element_count); +} + +//////////////////////////////////////////////////////////////////// +// Function: TimedCycle::fillin +// Access: Protected +// Description: This internal function is called by make_TimedCycle to +// read in all of the relevant data from the BamFile for +// the new TimedCycle. +//////////////////////////////////////////////////////////////////// +void TimedCycle:: +fillin(DatagramIterator &scan) { + _cycle_time = scan.get_float64(); + _element_count = scan.get_uint16(); + _inv_cycle_time = 1. / _cycle_time; + + _global_clock = ClockObject::get_global_clock(); + _next_switch = _global_clock->get_real_time() + _cycle_time; + _current_child = 0; +} diff --git a/panda/src/putil/timedCycle.h b/panda/src/putil/timedCycle.h new file mode 100644 index 0000000000..1bd60f4489 --- /dev/null +++ b/panda/src/putil/timedCycle.h @@ -0,0 +1,52 @@ +// Filename: timedCycle.h +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TIMED_CYCLE_H +#define TIMED_CYCLE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "clockObject.h" + +class Datagram; +class DatagramIterator; + +//////////////////////////////////////////////////////////////////// +// Class : TimedCycle +// Description : A class for anything that needs to cycle over +// some finite list of elements in increments based on +// time. All time variables are assumed to be set in +// seconds. +//////////////////////////////////////////////////////////////////// + +class EXPCL_PANDA TimedCycle +{ +public: + INLINE TimedCycle(); + INLINE TimedCycle(float cycle_time, int element_count); + + INLINE void set_element_count(int element_count); + INLINE void set_cycle_time(float cycle_time); + INLINE int next_element(); + +public: + void write_datagram(Datagram &me); + void fillin(DatagramIterator &scan); + +private: + ClockObject* _global_clock; + float _cycle_time; + float _inv_cycle_time; + double _next_switch; + int _current_child; + int _element_count; +}; + +#include + +#endif diff --git a/panda/src/putil/typedWriteable.C.template b/panda/src/putil/typedWriteable.C.template new file mode 100644 index 0000000000..623cf7e966 --- /dev/null +++ b/panda/src/putil/typedWriteable.C.template @@ -0,0 +1,86 @@ +//FOR C Files +#include +#include +#include +#include + + +//////////////////////////////////////////////////////////////////// +// Function: Generic::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void Generic:: +write_datagram(BamWriter *manager, Datagram &me) +{ + GenericParent::write_datagram(manager, me); +} + +//////////////////////////////////////////////////////////////////// +// Function: Generic::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void Generic:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + GenericParent::fillin(scan, manager); +} + +//////////////////////////////////////////////////////////////////// +// Function: Generic::complete_pointers +// Access: Public +// Description: Takes in a vector of pointes to TypedWriteable +// objects that correspond to all the requests for +// pointers that this object made to BamReader. +//////////////////////////////////////////////////////////////////// +int Generic:: +complete_pointers(vector_typedWriteable &plist, BamReader *manager) +{ + int start = GenericParent::complete_pointers(plist, manager); +} + +//////////////////////////////////////////////////////////////////// +// Function: Generic::finalize +// Access: Public +// Description: Method to ensure that any necessary clean up tasks +// that have to be performed by this object are performed +//////////////////////////////////////////////////////////////////// +void Generic:: +finalize(void) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: Generic::make_Generic +// Access: Protected +// Description: Factory method to generate a Generic object +//////////////////////////////////////////////////////////////////// +TypedWriteable* Generic:: +make_Generic(const FactoryParams ¶ms) +{ + Generic *me = new Generic; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: Generic::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a Generic object +//////////////////////////////////////////////////////////////////// +void Generic:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_Generic); +} diff --git a/panda/src/putil/typedWriteable.I b/panda/src/putil/typedWriteable.I new file mode 100644 index 0000000000..e8cf38302a --- /dev/null +++ b/panda/src/putil/typedWriteable.I @@ -0,0 +1,30 @@ +// Filename: typedWriteable.I +// Created by: jason (08Jun00) +// + +//////////////////////////////////////////////////////////////////// +// Function: TypedWriteable::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypedWriteable:: +TypedWriteable() { +} + +//////////////////////////////////////////////////////////////////// +// Function: TypedWriteable::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypedWriteable:: +TypedWriteable(const TypedWriteable &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: TypedWriteable::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TypedWriteable:: +operator = (const TypedWriteable &) { +} diff --git a/panda/src/putil/typedWriteable.cxx b/panda/src/putil/typedWriteable.cxx new file mode 100644 index 0000000000..7df54afacb --- /dev/null +++ b/panda/src/putil/typedWriteable.cxx @@ -0,0 +1,31 @@ +// Filename: typeWriteable.h +// Created by: jason (08Jun00) +// + +#include "typedWriteable.h" + +TypeHandle TypedWriteable::_type_handle; +TypedWriteable* const TypedWriteable::Null = (TypedWriteable*)0L; + +//////////////////////////////////////////////////////////////////// +// Function: TypedWriteable::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +TypedWriteable::~TypedWriteable() +{ +} + + +//////////////////////////////////////////////////////////////////// +// Function: TypedWriteable::complete_pointers +// Access: Public, Virtual +// Description: Takes in a vector of pointers to TypedWriteable +// objects that correspond to all the requests for +// pointers that this object made to BamReader. +//////////////////////////////////////////////////////////////////// +int TypedWriteable:: +complete_pointers(vector_typedWriteable &, BamReader*) +{ + return 0; +} diff --git a/panda/src/putil/typedWriteable.h b/panda/src/putil/typedWriteable.h new file mode 100644 index 0000000000..d912e87863 --- /dev/null +++ b/panda/src/putil/typedWriteable.h @@ -0,0 +1,91 @@ +// Filename: typedWriteable.h +// Created by: jason (08Jun00) +// + +#ifndef __TYPED_WRITEABLE_ +#define __TYPED_WRITEABLE_ + +#include "typeHandle.h" +#include "writeable.h" +#include "vector_typedWriteable.h" + +class BamReader; + +//////////////////////////////////////////////////////////////////// +// Class : TypedWriteable +// Description : Convience class to not have to derive from TypedObject +// and writeable all the time +// +// Important : Every class derived from TypedWriteable that is +// not an abstract class, MUST define a factory method +// for creating and object of that type. This method +// must be static. This method must be of the form +// static make_(const FactoryParams &) +// Also, in the config file for each package, make sure +// to add in code to the ConfigureFN to register the +// creation function with BamReader's factory. +//////////////////////////////////////////////////////////////////// + +class EXPCL_PANDA TypedWriteable : public TypedObject, public Writeable { +public: + static TypedWriteable* const Null; + + INLINE TypedWriteable(); + INLINE TypedWriteable(const TypedWriteable ©); + INLINE void operator = (const TypedWriteable ©); + + virtual ~TypedWriteable(); + + //The essential virtual function interface to define + //how any writeable object, writes itself to a datagram + virtual void write_datagram(BamWriter *, Datagram &) = 0; + + //This function is the interface through which BamReader is + //able to pass the completed object references into each + //object that requested them. + //In other words, if an object (when it is creating itself + //from a datagram) requests BamReader to generate an object + //that this object points to, this is the function that is + //eventually called by BamReader to complete those references. + //Return the number of pointers read. This is useful for when + //a parent reads in a variable number of pointers, so the child + //knows where to start reading from. + virtual int complete_pointers(vector_typedWriteable &plist, + BamReader *manager); + + +protected: + //This interface function is written here, as a suggestion + //for a function to write in any class that will have children + //that are also TypedWriteable. To encourage code re-use + + //virtual void fillin(TypedWriteable*, DatagramIterator&, BamReader *); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedObject::init_type(); + Writeable::init_type(); + register_type(_type_handle, "TypedWriteable", + TypedObject::get_class_type(), + Writeable::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } + +private: + static TypeHandle _type_handle; +}; + +#include "typedWriteable.I" + +#endif + + diff --git a/panda/src/putil/typedWriteable.h.template b/panda/src/putil/typedWriteable.h.template new file mode 100644 index 0000000000..c45ef71bb6 --- /dev/null +++ b/panda/src/putil/typedWriteable.h.template @@ -0,0 +1,20 @@ +//FOR H File of files derived from TypedWriteable, WriteableConfigurable +//or TypedWriteableReferenceCount +class BamReader; + +//FOR H FILES + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + virtual int complete_pointers(vector_typedWriteable &plist, + BamReader *manager); + virtual void finalize(void); + + static TypedWriteable *make_Generic(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +private: + int _num_GenericPointers; diff --git a/panda/src/putil/typedWriteableReferenceCount.I b/panda/src/putil/typedWriteableReferenceCount.I new file mode 100644 index 0000000000..af120a3a19 --- /dev/null +++ b/panda/src/putil/typedWriteableReferenceCount.I @@ -0,0 +1,39 @@ +// Filename: typedWriteableReferenceCount.I +// Created by: jason (08Jun00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: TypedWriteableReferenceCount::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypedWriteableReferenceCount:: +TypedWriteableReferenceCount() { + MemoryUsage::update_type(this, TypedWriteableReferenceCount::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypedWriteableReferenceCount::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypedWriteableReferenceCount:: +TypedWriteableReferenceCount(const TypedWriteableReferenceCount ©) : + TypedWriteable(copy), + ReferenceCount(copy) +{ + MemoryUsage::update_type(this, TypedWriteableReferenceCount::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypedWriteableReferenceCount::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TypedWriteableReferenceCount:: +operator = (const TypedWriteableReferenceCount ©) { + TypedWriteable::operator = (copy); + ReferenceCount::operator = (copy); +} diff --git a/panda/src/putil/typedWriteableReferenceCount.cxx b/panda/src/putil/typedWriteableReferenceCount.cxx new file mode 100644 index 0000000000..c5532353a7 --- /dev/null +++ b/panda/src/putil/typedWriteableReferenceCount.cxx @@ -0,0 +1,7 @@ +// Filename: typedWriteableReferenceCount.cxx +// Created by: jason (08Jun00) +// + +#include "typedWriteableReferenceCount.h" + +TypeHandle TypedWriteableReferenceCount::_type_handle; diff --git a/panda/src/putil/typedWriteableReferenceCount.h b/panda/src/putil/typedWriteableReferenceCount.h new file mode 100644 index 0000000000..2f261b4414 --- /dev/null +++ b/panda/src/putil/typedWriteableReferenceCount.h @@ -0,0 +1,54 @@ +// Filename: typedWriteableReferenceCount.h +// Created by: jason (08Jun00) +// + +#ifndef TYPEDWRITEABLEREFERENCECOUNT_H +#define TYPEDWRITEABLEREFERENCECOUNT_H + +#include + +#include "typedWriteable.h" +#include "referenceCount.h" + +//////////////////////////////////////////////////////////////////// +// Class : TypedWriteableReferenceCount +// Description : A base class for things which need to inherit from +// both TypedWriteable and from ReferenceCount. It's +// convenient to define this intermediate base class +// instead of multiply inheriting from the two classes +// each time they are needed, so that we can sensibly +// pass around pointers to things which are both +// TypedWriteables and ReferenceCounters. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TypedWriteableReferenceCount : public TypedWriteable, public ReferenceCount { +public: + INLINE TypedWriteableReferenceCount(); + INLINE TypedWriteableReferenceCount(const TypedWriteableReferenceCount ©); + INLINE void operator = (const TypedWriteableReferenceCount ©); + +public: + virtual void write_datagram(BamWriter *, Datagram &) = 0; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedWriteable::init_type(); + ReferenceCount::init_type(); + register_type(_type_handle, "TypedWriteableReferenceCount", + TypedWriteable::get_class_type(), + ReferenceCount::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "typedWriteableReferenceCount.I" + +#endif diff --git a/panda/src/putil/updateSeq.I b/panda/src/putil/updateSeq.I new file mode 100644 index 0000000000..532de47f97 --- /dev/null +++ b/panda/src/putil/updateSeq.I @@ -0,0 +1,200 @@ +// Filename: updateSeq.I +// Created by: drose (30Sep99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: UpdateSeq::Default Constructor +// Access: Public +// Description: Creates an UpdateSeq in the 'initial' state. +//////////////////////////////////////////////////////////////////// +INLINE UpdateSeq:: +UpdateSeq() { + _seq = (unsigned)SC_initial; +} + +//////////////////////////////////////////////////////////////////// +// Function: UpdateSeq::initial (named constructor) +// Access: Public, Static +// Description: Returns an UpdateSeq in the 'initial' state. +//////////////////////////////////////////////////////////////////// +INLINE UpdateSeq UpdateSeq:: +initial() { + return UpdateSeq(); +} + +//////////////////////////////////////////////////////////////////// +// Function: UpdateSeq::old (named constructor) +// Access: Public, Static +// Description: Returns an UpdateSeq in the 'old' state. +//////////////////////////////////////////////////////////////////// +INLINE UpdateSeq UpdateSeq:: +old() { + UpdateSeq result; + result._seq = (unsigned)SC_old; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: UpdateSeq::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE UpdateSeq:: +UpdateSeq(const UpdateSeq ©) { + _seq = copy._seq; +} + +//////////////////////////////////////////////////////////////////// +// Function: UpdateSeq::Copy Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE UpdateSeq &UpdateSeq:: +operator = (const UpdateSeq ©) { + _seq = copy._seq; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: UpdateSeq::clear +// Access: Public +// Description: Resets the UpdateSeq to the 'initial' state. +//////////////////////////////////////////////////////////////////// +INLINE void UpdateSeq:: +clear() { + _seq = (unsigned)SC_initial; +} + +//////////////////////////////////////////////////////////////////// +// Function: UpdateSeq::is_initial +// Access: Public +// Description: Returns true if the UpdateSeq is in the 'initial' +// state. +//////////////////////////////////////////////////////////////////// +INLINE bool UpdateSeq:: +is_initial() const { + return _seq == SC_initial; +} + +//////////////////////////////////////////////////////////////////// +// Function: UpdateSeq::is_old +// Access: Public +// Description: Returns true if the UpdateSeq is in the 'old' state. +//////////////////////////////////////////////////////////////////// +INLINE bool UpdateSeq:: +is_old() const { + return _seq == SC_old; +} + +//////////////////////////////////////////////////////////////////// +// Function: UpdateSeq::is_special +// Access: Public +// Description: Returns true if the UpdateSeq is in any special +// states, i.e. 'initial' or 'old'. +//////////////////////////////////////////////////////////////////// +INLINE bool UpdateSeq:: +is_special() const { + return _seq <= (unsigned)SC_old; +} + +//////////////////////////////////////////////////////////////////// +// Function: UpdateSeq::Equality operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool UpdateSeq:: +operator == (const UpdateSeq &other) const { + return (_seq == other._seq); +} + +//////////////////////////////////////////////////////////////////// +// Function: UpdateSeq::Inequality operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool UpdateSeq:: +operator != (const UpdateSeq &other) const { + return (_seq != other._seq); +} + +//////////////////////////////////////////////////////////////////// +// Function: UpdateSeq::Comparison operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool UpdateSeq:: +operator < (const UpdateSeq &other) const { + // The special cases of SC_initial or SC_old are less than all other + // non-special numbers, and SC_initial is less than SC_old. For all + // other cases, we use a circular comparision such that n < m iff + // (signed)(n - m) < 0. + return (is_special() || other.is_special()) ? (_seq < other._seq) : + ((signed int)(_seq - other._seq) < 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: UpdateSeq::Comparison operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool UpdateSeq:: +operator <= (const UpdateSeq &other) const { + return (*this) == other || (*this) < other; +} + +//////////////////////////////////////////////////////////////////// +// Function: UpdateSeq::Preincrement operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE UpdateSeq UpdateSeq:: +operator ++ () { + ++_seq; + if (is_special()) { + // Oops, wraparound. We don't want to confuse the new value + // with our special cases. + _seq = (unsigned)SC_old + 1; + } + + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: UpdateSeq::Postincrement operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE UpdateSeq UpdateSeq:: +operator ++ (int) { + UpdateSeq temp = (*this); + operator ++ (); + return temp; +} + +//////////////////////////////////////////////////////////////////// +// Function: UpdateSeq::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void UpdateSeq:: +output(ostream &out) const { + switch (_seq) { + case SC_initial: + out << "initial"; + break; + + case SC_old: + out << "old"; + break; + + default: + out << _seq; + } +} + +INLINE ostream &operator << (ostream &out, const UpdateSeq &value) { + value.output(out); + return out; +} diff --git a/panda/src/putil/updateSeq.cxx b/panda/src/putil/updateSeq.cxx new file mode 100644 index 0000000000..3f164a2812 --- /dev/null +++ b/panda/src/putil/updateSeq.cxx @@ -0,0 +1,6 @@ +// Filename: updateSeq.cxx +// Created by: mike (30Sep99) +// +//////////////////////////////////////////////////////////////////// + +#include "updateSeq.h" diff --git a/panda/src/putil/updateSeq.h b/panda/src/putil/updateSeq.h new file mode 100644 index 0000000000..b4020c6188 --- /dev/null +++ b/panda/src/putil/updateSeq.h @@ -0,0 +1,67 @@ +// Filename: updateSeq.h +// Created by: drose (30Sep99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef UPDATE_SEQ +#define UPDATE_SEQ + +#include + +//////////////////////////////////////////////////////////////////// +// Class : UpdateSeq +// Description : This is a sequence number that increments +// monotonically. It can be used to track cache +// updates, or serve as a kind of timestamp for any +// changing properties. +// +// A special class is used instead of simply an int, so +// we can elegantly handle such things as wraparound and +// special cases. There are two special cases. +// Firstly, a sequence number is 'initial' when it is +// first created. This sequence is older than any other +// sequence number. Secondly, a sequence number may be +// explicitly set to 'old'. This is older than any +// other sequence number except 'initial'. All other +// sequences are numeric and are monotonically +// increasing. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA UpdateSeq { +public: + INLINE UpdateSeq(); + INLINE static UpdateSeq initial(); + INLINE static UpdateSeq old(); + + INLINE UpdateSeq(const UpdateSeq ©); + INLINE UpdateSeq &operator = (const UpdateSeq ©); + + INLINE void clear(); + + INLINE bool is_initial() const; + INLINE bool is_old() const; + INLINE bool is_special() const; + + INLINE bool operator == (const UpdateSeq &other) const; + INLINE bool operator != (const UpdateSeq &other) const; + INLINE bool operator < (const UpdateSeq &other) const; + INLINE bool operator <= (const UpdateSeq &other) const; + + INLINE UpdateSeq operator ++ (); + INLINE UpdateSeq operator ++ (int); + + INLINE void output(ostream &out) const; + +private: + enum SpecialCases { + SC_initial = 0, + SC_old = 1, + }; + + unsigned _seq; +}; + +INLINE ostream &operator << (ostream &out, const UpdateSeq &value); + +#include "updateSeq.I" + +#endif diff --git a/panda/src/putil/vector_double.cxx b/panda/src/putil/vector_double.cxx new file mode 100644 index 0000000000..e7baee2559 --- /dev/null +++ b/panda/src/putil/vector_double.cxx @@ -0,0 +1,11 @@ +// Filename: vector_double.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_double.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/putil/vector_double.h b/panda/src/putil/vector_double.h new file mode 100644 index 0000000000..06f9e69900 --- /dev/null +++ b/panda/src/putil/vector_double.h @@ -0,0 +1,30 @@ +// Filename: vector_double.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_DOUBLE_H +#define VECTOR_DOUBLE_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_double +// Description : A vector of doubles. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a vector of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, std::vector) +typedef vector vector_double; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/putil/vector_float.cxx b/panda/src/putil/vector_float.cxx new file mode 100644 index 0000000000..44f0f1dd25 --- /dev/null +++ b/panda/src/putil/vector_float.cxx @@ -0,0 +1,11 @@ +// Filename: vector_float.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_float.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/putil/vector_float.h b/panda/src/putil/vector_float.h new file mode 100644 index 0000000000..8aadb5dba4 --- /dev/null +++ b/panda/src/putil/vector_float.h @@ -0,0 +1,30 @@ +// Filename: vector_float.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_FLOAT_H +#define VECTOR_FLOAT_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_float +// Description : A vector of floats. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a vector of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, std::vector) +typedef vector vector_float; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/putil/vector_typedWriteable.cxx b/panda/src/putil/vector_typedWriteable.cxx new file mode 100644 index 0000000000..6aed4efe72 --- /dev/null +++ b/panda/src/putil/vector_typedWriteable.cxx @@ -0,0 +1,11 @@ +// Filename: vector_typedWriteable.cxx +// Created by: jason (19Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_typedWriteable.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/putil/vector_typedWriteable.h b/panda/src/putil/vector_typedWriteable.h new file mode 100644 index 0000000000..dfe3cb5fe9 --- /dev/null +++ b/panda/src/putil/vector_typedWriteable.h @@ -0,0 +1,32 @@ +// Filename: vector_typedWriteable.h +// Created by: jason (19Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_TYPED_WRITEABLE_H +#define VECTOR_TYPED_WRITEABLE_H + +#include + +#include + +class TypedWriteable; + +//////////////////////////////////////////////////////////////////// +// Class : vector_typedWriteable +// Description : A vector of TypedWriteable. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a vector of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, std::vector) +typedef vector vector_typedWriteable; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/putil/vector_uchar.cxx b/panda/src/putil/vector_uchar.cxx new file mode 100644 index 0000000000..2dcb9db006 --- /dev/null +++ b/panda/src/putil/vector_uchar.cxx @@ -0,0 +1,11 @@ +// Filename: vector_uchar.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_uchar.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/putil/vector_uchar.h b/panda/src/putil/vector_uchar.h new file mode 100644 index 0000000000..2f6525c4fd --- /dev/null +++ b/panda/src/putil/vector_uchar.h @@ -0,0 +1,30 @@ +// Filename: vector_uchar.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_UCHAR_H +#define VECTOR_UCHAR_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_uchar +// Description : A vector of uchars. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a vector of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, std::vector) +typedef vector vector_uchar; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/putil/vector_ushort.cxx b/panda/src/putil/vector_ushort.cxx new file mode 100644 index 0000000000..c3b0af4cb2 --- /dev/null +++ b/panda/src/putil/vector_ushort.cxx @@ -0,0 +1,11 @@ +// Filename: vector_ushort.cxx +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_ushort.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/putil/vector_ushort.h b/panda/src/putil/vector_ushort.h new file mode 100644 index 0000000000..7b98c5e80c --- /dev/null +++ b/panda/src/putil/vector_ushort.h @@ -0,0 +1,30 @@ +// Filename: vector_ushort.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_USHORT_H +#define VECTOR_USHORT_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_ushort +// Description : A vector of ushorts. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a vector of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, std::vector) +typedef vector vector_ushort; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/putil/vector_writeable.cxx b/panda/src/putil/vector_writeable.cxx new file mode 100644 index 0000000000..362a980cbd --- /dev/null +++ b/panda/src/putil/vector_writeable.cxx @@ -0,0 +1,11 @@ +// Filename: vector_writeable.cxx +// Created by: (15Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_writeable.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/panda/src/putil/vector_writeable.h b/panda/src/putil/vector_writeable.h new file mode 100644 index 0000000000..e1df9462b6 --- /dev/null +++ b/panda/src/putil/vector_writeable.h @@ -0,0 +1,32 @@ +// Filename: vector_writeable.h +// Created by: jason (14Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_WRITEABLE_H +#define VECTOR_WRITEABLE_H + +#include + +#include + +class Writeable; + +//////////////////////////////////////////////////////////////////// +// Class : vector_writeable +// Description : A vector of TypedWriteable. This class is defined once here, +// and exported to PANDA.DLL; other packages that want +// to use a vector of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, std::vector) +typedef vector vector_writeable; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/panda/src/putil/writeable.I b/panda/src/putil/writeable.I new file mode 100644 index 0000000000..27a2a493cb --- /dev/null +++ b/panda/src/putil/writeable.I @@ -0,0 +1,31 @@ +// Filename: writeable.I +// Created by: jason (19Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Writeable::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Writeable:: +Writeable() { +} + +//////////////////////////////////////////////////////////////////// +// Function: Writeable::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Writeable:: +Writeable(const Writeable &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: Writeable::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Writeable:: +operator = (const Writeable &) { +} diff --git a/panda/src/putil/writeable.cxx b/panda/src/putil/writeable.cxx new file mode 100644 index 0000000000..5780e9b596 --- /dev/null +++ b/panda/src/putil/writeable.cxx @@ -0,0 +1,19 @@ +// Filename: writeable.cxx +// Created by: jason (19Jun00) +// +//////////////////////////////////////////////////////////////////// + + +#include "writeable.h" + +TypeHandle Writeable::_type_handle; +Writeable* const Writeable::Null = (Writeable*)0L; + +//////////////////////////////////////////////////////////////////// +// Function: Writeable::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +Writeable::~Writeable() +{ +} diff --git a/panda/src/putil/writeable.h b/panda/src/putil/writeable.h new file mode 100644 index 0000000000..5347f494fe --- /dev/null +++ b/panda/src/putil/writeable.h @@ -0,0 +1,84 @@ +// Filename: writeable.h +// Created by: jason (08Jun00) +// + +#ifndef __WRITEABLE_ +#define __WRITEABLE_ + +#include "vector_writeable.h" +#include "typeHandle.h" + +class BamWriter; +class Datagram; +class DatagramIterator; +class FactoryParams; + +//////////////////////////////////////////////////////////////////// +// Class : Writeable +// Description : This is an abstract class that all classes which +// need to write themselves in binary form to some +// media should inherit from +//////////////////////////////////////////////////////////////////// + +class EXPCL_PANDA Writeable { +public: + static Writeable* const Null; + + INLINE Writeable(); + INLINE Writeable(const Writeable ©); + INLINE void operator = (const Writeable ©); + + virtual ~Writeable(); + + //The essential virtual function interface to define + //how any writeable object, writes itself to a datagram + virtual void write_datagram(BamWriter *manager, Datagram &me) = 0; + + //This function is the interface through which BamReader is + //able to pass the completed object references into each + //object that requested them. + //In other words, if an object (when it is creating itself + //from a datagram) requests BamReader to generate an object + //that this object points to, this is the function that is + //eventually called by BamReader to complete those references. + //Return the number of pointers read. This is useful for when + //a parent reads in a variable number of pointers, so the child + //knows where to start reading from. + //virtual int complete_pointers(vector_typedWriteable &plist, + // BamReader *manager) {} + + + //When everything else is done, this is the function called + //by BamReader to perform any final actions needed for setting + //up the object + virtual void finalize(void) {} + +protected: + //This interface function is written here, as a suggestion + //for a function to write in any class that will have children + //that are also Writeable. To encourage code re-use + + //virtual void fillin(DatagramIterator& scan, BamReader *manager); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + register_type(_type_handle, "Writeable"); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } + +private: + static TypeHandle _type_handle; +}; + +#include "writeable.I" + +#endif diff --git a/panda/src/putil/writeableConfigurable.cxx b/panda/src/putil/writeableConfigurable.cxx new file mode 100644 index 0000000000..eb831c2507 --- /dev/null +++ b/panda/src/putil/writeableConfigurable.cxx @@ -0,0 +1,8 @@ +// Filename: writeableConfigurable.cxx +// Created by: jason (19Jun00) +// + +#include "writeableConfigurable.h" + + +TypeHandle WriteableConfigurable::_type_handle; diff --git a/panda/src/putil/writeableConfigurable.h b/panda/src/putil/writeableConfigurable.h new file mode 100644 index 0000000000..fa7d2f76f3 --- /dev/null +++ b/panda/src/putil/writeableConfigurable.h @@ -0,0 +1,78 @@ +// Filename: writeableConfigurable.h +// Created by: jason (19Jun00) +// + +// +#ifndef WRITEABLECONFIGURABLE_H +#define WRITEABLECONFIGURABLE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// + +#include + +#include "typedWriteable.h" + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : WriteableConfigurable +// Description : Defined as a fix to allow creating Configurable and +// Writeable objects. Otherwise the compilter gets +// confused since both TypedWriteable and Configurable +// inherit from TypedObject +// +// An object that has data or parameters that are set +// less frequently (at least occasionally) than every +// frame. We can cache the configuration info by +// by using the "dirty" flag. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA WriteableConfigurable : public TypedWriteable { + +public: + WriteableConfigurable( void ) { make_dirty(); } + virtual void config( void ) { _dirty = false; } + INLINE void check_config() const { + if (_dirty) { + // This is a sneaky trick to allow check_config() to be called + // from a const member function. Even though we will be calling + // config(), a non-const function that modifies the class + // object, in some sense it's not really modifying the class + // object--it's just updating a few internal settings for + // consistency. + ((WriteableConfigurable *)this)->config(); + } + } + + INLINE bool is_dirty(void) const { return _dirty; } + INLINE void make_dirty(void) { _dirty = true; } + +private: + bool _dirty; + +public: + virtual void write_datagram(BamWriter*, Datagram&) = 0; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedWriteable::init_type(); + register_type(_type_handle, "WriteableConfigurable", + TypedWriteable::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/putil/writeableParam.I b/panda/src/putil/writeableParam.I new file mode 100644 index 0000000000..8c6bd33c9b --- /dev/null +++ b/panda/src/putil/writeableParam.I @@ -0,0 +1,58 @@ +// Filename: writeableParam.I +// Created by: jason (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: WriteableParam::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE WriteableParam:: +WriteableParam(const Datagram &datagram) : + _packet(datagram) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: WriteableParam::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE WriteableParam:: +WriteableParam(const WriteableParam ©) : + FactoryParam(copy), + _packet(copy._packet) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: WriteableParam::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE WriteableParam:: +~WriteableParam(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function: WriteableParam::Copy Assignment Operator +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void WriteableParam:: +operator = (const WriteableParam &) { + // The assignment operator cannot be used for this class. + nassertv(false); +} + +//////////////////////////////////////////////////////////////////// +// Function: WriteableParam::get_datagram +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const Datagram& WriteableParam:: +get_datagram(void) { + return _packet; +} + diff --git a/panda/src/putil/writeableParam.cxx b/panda/src/putil/writeableParam.cxx new file mode 100644 index 0000000000..418663fc2d --- /dev/null +++ b/panda/src/putil/writeableParam.cxx @@ -0,0 +1,8 @@ +// Filename: writeableParam.cxx +// Created by: jason (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "writeableParam.h" + +TypeHandle WriteableParam::_type_handle; diff --git a/panda/src/putil/writeableParam.h b/panda/src/putil/writeableParam.h new file mode 100644 index 0000000000..1dca0a1495 --- /dev/null +++ b/panda/src/putil/writeableParam.h @@ -0,0 +1,60 @@ +// Filename: writeableParam.h +// Created by: jason (13Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef WRITEABLEPARAM_H +#define WRITEABLEPARAM_H + +#include + +#include "factoryParam.h" +#include "datagram.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : WriteableParam +// Description : The specific derivation of FactoryParam that +// contains the information needed by a TypedWriteable +// object. Simply contains a Datagram for the object +// to construct itself from. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA WriteableParam : public FactoryParam { +public: + INLINE const Datagram &get_datagram(void); + +private: + const Datagram &_packet; + +public: + INLINE WriteableParam(const Datagram &datagram); + INLINE WriteableParam(const WriteableParam &other); + INLINE ~WriteableParam(); + +private: + // The assignment operator cannot be used for this class. + INLINE void operator = (const WriteableParam &other); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "WriteableParam", + FactoryParam::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "writeableParam.I" + +#endif + diff --git a/panda/src/putil/writeables.txt b/panda/src/putil/writeables.txt new file mode 100644 index 0000000000..99f1a7c596 --- /dev/null +++ b/panda/src/putil/writeables.txt @@ -0,0 +1,92 @@ +OVERVIEW + +The system for writing out objects works as follows. + +There are 2 types of managers that control the overall process for +reading and writing, BamWriter and BamReader. The initial request to +write/read an object is made to the manager. The manager will take +care of breaking/regenerating circular dependencies, making sure that +type information is written/read correctly, ensuring shared memory +references are refelected in or regenerated from the binary media, +etc... + +The overall process goes as follows. +1) BamWriter needs to be created and given an appropriate DatagramSink for it to send + datagrams into. +2) Request to write an object is made to BamWriter +3) BamWriter checks to see if that Type of object has been written before or not, if it has then + the index of that type is written, otherwise the index and type name are written. +4) BamWriter then has the Object write itself into the datagram, and passes a reference to itself + to the object. +5) The object will write all information necessary to regenerate itself into the datagram, with + 2 exceptions. + A) Any pointers to other objects needing to be written, are actually written through + requests to BamWriter. This ensures that the object being pointed to does get + written to the datagram, and that multiple references do not cause the object + to be written multiple times. + B) Any PointerToArrays are also written through requests to BamWriter. This ensures + that multiple references to the same PTA are not fully written multiple times and + that the information necessary to regenerate that sharing is written. There is a + an interaction between the object and BamWriter that ensures this that is taken + care of in an #define function called WRITE_PTA defined in BamWriter.h (with the + equivalent READ_PTA defined in BamReader.h) +6) Once the object has completed writing itself, BamWriter will send the datagram to the + DatagramSink. + + +Due to BamWriter's ability to handle pointer requests, only one write on the root of any linked +structure will be necessary if all of the objects are correctly "writeable". + +For reading the process goes as follows +1) Create a BamReader with an appropriate DatagramGenerator source. +2) Requets to read an object from BamReader +3) BamReader will then read in the type of the object, and check that type against the current + dynamic index. +4) BamReader will then make a call to the TypedWriteable factory to generate an object of that + type. Passing a reference to itself and the datagram defining that object as params. +5) The make method of that type will be called +6) The make method of that type of object will create a new object and "fillin" in itself from the + data in the datagram. +7) As with Writing any Pointers to objects or PTA's will be filled through requests to BamReader. + Pointers will not be completed at this time, as the object may not have been read in. BamReader + will register the requestor and when resolve is called, BamReader will pass all the pointers + requested back to the caller through the common interface complete_pointers. +8) Finally, if the object needs to have any code run only after all pointers have been completed + it can register itself with BamReader to be "finalized" + + +CREATING A WRITEABLE CLASS + +There is a file called template_for_typedWriteables.C that has the +"outline" code necessary to make a class writeable that is mostly +self-explanatory. A few clarifications are that not all of the +functions in that file are needed by every class. The finalize +function is currently only needed by PartBundles so you probably won't +need it. And complete_pointers is only needed if the object +writes/reads pointers to other objects. The make functions should +only be defined in classes that are non-abstract, that are actually +created and used. + +Any class that is writeable has one last step besides adding all the +functionality into it. Every "makeable" class needs to have a call to +it's register_with_read_factory method put into the config file for +the package that is is apart of. + +For working examples see +NodeRelation in the package graph +PartBundle in the package chan +Geom in the package gobj + +For examples of the classes that are not TypedWriteable but it is useful for them to write and read +themselves into/from datagrams see +LVecBase(2)(3)(4) in the package linmath +ComputedVerticesMorph in the package char + + + + + + + + + diff --git a/panda/src/ribdisplay/Sources.pp b/panda/src/ribdisplay/Sources.pp new file mode 100644 index 0000000000..f853efa8f7 --- /dev/null +++ b/panda/src/ribdisplay/Sources.pp @@ -0,0 +1,20 @@ +#define DIRECTORY_IF_RIB yes + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET ribdisplay + #define LOCAL_LIBS \ + display ribgsg sgraph + + #define SOURCES \ + config_ribdisplay.cxx config_ribdisplay.h ribGraphicsPipe.I \ + ribGraphicsPipe.cxx ribGraphicsPipe.h ribGraphicsWindow.I \ + ribGraphicsWindow.cxx ribGraphicsWindow.h + + #define INSTALL_HEADERS \ + ribGraphicsPipe.I ribGraphicsPipe.h ribGraphicsWindow.I \ + ribGraphicsWindow.h + +#end lib_target + diff --git a/panda/src/ribdisplay/config_ribdisplay.cxx b/panda/src/ribdisplay/config_ribdisplay.cxx new file mode 100644 index 0000000000..30082226b7 --- /dev/null +++ b/panda/src/ribdisplay/config_ribdisplay.cxx @@ -0,0 +1,22 @@ +// Filename: config_ribdisplay.cxx +// Created by: cary (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_ribdisplay.h" +#include "ribGraphicsPipe.h" +#include "ribGraphicsWindow.h" + +#include + +Configure(config_ribdisplay); +NotifyCategoryDef(ribdisplay, "display"); + +ConfigureFn(config_ribdisplay) { + RIBGraphicsPipe::init_type(); + GraphicsPipe::_factory.register_factory(RIBGraphicsPipe::get_class_type(), + RIBGraphicsPipe::make_RIBGraphicsPipe); + RIBGraphicsWindow::init_type(); + GraphicsWindow::_factory.register_factory(RIBGraphicsWindow::get_class_type(), + RIBGraphicsWindow::make_RibGraphicsWindow); +} diff --git a/panda/src/ribdisplay/config_ribdisplay.h b/panda/src/ribdisplay/config_ribdisplay.h new file mode 100644 index 0000000000..9bac41aa93 --- /dev/null +++ b/panda/src/ribdisplay/config_ribdisplay.h @@ -0,0 +1,14 @@ +// Filename: config_ribdisplay.h +// Created by: cary (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_RIBDISPLAY_H +#define CONFIG_RIBDISPLAY_H + +#include +#include + +NotifyCategoryDecl(ribdisplay, EXPCL_PANDARIB, EXPTP_PANDARIB); + +#endif diff --git a/panda/src/ribdisplay/ribGraphicsPipe.I b/panda/src/ribdisplay/ribGraphicsPipe.I new file mode 100644 index 0000000000..ce736bd6c4 --- /dev/null +++ b/panda/src/ribdisplay/ribGraphicsPipe.I @@ -0,0 +1,57 @@ +// Filename: ribGraphicsPipe.I +// Created by: drose (17Feb99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsPipe::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE RIBGraphicsPipe:: +RIBGraphicsPipe(const PipeSpecifier& spec) + : NoninteractiveGraphicsPipe(spec), _filename(spec.get_file_name()) { +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsPipe::get_file_name +// Access: Public +// Description: gets the base filename +//////////////////////////////////////////////////////////////////// +INLINE std::string RIBGraphicsPipe:: +get_file_name(void) const { + return _filename; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsPipe::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE RIBGraphicsPipe:: +RIBGraphicsPipe(void) { + cerr << "RIBGraphicsPipes should not be created with default constructor" + << endl; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsPipe::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE RIBGraphicsPipe:: +RIBGraphicsPipe(const RIBGraphicsPipe&) { + cerr << "RIBGraphicsPipes should not be copied" << endl; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsPipe::operator= +// Access: Public +// Description: assignment operator +//////////////////////////////////////////////////////////////////// +INLINE RIBGraphicsPipe& RIBGraphicsPipe:: +operator=(const RIBGraphicsPipe&) { + cerr << "RIBGraphicsPipes should not be assigned" << endl; + return *this; +} diff --git a/panda/src/ribdisplay/ribGraphicsPipe.cxx b/panda/src/ribdisplay/ribGraphicsPipe.cxx new file mode 100644 index 0000000000..f3919d3318 --- /dev/null +++ b/panda/src/ribdisplay/ribGraphicsPipe.cxx @@ -0,0 +1,46 @@ +// Filename: ribGraphicsPipe.cxx +// Created by: drose (15Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "ribGraphicsPipe.h" +#include "ribGraphicsWindow.h" +#include "config_ribdisplay.h" + +TypeHandle RIBGraphicsPipe::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsPipe::get_window_type +// Access: Public, Virtual +// Description: Returns the TypeHandle of the kind of window +// preferred by this kind of pipe. +//////////////////////////////////////////////////////////////////// +TypeHandle RIBGraphicsPipe:: +get_window_type() const { + return RIBGraphicsWindow::get_class_type(); +} + + +GraphicsPipe *RIBGraphicsPipe:: +make_RIBGraphicsPipe(const FactoryParams ¶ms) { + GraphicsPipe::PipeSpec *pipe_param; + if (!get_param_into(pipe_param, params)) { + return new RIBGraphicsPipe(PipeSpecifier()); + } else { + return new RIBGraphicsPipe(pipe_param->get_specifier()); + } +} + +TypeHandle RIBGraphicsPipe::get_class_type(void) { + return _type_handle; +} + +void RIBGraphicsPipe::init_type(void) { + NoninteractiveGraphicsPipe::init_type(); + register_type(_type_handle, "RIBGraphicsPipe", + NoninteractiveGraphicsPipe::get_class_type()); +} + +TypeHandle RIBGraphicsPipe::get_type(void) const { + return get_class_type(); +} diff --git a/panda/src/ribdisplay/ribGraphicsPipe.h b/panda/src/ribdisplay/ribGraphicsPipe.h new file mode 100644 index 0000000000..4949eba416 --- /dev/null +++ b/panda/src/ribdisplay/ribGraphicsPipe.h @@ -0,0 +1,60 @@ +// Filename: ribGraphicsPipe.h +// Created by: drose (15Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef RIBGRAPHICSPIPE_H +#define RIBGRAPHICSPIPE_H + +#include + +#include +#include + + +//////////////////////////////////////////////////////////////////// +// Class : RIBGraphicsPipe +// Description : A place to grab RIBGraphicsWindows from. Not +// terribly exciting in itself. The name of the pipe is +// used as the default RIB output filename for each +// window, but the window may change this. +//////////////////////////////////////////////////////////////////// +class RIBGraphicsPipe : public NoninteractiveGraphicsPipe { +public: + + INLINE RIBGraphicsPipe(const PipeSpecifier&); + + virtual TypeHandle get_window_type() const; + +public: + + static GraphicsPipe *make_RIBGraphicsPipe(const FactoryParams ¶ms); + + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +public: + + INLINE std::string get_file_name(void) const; + +protected: + + std::string _filename; + +private: + + static TypeHandle _type_handle; + +protected: + + INLINE RIBGraphicsPipe(void); + INLINE RIBGraphicsPipe(const RIBGraphicsPipe&); + INLINE RIBGraphicsPipe& operator=(const RIBGraphicsPipe&); +}; + +#include "ribGraphicsPipe.I" + +#endif + diff --git a/panda/src/ribdisplay/ribGraphicsWindow.I b/panda/src/ribdisplay/ribGraphicsWindow.I new file mode 100644 index 0000000000..9e078826f5 --- /dev/null +++ b/panda/src/ribdisplay/ribGraphicsWindow.I @@ -0,0 +1,164 @@ +// Filename: ribGraphicsWindow.I +// Created by: drose (17Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::set_rib_filename_template +// Access: Public +// Description: Specifies the string that defines the filename that +// will be generated for each RIB file output by the +// GraphicsWindow. The template may contain any of the +// % format characters described in ribGraphicsWindow.h. +// In particular, if it contains %f (or some variant), +// there will be a separate RIB file generated for each +// frame; otherwise, all the frames will be written to +// the same RIB file. +//////////////////////////////////////////////////////////////////// +INLINE void RIBGraphicsWindow:: +set_rib_filename_template(const string &str) { + _rib_filename_template = str; + _rib_per_frame = check_per_frame(str); +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::get_rib_filename_template +// Access: Public +// Description: Returns the string that defines the RIB filename. +// See set_rib_filename_template(). +//////////////////////////////////////////////////////////////////// +INLINE string RIBGraphicsWindow:: +get_rib_filename_template() const { + return _rib_filename_template; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::get_rib_filename +// Access: Public +// Description: Returns the name of the RIB file that will be created +// at the next (or possibly only the first) call to +// begin_frame(). +//////////////////////////////////////////////////////////////////// +INLINE string RIBGraphicsWindow:: +get_rib_filename() const { + return format_name(_rib_filename_template); +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::rib_per_frame +// Access: Public +// Description: Returns true if the RIB filename template contains +// the format specification %f, which means there will +// be a separate RIB file generated for each frame. +//////////////////////////////////////////////////////////////////// +INLINE bool RIBGraphicsWindow:: +rib_per_frame() const { + return _rib_per_frame; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::set_image_filename_template +// Access: Public +// Description: Specifies the string that defines the filename that +// will be written to the RIB file as an output filename +// request. The template may contain any of the +// % format characters descimageed in RIBGraphicsWindow.h. +// In particular, if it contains %f (or some variant), +// there will be a separate image file generated for each +// frame; otherwise, all the frames will be written to +// the same image file, whatever that might mean. +// +// This string may also be empty. If it is, no +// "Display" command will be written to the RIB file, +// and the renderer will choose whatever output filename +// is appropriate. +//////////////////////////////////////////////////////////////////// +INLINE void RIBGraphicsWindow:: +set_image_filename_template(const string &str) { + _image_filename_template = str; + _image_per_frame = check_per_frame(str); +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::get_image_filename_template +// Access: Public +// Description: Returns the string that defines the image filename. +// See set_image_filename_template(). +//////////////////////////////////////////////////////////////////// +INLINE string RIBGraphicsWindow:: +get_image_filename_template() const { + return _image_filename_template; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::get_image_filename +// Access: Public +// Description: Returns the name of the image file that will be +// created at the next call to begin_frame(). +//////////////////////////////////////////////////////////////////// +INLINE string RIBGraphicsWindow:: +get_image_filename() const { + return format_name(_image_filename_template); +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::image_per_frame +// Access: Public +// Description: Returns true if the image filename template contains +// the format specification %f, which means there will +// be a separate image file generated for each frame. +//////////////////////////////////////////////////////////////////// +INLINE bool RIBGraphicsWindow:: +image_per_frame() const { + return _image_per_frame; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateWindow::set_texture_directory +// Access: Public +// Description: Sets the name of the directory into which texture +// maps are copied to be available to the RIB file. +//////////////////////////////////////////////////////////////////// +INLINE void RIBGraphicsWindow:: +set_texture_directory(const string &directory) { + DCAST(RIBGraphicsStateGuardian, _gsg)->set_texture_directory(directory); +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::get_texture_directory +// Access: Public +// Description: Returns the name of the directory into which texture +// maps are copied to be available to the RIB file. +//////////////////////////////////////////////////////////////////// +INLINE string RIBGraphicsWindow:: +get_texture_directory() const { + return DCAST(RIBGraphicsStateGuardian, _gsg)->get_texture_directory(); +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::set_texture_extension +// Access: Public +// Description: Specifies the filename extension that texture map +// files are given when they are copied into the +// directory for RIB files. This might also imply an +// image type. The default is "tiff", which implies +// TIFF files. +//////////////////////////////////////////////////////////////////// +INLINE void RIBGraphicsWindow:: +set_texture_extension(const string &extension) { + DCAST(RIBGraphicsStateGuardian, _gsg)->set_texture_extension(extension); +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::get_texture_extension +// Access: Public +// Description: Returns the filename extension that texture map +// files are given when they are copied into the +// directory for RIB files. +//////////////////////////////////////////////////////////////////// +INLINE string RIBGraphicsWindow:: +get_texture_extension() const { + return DCAST(RIBGraphicsStateGuardian, _gsg)->get_texture_extension(); +} diff --git a/panda/src/ribdisplay/ribGraphicsWindow.cxx b/panda/src/ribdisplay/ribGraphicsWindow.cxx new file mode 100644 index 0000000000..85c7fcdd59 --- /dev/null +++ b/panda/src/ribdisplay/ribGraphicsWindow.cxx @@ -0,0 +1,398 @@ +// Filename: ribGraphicsWindow.cxx +// Created by: drose (15Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "ribGraphicsWindow.h" +#include "ribGraphicsPipe.h" +#include "config_ribdisplay.h" + +#include +#include + +TypeHandle RIBGraphicsWindow::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +RIBGraphicsWindow:: +RIBGraphicsWindow(GraphicsPipe *pipe) : GraphicsWindow(pipe) { + setup_window(pipe); +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +RIBGraphicsWindow:: +RIBGraphicsWindow(GraphicsPipe *pipe, + const GraphicsWindow::Properties &props) : + GraphicsWindow(pipe, props) +{ + setup_window(pipe); +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +RIBGraphicsWindow:: +~RIBGraphicsWindow(void) { + flush_file(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::flush_file +// Access: Public +// Description: Finishes the RIB file currently being output and +// closes it. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsWindow:: +flush_file() { + if (_file_begun) { + end_file(); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::begin_frame +// Access: Public, Virtual +// Description: Prepares to write a new frame to the RIB file. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsWindow:: +begin_frame() { + if (!_file_begun) { + // Open a new RIB file if we need to. + begin_file(); + } + + _file << "FrameBegin " << _frame_number << "\n"; + + if (image_per_frame()) { + // If we're writing out an image file for each frame, specify it + // here, inside the Frame control group. + _file << " Display \"" << get_image_filename() + << "\" \"file\" \"rgba\"\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::end_frame +// Access: Public, Virtual +// Description: Finalizes a frame. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsWindow:: +end_frame() { + _file << "FrameEnd\n"; + + if (rib_per_frame()) { + // If we're outputting a RIB file for each frame, close the file now. + end_file(); + } + + GraphicsWindow::end_frame(); +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::setup_window +// Access: Protected +// Description: Called by the constructor to initialize whatever +// internal structures are necessary, given the +// indicated pipe and requested properties. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsWindow:: +setup_window(GraphicsPipe *pipe) { + RIBGraphicsPipe *rp = DCAST(RIBGraphicsPipe, pipe); + + _file_begun = false; + + set_rib_filename_template(rp->get_file_name()); + set_image_filename_template(""); + + make_gsg(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::begin_file +// Access: Protected +// Description: Called internally when a new RIB file needs to be +// created and its suitable headers written. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsWindow:: +begin_file() { + assert(!_file_begun); + RIBGraphicsStateGuardian *rgsg = DCAST(RIBGraphicsStateGuardian, _gsg); + + _file_begun = true; + _current_rib_filename = Filename::text_filename(get_rib_filename()); + assert(!_current_rib_filename.empty()); + + _current_rib_filename.open_write(_file); + rgsg->reset_file(_file); + + if (!_file) { + cerr << "Unable to write to " << _current_rib_filename << "\n"; + return; + } + + _file << + "#\n" + "# RIB file " << _current_rib_filename << "\n" + "#\n"; + + // World set-up + _file << + // "TextureCoordinates 0 1 1 1 0 0 1 0\n" + "Option \"searchpath\" \"shader\" \"shaders:@\"\n"; + + // A default ambient light for when lighting is off + _file << + "LightSource \"ambientlight\" 0 \"intensity\" 1 \"lightcolor\" [ 1 1 1 ]\n" + "Illuminate 0 1\n"; + + + if (!get_image_filename_template().empty() && + !image_per_frame()) { + // If we have an image filename, and it's the same file for all + // frames, specify it outside the Frame control group. Maybe the + // renderer will be able to generate a multi-frame output file + // somehow. + _file << "Display \"" << get_image_filename() + << "\" \"file\" \"rgb\"\n"; + } + +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::end_file +// Access: Protected +// Description: Called internally to wrap up the current RIB file and +// close it. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsWindow:: +end_file() { + assert(_file_begun); + RIBGraphicsStateGuardian *rgsg = DCAST(RIBGraphicsStateGuardian, _gsg); + rgsg->reset(); + + _file_begun = false; + _file.close(); + + cerr << "Wrote " << _current_rib_filename << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::format_name +// Access: Protected +// Description: Given a filename template which contains symbols like +// %f etc., replaces the symbols with the appropriate +// values to generate an actual filename. +//////////////////////////////////////////////////////////////////// +string RIBGraphicsWindow:: +format_name(const string &name_template) const { + string name; + + string::const_iterator ci; + ci = name_template.begin(); + while (ci != name_template.end()) { + if (*ci == '%') { + ++ci; + string::const_iterator pi = ci; + while (ci != name_template.end() && isdigit(*ci)) { + ++ci; + } + string format_spec(pi, ci); + + if (ci != name_template.end()) { + switch (*ci) { + case 'f': + // %f : insert frame number + name += format_integer(format_spec, _frame_number); + break; + + case 't': + // %t : insert window title + name += format_string(format_spec, _props._title); + break; + + case '%': + // %% : insert percent sign + name += '%'; + break; + + default: + cerr << "Invalid filename template specification: %" + << format_spec << *ci << "\n"; + } + ++ci; + } else { + cerr << "Incomplete filename template specification: %" + << format_spec << "\n"; + } + } else { + name += *ci; + ++ci; + } + } + + return name; +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::format_integer +// Access: Protected, Static +// Description: Formats an integer according to a %d-like format +// specification found in a filename template. +// format_spec is the string between the percent sign +// and the command letter (which might be empty, or +// might contain a field width), and number is the value +// to format. +//////////////////////////////////////////////////////////////////// +string RIBGraphicsWindow:: +format_integer(const string &format_spec, int number) { + // Get the field width requirement. We don't care if it begins with + // a leading zero or not, since we always pad with zeroes. + int width = atoi(format_spec.c_str()); + + string result; + string sign; + + // Is the number negative? + if (number < 0) { + sign = '-'; + number = -number; + } + + // Now build the number from the least-significant digit up. We + // keep going until the width runs out or the number does, whichever + // lasts longer. + do { + int digit = number % 10; + number /= 10; + + result = (char)(digit + '0') + result; + width--; + } while (width > 0 || number != 0); + + return sign + result; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::format_string +// Access: Protected, Static +// Description: Formats a string according to a %s-like format +// specification found in a filename template. +// format_spec is the string between the percent sign +// and the command letter (which might be empty, or +// might contain a field width), and str is the value +// to format. +//////////////////////////////////////////////////////////////////// +string RIBGraphicsWindow:: +format_string(const string &format_spec, const string &str) { + int width = atoi(format_spec.c_str()); + if (width <= str.length()) { + return str; + } + + // Now we have to pad the string out. + string pad; + + for (int extra = width; extra < str.length(); extra++) { + pad += '-'; + } + return pad + str; +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::format_string +// Access: Protected, Static +// Description: Scans a name template for the appearance of %f (or +// some variant), and returns true if it appears, false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool RIBGraphicsWindow:: +check_per_frame(const string &name_template) { + string::const_iterator ci; + ci = name_template.begin(); + while (ci != name_template.end()) { + if (*ci == '%') { + ++ci; + string::const_iterator pi = ci; + while (ci != name_template.end() && isdigit(*ci)) { + ++ci; + } + + if (ci != name_template.end()) { + if ((*ci) == 'f') { + return true; + } + ++ci; + } + } else { + ++ci; + } + } + + return false; +} + +void RIBGraphicsWindow::make_current(void) { +} + +void RIBGraphicsWindow::unmake_current(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsWindow::get_gsg_type +// Access: Public, Virtual +// Description: Returns the TypeHandle of the kind of GSG preferred +// by this kind of window. +//////////////////////////////////////////////////////////////////// +TypeHandle RIBGraphicsWindow:: +get_gsg_type() const { + return RIBGraphicsStateGuardian::get_class_type(); +} + +GraphicsWindow* +RIBGraphicsWindow::make_RibGraphicsWindow(const FactoryParams ¶ms) { + GraphicsWindow::WindowPipe *pipe_param; + if (!get_param_into(pipe_param, params)) { + ribdisplay_cat.error() + << "No pipe specified for window creation!" << endl; + return NULL; + } + + GraphicsPipe *pipe = pipe_param->get_pipe(); + + GraphicsWindow::WindowProps *props_param; + if (!get_param_into(props_param, params)) { + return new RIBGraphicsWindow(pipe); + } else { + return new RIBGraphicsWindow(pipe, props_param->get_properties()); + } +} + +TypeHandle RIBGraphicsWindow::get_class_type(void) { + return _type_handle; +} + +void RIBGraphicsWindow::init_type(void) { + GraphicsWindow::init_type(); + register_type(_type_handle, "RIBGraphicsWindow", + GraphicsWindow::get_class_type()); +} + +TypeHandle RIBGraphicsWindow::get_type(void) const { + return get_class_type(); +} diff --git a/panda/src/ribdisplay/ribGraphicsWindow.h b/panda/src/ribdisplay/ribGraphicsWindow.h new file mode 100644 index 0000000000..6c7ead2934 --- /dev/null +++ b/panda/src/ribdisplay/ribGraphicsWindow.h @@ -0,0 +1,134 @@ +// Filename: ribGraphicsWindow.h +// Created by: drose (15Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef RIBGRAPHICSWINDOW_H +#define RIBGRAPHICSWINDOW_H + +//////////////////////////////////////////////////////////////////// +// +// A RIBGraphicsWindow, instead of actually being a window on some +// raster display device, instead represents a file (or sequence of +// files) on disk that will be filled with a RIB scene description +// when the scene is "rendered" to the window. This RIB may +// subsequently be used as input to a standalone renderer such as +// prman or BMRT to generate one or more images. +// +// The output RIB filename as specified to the window is actually a +// filename template, and may contain any of the following: +// +// %f This stands for the current frame number. The frame number +// starts counting at zero when the window is created and +// increments by one each time a frame is rendered. An optional +// field width may appear after the %, printf-style. +// +// %t This stands for the window title. +// +// %% This stands for a single percent sign. +// +// If the RIB filename contains %f, a separate RIB file is generated +// for each frame rendered; otherwise, all frames are written into a +// single multi-frame RIB file. +// +// +// The image filename may also be specified. If specified, a +// "Display" command is written to the RIB file to output the image to +// the indicated filename. If unspecified or empty, the "Display" +// command is not written, and the renderer chooses the output +// filename. The image filename may also contain any of the % +// sequences described above. +// +//////////////////////////////////////////////////////////////////// + + +#include + +#include +#include + +class RIBGraphicsPipe; + + +//////////////////////////////////////////////////////////////////// +// Class : RIBGraphicsWindow +// Description : Represents a specific RIB file (or sequence of files) +// that can be "rendered" to. Rendering to a RIB file +// means writing out a scene description to the file. +// +// The filename of the RIB file is initially taken from +// the name of the RIBGraphicsPipe used to create the +// window, but it may subsequently be changed via +// set_rib_filename_template(). The filename may +// contain any of the % sequences described above. +//////////////////////////////////////////////////////////////////// +class RIBGraphicsWindow : public GraphicsWindow { + public: + + RIBGraphicsWindow(GraphicsPipe *pipe); + RIBGraphicsWindow(GraphicsPipe *pipe, + const GraphicsWindow::Properties &props); + virtual ~RIBGraphicsWindow(void); + + INLINE void set_rib_filename_template(const string &str); + INLINE string get_rib_filename_template() const; + INLINE string get_rib_filename() const; + INLINE bool rib_per_frame() const; + + INLINE void set_image_filename_template(const string &str); + INLINE string get_image_filename_template() const; + INLINE string get_image_filename() const; + INLINE bool image_per_frame() const; + + INLINE void set_texture_directory(const string &directory); + INLINE string get_texture_directory() const; + INLINE void set_texture_extension(const string &extension); + INLINE string get_texture_extension() const; + + void flush_file(); + + virtual void begin_frame(); + virtual void end_frame(); + + virtual void make_current(void); + virtual void unmake_current(void); + + virtual TypeHandle get_gsg_type() const; + static GraphicsWindow* make_RibGraphicsWindow(const FactoryParams ¶ms); + +protected: + void setup_window(GraphicsPipe *pipe); + + void begin_file(); + void end_file(); + + string format_name(const string &name_template) const; + static string format_integer(const string &format_spec, int number); + static string format_string(const string &format_spec, const string &str); + + static bool check_per_frame(const string &name_template); + + string _rib_filename_template; + string _image_filename_template; + bool _rib_per_frame; + bool _image_per_frame; + + bool _file_begun; + Filename _current_rib_filename; + ofstream _file; + +public: + + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + + static TypeHandle _type_handle; +}; + +#include "ribGraphicsWindow.I" + +#endif diff --git a/panda/src/ribgsg/Sources.pp b/panda/src/ribgsg/Sources.pp new file mode 100644 index 0000000000..e89f7dbf7f --- /dev/null +++ b/panda/src/ribgsg/Sources.pp @@ -0,0 +1,19 @@ +#define DIRECTORY_IF_RIB yes + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET ribgsg + #define LOCAL_LIBS \ + gsgmisc display gobj sgattrib sgraph sgraphutil light + + #define SOURCES \ + config_ribgsg.cxx config_ribgsg.h ribGraphicsStateGuardian.I \ + ribGraphicsStateGuardian.cxx ribGraphicsStateGuardian.h \ + ribStuffTraverser.cxx ribStuffTraverser.h + + #define INSTALL_HEADERS \ + ribGraphicsStateGuardian.h + +#end lib_target + diff --git a/panda/src/ribgsg/config_ribgsg.cxx b/panda/src/ribgsg/config_ribgsg.cxx new file mode 100644 index 0000000000..1ab61515f2 --- /dev/null +++ b/panda/src/ribgsg/config_ribgsg.cxx @@ -0,0 +1,19 @@ +// Filename: config_ribgsg.cxx +// Created by: cary (08Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_ribgsg.h" +#include "ribGraphicsStateGuardian.h" + +#include + +Configure(config_ribgsg); +NotifyCategoryDef(ribgsg, ":display:gsg"); + +ConfigureFn(config_ribgsg) { + RIBGraphicsStateGuardian::init_type(); + GraphicsStateGuardian::_factory. + register_factory(RIBGraphicsStateGuardian::get_class_type(), + RIBGraphicsStateGuardian::make_RIBGraphicsStateGuardian); +} diff --git a/panda/src/ribgsg/config_ribgsg.h b/panda/src/ribgsg/config_ribgsg.h new file mode 100644 index 0000000000..cb8a7bdd47 --- /dev/null +++ b/panda/src/ribgsg/config_ribgsg.h @@ -0,0 +1,14 @@ +// Filename: config_ribgsg.h +// Created by: cary (08Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_RIBGSG_H +#define CONFIG_RIBGSG_H + +#include +#include + +NotifyCategoryDecl(ribgsg, EXPCL_PANDARIB, EXPTP_PANDARIB); + +#endif diff --git a/panda/src/ribgsg/ribGraphicsStateGuardian.I b/panda/src/ribgsg/ribGraphicsStateGuardian.I new file mode 100644 index 0000000000..c85ec5e6f0 --- /dev/null +++ b/panda/src/ribgsg/ribGraphicsStateGuardian.I @@ -0,0 +1,4 @@ +// Filename: ribGraphicsStateGuardian.I +// Created by: drose (15Feb99) +// +//////////////////////////////////////////////////////////////////// diff --git a/panda/src/ribgsg/ribGraphicsStateGuardian.cxx b/panda/src/ribgsg/ribGraphicsStateGuardian.cxx new file mode 100644 index 0000000000..3fa042668b --- /dev/null +++ b/panda/src/ribgsg/ribGraphicsStateGuardian.cxx @@ -0,0 +1,1234 @@ +// Filename: ribGraphicsStateGuardian.cxx +// Created by: drose (15Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "ribGraphicsStateGuardian.h" +#include "ribStuffTraverser.h" +#include "config_ribgsg.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +TypeHandle RIBGraphicsStateGuardian::_type_handle; + +// Here are some global arrays we use in the various draw() routines +// to hold temporary coordinate values for each Geom for formatting. + +static vector rib_vertices; +static vector rib_normals; +static vector rib_texcoords; +static vector rib_colors; + +static void +issue_vertex_rib(const Geom *geom, Geom::VertexIterator &vi) { + rib_vertices.push_back(geom->get_next_vertex(vi)); +} + +static void +issue_normal_rib(const Geom *geom, Geom::NormalIterator &ni) { + rib_normals.push_back(geom->get_next_normal(ni)); +} + +static void +issue_texcoord_rib(const Geom *geom, Geom::TexCoordIterator &ti) { + // We need to reverse the V coordinate for RIB. + static LMatrix3f + texmat(1.0, 0.0, 0.0, + 0.0, -1.0, 0.0, + 0.0, 1.0, 1.0); + rib_texcoords.push_back(geom->get_next_texcoord(ti) * texmat); +} + +static void +issue_color_rib(const Geom *geom, Geom::ColorIterator &ci, + const GraphicsStateGuardianBase *) { + // RIB only cares about three-component color, so we have to convert + // the four-component color attribute to three-component color here. + rib_colors.push_back((const RGBColorf &)geom->get_next_color(ci)); +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +RIBGraphicsStateGuardian:: +RIBGraphicsStateGuardian(GraphicsWindow *win) : GraphicsStateGuardian(win) { + reset(); + + // Create a default RenderTraverser. + _render_traverser = + new DirectRenderTraverser(this, RenderRelation::get_class_type()); + + _texture_directory = "maps"; + _texture_extension = "tiff"; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::reset +// Access: Public, Virtual +// Description: Resets all internal state and prepares a new RIB +// file. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +reset() { + GraphicsStateGuardian::reset(); + + // We only have a color buffer in RenderMan. + _buffer_mask = RenderBuffer::T_color; + + _output = NULL; + _indent_level = 0; + + // We clear the texture names only for each file, not for each + // frame, because texture definitions remain across frames. + _texture_names.clear(); + + reset_frame(); +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::reset_file +// Access: Public +// Description: Resets all internal state and prepares a new RIB +// file to the indicated output stream. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +reset_file(ostream &out) { + reset(); + _output = &out; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::reset_frame +// Access: Public +// Description: Resets whatever state is appropriate at the end of a +// frame. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +reset_frame() { + // We must clear the light definitions for each frame. + _light_ids.clear(); + _enabled_lights.clear(); + _enabled_lights.push_back(true); + + _current_color.set(1.0, 1.0, 1.0); + _state.clear(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::clear +// Access: Public, Virtual +// Description: Clears all of the indicated buffers to their assigned +// colors. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +clear(const RenderBuffer &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::clear +// Access: Public, Virtual +// Description: Clears all of the indicated buffers to their assigned +// colors. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +clear(const RenderBuffer &, const DisplayRegion* ) { +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::prepare_display_region +// Access: Public +// Description: Prepare a display region for rendering (set up +// scissor region and viewport) +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +prepare_display_region() { +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::render_frame +// Access: Public, Virtual +// Description: Renders an entire frame, including all display +// regions within the frame, and includes any necessary +// pre- and post-processing. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +render_frame(const AllAttributesWrapper &initial_state) { + _win->begin_frame(); + assert(_output != NULL); + _indent_level += 2; + + int max_channel_index = _win->get_max_channel_index(); + for (int c = 0; c < max_channel_index; c++) { + if (_win->is_channel_defined(c)) { + GraphicsChannel *chan = _win->get_channel(c); + if (chan->is_active()) { + int num_layers = chan->get_num_layers(); + for (int l = 0; l < num_layers; l++) { + GraphicsLayer *layer = chan->get_layer(l); + if (layer->is_active()) { + int num_drs = layer->get_num_drs(); + for (int d = 0; d < num_drs; d++) { + DisplayRegion *dr = layer->get_dr(d); + Camera *cam = dr->get_camera(); + + // For each display region, render from the camera's view. + if (dr->is_active() && cam != (Camera *)NULL && + cam->is_active() && cam->get_scene() != (Node *)NULL) { + DisplayRegionStack old_dr = push_display_region(dr); + prepare_display_region(); + render_scene(cam->get_scene(), cam, initial_state); + pop_display_region(old_dr); + } + } + } + } + } + } + } + + _indent_level -= 2; + _win->end_frame(); + + reset_frame(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::render_scene +// Access: Public, Virtual +// Description: Renders an entire scene, from the root node of the +// scene graph, as seen from a particular ProjectionNode +// and with a given initial state. This initial state +// may be modified during rendering. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +render_scene(Node *root, const ProjectionNode *projnode, + const AllAttributesWrapper &initial_state) { + _current_root_node = root; + + render_subgraph(_render_traverser, root, projnode, + initial_state, AllTransitionsWrapper()); +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::render_subgraph +// Access: Public, Virtual +// Description: Renders a subgraph of the scene graph as seen from a +// given projection node, and with a particular initial +// state. This state may be modified by the render +// process. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +render_subgraph(RenderTraverser *traverser, + Node *subgraph, const ProjectionNode *projnode, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans) { + const ProjectionNode *old_projection_node = _current_projection_node; + _current_projection_node = projnode; + + (*_output) << "\n"; + + int width = _win->get_width(); + int height = _win->get_height(); + float frame_aspect = (float)width / (float)height; + + // BMRT, for one, doesn't seem like general matrices for the + // projection matrix. Therefore, we'll examine the projection type + // and write out the high-level description of the projection if we + // can, instead of just dumping a matrix. + + const Projection *projection = projnode->get_projection(); + if (projection->is_of_type(PerspectiveProjection::get_class_type())) { + const PerspectiveProjection &pp = + *DCAST(PerspectiveProjection, projection); + const Frustumf &frustum = pp.get_frustum(); + + float yfov, fnear, ffar; + frustum.get_perspective_params(yfov, frame_aspect, fnear, ffar); + + switch (_coordinate_system) { + case CS_zup_right: + new_line() << "Scale [ 1 1 -1 ]\n"; // left-handed to right-handed + new_line() << "Rotate [ -90 1 0 0 ]\n"; // y-up to z-up + break; + + case CS_zup_left: + new_line() << "Rotate [ -90 1 0 0 ]\n"; // y-up to z-up + break; + + case CS_yup_right: + new_line() << "Scale [ 1 1 -1 ]\n"; // left-handed to right-handed + break; + + case CS_yup_left: + break; + }; + + new_line() << "Orientation \"lh\"\n"; + new_line() << "Clipping " << fnear << " " << ffar << "\n"; + new_line() << "Projection \"perspective\" \"fov\" " << yfov << "\n"; + + } else { + // Hmm, some unknown projection type. We'll have to just write + // out the projection matrix and hope for the best. + LMatrix4f proj_mat = projection->get_projection_mat(_coordinate_system); + concat_transform(proj_mat); + + new_line() << "Orientation \"lh\"\n"; + new_line() << "Projection \"null\"\n"; + } + + new_line() << "Format " << width << " " << height << " " + << (float)height * frame_aspect / (float)width << "\n"; + new_line() << "FrameAspectRatio " << frame_aspect << "\n"; + + new_line() << "Sides 1\n"; + new_line() << "Color [ 1 1 1 ]\n"; + + // Get the lights and stuff, while we're here in camera space. + get_rib_stuff(_current_root_node, initial_state); + + // We infer the modelview matrix by doing a wrt on the projection + // node. + LMatrix4f modelview_mat; + get_rel_mat(subgraph, _current_projection_node, modelview_mat); + concat_transform(modelview_mat); + + /* + // And then we must make sure the matrix transform is cleared from + // the initial state. + initial_state.clear_attribute(TransformTransition::get_class_type()); + */ + + new_line() << "WorldBegin\n"; + _indent_level += 2; + + render_subgraph(traverser, subgraph, initial_state, net_trans); + + _indent_level -= 2; + new_line() << "WorldEnd\n"; +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::render_subgraph +// Access: Public, Virtual +// Description: Renders a subgraph of the scene graph as seen from the +// current projection node, and with a particular +// initial state. This state may be modified during the +// render process. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +render_subgraph(RenderTraverser *traverser, + Node *subgraph, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans) { + nassertv(traverser != (RenderTraverser *)NULL); + traverser->traverse(subgraph, initial_state, net_trans); +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::wants_normals +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool RIBGraphicsStateGuardian:: +wants_normals() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::wants_texcoords +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool RIBGraphicsStateGuardian:: +wants_texcoords() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::wants_colors +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool RIBGraphicsStateGuardian:: +wants_colors() const { + // If we have scene graph color enabled, return false to indicate we + // shouldn't bother issuing geometry color commands. + + const ColorAttribute *catt; + if (!get_attribute_into(catt, _state, ColorTransition::get_class_type())) { + // No scene graph color at all. + return true; + } + + // We should issue geometry colors only if the scene graph color is + // off. + return catt->is_off(); +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::compute_distance_to +// Access: Public, Virtual +// Description: This function may only be called during a render +// traversal; it will compute the distance to the +// indicated point, assumed to be in modelview +// coordinates, from the camera plane. +//////////////////////////////////////////////////////////////////// +float RIBGraphicsStateGuardian:: +compute_distance_to(const LPoint3f &point) const { + // In the case of a RIBGraphicsStateGuardian, we know the modelview + // matrix does not include the camera transform, so we should apply + // that now. But for now we'll punt, since no one cares anyway. + + return point[1]; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::draw_point +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +draw_point(const GeomPoint *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::draw_line +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +draw_line(const GeomLine *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::draw_sprite +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +draw_sprite(const GeomSprite *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::draw_polygon +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +draw_polygon(const GeomPolygon *geom) { + draw_simple_poly(geom); +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::draw_tri +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +draw_tri(const GeomTri *geom) { + draw_simple_poly(geom); +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::draw_quad +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +draw_quad(const GeomQuad *geom) { + draw_simple_poly(geom); +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::draw_tristrip +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +draw_tristrip(const GeomTristrip *geom) { + Geom *temp = geom->explode(); + draw_simple_poly(temp); + delete temp; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::draw_trifan +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +draw_trifan(const GeomTrifan *geom) { + Geom *temp = geom->explode(); + draw_simple_poly(temp); + delete temp; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::draw_sphere +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +draw_sphere(const GeomSphere *) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::prepare_texture +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +TextureContext *RIBGraphicsStateGuardian:: +prepare_texture(Texture *tex) { + TextureContext *tc = new TextureContext(tex); + + bool inserted = mark_prepared_texture(tc); + + // If this assertion fails, the same texture was prepared twice, + // which shouldn't be possible, since the texture itself should + // detect this. + assert(inserted); + + return tc; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::apply_texture +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +apply_texture(TextureContext *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::release_texture +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +release_texture(TextureContext *tc) { + Texture *tex = tc->_texture; + + bool erased = unmark_prepared_texture(tc); + + // If this assertion fails, a texture was released that hadn't been + // prepared (or a texture was released twice). + assert(erased); + + tex->clear_gsg(this); + + delete tc; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::copy_texture +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +copy_texture(TextureContext *, const DisplayRegion *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::copy_texture +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +copy_texture(TextureContext *, const DisplayRegion *, const RenderBuffer &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::draw_texture +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +draw_texture(TextureContext *, const DisplayRegion *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::draw_texture +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +draw_texture(TextureContext *, const DisplayRegion *, const RenderBuffer &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::copy_pixel_buffer +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +copy_pixel_buffer(PixelBuffer *, const DisplayRegion *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::copy_pixel_buffer +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +copy_pixel_buffer(PixelBuffer *, const DisplayRegion *, const RenderBuffer &) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::draw_pixel_buffer +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +draw_pixel_buffer(PixelBuffer *, const DisplayRegion *, + const NodeAttributes &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::draw_pixel_buffer +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +draw_pixel_buffer(PixelBuffer *, const DisplayRegion *, const RenderBuffer &, + const NodeAttributes &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::issue_transform +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +issue_transform(const TransformAttribute *attrib) { + reset_transform(attrib->get_matrix()); +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::issue_color +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +issue_color(const ColorAttribute *attrib) { + if (attrib->is_on() && attrib->is_real()) { + const Colorf c = attrib->get_color(); + set_color(RGBColorf(c[0], c[1], c[2])); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::issue_texture +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +issue_texture(const TextureAttribute *attrib) { + if (attrib->is_off()) { + // If no textures are enabled, we can use the nontextured shader. + new_line() + << "Surface \"plastic\"\n"; + + } else { + // If we have a texture enabled, just use the normal + // paintedplastic shader. + + Texture *tex = attrib->get_texture(); + nassertv(tex != (Texture *)NULL); + const Filename &rib_name = _texture_names[tex]; + + // We should have gotten all the texture names already in the + // get_rib_stuff() call. If this name is empty, we somehow + // missed it! + nassertv(!rib_name.empty()); + + new_line() + << "Surface \"paintedplastic\" \"texturename\" \"" + << rib_name << "\"\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::issue_light +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +issue_light(const LightAttribute *attrib) { + nassertv(attrib->get_properties_is_on()); + int num_enabled = attrib->size(); + if (num_enabled == 0) { + // If no lights are enabled, lighting is off. Turn off all lights + // except the default one. + + // (if the default light is already on, we'll assume everything + // else is already off and won't bother to run the list.) + if (!_enabled_lights[0]) { + for (int i = 1; i < _enabled_lights.size(); i++) { + if (_enabled_lights[i]) { + new_line() << "Illuminate " << i << " 0\n"; + _enabled_lights[i] = false; + } + } + + // And turn on the default light, which illuminates the scene in + // the absence of lighting. + new_line() << "Illuminate 0 1\n"; + _enabled_lights[0] = true; + } + + } else { + // If some lights are enabled, we'll turn off the ones that were + // enabled from before, and turn on the ones we need. + + LightAttribute::const_iterator li; + for (li = attrib->begin(); li != attrib->end(); ++li) { + Light *light = (*li); + LightIDs::const_iterator ii = _light_ids.find(light); + assert(ii != _light_ids.end()); + int id = (*ii).second; + + if (!_enabled_lights[id]) { + new_line() << "Illuminate " << id << " 1\n"; + } + + // We'll temporarily set the enabled flag to false, even + // though we've just activated the light. This is so we can + // later identify the lights we need to turn off. + _enabled_lights[id] = false; + } + + // Now turn off all the lights that are still marked "on". + for (int i = 0; i < _enabled_lights.size(); i++) { + if (_enabled_lights[i]) { + new_line() << "Illuminate " << i << " 0\n"; + _enabled_lights[i] = false; + } + } + + // Finally, mark as "on" all the ones that we just enabled. + for (li = attrib->begin(); li != attrib->end(); ++li) { + Light *light = (*li); + int id = _light_ids[light]; + _enabled_lights[id] = true; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::set_texture_directory +// Access: Public +// Description: Sets the name of the directory into which texture +// maps are copied to be available to the RIB file. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +set_texture_directory(const string &directory) { + _texture_directory = directory; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::get_texture_directory +// Access: Public +// Description: Returns the name of the directory into which texture +// maps are copied to be available to the RIB file. +//////////////////////////////////////////////////////////////////// +string RIBGraphicsStateGuardian:: +get_texture_directory() const { + return _texture_directory; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::set_texture_extension +// Access: Public +// Description: Specifies the filename extension that texture map +// files are given when they are copied into the +// directory for RIB files. This might also imply an +// image type. The default is "tiff", which implies +// TIFF files. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +set_texture_extension(const string &extension) { + _texture_extension = extension; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::get_texture_extension +// Access: Public +// Description: Returns the filename extension that texture map +// files are given when they are copied into the +// directory for RIB files. +//////////////////////////////////////////////////////////////////// +string RIBGraphicsStateGuardian:: +get_texture_extension() const { + return _texture_extension; +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::save_frame_buffer +// Access: Public +// Description: Saves the indicated planes of the frame buffer +// (within the indicated display region) and returns it +// in some meaningful form that can be restored later +// via restore_frame_buffer(). This is a helper +// function for push_frame_buffer() and +// pop_frame_buffer(). +//////////////////////////////////////////////////////////////////// +PT(SavedFrameBuffer) RIBGraphicsStateGuardian:: +save_frame_buffer(const RenderBuffer &buffer, + CPT(DisplayRegion) dr) { + return new SavedFrameBuffer(buffer, dr); +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::restore_frame_buffer +// Access: Public +// Description: Restores the frame buffer that was previously saved. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +restore_frame_buffer(SavedFrameBuffer *) { +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::set_color +// Access: Protected +// Description: Issues the sequence to change the node color to that +// indicated. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +set_color(const RGBColorf &color) { + if (_current_color != color) { + new_line() << "Color [ " << color << " ]\n"; + _current_color = color; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::get_rib_stuff +// Access: Protected +// Description: Traverses the scene graph to identify any textures +// or lights, or anything that we need to define up +// front in RIB. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +get_rib_stuff(Node *root, const AllAttributesWrapper &initial_state) { + RibStuffTraverser trav(this); + df_traverse(root, trav, initial_state, NullLevelState(), + RenderRelation::get_class_type()); +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::define_texture +// Access: Protected +// Description: Called by the RibStuffTraverser (initiated above), +// this defines a single texture object in the RIB file +// if it has not already been defined. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +define_texture(const Texture *tex) { + Filename &rib_name = _texture_names[tex]; + + if (rib_name.empty()) { + Filename image_filename = tex->get_name(); + image_filename.set_dirname(_texture_directory); + image_filename.set_extension(_texture_extension); + tex->write(image_filename); + + rib_name = image_filename; + rib_name.set_extension("tx"); + + new_line() << "MakeTexture \"" << image_filename << "\"\n"; + new_line(12) << "\"" << rib_name << "\""; + + if (tex->get_wrapu() == Texture::WM_clamp) { + (*_output) << " \"clamp\""; + } else { + (*_output) << " \"periodic\""; + } + + if (tex->get_wrapv() == Texture::WM_clamp) { + (*_output) << " \"clamp\""; + } else { + (*_output) << " \"periodic\""; + } + + (*_output) << " \"box\" 1 1\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::define_light +// Access: Protected +// Description: Called by the RibStuffTraverser (initiated above), +// this defines a single light object in the RIB file +// if it has not already been defined. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +define_light(const Light *light) { + LightIDs::const_iterator li = _light_ids.find(light); + if (li == _light_ids.end()) { + // This is the first time this light has been encountered; define + // it. + + // Create a new ID number. + int id = _light_ids.size() + 1; + _light_ids[light] = id; + assert(id == _enabled_lights.size()); + _enabled_lights.push_back(false); + + if (light->get_light_type() == PointLight::get_class_type()) { + const PointLight *plight = (const PointLight *)light; + new_line() << "LightSource \"pointlight\" " << id; + write_light_color(plight->get_color()); + write_light_from(plight); + (*_output) << "\n"; + + } else if (light->get_light_type() == DirectionalLight::get_class_type()) { + const DirectionalLight *dlight = (const DirectionalLight *)light; + new_line() << "LightSource \"distantlight\" " << id; + write_light_color(dlight->get_color()); + write_light_from(dlight); + write_light_to(dlight); + (*_output) << "\n"; + + } else if (light->get_light_type() == Spotlight::get_class_type()) { + const Spotlight *slight = (const Spotlight *)light; + new_line() << "LightSource \"spotlight\" " << id; + write_light_color(slight->get_color()); + write_light_from(slight); + write_light_to(slight); + (*_output) + << " \"coneangle\" " << deg_2_rad(slight->get_cutoff_angle()) + << "\n"; + + } else if (light->get_light_type() == AmbientLight::get_class_type()) { + const AmbientLight *alight = (const AmbientLight *)light; + new_line() << "LightSource \"ambientlight\" " << id; + write_light_color(alight->get_color()); + (*_output) << "\n"; + + } else { + cerr << "Ignoring unknown light type " << light->get_light_type() << "\n"; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::write_light_color +// Access: Protected +// Description: Called by define_light() to write out a single +// light's color and intensity values. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +write_light_color(const Colorf &color) const { + RGBColorf output_color; + float intensity; + + get_color_and_intensity((const RGBColorf &)color, output_color, intensity); + (*_output) << " \"lightcolor\" [ " << output_color << " ] \"intensity\" " + << intensity; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::write_light_from +// Access: Protected +// Description: Called by define_light() to write out a single +// light's position. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +write_light_from(const Node *light) const { + LPoint3f pos = get_rel_pos(light, _current_projection_node); + + (*_output) << " \"from\" [ " << pos << " ]"; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::write_light_to +// Access: Protected +// Description: Called by define_light() to write out a single +// light's direction. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +write_light_to(const Node *light) const { + LPoint3f pos = get_rel_pos(light, _current_projection_node); + LVector3f forward = get_rel_forward(light, _current_projection_node); + + (*_output) << " \"to\" [ " << pos + forward << " ]"; +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::new_line +// Access: Protected +// Description: Beins a new line of output at the current indenting +// level. (Does not actually issue the newline +// character, however). +//////////////////////////////////////////////////////////////////// +ostream &RIBGraphicsStateGuardian:: +new_line(int extra_indent) const { + return indent(*_output, _indent_level + extra_indent); +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::reset_transform +// Access: Protected +// Description: Outputs an RiTransform command with the given +// transformation matrix, which resets the current +// transformation to that specified. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +reset_transform(const LMatrix4f &mat) const { + new_line() << "Transform [ " << mat(0,0) << " " << mat(0,1) << " " + << mat(0,2) << " " << mat(0,3) << "\n"; + new_line(12) << mat(1,0) << " " << mat(1,1) << " " + << mat(1,2) << " " << mat(1,3) << "\n"; + new_line(12) << mat(2,0) << " " << mat(2,1) << " " + << mat(2,2) << " " << mat(2,3) << "\n"; + new_line(12) << mat(3,0) << " " << mat(3,1) << " " + << mat(3,2) << " " << mat(3,3) << " ]\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::concat_transform +// Access: Protected +// Description: Outputs an RiTransform command with the given +// transformation matrix, which composes the specified +// matrix with the current transformation. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +concat_transform(const LMatrix4f &mat) const { + new_line() << "ConcatTransform [ " << mat(0,0) << " " << mat(0,1) << " " + << mat(0,2) << " " << mat(0,3) << "\n"; + new_line(18) << mat(1,0) << " " << mat(1,1) << " " + << mat(1,2) << " " << mat(1,3) << "\n"; + new_line(18) << mat(2,0) << " " << mat(2,1) << " " + << mat(2,2) << " " << mat(2,3) << "\n"; + new_line(18) << mat(3,0) << " " << mat(3,1) << " " + << mat(3,2) << " " << mat(3,3) << " ]\n"; +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::draw_simple_poly +// Access: Protected +// Description: Draws a GeomPolygon, GeomTri, or GeomQuad object. +// This consists of one or more unconnected polygons. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +draw_simple_poly(const Geom *geom) { + if (geom == NULL) { + return; + } + + int nprims = geom->get_num_prims(); + Geom::VertexIterator vi = geom->make_vertex_iterator(); + Geom::NormalIterator ni = geom->make_normal_iterator(); + Geom::TexCoordIterator ti = geom->make_texcoord_iterator(); + Geom::ColorIterator ci = geom->make_color_iterator(); + + GeomIssuer issuer(geom, this, + issue_vertex_rib, + issue_normal_rib, + issue_texcoord_rib, + issue_color_rib); + + for (int i = 0; i < nprims; i++) { + // First, for each primitive, we build up the various polygon + // attributes in our global arrays. + + // If the colors or normals have overall binding, we'll need to + // repeat it for each primitive. Thus, we need to reset the + // iterator for each primitive. + if (geom->get_binding(G_COLOR) == G_OVERALL) { + ci = geom->make_color_iterator(); + } + if (geom->get_binding(G_NORMAL) == G_OVERALL) { + ni = geom->make_normal_iterator(); + } + + // Draw overall + issuer.issue_color(G_OVERALL, ci); + issuer.issue_normal(G_OVERALL, ni); + + // Draw per primitive + issuer.issue_color(G_PER_PRIM, ci); + issuer.issue_normal(G_PER_PRIM, ni); + + for (int j = 0; j < geom->get_length(i); j++) { + // Draw per vertex + issuer.issue_color(G_PER_VERTEX, ci); + issuer.issue_normal(G_PER_VERTEX, ni); + issuer.issue_texcoord(G_PER_VERTEX, ti); + issuer.issue_vertex(G_PER_VERTEX, vi); + } + + write_polygon(geom->get_length(i)); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::write_polygon +// Access: Protected +// Description: Writes out the RIB command to draw the polygon +// described by the global rib_* arrays. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +write_polygon(int num_verts) { + if (num_verts < 3) { + return; + } + + assert(rib_vertices.size() == num_verts); + + if (rib_colors.size() == 1) { + // This polygon has a flat color; just issue the color. + set_color(rib_colors[0]); + rib_colors.clear(); + } + + + // We reverse the order of the vertices when we write then out, + // because RIB has a clockwise-ordering convention. + + // Vertices are always per-vertex. + write_long_list(*_output, _indent_level, + rib_vertices.rbegin(), rib_vertices.rend(), + "Polygon \"P\" ", " ", 72); + + if (rib_normals.size() == 1) { + // A single polygon normal. + write_long_list(*_output, _indent_level, + rib_normals.rbegin(), rib_normals.rend(), + " \"Np\" ", " ", 72); + } else if (!rib_normals.empty()) { + // Multiple per-vertex normals. + assert(rib_normals.size() == num_verts); + write_long_list(*_output, _indent_level, + rib_normals.rbegin(), rib_normals.rend(), + " \"N\" ", " ", 72); + } + + if (!rib_texcoords.empty()) { + // Per-vertex texcoords. + assert(rib_texcoords.size() == num_verts); + write_long_list(*_output, _indent_level, + rib_texcoords.rbegin(), rib_texcoords.rend(), + " \"st\" ", " ", 72); + } + + if (!rib_colors.empty()) { + assert(rib_colors.size() == num_verts); + + write_long_list(*_output, _indent_level, + rib_colors.rbegin(), rib_colors.rend(), + " \"Cs\" ", " ", 72); + } + + // Clear the arrays for the next primitive. + rib_vertices.clear(); + rib_normals.clear(); + rib_texcoords.clear(); + rib_colors.clear(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: RIBGraphicsStateGuardian::get_color_and_intensity +// Access: Protected, Static +// Description: Given a three-component color value, extracts it into +// a normalized three-component color with each +// component in the range [0..1], and a separate +// intensity value. +//////////////////////////////////////////////////////////////////// +void RIBGraphicsStateGuardian:: +get_color_and_intensity(const RGBColorf &input, + RGBColorf &output, + float &intensity) { + intensity = max(max(input[0], input[1]), input[2]); + if (intensity == 0.0) { + output.set(1.0, 1.0, 1.0); + } else { + output = input / intensity; + } +} + +// type and factory stuff + +GraphicsStateGuardian *RIBGraphicsStateGuardian:: +make_RIBGraphicsStateGuardian(const FactoryParams ¶ms) { + GraphicsStateGuardian::GsgWindow *win_param; + if (!get_param_into(win_param, params)) { + ribgsg_cat.error() + << "No window specified for gsg creation!" << endl; + return NULL; + } + + GraphicsWindow *win = win_param->get_window(); + return new RIBGraphicsStateGuardian(win); +} + +TypeHandle RIBGraphicsStateGuardian::get_type(void) const { + return get_class_type(); +} + +TypeHandle RIBGraphicsStateGuardian::get_class_type(void) { + return _type_handle; +} + +void RIBGraphicsStateGuardian::init_type(void) { + GraphicsStateGuardian::init_type(); + register_type(_type_handle, "RIBGraphicsStateGuardian", + GraphicsStateGuardian::get_class_type()); +} diff --git a/panda/src/ribgsg/ribGraphicsStateGuardian.h b/panda/src/ribgsg/ribGraphicsStateGuardian.h new file mode 100644 index 0000000000..712d99e3a1 --- /dev/null +++ b/panda/src/ribgsg/ribGraphicsStateGuardian.h @@ -0,0 +1,169 @@ +// Filename: ribGraphicsStateGuardian.h +// Created by: drose (15Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef RIBGRAPHICSSTATEGUARDIAN_H +#define RIBGRAPHICSSTATEGUARDIAN_H + +#include + +#include +#include + +class Geom; +class Texture; +class Light; +class Material; +class DisplayRegion; +class RenderBuffer; +class PixelBuffer; +class Fog; + +//////////////////////////////////////////////////////////////////// +// Class : RIBGraphicsStateGuardian +// Description : A GraphicsStateGuardian specialized for creating RIB +// files, suitable for shipping off to a +// Renderman-friendly non-real-time renderer. +//////////////////////////////////////////////////////////////////// +class RIBGraphicsStateGuardian : public GraphicsStateGuardian { +public: + RIBGraphicsStateGuardian(GraphicsWindow *win); + + virtual void reset(); + void reset_file(ostream &out); + void reset_frame(); + + virtual void clear(const RenderBuffer &buffer); + virtual void clear(const RenderBuffer &buffer, const DisplayRegion *region); + + virtual void prepare_display_region(); + + virtual void render_frame(const AllAttributesWrapper &initial_state); + virtual void render_scene(Node *root, const ProjectionNode *projnode, + const AllAttributesWrapper &initial_state); + virtual void render_subgraph(RenderTraverser *traverser, + Node *subgraph, const ProjectionNode *projnode, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans); + virtual void render_subgraph(RenderTraverser *traverser, + Node *subgraph, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans); + + virtual bool wants_normals(void) const; + virtual bool wants_texcoords(void) const; + virtual bool wants_colors(void) const; + + virtual float compute_distance_to(const LPoint3f &point) const; + + virtual void draw_point(const GeomPoint *geom); + virtual void draw_line(const GeomLine *geom); + virtual void draw_linestrip(const GeomLinestrip *) { } + virtual void draw_sprite(const GeomSprite *geom); + virtual void draw_polygon(const GeomPolygon *geom); + virtual void draw_quad(const GeomQuad *geom); + virtual void draw_tri(const GeomTri *geom); + virtual void draw_tristrip(const GeomTristrip *geom); + virtual void draw_trifan(const GeomTrifan *geom); + virtual void draw_sphere(const GeomSphere *geom); + + virtual TextureContext *prepare_texture(Texture *tex); + virtual void apply_texture(TextureContext *tc); + virtual void release_texture(TextureContext *tc); + + virtual void copy_texture(TextureContext *tc, const DisplayRegion *dr); + virtual void copy_texture(TextureContext *tc, const DisplayRegion *dr, + const RenderBuffer &rb); + virtual void draw_texture(TextureContext *tc, const DisplayRegion *dr); + virtual void draw_texture(TextureContext *tc, const DisplayRegion *dr, + const RenderBuffer &rb); + + virtual void texture_to_pixel_buffer(TextureContext *, PixelBuffer *) { } + virtual void texture_to_pixel_buffer(TextureContext *, PixelBuffer *, + const DisplayRegion *) { } + + virtual void copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr); + virtual void copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const RenderBuffer &rb); + virtual void draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const NodeAttributes& na=NodeAttributes()); + virtual void draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, + const RenderBuffer &rb, + const NodeAttributes& na=NodeAttributes()); + + virtual void apply_material(Material*) { } + virtual void apply_fog(Fog*) { } + + virtual void apply_light(PointLight*) { } + virtual void apply_light(DirectionalLight*) { } + virtual void apply_light(Spotlight*) { } + virtual void apply_light(AmbientLight*) { } + + virtual void issue_transform(const TransformAttribute *attrib); + virtual void issue_color(const ColorAttribute *attrib); + virtual void issue_texture(const TextureAttribute *attrib); + virtual void issue_light(const LightAttribute *attrib); + + // Normally, these functions are called through the RIBGraphicsWindow. + void set_texture_directory(const string &directory); + string get_texture_directory() const; + void set_texture_extension(const string &extension); + string get_texture_extension() const; + +protected: + virtual PT(SavedFrameBuffer) save_frame_buffer(const RenderBuffer &buffer, + CPT(DisplayRegion) dr); + virtual void restore_frame_buffer(SavedFrameBuffer *frame_buffer); + + void set_color(const RGBColorf &color); + + void get_rib_stuff(Node *root, const AllAttributesWrapper &initial_state); + void define_texture(const Texture *tex); + void define_light(const Light *light); + void write_light_color(const Colorf &color) const; + void write_light_from(const Node *light) const; + void write_light_to(const Node *light) const; + + ostream &new_line(int extra_indent = 0) const; + void reset_transform(const LMatrix4f &mat) const; + void concat_transform(const LMatrix4f &mat) const; + + void draw_simple_poly(const Geom *geom); + void write_polygon(int num_verts); + + static void get_color_and_intensity(const RGBColorf &input, + RGBColorf &output, + float &intensity); + + RGBColorf _current_color; + + string _texture_directory; + string _texture_extension; + ostream *_output; + int _indent_level; + typedef map TextureNames; + TextureNames _texture_names; + typedef map LightIDs; + LightIDs _light_ids; + typedef vector EnabledLights; + EnabledLights _enabled_lights; + + +public: + static GraphicsStateGuardian * + make_RIBGraphicsStateGuardian(const FactoryParams ¶ms); + + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type(void); + static void init_type(void); + +private: + static TypeHandle _type_handle; + + friend class RibStuffTraverser; +}; + +#endif + diff --git a/panda/src/ribgsg/ribStuffTraverser.cxx b/panda/src/ribgsg/ribStuffTraverser.cxx new file mode 100644 index 0000000000..306d90fa42 --- /dev/null +++ b/panda/src/ribgsg/ribStuffTraverser.cxx @@ -0,0 +1,43 @@ +// Filename: ribStuffTraverser.cxx +// Created by: drose (16Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "ribStuffTraverser.h" +#include "ribGraphicsStateGuardian.h" + +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: RibStuffTraverser::reached_node +// Access: Public +// Description: Called for each node of the scene graph; this does +// the work of identifying new things and registering +// them in the RIB file. +//////////////////////////////////////////////////////////////////// +bool RibStuffTraverser:: +reached_node(Node *node, AllAttributesWrapper &state, NullLevelState &) { + if (node->is_of_type(GeomNode::get_class_type())) { + const TextureAttribute *tex_attrib; + if (!get_attribute_into(tex_attrib, state, + TextureTransition::get_class_type())) { + if (tex_attrib->is_on()) { + _gsg->define_texture(tex_attrib->get_texture()); + } + } + + const LightAttribute *light_attrib; + if (!get_attribute_into(light_attrib, state, + LightTransition::get_class_type())) { + LightAttribute::const_iterator li; + for (li = light_attrib->begin(); li != light_attrib->end(); ++li) { + _gsg->define_light(*li); + } + } + } + return true; +} diff --git a/panda/src/ribgsg/ribStuffTraverser.h b/panda/src/ribgsg/ribStuffTraverser.h new file mode 100644 index 0000000000..1680f260e9 --- /dev/null +++ b/panda/src/ribgsg/ribStuffTraverser.h @@ -0,0 +1,35 @@ +// Filename: ribStuffTraverser.h +// Created by: drose (16Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef RIBSTUFFTRAVERSER_H +#define RIBSTUFFTRAVERSER_H + +#include + +#include +#include +#include +#include +#include + +class RIBGraphicsStateGuardian; + +//////////////////////////////////////////////////////////////////// +// Class : RibStuffTraverser +// Description : +//////////////////////////////////////////////////////////////////// +class RibStuffTraverser : + public TraverserVisitor { +public: + RibStuffTraverser(RIBGraphicsStateGuardian *gsg) : _gsg(gsg) { } + bool reached_node(Node *node, AllAttributesWrapper &state, + NullLevelState &); + +public: + RIBGraphicsStateGuardian *_gsg; +}; + +#endif + diff --git a/panda/src/sgattrib/alphaTransformAttribute.I b/panda/src/sgattrib/alphaTransformAttribute.I new file mode 100644 index 0000000000..50f94b3d2c --- /dev/null +++ b/panda/src/sgattrib/alphaTransformAttribute.I @@ -0,0 +1,55 @@ +// Filename: alphaTransformAttribute.I +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AlphaTransformAttribute:: +AlphaTransformAttribute() +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformAttribute::set_offset +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void AlphaTransformAttribute:: +set_offset(float offset) { + _state.set_offset(offset); +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformAttribute::get_offset +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float AlphaTransformAttribute:: +get_offset() const { + return _state.get_offset(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformAttribute::set_scale +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void AlphaTransformAttribute:: +set_scale(float scale) { + _state.set_scale(scale); +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformAttribute::get_scale +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float AlphaTransformAttribute:: +get_scale() const { + return _state.get_scale(); +} diff --git a/panda/src/sgattrib/alphaTransformAttribute.cxx b/panda/src/sgattrib/alphaTransformAttribute.cxx new file mode 100644 index 0000000000..b76b86f47d --- /dev/null +++ b/panda/src/sgattrib/alphaTransformAttribute.cxx @@ -0,0 +1,106 @@ +// Filename: alphaTransformAttribute.cxx +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "alphaTransformAttribute.h" +#include "alphaTransformTransition.h" + +#include +#include + +TypeHandle AlphaTransformAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle AlphaTransformAttribute:: +get_handle() const { + return AlphaTransformTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated AlphaTransformAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *AlphaTransformAttribute:: +make_copy() const { + return new AlphaTransformAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated AlphaTransformAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *AlphaTransformAttribute:: +make_initial() const { + return new AlphaTransformAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void AlphaTransformAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_alpha_transform(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformAttribute::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the indicated transition +// pointer, which is guaranteed to be of type +// AlphaTransformTransition. +//////////////////////////////////////////////////////////////////// +void AlphaTransformAttribute:: +set_value_from(const OnTransition *other) { + const AlphaTransformTransition *ot; + DCAST_INTO_V(ot, other); + _state = ot->_state; +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformAttribute::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int AlphaTransformAttribute:: +compare_values(const OnAttribute *other) const { + const AlphaTransformAttribute *ot; + DCAST_INTO_R(ot, other, false); + return _state.compare_to(ot->_state); +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void AlphaTransformAttribute:: +output_value(ostream &out) const { + out << _state; +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void AlphaTransformAttribute:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _state << "\n"; +} + + diff --git a/panda/src/sgattrib/alphaTransformAttribute.h b/panda/src/sgattrib/alphaTransformAttribute.h new file mode 100644 index 0000000000..20eb4f722e --- /dev/null +++ b/panda/src/sgattrib/alphaTransformAttribute.h @@ -0,0 +1,62 @@ +// Filename: alphaTransformAttribute.h +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ALPHA_TRANSFORM_TRANSITION_H +#define ALPHA_TRANSFORM_TRANSITION_H + +#include + +#include "alphaTransformProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : AlphaTransformAttribute +// Description : See AlphaTransformTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA AlphaTransformAttribute : public OnAttribute { +public: + INLINE AlphaTransformAttribute(); + + INLINE void set_offset(float offset); + INLINE float get_offset() const; + INLINE void set_scale(float scale); + INLINE float get_scale() const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + AlphaTransformProperty _state; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnAttribute::init_type(); + register_type(_type_handle, "AlphaTransformAttribute", + OnAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "alphaTransformAttribute.I" + +#endif diff --git a/panda/src/sgattrib/alphaTransformProperty.I b/panda/src/sgattrib/alphaTransformProperty.I new file mode 100644 index 0000000000..d30af1ad98 --- /dev/null +++ b/panda/src/sgattrib/alphaTransformProperty.I @@ -0,0 +1,80 @@ +// Filename: alphaTransformProperty.I +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformProperty::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AlphaTransformProperty:: +AlphaTransformProperty() : + _offset(0), _scale(1) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformProperty::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AlphaTransformProperty:: +AlphaTransformProperty(float offset, float scale) : + _offset(offset), _scale(scale) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformProperty::set_offset +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void AlphaTransformProperty:: +set_offset(float offset) { + _offset = offset; +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformProperty::get_offset +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float AlphaTransformProperty:: +get_offset() const { + return _offset; +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformProperty::set_scale +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void AlphaTransformProperty:: +set_scale(float scale) { + _scale = scale; +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformProperty::get_scale +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float AlphaTransformProperty:: +get_scale() const { + return _scale; +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformProperty::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int AlphaTransformProperty:: +compare_to(const AlphaTransformProperty &other) const { + if (_offset < other._offset) + return -1; + else if (_offset > other._offset) + return 1; + return 0; +} diff --git a/panda/src/sgattrib/alphaTransformProperty.cxx b/panda/src/sgattrib/alphaTransformProperty.cxx new file mode 100644 index 0000000000..e8630ce480 --- /dev/null +++ b/panda/src/sgattrib/alphaTransformProperty.cxx @@ -0,0 +1,16 @@ +// Filename: alphaTransformProperty.cxx +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "alphaTransformProperty.h" + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformProperty::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void AlphaTransformProperty:: +output(ostream &out) const { + out << "Offset " << _offset << " Scale " << _scale << endl; +} diff --git a/panda/src/sgattrib/alphaTransformProperty.h b/panda/src/sgattrib/alphaTransformProperty.h new file mode 100644 index 0000000000..6ec57f0e2d --- /dev/null +++ b/panda/src/sgattrib/alphaTransformProperty.h @@ -0,0 +1,42 @@ +// Filename: alphaTransformProperty.h +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ALPHA_TRANSFORM_PROPERTY_H +#define ALPHA_TRANSFORM_PROPERTY_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : AlphaTransformProperty +// Description : This class defines the set state for polygon offseting +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA AlphaTransformProperty { +public: + INLINE AlphaTransformProperty(); + INLINE AlphaTransformProperty(float offset, float scale); + + INLINE void set_offset(float offset); + INLINE float get_offset() const; + INLINE void set_scale(float scale); + INLINE float get_scale() const; + + INLINE int compare_to(const AlphaTransformProperty &other) const; + void output(ostream &out) const; + +private: + float _offset; + float _scale; +}; + +INLINE ostream &operator << (ostream &out, const AlphaTransformProperty &prop) { + prop.output(out); + return out; +} + +#include "alphaTransformProperty.I" + +#endif diff --git a/panda/src/sgattrib/alphaTransformTransition.I b/panda/src/sgattrib/alphaTransformTransition.I new file mode 100644 index 0000000000..02b2948f5d --- /dev/null +++ b/panda/src/sgattrib/alphaTransformTransition.I @@ -0,0 +1,55 @@ +// Filename: alphaTransformTransition.I +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AlphaTransformTransition:: +AlphaTransformTransition(float offset, float scale) : + _state(offset, scale) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformTransition::set_offset +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void AlphaTransformTransition:: +set_offset(float offset) { + _state.set_offset(offset); +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformTransition::get_offset +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float AlphaTransformTransition:: +get_offset() const { + return _state.get_offset(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformTransition::set_scale +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void AlphaTransformTransition:: +set_scale(float scale) { + _state.set_scale(scale); +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformTransition::get_scale +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float AlphaTransformTransition:: +get_scale() const { + return _state.get_scale(); +} diff --git a/panda/src/sgattrib/alphaTransformTransition.cxx b/panda/src/sgattrib/alphaTransformTransition.cxx new file mode 100644 index 0000000000..405a6d799e --- /dev/null +++ b/panda/src/sgattrib/alphaTransformTransition.cxx @@ -0,0 +1,78 @@ +// Filename: alphaTransformTransition.cxx +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "alphaTransformTransition.h" +#include "alphaTransformAttribute.h" + +#include + +TypeHandle AlphaTransformTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated AlphaTransformTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *AlphaTransformTransition:: +make_copy() const { + return new AlphaTransformTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated AlphaTransformAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *AlphaTransformTransition:: +make_attrib() const { + return new AlphaTransformAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformTransition::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the other transition pointer, +// which is guaranteed to be another AlphaTransformTransition. +//////////////////////////////////////////////////////////////////// +void AlphaTransformTransition:: +set_value_from(const OnTransition *other) { + const AlphaTransformTransition *ot; + DCAST_INTO_V(ot, other); + _state = ot->_state; +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformTransition::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int AlphaTransformTransition:: +compare_values(const OnTransition *other) const { + const AlphaTransformTransition *ot; + DCAST_INTO_R(ot, other, false); + return _state.compare_to(ot->_state); +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void AlphaTransformTransition:: +output_value(ostream &out) const { + out << _state; +} + +//////////////////////////////////////////////////////////////////// +// Function: AlphaTransformTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void AlphaTransformTransition:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _state << "\n"; +} diff --git a/panda/src/sgattrib/alphaTransformTransition.h b/panda/src/sgattrib/alphaTransformTransition.h new file mode 100644 index 0000000000..f047742c2e --- /dev/null +++ b/panda/src/sgattrib/alphaTransformTransition.h @@ -0,0 +1,62 @@ +// Filename: alphaTransformTransition.h +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ALPHA_TRANSFORM_TRANSTION_H +#define ALPHA_TRANSFORM_TRANSTION_H + +#include + +#include "alphaTransformProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : AlphaTransformTransition +// Description : This allows for scaling and offseting of current +// alpha values +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA AlphaTransformTransition : public OnTransition { +public: + INLINE AlphaTransformTransition(float offset = 0, float scale = 1); + + INLINE void set_offset(float offset); + INLINE float get_offset() const; + INLINE void set_scale(float scale); + INLINE float get_scale() const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + AlphaTransformProperty _state; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnTransition::init_type(); + register_type(_type_handle, "AlphaTransformTransition", + OnTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class AlphaTransformAttribute; +}; + + +#include "alphaTransformTransition.I" + +#endif diff --git a/panda/src/sgattrib/attribTraverser.cxx b/panda/src/sgattrib/attribTraverser.cxx new file mode 100644 index 0000000000..edc84bfd5c --- /dev/null +++ b/panda/src/sgattrib/attribTraverser.cxx @@ -0,0 +1,188 @@ +// Filename: attribTraverser.cxx +// Created by: mike (16Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "attribTraverser.h" +#include "renderRelation.h" +#include +#include +#include + +#include "textureTransition.h" +#include "textureAttribute.h" + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: AttribTraverser::constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +AttribTraverser:: +AttribTraverser() + : _has_attrib(false), _has_transition(false) +{ + _attrib_type = TypeHandle::none(); + _transition_type = TypeHandle::none(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribTraverser::reached_node +// Access: Public +// Description: Called for each node of the scene graph +//////////////////////////////////////////////////////////////////// +bool AttribTraverser:: +reached_node(Node *node, NodeAttributeWrapper &state, NullLevelState &) { + + //Short circuit if we aren't looking for an attribute + if (_attrib_type == TypeHandle::none()) + { + return true; + } + + if (node->is_of_type(GeomNode::get_class_type())) + { + NodeAttribute *attrib = state.get_attrib(); + if (attrib != (NodeAttribute *)NULL) + { + nassertr(attrib->is_of_type(_attrib_type), false); + + if (attrib->is_of_type(OnOffAttribute::get_class_type())) + { + if(DCAST(OnOffAttribute, attrib)->is_on()) + { + _has_attrib = true; + } + } + else + { + _has_attrib = true; + } + + } + } + + // Prune the search if we've discovered that we have the attribute + // being searched for anywhere. + return !_has_attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribTraverser::forward_arc +// Access: Public +// Description: Called for each forward arc of the scene graph +//////////////////////////////////////////////////////////////////// +bool AttribTraverser:: +forward_arc(NodeRelation *, TransitionWrapper &trans, + NodeAttributeWrapper &, NodeAttributeWrapper &, + NullLevelState &) +{ + //Short circuit if we aren't looking for a transition + if (_transition_type == TypeHandle::none()) + { + return true; + } + + NodeTransition *transition = trans.get_trans(); + + if (transition != (NodeTransition *)NULL) + { + nassertr(transition->is_of_type(_transition_type), false); + _has_transition = true; + } + // Prune the search if we've discovered that we have the transition + // being searched for anywhere. + return !_has_transition; +} +//////////////////////////////////////////////////////////////////// +// Function: AttribTraverser::set_attrib_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void AttribTraverser:: +set_attrib_type(TypeHandle type) +{ + _attrib_type = type; +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribTraverser::set_transition_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void AttribTraverser:: +set_transition_type(TypeHandle type) +{ + _transition_type = type; + nassertv(_transition_type.is_derived_from(ImmediateTransition::get_class_type())); +} + +//////////////////////////////////////////////////////////////////// +// Function: is_textured +// Description: Recursively checks the tree of nodes from root to +// see if any geometry is textured +//////////////////////////////////////////////////////////////////// +bool +is_textured(Node* root) { + AttribTraverser trav; + + trav.set_attrib_type(TextureAttribute::get_class_type()); + + df_traverse(root, trav, + NodeAttributeWrapper(TextureTransition::get_class_type()), + NullLevelState(), RenderRelation::get_class_type()); + + return trav._has_attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: is_textured +// Description: Recursively checks the tree of nodes from root to +// see if any geometry is textured +//////////////////////////////////////////////////////////////////// +bool +is_textured(Node* root, const AllAttributesWrapper &init_state) +{ + AttribTraverser trav; + + trav.set_attrib_type(TextureAttribute::get_class_type()); + + NodeAttributeWrapper state(TextureTransition::get_class_type()); + state.set_attrib(init_state.get_attribute(TextureTransition::get_class_type())); + + df_traverse(root, trav, state, NullLevelState(), RenderRelation::get_class_type()); + + return trav._has_attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: is_shaded +// Description: Recursively checks the tree of nodes from root to +// see if any geometry is shaded +//////////////////////////////////////////////////////////////////// +bool +is_shaded(Node* root) { + AttribTraverser trav; + + //We use the TypeRegistry here because attribTraverser is defined in + //the package sgattrib. And this package knows nothing about + //Shaders. So we can just include shaderTransition.h, as it may not + //have been compiled yet. So use this workaround to get the type + //handle of a ShaderTransition + trav.set_transition_type(TypeRegistry::ptr()->find_type("ShaderTransition")); + + df_traverse(root, trav, + NodeAttributeWrapper(TypeRegistry::ptr()->find_type("ShaderTransition")), + NullLevelState(), RenderRelation::get_class_type()); + + return trav._has_attrib; +} + + + + + diff --git a/panda/src/sgattrib/attribTraverser.h b/panda/src/sgattrib/attribTraverser.h new file mode 100644 index 0000000000..2674c673c0 --- /dev/null +++ b/panda/src/sgattrib/attribTraverser.h @@ -0,0 +1,48 @@ +// Filename: attribTraverser.h +// Created by: mike (16Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef ATTRIBTRAVERSER_H +#define ATTRIBTRAVERSER_H + +#include + +#include +#include +#include +#include + +class AllAttributesWrapper; + +//////////////////////////////////////////////////////////////////// +// Class : AttribTraverser +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA AttribTraverser : + public TraverserVisitor { +public: + AttribTraverser(); + bool reached_node(Node *node, NodeAttributeWrapper &state, NullLevelState &); + bool forward_arc(NodeRelation *arc, TransitionWrapper &trans, + NodeAttributeWrapper &, NodeAttributeWrapper &, + NullLevelState &); + + + void set_attrib_type(TypeHandle type); + void set_transition_type(TypeHandle type); +public: + bool _has_attrib; + bool _has_transition; +private: + TypeHandle _attrib_type; + TypeHandle _transition_type; +}; + +bool EXPCL_PANDA is_textured(Node* root); +bool EXPCL_PANDA is_textured(Node* root, const AllAttributesWrapper &init_state); + +bool EXPCL_PANDA is_shaded(Node* root); + +#endif + diff --git a/panda/src/sgattrib/billboardTransition.I b/panda/src/sgattrib/billboardTransition.I new file mode 100644 index 0000000000..729bd2b924 --- /dev/null +++ b/panda/src/sgattrib/billboardTransition.I @@ -0,0 +1,143 @@ +// Filename: billboardTransition.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BillboardTransition:: +BillboardTransition() { + _up_vector = LVector3f::up(); + _eye_relative = false; + _axial_rotate = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::axis +// Access: Public, Static +// Description: This named constructor returns a BillboardTransition +// set up to rotate about whatever up axis is +// appropriate to the given coordinate system. +//////////////////////////////////////////////////////////////////// +INLINE BillboardTransition BillboardTransition:: +axis(CoordinateSystem cs) { + BillboardTransition t; + t.set_up_vector(LVector3f::up(cs)); + t.set_eye_relative(false); + t.set_axial_rotate(true); + return t; +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::point_eye +// Access: Public, Static +// Description: This named constructor returns a BillboardTransition +// set up to rotate about the indicated point in eye +// (camera) coordinates. +//////////////////////////////////////////////////////////////////// +INLINE BillboardTransition BillboardTransition:: +point_eye(CoordinateSystem cs) { + BillboardTransition t; + t.set_up_vector(LVector3f::up(cs)); + t.set_eye_relative(true); + t.set_axial_rotate(false); + return t; +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::point_world +// Access: Public, Static +// Description: This named constructor returns a BillboardTransition +// set up to rotate about the indicated point in world +// coordinates. +//////////////////////////////////////////////////////////////////// +INLINE BillboardTransition BillboardTransition:: +point_world(CoordinateSystem cs) { + BillboardTransition t; + t.set_up_vector(LVector3f::up(cs)); + t.set_eye_relative(false); + t.set_axial_rotate(false); + return t; +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::set_up_vector +// Access: Public +// Description: Sets the vector that determines the "up" direction +// for the billboard. In axial_rotate mode, this is the +// vector around which the billboard is constrained to +// rotate. When axial_rotate mode is off (i.e. in point +// rotate mode), this vector describes the direction +// that the billboard tries to keep facing upward as it +// rotates around the point. +// +// This vector may be either world-relative or +// camera-relative, according to the eye_relative flag. +//////////////////////////////////////////////////////////////////// +INLINE void BillboardTransition:: +set_up_vector(const LVector3f &up_vector) { + nassertv(!up_vector.almost_equal(LVector3f(0.0, 0.0, 0.0))); + _up_vector = up_vector; + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::get_up_vector +// Access: Public +// Description: See set_up_vector. +//////////////////////////////////////////////////////////////////// +INLINE LVector3f BillboardTransition:: +get_up_vector() const { + return _up_vector; +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::set_eye_relative +// Access: Public +// Description: Sets the flag indicating whether the billboard's up +// vector should be considered to be in camera space +// (true) or world space (false). +//////////////////////////////////////////////////////////////////// +INLINE void BillboardTransition:: +set_eye_relative(bool eye_relative) { + _eye_relative = eye_relative; + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::get_eye_relative +// Access: Public +// Description: See set_eye_relative. +//////////////////////////////////////////////////////////////////// +INLINE bool BillboardTransition:: +get_eye_relative() const { + return _eye_relative; +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::set_axial_rotate +// Access: Public +// Description: When this flag is true, the billboard will be +// constrained to rotate in two dimensions about its up +// vector only. When false, the billboard will rotate +// in all three dimensions as necessary to keep itself +// facing the camera directly. +//////////////////////////////////////////////////////////////////// +INLINE void BillboardTransition:: +set_axial_rotate(bool axial_rotate) { + _axial_rotate = axial_rotate; + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::get_axial_rotate +// Access: Public +// Description: See set_axial_rotate. +//////////////////////////////////////////////////////////////////// +INLINE bool BillboardTransition:: +get_axial_rotate() const { + return _axial_rotate; +} diff --git a/panda/src/sgattrib/billboardTransition.cxx b/panda/src/sgattrib/billboardTransition.cxx new file mode 100644 index 0000000000..c410325d28 --- /dev/null +++ b/panda/src/sgattrib/billboardTransition.cxx @@ -0,0 +1,226 @@ +// Filename: billboardTransition.cxx +// Created by: mike (06Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "billboardTransition.h" +#include "transformTransition.h" +#include "transformAttribute.h" +#include "renderRelation.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TypeHandle BillboardTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +NodeTransition *BillboardTransition:: +make_copy() const { + return new BillboardTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::sub_render +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool BillboardTransition:: +sub_render(NodeRelation *arc, const AllAttributesWrapper &, + AllTransitionsWrapper &trans, GraphicsStateGuardianBase *gsgbase) { + Node *node = arc->get_child(); + GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, gsgbase); + + // Get the current camera from the gsg + const ProjectionNode *camera = gsg->get_current_projection_node(); + nassertr(camera != (ProjectionNode *)NULL, true); + + // And the relative coordinate space. + LMatrix4f rel_mat; + NodeTransitionWrapper ntw(TransformTransition::get_class_type()); + wrt(camera, node, (&arc), (&arc) + 1, ntw, RenderRelation::get_class_type()); + TransformTransition *tt; + if (!get_transition_into(tt, ntw)) { + // No relative transform. + rel_mat = LMatrix4f::ident_mat(); + } else { + rel_mat = tt->get_matrix(); + } + + LVector3f camera_pos = -rel_mat.get_row3(3); + LVector3f up = _up_vector; + + // If this is an eye-relative Billboard, then (a) the up vector is + // relative to the camera, not to the world, and (b) the look + // direction is towards the plane that contains the camera, + // perpendicular to the forward direction, not directly to the + // camera. + if (_eye_relative) { + up = _up_vector * rel_mat; + camera_pos = LVector3f::forward(gsg->get_coordinate_system()) * rel_mat; + } + + // Now determine the rotation matrix for the Billboard. + LMatrix4f rotate; + if (_axial_rotate) { + heads_up(rotate, camera_pos, up, gsg->get_coordinate_system()); + } else { + look_at(rotate, camera_pos, up, gsg->get_coordinate_system()); + } + + // And finally, apply the rotation transform to the set of + // transitions we've accumulated for this node. + AllTransitionsWrapper new_trans; + new_trans.set_transition(new TransformTransition(rotate)); + + trans.compose_in_place(new_trans); + + // Continue the render pass + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::has_sub_render +// Access: Public, Virtual +// Description: Should be redefined to return true if the function +// sub_render(), above, expects to be called during +// traversal. +//////////////////////////////////////////////////////////////////// +bool BillboardTransition:: +has_sub_render() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void BillboardTransition:: +output(ostream &out) const { + if (_axial_rotate) { + out << "axial"; + } else { + out << "point"; + } + if (_eye_relative) { + out << " eye"; + } + out << " Billboard"; +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void BillboardTransition:: +write(ostream &out, int indent_level) const { + indent(out, indent_level); + if (_axial_rotate) { + out << "axial"; + } else { + out << "point"; + } + if (_eye_relative) { + out << " eye"; + } + out << " Billboard:\n"; + + indent(out, indent_level + 2) + << "up vector: " << _up_vector << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::internal_compare_to +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int BillboardTransition:: +internal_compare_to(const NodeTransition *other) const { + const BillboardTransition *ot; + DCAST_INTO_R(ot, other, false); + + if (_eye_relative != ot->_eye_relative) { + return (int)_eye_relative - (int)ot->_eye_relative; + } + if (_axial_rotate != ot->_axial_rotate) { + return (int)_axial_rotate - (int)ot->_axial_rotate; + } + return _up_vector.compare_to(ot->_up_vector); +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void BillboardTransition:: +write_datagram(BamWriter *manager, Datagram &me) +{ + ImmediateTransition::write_datagram(manager, me); + _up_vector.write_datagram(me); + me.add_uint8(_eye_relative); + me.add_uint8(_axial_rotate); +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void BillboardTransition:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + ImmediateTransition::fillin(scan, manager); + _up_vector.read_datagram(scan); + _eye_relative = (bool) scan.get_uint8(); + _axial_rotate = (bool) scan.get_uint8(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::make_BillboardTransition +// Access: Protected +// Description: Factory method to generate a BillboardTransition object +//////////////////////////////////////////////////////////////////// +TypedWriteable* BillboardTransition:: +make_BillboardTransition(const FactoryParams ¶ms) +{ + BillboardTransition *me = new BillboardTransition; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardTransition::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a BillboardTransition object +//////////////////////////////////////////////////////////////////// +void BillboardTransition:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_BillboardTransition); +} + diff --git a/panda/src/sgattrib/billboardTransition.h b/panda/src/sgattrib/billboardTransition.h new file mode 100644 index 0000000000..e2a5f826db --- /dev/null +++ b/panda/src/sgattrib/billboardTransition.h @@ -0,0 +1,91 @@ +// Filename: billboardTransition.h +// Created by: mike (19Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BILLBOARDTRANSITION_H +#define BILLBOARDTRANSITION_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : BillboardTransition +// Description : This transition, when applied to an arc, causes that +// arc and everything below it to be rendered so that it +// always faces the camera. There are all kinds of ways +// that billboards can be set to rotate. +// +// A BillboardTransition is neither on nor off, and it +// does not compose with nested BillboardTransitions. +// Instead, it has an immediate effect. Once a +// billboard transition is in place, it affects +// everything below it. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA BillboardTransition : public ImmediateTransition { +public: + INLINE BillboardTransition(); + INLINE static BillboardTransition axis(CoordinateSystem cs = CS_default); + INLINE static BillboardTransition point_eye(CoordinateSystem cs = CS_default); + INLINE static BillboardTransition point_world(CoordinateSystem cs = CS_default); + + INLINE void set_up_vector(const LVector3f &up_vector); + INLINE LVector3f get_up_vector() const; + + INLINE void set_eye_relative(bool eye_relative); + INLINE bool get_eye_relative() const; + + INLINE void set_axial_rotate(bool axial_rotate); + INLINE bool get_axial_rotate() const; + + virtual NodeTransition *make_copy() const; + + virtual bool sub_render(NodeRelation *arc, + const AllAttributesWrapper &attrib, + AllTransitionsWrapper &trans, + GraphicsStateGuardianBase *gsgbase); + virtual bool has_sub_render() const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + virtual int internal_compare_to(const NodeTransition *other) const; + +private: + LVector3f _up_vector; + bool _eye_relative; + bool _axial_rotate; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + + static TypedWriteable *make_BillboardTransition(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ImmediateTransition::init_type(); + register_type(_type_handle, "BillboardTransition", + ImmediateTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "billboardTransition.I" + +#endif diff --git a/panda/src/sgattrib/clipPlaneAttribute.I b/panda/src/sgattrib/clipPlaneAttribute.I new file mode 100644 index 0000000000..7eb3922df5 --- /dev/null +++ b/panda/src/sgattrib/clipPlaneAttribute.I @@ -0,0 +1,53 @@ +// Filename: clipPlaneAttribute.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ClipPlaneAttribute:: +ClipPlaneAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneAttribute::set_on +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ClipPlaneAttribute:: +set_on(const PT(PlaneNode) &property) { + MultiNodeAttribute::set_on(property.p()); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneAttribute::set_off +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ClipPlaneAttribute:: +set_off(const PT(PlaneNode) &property) { + MultiNodeAttribute::set_off(property.p()); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneAttribute::is_on +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ClipPlaneAttribute:: +is_on(const PT(PlaneNode) &property) const { + return MultiNodeAttribute::is_on(property.p()); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneAttribute::is_off +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ClipPlaneAttribute:: +is_off(const PT(PlaneNode) &property) const { + return MultiNodeAttribute::is_off(property.p()); +} diff --git a/panda/src/sgattrib/clipPlaneAttribute.cxx b/panda/src/sgattrib/clipPlaneAttribute.cxx new file mode 100644 index 0000000000..8063240638 --- /dev/null +++ b/panda/src/sgattrib/clipPlaneAttribute.cxx @@ -0,0 +1,84 @@ +// Filename: clipPlaneAttribute.cxx +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "clipPlaneAttribute.h" +#include "clipPlaneTransition.h" + +#include +#include + +TypeHandle ClipPlaneAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle ClipPlaneAttribute:: +get_handle() const { + return ClipPlaneTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated ClipPlaneAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ClipPlaneAttribute:: +make_copy() const { + return new ClipPlaneAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated ClipPlaneAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ClipPlaneAttribute:: +make_initial() const { + return new ClipPlaneAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void ClipPlaneAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_clip_plane(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void ClipPlaneAttribute:: +output_property(ostream &out, const PT_Node &prop) const { + const PlaneNode *node; + DCAST_INTO_V(node, prop); + out << node->get_name() << "=" << node->get_plane(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void ClipPlaneAttribute:: +write_property(ostream &out, const PT_Node &prop, + int indent_level) const { + const PlaneNode *node; + DCAST_INTO_V(node, prop); + indent(out, indent_level) + << node->get_name() << "=" << node->get_plane() << "\n"; +} diff --git a/panda/src/sgattrib/clipPlaneAttribute.h b/panda/src/sgattrib/clipPlaneAttribute.h new file mode 100644 index 0000000000..128b132fe9 --- /dev/null +++ b/panda/src/sgattrib/clipPlaneAttribute.h @@ -0,0 +1,61 @@ +// Filename: clipPlaneAttribute.h +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CLIPPLANEATTRIBUTE_H +#define CLIPPLANEATTRIBUTE_H + +#include + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : ClipPlaneAttribute +// Description : See ClipPlaneTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ClipPlaneAttribute : public MultiNodeAttribute { +public: + INLINE ClipPlaneAttribute(); + + INLINE void set_on(const PT(PlaneNode) &prop); + INLINE void set_off(const PT(PlaneNode) &prop); + + INLINE bool is_on(const PT(PlaneNode) &prop) const; + INLINE bool is_off(const PT(PlaneNode) &prop) const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void output_property(ostream &out, const PT_Node &prop) const; + virtual void write_property(ostream &out, const PT_Node &prop, + int indent_level) const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MultiNodeAttribute::init_type(); + register_type(_type_handle, "ClipPlaneAttribute", + MultiNodeAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "clipPlaneAttribute.I" + +#endif diff --git a/panda/src/sgattrib/clipPlaneTransition.I b/panda/src/sgattrib/clipPlaneTransition.I new file mode 100644 index 0000000000..8032f846fc --- /dev/null +++ b/panda/src/sgattrib/clipPlaneTransition.I @@ -0,0 +1,86 @@ +// Filename: clipPlaneTransition.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ClipPlaneTransition:: +ClipPlaneTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneTransition::all_off +// Access: Public +// Description: This named constructor returns a ClipPlaneTransition +// that's preconfigured to turn off all planes below it. +//////////////////////////////////////////////////////////////////// +INLINE ClipPlaneTransition ClipPlaneTransition:: +all_off() { + ClipPlaneTransition t; + t.set_default_dir(TD_off); + return t; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneTransition::set_identity +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ClipPlaneTransition:: +set_identity(const PT(PlaneNode) &property) { + MultiNodeTransition::set_identity(property.p()); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneTransition::set_on +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ClipPlaneTransition:: +set_on(const PT(PlaneNode) &property) { + MultiNodeTransition::set_on(property.p()); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneTransition::set_off +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ClipPlaneTransition:: +set_off(const PT(PlaneNode) &property) { + MultiNodeTransition::set_off(property.p()); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneTransition::is_identity +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ClipPlaneTransition:: +is_identity(const PT(PlaneNode) &property) const { + return MultiNodeTransition::is_identity(property.p()); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneTransition::is_on +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ClipPlaneTransition:: +is_on(const PT(PlaneNode) &property) const { + return MultiNodeTransition::is_on(property.p()); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneTransition::is_off +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ClipPlaneTransition:: +is_off(const PT(PlaneNode) &property) const { + return MultiNodeTransition::is_off(property.p()); +} diff --git a/panda/src/sgattrib/clipPlaneTransition.cxx b/panda/src/sgattrib/clipPlaneTransition.cxx new file mode 100644 index 0000000000..6b77017293 --- /dev/null +++ b/panda/src/sgattrib/clipPlaneTransition.cxx @@ -0,0 +1,70 @@ +// Filename: clipPlaneTransition.cxx +// Created by: mike (06Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "clipPlaneTransition.h" +#include "clipPlaneAttribute.h" + +#include + +TypeHandle ClipPlaneTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated ClipPlaneTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *ClipPlaneTransition:: +make_copy() const { + return new ClipPlaneTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated ClipPlaneAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ClipPlaneTransition:: +make_attrib() const { + return new ClipPlaneAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneTransition::make_identity +// Access: Public, Virtual +// Description: Returns a newly allocated ClipPlaneTransition in the +// initial state. +//////////////////////////////////////////////////////////////////// +NodeTransition *ClipPlaneTransition:: +make_identity() const { + return new ClipPlaneTransition; +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void ClipPlaneTransition:: +output_property(ostream &out, const PT_Node &prop) const { + const PlaneNode *node; + DCAST_INTO_V(node, prop.p()); + out << node->get_name() << "=" << node->get_plane(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void ClipPlaneTransition:: +write_property(ostream &out, const PT_Node &prop, + int indent_level) const { + const PlaneNode *node; + DCAST_INTO_V(node, prop); + indent(out, indent_level) + << node->get_name() << "=" << node->get_plane() << "\n"; +} diff --git a/panda/src/sgattrib/clipPlaneTransition.h b/panda/src/sgattrib/clipPlaneTransition.h new file mode 100644 index 0000000000..e31a679202 --- /dev/null +++ b/panda/src/sgattrib/clipPlaneTransition.h @@ -0,0 +1,66 @@ +// Filename: clipPlaneTransition.h +// Created by: mike (19Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CLIPPLANETRANSITION_H +#define CLIPPLANETRANSITION_H + +#include + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : ClipPlaneTransition +// Description : This allows the definition of zero or more arbitrary +// clipping planes. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ClipPlaneTransition : public MultiNodeTransition { +public: + INLINE ClipPlaneTransition(); + INLINE static ClipPlaneTransition all_off(); + + INLINE void set_identity(const PT(PlaneNode) &prop); + INLINE void set_on(const PT(PlaneNode) &prop); + INLINE void set_off(const PT(PlaneNode) &prop); + + INLINE bool is_identity(const PT(PlaneNode) &prop) const; + INLINE bool is_on(const PT(PlaneNode) &prop) const; + INLINE bool is_off(const PT(PlaneNode) &prop) const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + virtual NodeTransition *make_identity() const; + +protected: + virtual void output_property(ostream &out, const PT_Node &prop) const; + virtual void write_property(ostream &out, const PT_Node &prop, + int indent_level) const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MultiNodeTransition::init_type(); + register_type(_type_handle, "ClipPlaneTransition", + MultiNodeTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class ClipPlaneAttribute; +}; + +#include "clipPlaneTransition.I" + +#endif + + diff --git a/panda/src/sgattrib/colorAttribute.I b/panda/src/sgattrib/colorAttribute.I new file mode 100644 index 0000000000..b8bde44abc --- /dev/null +++ b/panda/src/sgattrib/colorAttribute.I @@ -0,0 +1,72 @@ +// Filename: colorAttribute.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ColorAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorAttribute:: +ColorAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorAttribute::set_on +// Access: Public +// Description: Changes the ColorAttribute to turn on the particular +// color. +//////////////////////////////////////////////////////////////////// +INLINE void ColorAttribute:: +set_on(const Colorf &color) { + _value = ColorProperty(color); + OnOffAttribute::set_on(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorAttribute::set_on +// Access: Public +// Description: Changes the ColorAttribute to turn on the particular +// color. +//////////////////////////////////////////////////////////////////// +INLINE void ColorAttribute:: +set_on(float r, float g, float b, float a) { + set_on(Colorf(r, g, b, a)); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorAttribute::set_uncolor +// Access: Public +// Description: Changes the ColorAttribute to turn on uncolor. +//////////////////////////////////////////////////////////////////// +INLINE void ColorAttribute:: +set_uncolor() { + _value = ColorProperty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorAttribute::is_real +// Access: Public +// Description: Returns true if the ColorAttribute represents a real +// color, false if it represents the uncolor. It is +// only valid to call this if is_on() has returned true. +//////////////////////////////////////////////////////////////////// +INLINE bool ColorAttribute:: +is_real() const { + nassertr(is_on(), false); + return _value.is_real(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorAttribute::get_color +// Access: Public +// Description: Returns the color that the ColorAttribute +// represents. It is only valid to call this if is_on() +// and is_real() have both returned true. +//////////////////////////////////////////////////////////////////// +INLINE Colorf ColorAttribute:: +get_color() const { + nassertr(is_on(), false); + return _value.get_color(); +} diff --git a/panda/src/sgattrib/colorAttribute.cxx b/panda/src/sgattrib/colorAttribute.cxx new file mode 100644 index 0000000000..2f69c0db8b --- /dev/null +++ b/panda/src/sgattrib/colorAttribute.cxx @@ -0,0 +1,106 @@ +// Filename: colorAttribute.cxx +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "colorAttribute.h" +#include "colorTransition.h" + +#include +#include + +TypeHandle ColorAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ColorAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle ColorAttribute:: +get_handle() const { + return ColorTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated ColorAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ColorAttribute:: +make_copy() const { + return new ColorAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated ColorAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ColorAttribute:: +make_initial() const { + return new ColorAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void ColorAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_color(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorAttribute::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the indicated transition +// pointer, which is guaranteed to be of type +// ColorTransition. +//////////////////////////////////////////////////////////////////// +void ColorAttribute:: +set_value_from(const OnOffTransition *other) { + const ColorTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorAttribute::compare_values +// Access: Protected, Virtual +// Description: Returns true if the two attributes have the same +// value. It is guaranteed that the other attribute is +// another ColorAttribute, and that both are "on". +//////////////////////////////////////////////////////////////////// +int ColorAttribute:: +compare_values(const OnOffAttribute *other) const { + const ColorAttribute *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void ColorAttribute:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void ColorAttribute:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/colorAttribute.h b/panda/src/sgattrib/colorAttribute.h new file mode 100644 index 0000000000..1891eccafd --- /dev/null +++ b/panda/src/sgattrib/colorAttribute.h @@ -0,0 +1,64 @@ +// Filename: colorAttribute.h +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLORATTRIBUTE_H +#define COLORATTRIBUTE_H + +#include + +#include "colorProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : ColorAttribute +// Description : See ColorTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ColorAttribute : public OnOffAttribute { +public: + INLINE ColorAttribute(); + + INLINE void set_on(const Colorf &color); + INLINE void set_on(float r, float g, float b, float a); + INLINE void set_uncolor(); + + INLINE bool is_real() const; + INLINE Colorf get_color() const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void set_value_from(const OnOffTransition *other); + virtual int compare_values(const OnOffAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + ColorProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnOffAttribute::init_type(); + register_type(_type_handle, "ColorAttribute", + OnOffAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "colorAttribute.I" + +#endif diff --git a/panda/src/sgattrib/colorBlendAttribute.I b/panda/src/sgattrib/colorBlendAttribute.I new file mode 100644 index 0000000000..5d1d358951 --- /dev/null +++ b/panda/src/sgattrib/colorBlendAttribute.I @@ -0,0 +1,35 @@ +// Filename: colorBlendAttribute.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorBlendAttribute:: +ColorBlendAttribute() : + _value(ColorBlendProperty::M_none) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendAttribute::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ColorBlendAttribute:: +set_mode(ColorBlendProperty::Mode mode) { + _value.set_mode(mode); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendAttribute::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorBlendProperty::Mode ColorBlendAttribute:: +get_mode() const { + return _value.get_mode(); +} diff --git a/panda/src/sgattrib/colorBlendAttribute.cxx b/panda/src/sgattrib/colorBlendAttribute.cxx new file mode 100644 index 0000000000..1ef0a7928f --- /dev/null +++ b/panda/src/sgattrib/colorBlendAttribute.cxx @@ -0,0 +1,104 @@ +// Filename: colorBlendAttribute.cxx +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "colorBlendAttribute.h" +#include "colorBlendTransition.h" + +#include +#include + +TypeHandle ColorBlendAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle ColorBlendAttribute:: +get_handle() const { + return ColorBlendTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated ColorBlendAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ColorBlendAttribute:: +make_copy() const { + return new ColorBlendAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated ColorBlendAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ColorBlendAttribute:: +make_initial() const { + return new ColorBlendAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void ColorBlendAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_color_blend(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendAttribute::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the indicated transition +// pointer, which is guaranteed to be of type +// ColorBlendTransition. +//////////////////////////////////////////////////////////////////// +void ColorBlendAttribute:: +set_value_from(const OnTransition *other) { + const ColorBlendTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendAttribute::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int ColorBlendAttribute:: +compare_values(const OnAttribute *other) const { + const ColorBlendAttribute *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void ColorBlendAttribute:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void ColorBlendAttribute:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/colorBlendAttribute.h b/panda/src/sgattrib/colorBlendAttribute.h new file mode 100644 index 0000000000..a804359bac --- /dev/null +++ b/panda/src/sgattrib/colorBlendAttribute.h @@ -0,0 +1,60 @@ +// Filename: colorBlendAttribute.h +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLORBLENDATTRIBUTE_H +#define COLORBLENDATTRIBUTE_H + +#include + +#include "colorBlendProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : ColorBlendAttribute +// Description : See ColorBlendTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ColorBlendAttribute : public OnAttribute { +public: + INLINE ColorBlendAttribute(); + + INLINE void set_mode(ColorBlendProperty::Mode mode); + INLINE ColorBlendProperty::Mode get_mode() const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + ColorBlendProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnAttribute::init_type(); + register_type(_type_handle, "ColorBlendAttribute", + OnAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "colorBlendAttribute.I" + +#endif diff --git a/panda/src/sgattrib/colorBlendProperty.I b/panda/src/sgattrib/colorBlendProperty.I new file mode 100644 index 0000000000..9cd417c006 --- /dev/null +++ b/panda/src/sgattrib/colorBlendProperty.I @@ -0,0 +1,45 @@ +// Filename: colorBlendProperty.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendProperty::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorBlendProperty:: +ColorBlendProperty(ColorBlendProperty::Mode mode) : + _mode(mode) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendProperty::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ColorBlendProperty:: +set_mode(ColorBlendProperty::Mode mode) { + _mode = mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendProperty::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorBlendProperty::Mode ColorBlendProperty:: +get_mode() const { + return _mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendProperty::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int ColorBlendProperty:: +compare_to(const ColorBlendProperty &other) const { + return (int)_mode - (int)other._mode; +} diff --git a/panda/src/sgattrib/colorBlendProperty.cxx b/panda/src/sgattrib/colorBlendProperty.cxx new file mode 100644 index 0000000000..8b291ced68 --- /dev/null +++ b/panda/src/sgattrib/colorBlendProperty.cxx @@ -0,0 +1,38 @@ +// Filename: colorBlendProperty.cxx +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "colorBlendProperty.h" + +ostream & +operator << (ostream &out, ColorBlendProperty::Mode mode) { + switch (mode) { + case ColorBlendProperty::M_none: + return out << "none"; + + case ColorBlendProperty::M_multiply: + return out << "multiply"; + + case ColorBlendProperty::M_add: + return out << "add"; + + case ColorBlendProperty::M_multiply_add: + return out << "multiply_add"; + + case ColorBlendProperty::M_alpha: + return out << "alpha"; + } + + return out << "**invalid**(" << (int)mode << ")"; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendProperty::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ColorBlendProperty:: +output(ostream &out) const { + out << _mode; +} diff --git a/panda/src/sgattrib/colorBlendProperty.h b/panda/src/sgattrib/colorBlendProperty.h new file mode 100644 index 0000000000..a021a9ac41 --- /dev/null +++ b/panda/src/sgattrib/colorBlendProperty.h @@ -0,0 +1,49 @@ +// Filename: colorBlendProperty.h +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLORBLENDPROPERTY_H +#define COLORBLENDPROPERTY_H + +#include + +//////////////////////////////////////////////////////////////////// +// Class : ColorBlendProperty +// Description : This defines the types of color blending we can +// perform. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ColorBlendProperty { +public: + enum Mode { + M_none, // Blending is disabled + M_multiply, // color already in fbuffer * incoming color + M_add, // color already in fbuffer + incoming color + M_multiply_add, // color already in fbuffer * incoming color + + // color already in fbuffer + M_alpha, // ???? + }; + +public: + INLINE ColorBlendProperty(Mode mode); + + INLINE void set_mode(Mode mode); + INLINE Mode get_mode() const; + + INLINE int compare_to(const ColorBlendProperty &other) const; + void output(ostream &out) const; + +private: + Mode _mode; +}; + +ostream &operator << (ostream &out, ColorBlendProperty::Mode mode); + +INLINE ostream &operator << (ostream &out, const ColorBlendProperty &prop) { + prop.output(out); + return out; +} + +#include "colorBlendProperty.I" + +#endif diff --git a/panda/src/sgattrib/colorBlendTransition.I b/panda/src/sgattrib/colorBlendTransition.I new file mode 100644 index 0000000000..4896bfe25e --- /dev/null +++ b/panda/src/sgattrib/colorBlendTransition.I @@ -0,0 +1,36 @@ +// Filename: colorBlendTransition.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorBlendTransition:: +ColorBlendTransition(ColorBlendProperty::Mode mode) : + _value(mode) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendTransition::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ColorBlendTransition:: +set_mode(ColorBlendProperty::Mode mode) { + _value.set_mode(mode); + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendTransition::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorBlendProperty::Mode ColorBlendTransition:: +get_mode() const { + return _value.get_mode(); +} diff --git a/panda/src/sgattrib/colorBlendTransition.cxx b/panda/src/sgattrib/colorBlendTransition.cxx new file mode 100644 index 0000000000..360dca1a6f --- /dev/null +++ b/panda/src/sgattrib/colorBlendTransition.cxx @@ -0,0 +1,78 @@ +// Filename: colorBlendTransition.cxx +// Created by: mike (28Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "colorBlendTransition.h" +#include "colorBlendAttribute.h" + +#include + +TypeHandle ColorBlendTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated ColorBlendTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *ColorBlendTransition:: +make_copy() const { + return new ColorBlendTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated ColorBlendAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ColorBlendTransition:: +make_attrib() const { + return new ColorBlendAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendTransition::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the other transition pointer, +// which is guaranteed to be another ColorBlendTransition. +//////////////////////////////////////////////////////////////////// +void ColorBlendTransition:: +set_value_from(const OnTransition *other) { + const ColorBlendTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendTransition::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int ColorBlendTransition:: +compare_values(const OnTransition *other) const { + const ColorBlendTransition *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void ColorBlendTransition:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorBlendTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void ColorBlendTransition:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/colorBlendTransition.h b/panda/src/sgattrib/colorBlendTransition.h new file mode 100644 index 0000000000..9bb8f6d15f --- /dev/null +++ b/panda/src/sgattrib/colorBlendTransition.h @@ -0,0 +1,59 @@ +// Filename: colorBlendTransition.h +// Created by: mike (18Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLORBLENDTRANSITION_H +#define COLORBLENDTRANSITION_H + +#include + +#include "colorBlendProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : ColorBlendTransition +// Description : This controls the kinds of blending between colors +// being rendered and the existing frame buffer. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ColorBlendTransition : public OnTransition { +public: + INLINE ColorBlendTransition(ColorBlendProperty::Mode mode); + + INLINE void set_mode(ColorBlendProperty::Mode mode); + INLINE ColorBlendProperty::Mode get_mode() const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + ColorBlendProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnTransition::init_type(); + register_type(_type_handle, "ColorBlendTransition", + OnTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class ColorBlendAttribute; +}; + +#include "colorBlendTransition.I" + +#endif diff --git a/panda/src/sgattrib/colorMaskAttribute.I b/panda/src/sgattrib/colorMaskAttribute.I new file mode 100644 index 0000000000..68d5dca8c7 --- /dev/null +++ b/panda/src/sgattrib/colorMaskAttribute.I @@ -0,0 +1,79 @@ +// Filename: colorMaskAttribute.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorMaskAttribute:: +ColorMaskAttribute() : + _value(ColorMaskProperty::all_on()) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskAttribute::set_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ColorMaskAttribute:: +set_mask(int mask) { + _value.set_mask(mask); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskAttribute::get_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int ColorMaskAttribute:: +get_mask() const { + return _value.get_mask(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskAttribute::is_write_r +// Access: Public +// Description: Returns true if the red component is enabled for +// writing. +//////////////////////////////////////////////////////////////////// +INLINE bool ColorMaskAttribute:: +is_write_r() const { + return (_value.get_mask() & ColorMaskProperty::M_r) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskAttribute::is_write_g +// Access: Public +// Description: Returns true if the green component is enabled for +// writing. +//////////////////////////////////////////////////////////////////// +INLINE bool ColorMaskAttribute:: +is_write_g() const { + return (_value.get_mask() & ColorMaskProperty::M_g) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskAttribute::is_write_b +// Access: Public +// Description: Returns true if the blue component is enabled for +// writing. +//////////////////////////////////////////////////////////////////// +INLINE bool ColorMaskAttribute:: +is_write_b() const { + return (_value.get_mask() & ColorMaskProperty::M_b) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskAttribute::is_write_a +// Access: Public +// Description: Returns true if the alpha component is enabled for +// writing. +//////////////////////////////////////////////////////////////////// +INLINE bool ColorMaskAttribute:: +is_write_a() const { + return (_value.get_mask() & ColorMaskProperty::M_a) != 0; +} diff --git a/panda/src/sgattrib/colorMaskAttribute.cxx b/panda/src/sgattrib/colorMaskAttribute.cxx new file mode 100644 index 0000000000..54542cca00 --- /dev/null +++ b/panda/src/sgattrib/colorMaskAttribute.cxx @@ -0,0 +1,104 @@ +// Filename: colorMaskAttribute.cxx +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "colorMaskAttribute.h" +#include "colorMaskTransition.h" + +#include +#include + +TypeHandle ColorMaskAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle ColorMaskAttribute:: +get_handle() const { + return ColorMaskTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated ColorMaskAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ColorMaskAttribute:: +make_copy() const { + return new ColorMaskAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated ColorMaskAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ColorMaskAttribute:: +make_initial() const { + return new ColorMaskAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void ColorMaskAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_color_mask(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskAttribute::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the indicated transition +// pointer, which is guaranteed to be of type +// ColorMaskTransition. +//////////////////////////////////////////////////////////////////// +void ColorMaskAttribute:: +set_value_from(const OnTransition *other) { + const ColorMaskTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskAttribute::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int ColorMaskAttribute:: +compare_values(const OnAttribute *other) const { + const ColorMaskAttribute *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void ColorMaskAttribute:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void ColorMaskAttribute:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/colorMaskAttribute.h b/panda/src/sgattrib/colorMaskAttribute.h new file mode 100644 index 0000000000..6675b8854c --- /dev/null +++ b/panda/src/sgattrib/colorMaskAttribute.h @@ -0,0 +1,65 @@ +// Filename: colorMaskAttribute.h +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLORMASKATTRIBUTE_H +#define COLORMASKATTRIBUTE_H + +#include + +#include "colorMaskProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : ColorMaskAttribute +// Description : See ColorMaskTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ColorMaskAttribute : public OnAttribute { +public: + INLINE ColorMaskAttribute(); + + INLINE void set_mask(int mask); + INLINE int get_mask() const; + + INLINE bool is_write_r() const; + INLINE bool is_write_g() const; + INLINE bool is_write_b() const; + INLINE bool is_write_a() const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + ColorMaskProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnAttribute::init_type(); + register_type(_type_handle, "ColorMaskAttribute", + OnAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "colorMaskAttribute.I" + +#endif diff --git a/panda/src/sgattrib/colorMaskProperty.I b/panda/src/sgattrib/colorMaskProperty.I new file mode 100644 index 0000000000..f80780073d --- /dev/null +++ b/panda/src/sgattrib/colorMaskProperty.I @@ -0,0 +1,59 @@ +// Filename: colorMaskProperty.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskProperty::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorMaskProperty:: +ColorMaskProperty(int mask) : + _mask(mask) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskProperty::all_on +// Access: Public, Static +// Description: This named constructor returns a ColorMaskProperty +// whose bits are all on: that property that writes to +// all the color planes at once (the normal case). +//////////////////////////////////////////////////////////////////// +INLINE ColorMaskProperty ColorMaskProperty:: +all_on() { + return ColorMaskProperty(M_r | M_g | M_b | M_a); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskProperty::set_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ColorMaskProperty:: +set_mask(int mask) { + _mask = mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskProperty::get_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int ColorMaskProperty:: +get_mask() const { + return _mask; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskProperty::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int ColorMaskProperty:: +compare_to(const ColorMaskProperty &other) const { + return (int)_mask - (int)other._mask; +} diff --git a/panda/src/sgattrib/colorMaskProperty.cxx b/panda/src/sgattrib/colorMaskProperty.cxx new file mode 100644 index 0000000000..d687f5525e --- /dev/null +++ b/panda/src/sgattrib/colorMaskProperty.cxx @@ -0,0 +1,35 @@ +// Filename: colorMaskProperty.cxx +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "colorMaskProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskProperty::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ColorMaskProperty:: +output(ostream &out) const { + char hex[20]; + sprintf(hex, "%02x", _mask); + nassertv(strlen(hex) < 20); + + out << "0x" << hex << "("; + if ((_mask & M_r) != 0) { + out << "r"; + } + if ((_mask & M_g) != 0) { + out << "g"; + } + if ((_mask & M_b) != 0) { + out << "b"; + } + if ((_mask & M_a) != 0) { + out << "a"; + } + out << ")"; +} diff --git a/panda/src/sgattrib/colorMaskProperty.h b/panda/src/sgattrib/colorMaskProperty.h new file mode 100644 index 0000000000..c9eedb67a0 --- /dev/null +++ b/panda/src/sgattrib/colorMaskProperty.h @@ -0,0 +1,45 @@ +// Filename: colorMaskProperty.h +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLORMASKPROPERTY_H +#define COLORMASKPROPERTY_H + +#include + +//////////////////////////////////////////////////////////////////// +// Class : ColorMaskProperty +// Description : This defines the set of color planes that may be +// active for writing to the color buffer. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ColorMaskProperty { +public: + enum Mask { + M_r = 0x001, + M_g = 0x002, + M_b = 0x004, + M_a = 0x008 + }; + + INLINE ColorMaskProperty(int mask); + INLINE static ColorMaskProperty all_on(); + + INLINE void set_mask(int mask); + INLINE int get_mask() const; + + INLINE int compare_to(const ColorMaskProperty &other) const; + void output(ostream &out) const; + +private: + int _mask; +}; + +INLINE ostream &operator << (ostream &out, const ColorMaskProperty &prop) { + prop.output(out); + return out; +} + +#include "colorMaskProperty.I" + +#endif diff --git a/panda/src/sgattrib/colorMaskTransition.I b/panda/src/sgattrib/colorMaskTransition.I new file mode 100644 index 0000000000..e3eafbc33f --- /dev/null +++ b/panda/src/sgattrib/colorMaskTransition.I @@ -0,0 +1,50 @@ +// Filename: colorMaskTransition.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorMaskTransition:: +ColorMaskTransition(int mask) : + _value(mask) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskTransition::all_on +// Access: Public, Static +// Description: This named constructor returns a ColorMaskTransition +// whose bits are all on: that transition that writes to +// all the color planes at once (the normal case). +//////////////////////////////////////////////////////////////////// +INLINE ColorMaskTransition ColorMaskTransition:: +all_on() { + ColorMaskTransition t(0); + t._value = ColorMaskProperty::all_on(); + return t; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskTransition::set_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ColorMaskTransition:: +set_mask(int mask) { + _value.set_mask(mask); + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskTransition::get_mask +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int ColorMaskTransition:: +get_mask() const { + return _value.get_mask(); +} diff --git a/panda/src/sgattrib/colorMaskTransition.cxx b/panda/src/sgattrib/colorMaskTransition.cxx new file mode 100644 index 0000000000..35d38dec40 --- /dev/null +++ b/panda/src/sgattrib/colorMaskTransition.cxx @@ -0,0 +1,78 @@ +// Filename: colorMaskTransition.cxx +// Created by: mike (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "colorMaskTransition.h" +#include "colorMaskAttribute.h" + +#include + +TypeHandle ColorMaskTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated ColorMaskTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *ColorMaskTransition:: +make_copy() const { + return new ColorMaskTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated ColorMaskAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ColorMaskTransition:: +make_attrib() const { + return new ColorMaskAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskTransition::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the other transition pointer, +// which is guaranteed to be another ColorMaskTransition. +//////////////////////////////////////////////////////////////////// +void ColorMaskTransition:: +set_value_from(const OnTransition *other) { + const ColorMaskTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskTransition::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int ColorMaskTransition:: +compare_values(const OnTransition *other) const { + const ColorMaskTransition *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void ColorMaskTransition:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMaskTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void ColorMaskTransition:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/colorMaskTransition.h b/panda/src/sgattrib/colorMaskTransition.h new file mode 100644 index 0000000000..e6f2fdba10 --- /dev/null +++ b/panda/src/sgattrib/colorMaskTransition.h @@ -0,0 +1,65 @@ +// Filename: colorMaskTransition.h +// Created by: mike (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLORMASKTRANSITION_H +#define COLORMASKTRANSITION_H + +#include + +#include "colorMaskProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : ColorMaskTransition +// Description : This is scene graph colorMask (which is not to be +// confused with geometry colorMask). By setting a +// ColorMaskTransition on the scene graph, you can override +// the colorMask at the object level, but not at the +// individual primitive or vertex level. If a scene +// graph colorMask is set, it overrides the primitive colorMask; +// otherwise, the primitive colorMask shows through. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ColorMaskTransition : public OnTransition { +public: + INLINE ColorMaskTransition(int mask); + INLINE static ColorMaskTransition all_on(); + + INLINE void set_mask(int mask); + INLINE int get_mask() const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + ColorMaskProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnTransition::init_type(); + register_type(_type_handle, "ColorMaskTransition", + OnTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class ColorMaskAttribute; +}; + +#include "colorMaskTransition.I" + +#endif diff --git a/panda/src/sgattrib/colorMatrixAttribute.I b/panda/src/sgattrib/colorMatrixAttribute.I new file mode 100644 index 0000000000..b8d2d31ec6 --- /dev/null +++ b/panda/src/sgattrib/colorMatrixAttribute.I @@ -0,0 +1,13 @@ +// Filename: colorMatrixAttribute.I +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ColorMatrixAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorMatrixAttribute:: +ColorMatrixAttribute() { +} diff --git a/panda/src/sgattrib/colorMatrixAttribute.cxx b/panda/src/sgattrib/colorMatrixAttribute.cxx new file mode 100644 index 0000000000..d4604755ef --- /dev/null +++ b/panda/src/sgattrib/colorMatrixAttribute.cxx @@ -0,0 +1,57 @@ +// Filename: colorMatrixAttribute.cxx +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "colorMatrixAttribute.h" +#include "colorMatrixTransition.h" + +#include +#include + +TypeHandle ColorMatrixAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ColorMatrixAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle ColorMatrixAttribute:: +get_handle() const { + return ColorMatrixTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMatrixAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated ColorMatrixAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ColorMatrixAttribute:: +make_copy() const { + return new ColorMatrixAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMatrixAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated ColorMatrixAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ColorMatrixAttribute:: +make_initial() const { + return new ColorMatrixAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMatrixAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void ColorMatrixAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_color_transform(this); +} diff --git a/panda/src/sgattrib/colorMatrixAttribute.h b/panda/src/sgattrib/colorMatrixAttribute.h new file mode 100644 index 0000000000..7cb8d2a210 --- /dev/null +++ b/panda/src/sgattrib/colorMatrixAttribute.h @@ -0,0 +1,48 @@ +// Filename: colorMatrixAttribute.h +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLOR_MATRIX_ATTRIBUTE_H +#define COLOR_MATRIX_ATTRIBUTE_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : ColorMatrixAttribute +// Description : See ColorMatrixTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ColorMatrixAttribute : public LMatrix4fAttribute { +public: + INLINE ColorMatrixAttribute(); + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + LMatrix4fAttribute::init_type(); + register_type(_type_handle, "ColorMatrixAttribute", + LMatrix4fAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "colorMatrixAttribute.I" + +#endif diff --git a/panda/src/sgattrib/colorMatrixTransition.I b/panda/src/sgattrib/colorMatrixTransition.I new file mode 100644 index 0000000000..86571a9a0c --- /dev/null +++ b/panda/src/sgattrib/colorMatrixTransition.I @@ -0,0 +1,24 @@ +// Filename: colorMatrixTransition.I +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ColorMatrixTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorMatrixTransition:: +ColorMatrixTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMatrixTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorMatrixTransition:: +ColorMatrixTransition(const LMatrix4f &matrix) : + LMatrix4fTransition(matrix) +{ +} diff --git a/panda/src/sgattrib/colorMatrixTransition.cxx b/panda/src/sgattrib/colorMatrixTransition.cxx new file mode 100644 index 0000000000..bd070b2259 --- /dev/null +++ b/panda/src/sgattrib/colorMatrixTransition.cxx @@ -0,0 +1,76 @@ +// Filename: colorMatrixTransition.cxx +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "colorMatrixTransition.h" +#include "colorMatrixAttribute.h" + +#include +#include +#include +#include +#include + +TypeHandle ColorMatrixTransition::_type_handle; + +////////////////////////////////////////////////////////////////////s +// Function: ColorMatrixTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated ColorMatrixTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *ColorMatrixTransition:: +make_copy() const { + return new ColorMatrixTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMatrixTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated ColorMatrixAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ColorMatrixTransition:: +make_attrib() const { + return new ColorMatrixAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMatrixTransition::make_with_matrix +// Access: Protected, Virtual +// Description: Returns a new transition with the indicated matrix. +//////////////////////////////////////////////////////////////////// +MatrixTransition *ColorMatrixTransition:: +make_with_matrix(const LMatrix4f &matrix) const { + return new ColorMatrixTransition(matrix); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMatrixTransition::make_ColorMatrixTransition +// Access: Protected +// Description: Factory method to generate a ColorMatrixTransition object +//////////////////////////////////////////////////////////////////// +TypedWriteable* ColorMatrixTransition:: +make_ColorMatrixTransition(const FactoryParams ¶ms) +{ + ColorMatrixTransition *me = new ColorMatrixTransition; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMatrixTransition::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a ColorMatrixTransition object +//////////////////////////////////////////////////////////////////// +void ColorMatrixTransition:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_ColorMatrixTransition); +} diff --git a/panda/src/sgattrib/colorMatrixTransition.h b/panda/src/sgattrib/colorMatrixTransition.h new file mode 100644 index 0000000000..25516686e6 --- /dev/null +++ b/panda/src/sgattrib/colorMatrixTransition.h @@ -0,0 +1,59 @@ +// Filename: colorMatrixTransition.h +// Created by: jason (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLOR_MATRIX_TRANSITION_H +#define COLOR_MATRIX_TRANSITION_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : ColorMatrixTransition +// Description : This defines a transformation for color. I.E. to +// make something blue, or rotate the colors, or whatever +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ColorMatrixTransition : public LMatrix4fTransition { +public: + INLINE ColorMatrixTransition(); + INLINE ColorMatrixTransition(const LMatrix4f &matrix); + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + virtual MatrixTransition * + make_with_matrix(const LMatrix4f &matrix) const; + +public: + static void register_with_read_factory(void); + + static TypedWriteable *make_ColorMatrixTransition(const FactoryParams ¶ms); + +protected: + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + LMatrix4fTransition::init_type(); + register_type(_type_handle, "ColorMatrixTransition", + LMatrix4fTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class TransformAttribute; +}; + +#include "colorMatrixTransition.I" + +#endif diff --git a/panda/src/sgattrib/colorProperty.I b/panda/src/sgattrib/colorProperty.I new file mode 100644 index 0000000000..cf9213ccc3 --- /dev/null +++ b/panda/src/sgattrib/colorProperty.I @@ -0,0 +1,68 @@ +// Filename: colorProperty.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////// +// Function: ColorProperty::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorProperty:: +ColorProperty() : + _real(false) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorProperty::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorProperty:: +ColorProperty(const Colorf &color) : + _color(color), + _real(true) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorProperty::is_real +// Access: Public +// Description: Returns true if the color is a real color, false if +// it is the uncolor. +//////////////////////////////////////////////////////////////////// +INLINE bool ColorProperty:: +is_real() const { + return _real; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorProperty::get_color +// Access: Public +// Description: Returns the four-component color. It is only valid +// to call this method if is_real() returns true. +//////////////////////////////////////////////////////////////////// +INLINE Colorf ColorProperty:: +get_color() const { + nassertr(is_real(), Colorf(0.0, 0.0, 0.0, 0.0)); + return _color; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorProperty::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int ColorProperty:: +compare_to(const ColorProperty &other) const { + if (_real != other._real) { + return (int)_real - (int)other._real; + } + if (_real) { + return _color.compare_to(other._color); + } + return 0; +} diff --git a/panda/src/sgattrib/colorProperty.cxx b/panda/src/sgattrib/colorProperty.cxx new file mode 100644 index 0000000000..76d24ae6f9 --- /dev/null +++ b/panda/src/sgattrib/colorProperty.cxx @@ -0,0 +1,20 @@ +// Filename: colorProperty.cxx +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "colorProperty.h" + +//////////////////////////////////////////////////////////////////// +// Function: ColorProperty::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ColorProperty:: +output(ostream &out) const { + if (is_real()) { + out << "rgba(" << get_color() << ")"; + } else { + out << "uncolor"; + } +} diff --git a/panda/src/sgattrib/colorProperty.h b/panda/src/sgattrib/colorProperty.h new file mode 100644 index 0000000000..462949201d --- /dev/null +++ b/panda/src/sgattrib/colorProperty.h @@ -0,0 +1,48 @@ +// Filename: colorProperty.h +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLORPROPERTY_H +#define COLORPROPERTY_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : ColorProperty +// Description : This class defines the scene graph color property +// that we may set on a transition. It makes a +// distinction between a real color and the uncolor. +// When a "real" color is set, it specifies the RGBA +// that will be sent in lieu of vertex color. When a +// "non-real" color (i.e. the uncolor) is set, it +// disables the sending of any color information at all. +// When no color is set (e.g. the transition is "off" or +// absent), the vertex color is sent. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ColorProperty { +public: + INLINE ColorProperty(); + INLINE ColorProperty(const Colorf &color); + + INLINE bool is_real() const; + INLINE Colorf get_color() const; + + INLINE int compare_to(const ColorProperty &other) const; + void output(ostream &out) const; + +private: + Colorf _color; + bool _real; +}; + +INLINE ostream &operator << (ostream &out, const ColorProperty &prop) { + prop.output(out); + return out; +} + +#include "colorProperty.I" + +#endif diff --git a/panda/src/sgattrib/colorTransition.I b/panda/src/sgattrib/colorTransition.I new file mode 100644 index 0000000000..930e467202 --- /dev/null +++ b/panda/src/sgattrib/colorTransition.I @@ -0,0 +1,126 @@ +// Filename: colorTransition.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ColorTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorTransition:: +ColorTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorTransition:: +ColorTransition(const Colorf &color) : + OnOffTransition(TD_on), + _value(color) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ColorTransition:: +ColorTransition(float r, float g, float b, float a) : + OnOffTransition(TD_on), + _value(Colorf(r, g, b, a)) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorTransition::uncolor +// Access: Public +// Description: This named constructor returns a ColorTransition that +// turns on the "uncolor", i.e. turns off all sending of +// color commands down the pipe. +//////////////////////////////////////////////////////////////////// +INLINE ColorTransition ColorTransition:: +uncolor() { + ColorTransition result; + result.set_uncolor(); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorTransition::off +// Access: Public +// Description: This named constructor returns a ColorTransition that +// turns off scene graph color, thus allowing the +// geometry's natural polygon or vertex color to show +// through. +//////////////////////////////////////////////////////////////////// +INLINE ColorTransition ColorTransition:: +off() { + ColorTransition result; + result.set_off(); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorTransition::set_on +// Access: Public +// Description: Changes the ColorTransition to turn on the particular +// color. +//////////////////////////////////////////////////////////////////// +INLINE void ColorTransition:: +set_on(const Colorf &color) { + _value = ColorProperty(color); + OnOffTransition::set_on(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorTransition::set_on +// Access: Public +// Description: Changes the ColorTransition to turn on the particular +// color. +//////////////////////////////////////////////////////////////////// +INLINE void ColorTransition:: +set_on(float r, float g, float b, float a) { + set_on(Colorf(r, g, b, a)); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorTransition::set_uncolor +// Access: Public +// Description: Changes the ColorTransition to turn on uncolor. +//////////////////////////////////////////////////////////////////// +INLINE void ColorTransition:: +set_uncolor() { + _value = ColorProperty(); + OnOffTransition::set_on(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorTransition::is_real +// Access: Public +// Description: Returns true if the ColorTransition represents a real +// color, false if it represents the uncolor. It is +// only valid to call this if is_on() has returned true. +//////////////////////////////////////////////////////////////////// +INLINE bool ColorTransition:: +is_real() const { + nassertr(is_on(), false); + return _value.is_real(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorTransition::get_color +// Access: Public +// Description: Returns the color that the ColorTransition +// represents. It is only valid to call this if is_on() +// and is_real() have both returned true. +//////////////////////////////////////////////////////////////////// +INLINE Colorf ColorTransition:: +get_color() const { + nassertr(is_on() && is_real(), Colorf(0.0, 0.0, 0.0, 0.0)); + return _value.get_color(); +} diff --git a/panda/src/sgattrib/colorTransition.cxx b/panda/src/sgattrib/colorTransition.cxx new file mode 100644 index 0000000000..1ba4298ffc --- /dev/null +++ b/panda/src/sgattrib/colorTransition.cxx @@ -0,0 +1,80 @@ +// Filename: colorTransition.cxx +// Created by: drose (28Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "colorTransition.h" +#include "colorAttribute.h" + +#include + +TypeHandle ColorTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ColorTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated ColorTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *ColorTransition:: +make_copy() const { + return new ColorTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated ColorAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ColorTransition:: +make_attrib() const { + return new ColorAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorTransition::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the other transition pointer, +// which is guaranteed to be another ColorTransition. +//////////////////////////////////////////////////////////////////// +void ColorTransition:: +set_value_from(const OnOffTransition *other) { + const ColorTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorTransition::compare_values +// Access: Protected, Virtual +// Description: Returns true if the two transitions have the same +// value. It is guaranteed that the other transition is +// another ColorTransition, and that both are "on". +//////////////////////////////////////////////////////////////////// +int ColorTransition:: +compare_values(const OnOffTransition *other) const { + const ColorTransition *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void ColorTransition:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void ColorTransition:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/colorTransition.h b/panda/src/sgattrib/colorTransition.h new file mode 100644 index 0000000000..17865c7220 --- /dev/null +++ b/panda/src/sgattrib/colorTransition.h @@ -0,0 +1,72 @@ +// Filename: colorTransition.h +// Created by: drose (18Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef COLORTRANSITION_H +#define COLORTRANSITION_H + +#include + +#include "colorProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : ColorTransition +// Description : This is scene graph color (which is not to be +// confused with geometry color). By setting a +// ColorTransition on the scene graph, you can override +// the color at the object level, but not at the +// individual primitive or vertex level. If a scene +// graph color is set, it overrides the primitive color; +// otherwise, the primitive color shows through. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ColorTransition : public OnOffTransition { +public: + INLINE ColorTransition(); + INLINE ColorTransition(const Colorf &color); + INLINE ColorTransition(float r, float g, float b, float a); + INLINE static ColorTransition uncolor(); + INLINE static ColorTransition off(); + + INLINE void set_on(const Colorf &color); + INLINE void set_on(float r, float g, float b, float a); + INLINE void set_uncolor(); + + INLINE bool is_real() const; + INLINE Colorf get_color() const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + virtual void set_value_from(const OnOffTransition *other); + virtual int compare_values(const OnOffTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + ColorProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnOffTransition::init_type(); + register_type(_type_handle, "ColorTransition", + OnOffTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class ColorAttribute; +}; + +#include "colorTransition.I" + +#endif diff --git a/panda/src/sgattrib/config_sgattrib.cxx b/panda/src/sgattrib/config_sgattrib.cxx new file mode 100644 index 0000000000..beb2b50a8d --- /dev/null +++ b/panda/src/sgattrib/config_sgattrib.cxx @@ -0,0 +1,143 @@ +// Filename: config_sgattrib.cxx +// Created by: drose (10Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_sgattrib.h" +#include "renderRelation.h" +#include "colorTransition.h" +#include "colorAttribute.h" +#include "textureTransition.h" +#include "textureAttribute.h" +#include "decalTransition.h" +#include "decalAttribute.h" +#include "depthTestTransition.h" +#include "depthTestAttribute.h" +#include "depthWriteTransition.h" +#include "depthWriteAttribute.h" +#include "colorBlendTransition.h" +#include "colorBlendAttribute.h" +#include "renderModeTransition.h" +#include "renderModeAttribute.h" +#include "materialTransition.h" +#include "materialAttribute.h" +#include "texGenTransition.h" +#include "texGenAttribute.h" +#include "cullFaceTransition.h" +#include "cullFaceAttribute.h" +#include "colorMaskTransition.h" +#include "colorMaskAttribute.h" +#include "stencilTransition.h" +#include "stencilAttribute.h" +#include "textureApplyTransition.h" +#include "textureApplyAttribute.h" +#include "clipPlaneTransition.h" +#include "clipPlaneAttribute.h" +#include "transparencyTransition.h" +#include "transparencyAttribute.h" +#include "fogTransition.h" +#include "fogAttribute.h" +#include "linesmoothTransition.h" +#include "linesmoothAttribute.h" +#include "transformTransition.h" +#include "transformAttribute.h" +#include "texMatrixTransition.h" +#include "texMatrixAttribute.h" +#include "billboardTransition.h" +#include "showHideTransition.h" +#include "showHideAttribute.h" +#include "polygonOffsetTransition.h" +#include "polygonOffsetAttribute.h" +#include "pruneTransition.h" +#include "drawBoundsTransition.h" +#include "pointShapeTransition.h" +#include "pointShapeAttribute.h" +#include "colorMatrixAttribute.h" +#include "colorMatrixTransition.h" +#include "alphaTransformAttribute.h" +#include "alphaTransformTransition.h" + + +#include + +Configure(config_sgattrib); +NotifyCategoryDef(sgattrib, ""); + + +// MPG - we want to ensure that texture transitions are applied before +// texgen transitions, so the texture transition must be initialized +// first. + +ConfigureFn(config_sgattrib) { + RenderRelation::init_type(); + TextureTransition::init_type(); + TextureAttribute::init_type(); + TransformTransition::init_type(); + TransformAttribute::init_type(); + TexMatrixTransition::init_type(); + TexMatrixAttribute::init_type(); + DecalTransition::init_type(); + DecalAttribute::init_type(); + DepthTestTransition::init_type(); + DepthTestAttribute::init_type(); + DepthWriteTransition::init_type(); + DepthWriteAttribute::init_type(); + ColorBlendTransition::init_type(); + ColorBlendAttribute::init_type(); + RenderModeTransition::init_type(); + RenderModeAttribute::init_type(); + MaterialTransition::init_type(); + MaterialAttribute::init_type(); + TexGenTransition::init_type(); + TexGenAttribute::init_type(); + CullFaceTransition::init_type(); + CullFaceAttribute::init_type(); + ColorMaskTransition::init_type(); + ColorMaskAttribute::init_type(); + StencilTransition::init_type(); + StencilAttribute::init_type(); + TextureApplyTransition::init_type(); + TextureApplyAttribute::init_type(); + ClipPlaneTransition::init_type(); + ClipPlaneAttribute::init_type(); + TransparencyTransition::init_type(); + TransparencyAttribute::init_type(); + FogTransition::init_type(); + FogAttribute::init_type(); + LinesmoothTransition::init_type(); + LinesmoothAttribute::init_type(); + ShowHideTransition::init_type(); + ShowHideAttribute::init_type(); + PruneTransition::init_type(); + ColorTransition::init_type(); + ColorAttribute::init_type(); + BillboardTransition::init_type(); + DrawBoundsTransition::init_type(); + PointShapeTransition::init_type(); + PointShapeAttribute::init_type(); + PolygonOffsetTransition::init_type(); + PolygonOffsetAttribute::init_type(); + ColorMatrixTransition::init_type(); + ColorMatrixAttribute::init_type(); + AlphaTransformTransition::init_type(); + AlphaTransformAttribute::init_type(); + + // Register the RenderRelation class for the + // NodeRelation::create_typed_arc() function. + RenderRelation::register_with_factory(); + + //Registration of writeable object's creation + //functions with BamReader's factory + RenderRelation::register_with_read_factory(); + TransformTransition::register_with_read_factory(); + TextureTransition::register_with_read_factory(); + TextureApplyTransition::register_with_read_factory(); + TransparencyTransition::register_with_read_factory(); + CullFaceTransition::register_with_read_factory(); + DecalTransition::register_with_read_factory(); + PruneTransition::register_with_read_factory(); + BillboardTransition::register_with_read_factory(); + ColorMatrixTransition::register_with_read_factory(); +} + + diff --git a/panda/src/sgattrib/config_sgattrib.h b/panda/src/sgattrib/config_sgattrib.h new file mode 100644 index 0000000000..512e37ca4e --- /dev/null +++ b/panda/src/sgattrib/config_sgattrib.h @@ -0,0 +1,14 @@ +// Filename: config_sgattrib.h +// Created by: drose (10Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_SGATTRIB_H +#define CONFIG_SGATTRIB_H + +#include +#include + +NotifyCategoryDecl(sgattrib, EXPCL_PANDA, EXPTP_PANDA); + +#endif diff --git a/panda/src/sgattrib/cullFaceAttribute.I b/panda/src/sgattrib/cullFaceAttribute.I new file mode 100644 index 0000000000..50c75bf791 --- /dev/null +++ b/panda/src/sgattrib/cullFaceAttribute.I @@ -0,0 +1,35 @@ +// Filename: cullFaceAttribute.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullFaceAttribute:: +CullFaceAttribute() : + _value(CullFaceProperty::M_cull_none) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceAttribute::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CullFaceAttribute:: +set_mode(CullFaceProperty::Mode mode) { + _value.set_mode(mode); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceAttribute::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullFaceProperty::Mode CullFaceAttribute:: +get_mode() const { + return _value.get_mode(); +} diff --git a/panda/src/sgattrib/cullFaceAttribute.cxx b/panda/src/sgattrib/cullFaceAttribute.cxx new file mode 100644 index 0000000000..471ba34012 --- /dev/null +++ b/panda/src/sgattrib/cullFaceAttribute.cxx @@ -0,0 +1,104 @@ +// Filename: cullFaceAttribute.cxx +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "cullFaceAttribute.h" +#include "cullFaceTransition.h" + +#include +#include + +TypeHandle CullFaceAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle CullFaceAttribute:: +get_handle() const { + return CullFaceTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated CullFaceAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *CullFaceAttribute:: +make_copy() const { + return new CullFaceAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated CullFaceAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *CullFaceAttribute:: +make_initial() const { + return new CullFaceAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void CullFaceAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_cull_face(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceAttribute::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the indicated transition +// pointer, which is guaranteed to be of type +// CullFaceTransition. +//////////////////////////////////////////////////////////////////// +void CullFaceAttribute:: +set_value_from(const OnTransition *other) { + const CullFaceTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceAttribute::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int CullFaceAttribute:: +compare_values(const OnAttribute *other) const { + const CullFaceAttribute *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void CullFaceAttribute:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void CullFaceAttribute:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/cullFaceAttribute.h b/panda/src/sgattrib/cullFaceAttribute.h new file mode 100644 index 0000000000..e60b042155 --- /dev/null +++ b/panda/src/sgattrib/cullFaceAttribute.h @@ -0,0 +1,60 @@ +// Filename: cullFaceAttribute.h +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CULLFACEATTRIBUTE_H +#define CULLFACEATTRIBUTE_H + +#include + +#include "cullFaceProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : CullFaceAttribute +// Description : See CullFaceTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CullFaceAttribute : public OnAttribute { +public: + INLINE CullFaceAttribute(); + + INLINE void set_mode(CullFaceProperty::Mode); + INLINE CullFaceProperty::Mode get_mode() const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + CullFaceProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnAttribute::init_type(); + register_type(_type_handle, "CullFaceAttribute", + OnAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "cullFaceAttribute.I" + +#endif diff --git a/panda/src/sgattrib/cullFaceProperty.I b/panda/src/sgattrib/cullFaceProperty.I new file mode 100644 index 0000000000..d9082e8166 --- /dev/null +++ b/panda/src/sgattrib/cullFaceProperty.I @@ -0,0 +1,60 @@ +// Filename: cullFaceProperty.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceProperty::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullFaceProperty:: +CullFaceProperty(Mode mode) : + _mode(mode) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceProperty::Constructor +// Access: Public +// Description: This should only be called in the Protected +// constructor of CullFaceTransition. It is made +// public, only for that to access, and is necessary +// for creating an object in make_CullFaceTransition +// before any information has been read +//////////////////////////////////////////////////////////////////// +INLINE CullFaceProperty:: +CullFaceProperty(void) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceProperty::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CullFaceProperty:: +set_mode(CullFaceProperty::Mode mode) { + _mode = mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceProperty::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullFaceProperty::Mode CullFaceProperty:: +get_mode() const { + return _mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceProperty::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int CullFaceProperty:: +compare_to(const CullFaceProperty &other) const { + return (int)_mode - (int)other._mode; +} + diff --git a/panda/src/sgattrib/cullFaceProperty.cxx b/panda/src/sgattrib/cullFaceProperty.cxx new file mode 100644 index 0000000000..3213b43966 --- /dev/null +++ b/panda/src/sgattrib/cullFaceProperty.cxx @@ -0,0 +1,61 @@ +// Filename: cullFaceProperty.cxx +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "cullFaceProperty.h" +#include +#include + +ostream & +operator << (ostream &out, CullFaceProperty::Mode mode) { + switch (mode) { + case CullFaceProperty::M_cull_none: + return out << "cull_none"; + + case CullFaceProperty::M_cull_clockwise: + return out << "cull_clockwise"; + + case CullFaceProperty::M_cull_counter_clockwise: + return out << "cull_counter_clockwise"; + + case CullFaceProperty::M_cull_all: + return out << "cull_all"; + } + + return out << "**invalid**(" << (int)mode << ")"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceProperty::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CullFaceProperty:: +output(ostream &out) const { + out << _mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceProperty::write_datagram +// Access: Public +// Description: Function to write the important information in +// this object to a Datagram +//////////////////////////////////////////////////////////////////// +void CullFaceProperty:: +write_datagram(Datagram &destination) +{ + destination.add_uint8(_mode); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceProperty::read_datagram +// Access: Public +// Description: Function to write the important information into +// this object out of a Datagram +//////////////////////////////////////////////////////////////////// +void CullFaceProperty:: +read_datagram(DatagramIterator &source) +{ + _mode = (enum Mode) source.get_uint8(); +} diff --git a/panda/src/sgattrib/cullFaceProperty.h b/panda/src/sgattrib/cullFaceProperty.h new file mode 100644 index 0000000000..ff950ba8b3 --- /dev/null +++ b/panda/src/sgattrib/cullFaceProperty.h @@ -0,0 +1,56 @@ +// Filename: cullFaceProperty.h +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CULLFACEPROPERTY_H +#define CULLFACEPROPERTY_H + +#include + +class Datagram; +class DatagramIterator; + +//////////////////////////////////////////////////////////////////// +// Class : CullFaceProperty +// Description : This defines the ways we can cull faces according to +// their vertex ordering (and hence orientation relative +// to the camera). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CullFaceProperty { +public: + enum Mode { + M_cull_none, // Cull no polygons + M_cull_clockwise, // Cull clockwise-oriented polygons + M_cull_counter_clockwise, // Cull counter-clockwise-oriented polygons + M_cull_all, // Cull all polygons (other primitives are still drawn) + }; + +public: + INLINE CullFaceProperty(Mode mode); + INLINE CullFaceProperty(void); + + INLINE void set_mode(Mode mode); + INLINE Mode get_mode() const; + + INLINE int compare_to(const CullFaceProperty &other) const; + void output(ostream &out) const; + +public: + void write_datagram(Datagram &destination); + void read_datagram(DatagramIterator &source); + +private: + Mode _mode; +}; + +ostream &operator << (ostream &out, CullFaceProperty::Mode mode); + +INLINE ostream &operator << (ostream &out, const CullFaceProperty &prop) { + prop.output(out); + return out; +} + +#include "cullFaceProperty.I" + +#endif diff --git a/panda/src/sgattrib/cullFaceTransition.I b/panda/src/sgattrib/cullFaceTransition.I new file mode 100644 index 0000000000..49fb9dfbee --- /dev/null +++ b/panda/src/sgattrib/cullFaceTransition.I @@ -0,0 +1,46 @@ +// Filename: cullFaceTransition.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullFaceTransition:: +CullFaceTransition(CullFaceProperty::Mode mode) : + _value(mode) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullFaceTransition:: +CullFaceTransition(void) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceTransition::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void CullFaceTransition:: +set_mode(CullFaceProperty::Mode mode) { + _value.set_mode(mode); + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceTransition::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CullFaceProperty::Mode CullFaceTransition:: +get_mode() const { + return _value.get_mode(); +} diff --git a/panda/src/sgattrib/cullFaceTransition.cxx b/panda/src/sgattrib/cullFaceTransition.cxx new file mode 100644 index 0000000000..6e80c6b93b --- /dev/null +++ b/panda/src/sgattrib/cullFaceTransition.cxx @@ -0,0 +1,145 @@ +// Filename: cullFaceTransition.cxx +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "cullFaceTransition.h" +#include "cullFaceAttribute.h" + +#include +#include +#include +#include +#include + +TypeHandle CullFaceTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated CullFaceTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *CullFaceTransition:: +make_copy() const { + return new CullFaceTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated CullFaceAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *CullFaceTransition:: +make_attrib() const { + return new CullFaceAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceTransition::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the other transition pointer, +// which is guaranteed to be another CullFaceTransition. +//////////////////////////////////////////////////////////////////// +void CullFaceTransition:: +set_value_from(const OnTransition *other) { + const CullFaceTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceTransition::compare_values +// Access: Protected, Virtual +// Description: Returns true if the two transitions have the same +// value. It is guaranteed that the other transition is +// another CullFaceTransition, and that both are "on". +//////////////////////////////////////////////////////////////////// +int CullFaceTransition:: +compare_values(const OnTransition *other) const { + const CullFaceTransition *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void CullFaceTransition:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void CullFaceTransition:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceTransition::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void CullFaceTransition:: +write_datagram(BamWriter *manager, Datagram &me) +{ + OnTransition::write_datagram(manager, me); + _value.write_datagram(me); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceTransition::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void CullFaceTransition:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + OnTransition::fillin(scan, manager); + _value.read_datagram(scan); +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceTransition::make_CullFaceTransition +// Access: Protected +// Description: Factory method to generate a CullFaceTransition object +//////////////////////////////////////////////////////////////////// +TypedWriteable* CullFaceTransition:: +make_CullFaceTransition(const FactoryParams ¶ms) +{ + CullFaceTransition *me = new CullFaceTransition; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: CullFaceTransition::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a CullFaceTransition object +//////////////////////////////////////////////////////////////////// +void CullFaceTransition:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_CullFaceTransition); +} + + + diff --git a/panda/src/sgattrib/cullFaceTransition.h b/panda/src/sgattrib/cullFaceTransition.h new file mode 100644 index 0000000000..c21bce7052 --- /dev/null +++ b/panda/src/sgattrib/cullFaceTransition.h @@ -0,0 +1,71 @@ +// Filename: cullFaceTransition.h +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CULLFACETRANSITION_H +#define CULLFACETRANSITION_H + +#include + +#include "cullFaceProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : CullFaceTransition +// Description : This controls how polygons are culled according to +// the ordering of their vertices after projection (and, +// hence, according to their facing relative to the +// camera). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA CullFaceTransition : public OnTransition { +public: + INLINE CullFaceTransition(CullFaceProperty::Mode mode); + + INLINE void set_mode(CullFaceProperty::Mode mode); + INLINE CullFaceProperty::Mode get_mode() const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + INLINE CullFaceTransition(void); + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + CullFaceProperty _value; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + + static TypedWriteable *make_CullFaceTransition(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnTransition::init_type(); + register_type(_type_handle, "CullFaceTransition", + OnTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class CullFaceAttribute; +}; + +#include "cullFaceTransition.I" + +#endif diff --git a/panda/src/sgattrib/decalAttribute.I b/panda/src/sgattrib/decalAttribute.I new file mode 100644 index 0000000000..1737845251 --- /dev/null +++ b/panda/src/sgattrib/decalAttribute.I @@ -0,0 +1,13 @@ +// Filename: decalAttribute.I +// Created by: drose (17Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: DecalAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DecalAttribute:: +DecalAttribute() { +} diff --git a/panda/src/sgattrib/decalAttribute.cxx b/panda/src/sgattrib/decalAttribute.cxx new file mode 100644 index 0000000000..6b55d6c689 --- /dev/null +++ b/panda/src/sgattrib/decalAttribute.cxx @@ -0,0 +1,43 @@ +// Filename: decalAttribute.cxx +// Created by: drose (17Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "decalAttribute.h" +#include "decalTransition.h" + +#include + +TypeHandle DecalAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: DecalAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle DecalAttribute:: +get_handle() const { + return DecalTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DecalAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated DecalAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *DecalAttribute:: +make_copy() const { + return new DecalAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: DecalAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated DecalAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *DecalAttribute:: +make_initial() const { + return new DecalAttribute; +} diff --git a/panda/src/sgattrib/decalAttribute.h b/panda/src/sgattrib/decalAttribute.h new file mode 100644 index 0000000000..0471b45174 --- /dev/null +++ b/panda/src/sgattrib/decalAttribute.h @@ -0,0 +1,45 @@ +// Filename: decalAttribute.h +// Created by: drose (17Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DECALATTRIBUTE_H +#define DECALATTRIBUTE_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : DecalAttribute +// Description : See DecalTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DecalAttribute : public OnOffAttribute { +public: + INLINE DecalAttribute(); + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnOffAttribute::init_type(); + register_type(_type_handle, "DecalAttribute", + OnOffAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "decalAttribute.I" + +#endif diff --git a/panda/src/sgattrib/decalTransition.I b/panda/src/sgattrib/decalTransition.I new file mode 100644 index 0000000000..9e8a338302 --- /dev/null +++ b/panda/src/sgattrib/decalTransition.I @@ -0,0 +1,26 @@ +// Filename: decalTransition.I +// Created by: drose (17Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: DecalTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DecalTransition:: +DecalTransition() { + set_on(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DecalTransition::off +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DecalTransition DecalTransition:: +off() { + DecalTransition result; + result.set_off(); + return result; +} diff --git a/panda/src/sgattrib/decalTransition.cxx b/panda/src/sgattrib/decalTransition.cxx new file mode 100644 index 0000000000..0a73be3416 --- /dev/null +++ b/panda/src/sgattrib/decalTransition.cxx @@ -0,0 +1,77 @@ +// Filename: decalTransition.cxx +// Created by: drose (17Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "decalTransition.h" +#include "decalAttribute.h" +#include +#include +#include +#include + +TypeHandle DecalTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: DecalTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated DecalTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *DecalTransition:: +make_copy() const { + return new DecalTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: DecalTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated DecalAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *DecalTransition:: +make_attrib() const { + return new DecalAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: DecalTransition::has_sub_render +// Access: Public, Virtual +// Description: DecalTransition doesn't actually have a sub_render() +// function, but it might as well, because it's treated +// as a special case. We set this function to return +// true so GraphReducer will behave correctly. +//////////////////////////////////////////////////////////////////// +bool DecalTransition:: +has_sub_render() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: DecalTransition::make_DecalTransition +// Access: Protected +// Description: Factory method to generate a DecalTransition object +//////////////////////////////////////////////////////////////////// +TypedWriteable* DecalTransition:: +make_DecalTransition(const FactoryParams ¶ms) +{ + DecalTransition *me = new DecalTransition; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: DecalTransition::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a DecalTransition object +//////////////////////////////////////////////////////////////////// +void DecalTransition:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_DecalTransition); +} diff --git a/panda/src/sgattrib/decalTransition.h b/panda/src/sgattrib/decalTransition.h new file mode 100644 index 0000000000..16b3705c5a --- /dev/null +++ b/panda/src/sgattrib/decalTransition.h @@ -0,0 +1,65 @@ +// Filename: decalTransition.h +// Created by: drose (17Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DECALTRANSITION_H +#define DECALTRANSITION_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : DecalTransition +// Description : When this transition is in effect, the first GeomNode +// encountered is rendered in a special mode, such that +// all of its *children* (indeed, the whole subtree +// beginning at the top GeomNode under the transition) +// are rendered as decals of the GeomNode. +// +// This means that the decal geometry (i.e. all geometry +// in GeomNodes parented somewhere below the top +// GeomNode) is assumed to be coplanar with the base +// geometry, and will be rendered so that no z-fighting +// will occur between it and the base geometry. +// +// The render behavior is undefined if the decal +// geometry is *not* coplanar with the base geometry. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DecalTransition : public OnOffTransition { +public: + INLINE DecalTransition(); + INLINE static DecalTransition off(); + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + + virtual bool has_sub_render() const; + +public: + static void register_with_read_factory(void); + static TypedWriteable *make_DecalTransition(const FactoryParams ¶ms); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnOffTransition::init_type(); + register_type(_type_handle, "DecalTransition", + OnOffTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class DecalAttribute; +}; + +#include "decalTransition.I" + +#endif diff --git a/panda/src/sgattrib/depthTestAttribute.I b/panda/src/sgattrib/depthTestAttribute.I new file mode 100644 index 0000000000..4d5746c729 --- /dev/null +++ b/panda/src/sgattrib/depthTestAttribute.I @@ -0,0 +1,35 @@ +// Filename: depthTestAttribute.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DepthTestAttribute:: +DepthTestAttribute() : + _value(DepthTestProperty::M_less) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestAttribute::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DepthTestAttribute:: +set_mode(DepthTestProperty::Mode mode) { + _value.set_mode(mode); +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestAttribute::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DepthTestProperty::Mode DepthTestAttribute:: +get_mode() const { + return _value.get_mode(); +} diff --git a/panda/src/sgattrib/depthTestAttribute.cxx b/panda/src/sgattrib/depthTestAttribute.cxx new file mode 100644 index 0000000000..e35a2aafac --- /dev/null +++ b/panda/src/sgattrib/depthTestAttribute.cxx @@ -0,0 +1,104 @@ +// Filename: depthTestAttribute.cxx +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "depthTestAttribute.h" +#include "depthTestTransition.h" + +#include +#include + +TypeHandle DepthTestAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle DepthTestAttribute:: +get_handle() const { + return DepthTestTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated DepthTestAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *DepthTestAttribute:: +make_copy() const { + return new DepthTestAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated DepthTestAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *DepthTestAttribute:: +make_initial() const { + return new DepthTestAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void DepthTestAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_depth_test(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestAttribute::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the indicated transition +// pointer, which is guaranteed to be of type +// DepthTestTransition. +//////////////////////////////////////////////////////////////////// +void DepthTestAttribute:: +set_value_from(const OnTransition *other) { + const DepthTestTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestAttribute::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int DepthTestAttribute:: +compare_values(const OnAttribute *other) const { + const DepthTestAttribute *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void DepthTestAttribute:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void DepthTestAttribute:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/depthTestAttribute.h b/panda/src/sgattrib/depthTestAttribute.h new file mode 100644 index 0000000000..e38bf7f6a6 --- /dev/null +++ b/panda/src/sgattrib/depthTestAttribute.h @@ -0,0 +1,60 @@ +// Filename: depthTestAttribute.h +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DEPTHTESTATTRIBUTE_H +#define DEPTHTESTATTRIBUTE_H + +#include + +#include "depthTestProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : DepthTestAttribute +// Description : See DepthTestTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DepthTestAttribute : public OnAttribute { +public: + INLINE DepthTestAttribute(); + + INLINE void set_mode(DepthTestProperty::Mode); + INLINE DepthTestProperty::Mode get_mode() const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + DepthTestProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnAttribute::init_type(); + register_type(_type_handle, "DepthTestAttribute", + OnAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "depthTestAttribute.I" + +#endif diff --git a/panda/src/sgattrib/depthTestProperty.I b/panda/src/sgattrib/depthTestProperty.I new file mode 100644 index 0000000000..77d8f318bb --- /dev/null +++ b/panda/src/sgattrib/depthTestProperty.I @@ -0,0 +1,45 @@ +// Filename: depthTestProperty.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestProperty::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DepthTestProperty:: +DepthTestProperty(DepthTestProperty::Mode mode) : + _mode(mode) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestProperty::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DepthTestProperty:: +set_mode(DepthTestProperty::Mode mode) { + _mode = mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestProperty::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DepthTestProperty::Mode DepthTestProperty:: +get_mode() const { + return _mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestProperty::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int DepthTestProperty:: +compare_to(const DepthTestProperty &other) const { + return (int)_mode - (int)other._mode; +} diff --git a/panda/src/sgattrib/depthTestProperty.cxx b/panda/src/sgattrib/depthTestProperty.cxx new file mode 100644 index 0000000000..fd69a32374 --- /dev/null +++ b/panda/src/sgattrib/depthTestProperty.cxx @@ -0,0 +1,50 @@ +// Filename: depthTestProperty.cxx +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "depthTestProperty.h" + +ostream & +operator << (ostream &out, DepthTestProperty::Mode mode) { + switch (mode) { + case DepthTestProperty::M_none: + return out << "none"; + + case DepthTestProperty::M_never: + return out << "never"; + + case DepthTestProperty::M_less: + return out << "less"; + + case DepthTestProperty::M_equal: + return out << "equal"; + + case DepthTestProperty::M_less_equal: + return out << "less_equal"; + + case DepthTestProperty::M_greater: + return out << "greater"; + + case DepthTestProperty::M_not_equal: + return out << "not_equal"; + + case DepthTestProperty::M_greater_equal: + return out << "greater_equal"; + + case DepthTestProperty::M_always: + return out << "always"; + } + + return out << "**invalid**(" << (int)mode << ")"; +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestProperty::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DepthTestProperty:: +output(ostream &out) const { + out << _mode; +} diff --git a/panda/src/sgattrib/depthTestProperty.h b/panda/src/sgattrib/depthTestProperty.h new file mode 100644 index 0000000000..ddc74a9a9d --- /dev/null +++ b/panda/src/sgattrib/depthTestProperty.h @@ -0,0 +1,53 @@ +// Filename: depthTestProperty.h +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DEPTHTESTPROPERTY_H +#define DEPTHTESTPROPERTY_H + +#include + +//////////////////////////////////////////////////////////////////// +// Class : DepthTestProperty +// Description : This defines the types of depth testing we can +// enable. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DepthTestProperty { +public: + enum Mode { + M_none, // No depth test; may still write to depth buffer. + M_never, // Never draw. + M_less, // incoming < stored + M_equal, // incoming == stored + M_less_equal, // incoming <= stored + M_greater, // incoming > stored + M_not_equal, // incoming != stored + M_greater_equal, // incoming >= stored + M_always // Always draw. Same effect as none, more expensive. + }; + +public: + INLINE DepthTestProperty(Mode mode); + + INLINE void set_mode(Mode mode); + INLINE Mode get_mode() const; + + INLINE int compare_to(const DepthTestProperty &other) const; + + void output(ostream &out) const; + +private: + Mode _mode; +}; + +ostream &operator << (ostream &out, DepthTestProperty::Mode mode); + +INLINE ostream &operator << (ostream &out, const DepthTestProperty &prop) { + prop.output(out); + return out; +} + +#include "depthTestProperty.I" + +#endif diff --git a/panda/src/sgattrib/depthTestTransition.I b/panda/src/sgattrib/depthTestTransition.I new file mode 100644 index 0000000000..c16f8ebb08 --- /dev/null +++ b/panda/src/sgattrib/depthTestTransition.I @@ -0,0 +1,36 @@ +// Filename: depthTestTransition.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DepthTestTransition:: +DepthTestTransition(DepthTestProperty::Mode mode) : + _value(mode) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestTransition::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void DepthTestTransition:: +set_mode(DepthTestProperty::Mode mode) { + _value.set_mode(mode); + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestTransition::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DepthTestProperty::Mode DepthTestTransition:: +get_mode() const { + return _value.get_mode(); +} diff --git a/panda/src/sgattrib/depthTestTransition.cxx b/panda/src/sgattrib/depthTestTransition.cxx new file mode 100644 index 0000000000..4d319d4ab6 --- /dev/null +++ b/panda/src/sgattrib/depthTestTransition.cxx @@ -0,0 +1,78 @@ +// Filename: depthTestTransition.cxx +// Created by: mike (28Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "depthTestTransition.h" +#include "depthTestAttribute.h" + +#include + +TypeHandle DepthTestTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated DepthTestTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *DepthTestTransition:: +make_copy() const { + return new DepthTestTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated DepthTestAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *DepthTestTransition:: +make_attrib() const { + return new DepthTestAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestTransition::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the other transition pointer, +// which is guaranteed to be another DepthTestTransition. +//////////////////////////////////////////////////////////////////// +void DepthTestTransition:: +set_value_from(const OnTransition *other) { + const DepthTestTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestTransition::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int DepthTestTransition:: +compare_values(const OnTransition *other) const { + const DepthTestTransition *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void DepthTestTransition:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthTestTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void DepthTestTransition:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/depthTestTransition.h b/panda/src/sgattrib/depthTestTransition.h new file mode 100644 index 0000000000..923304d3db --- /dev/null +++ b/panda/src/sgattrib/depthTestTransition.h @@ -0,0 +1,64 @@ +// Filename: depthTestTransition.h +// Created by: drose (18Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DEPTHTESTTRANSITION_H +#define DEPTHTESTTRANSITION_H + +#include + +#include "depthTestProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : DepthTestTransition +// Description : This transition controls the nature of the test +// against the depth buffer. It does not affect whether +// the depth buffer will be written to or not; that is +// handled by a separate transition, +// DepthWriteTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DepthTestTransition : public OnTransition { +public: + INLINE DepthTestTransition(DepthTestProperty::Mode mode); + + INLINE void set_mode(DepthTestProperty::Mode mode); + INLINE DepthTestProperty::Mode get_mode() const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + DepthTestProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnTransition::init_type(); + register_type(_type_handle, "DepthTestTransition", + OnTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class DepthTestAttribute; +}; + +#include "depthTestTransition.I" + +#endif + + diff --git a/panda/src/sgattrib/depthWriteAttribute.I b/panda/src/sgattrib/depthWriteAttribute.I new file mode 100644 index 0000000000..56e52f5090 --- /dev/null +++ b/panda/src/sgattrib/depthWriteAttribute.I @@ -0,0 +1,15 @@ +// Filename: depthWriteAttribute.I +// Created by: drose (31Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: DepthWriteAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DepthWriteAttribute:: +DepthWriteAttribute() : OnOffAttribute(true) { + // The default value for DepthWriteAttribute is true: write to the + // depth buffer. +} diff --git a/panda/src/sgattrib/depthWriteAttribute.cxx b/panda/src/sgattrib/depthWriteAttribute.cxx new file mode 100644 index 0000000000..80bf3e8ec8 --- /dev/null +++ b/panda/src/sgattrib/depthWriteAttribute.cxx @@ -0,0 +1,56 @@ +// Filename: depthWriteAttribute.cxx +// Created by: drose (31Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "depthWriteAttribute.h" +#include "depthWriteTransition.h" + +#include + +TypeHandle DepthWriteAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: DepthWriteAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle DepthWriteAttribute:: +get_handle() const { + return DepthWriteTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthWriteAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated DepthWriteAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *DepthWriteAttribute:: +make_copy() const { + return new DepthWriteAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthWriteAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated DepthWriteAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *DepthWriteAttribute:: +make_initial() const { + return new DepthWriteAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthWriteAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void DepthWriteAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_depth_write(this); +} diff --git a/panda/src/sgattrib/depthWriteAttribute.h b/panda/src/sgattrib/depthWriteAttribute.h new file mode 100644 index 0000000000..1c699c263e --- /dev/null +++ b/panda/src/sgattrib/depthWriteAttribute.h @@ -0,0 +1,47 @@ +// Filename: depthWriteAttribute.h +// Created by: drose (31Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DEPTHWRITEATTRIBUTE_H +#define DEPTHWRITEATTRIBUTE_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : DepthWriteAttribute +// Description : See DepthWriteTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DepthWriteAttribute : public OnOffAttribute { +public: + INLINE DepthWriteAttribute(); + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnOffAttribute::init_type(); + register_type(_type_handle, "DepthWriteAttribute", + OnOffAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "depthWriteAttribute.I" + +#endif diff --git a/panda/src/sgattrib/depthWriteTransition.I b/panda/src/sgattrib/depthWriteTransition.I new file mode 100644 index 0000000000..30f66af90b --- /dev/null +++ b/panda/src/sgattrib/depthWriteTransition.I @@ -0,0 +1,26 @@ +// Filename: depthWriteTransition.I +// Created by: drose (31Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: DepthWriteTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DepthWriteTransition:: +DepthWriteTransition() { + set_on(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthWriteTransition::off +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DepthWriteTransition DepthWriteTransition:: +off() { + DepthWriteTransition result; + result.set_off(); + return result; +} diff --git a/panda/src/sgattrib/depthWriteTransition.cxx b/panda/src/sgattrib/depthWriteTransition.cxx new file mode 100644 index 0000000000..73096c5272 --- /dev/null +++ b/panda/src/sgattrib/depthWriteTransition.cxx @@ -0,0 +1,30 @@ +// Filename: depthWriteTransition.cxx +// Created by: drose (31Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "depthWriteTransition.h" +#include "depthWriteAttribute.h" + +TypeHandle DepthWriteTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: DepthWriteTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated DepthWriteTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *DepthWriteTransition:: +make_copy() const { + return new DepthWriteTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: DepthWriteTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated DepthWriteAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *DepthWriteTransition:: +make_attrib() const { + return new DepthWriteAttribute; +} diff --git a/panda/src/sgattrib/depthWriteTransition.h b/panda/src/sgattrib/depthWriteTransition.h new file mode 100644 index 0000000000..59f2a7e0c0 --- /dev/null +++ b/panda/src/sgattrib/depthWriteTransition.h @@ -0,0 +1,47 @@ +// Filename: depthWriteTransition.h +// Created by: drose (31Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DEPTHWRITETRANSITION_H +#define DEPTHWRITETRANSITION_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : DepthWriteTransition +// Description : This enables or disables the writing to the depth +// buffer. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DepthWriteTransition : public OnOffTransition { +public: + INLINE DepthWriteTransition(); + INLINE static DepthWriteTransition off(); + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnOffTransition::init_type(); + register_type(_type_handle, "DepthWriteTransition", + OnOffTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class DepthWriteAttribute; +}; + +#include "depthWriteTransition.I" + +#endif diff --git a/panda/src/sgattrib/drawBoundsTransition.I b/panda/src/sgattrib/drawBoundsTransition.I new file mode 100644 index 0000000000..979466367a --- /dev/null +++ b/panda/src/sgattrib/drawBoundsTransition.I @@ -0,0 +1,5 @@ +// Filename: drawBoundsTransition.I +// Created by: drose (26Jun00) +// +//////////////////////////////////////////////////////////////////// + diff --git a/panda/src/sgattrib/drawBoundsTransition.cxx b/panda/src/sgattrib/drawBoundsTransition.cxx new file mode 100644 index 0000000000..a3f1ad1233 --- /dev/null +++ b/panda/src/sgattrib/drawBoundsTransition.cxx @@ -0,0 +1,137 @@ +// Filename: drawBoundsTransition.cxx +// Created by: drose (26Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_sgattrib.h" +#include "drawBoundsTransition.h" +#include "renderModeAttribute.h" +#include "renderModeTransition.h" +#include "cullFaceAttribute.h" +#include "cullFaceTransition.h" +#include "colorAttribute.h" +#include "colorTransition.h" +#include "transformTransition.h" +#include "transformAttribute.h" + +#include +#include +#include +#include +#include +#include +#include + +TypeHandle DrawBoundsTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: DrawBoundsTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DrawBoundsTransition:: +DrawBoundsTransition() { + RenderModeAttribute *rma = new RenderModeAttribute; + rma->set_mode(RenderModeProperty::M_wireframe); + CullFaceAttribute *cfao = new CullFaceAttribute; + cfao->set_mode(CullFaceProperty::M_cull_clockwise); + ColorAttribute *cao = new ColorAttribute; + cao->set_on(0.3, 1.0, 0.5, 1.0); + + CullFaceAttribute *cfai = new CullFaceAttribute; + cfai->set_mode(CullFaceProperty::M_cull_counter_clockwise); + ColorAttribute *cai = new ColorAttribute; + cai->set_on(0.15, 0.5, 0.25, 1.0); + + _outside_attrib.set_attribute(RenderModeTransition::get_class_type(), rma); + _outside_attrib.set_attribute(CullFaceTransition::get_class_type(), cfao); + _outside_attrib.set_attribute(ColorTransition::get_class_type(), cao); + + _inside_attrib.set_attribute(CullFaceTransition::get_class_type(), cfai); + _inside_attrib.set_attribute(ColorTransition::get_class_type(), cai); +} + +//////////////////////////////////////////////////////////////////// +// Function: DrawBoundsTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated DrawBoundsTransition just +// like this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *DrawBoundsTransition:: +make_copy() const { + return new DrawBoundsTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: DrawBoundsTransition::sub_render +// Access: Public, Virtual +// Description: This is called by the RenderTraverser to tell the +// drawBounds to do its thing. +//////////////////////////////////////////////////////////////////// +bool DrawBoundsTransition:: +sub_render(NodeRelation *arc, const AllAttributesWrapper &attrib, + AllTransitionsWrapper &, GraphicsStateGuardianBase *gsgbase) { + GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, gsgbase); + + const BoundingVolume &vol = arc->get_bound(); + if (!vol.is_empty() && !vol.is_infinite()) { + // This is a bit of work, because the bounding volume has already + // been transformed by this arc's transform. + + LMatrix4f mat; + TransformAttribute *ta; + if (get_attribute_into(ta, attrib, TransformTransition::get_class_type())) { + mat = ta->get_matrix(); + } else { + mat = LMatrix4f::ident_mat(); + } + + TransformTransition *tt; + if (get_transition_into(tt, arc)) { + // Undo the application of the arc's transform. Yuck. + mat = ::invert(tt->get_matrix()) * mat; + } + + TransformAttribute *new_ta = new TransformAttribute; + new_ta->set_matrix(mat); + _outside_attrib.set_attribute(TransformTransition::get_class_type(), new_ta); + + gsg->set_state(_outside_attrib, true); + + if (vol.is_of_type(BoundingSphere::get_class_type())) { + const BoundingSphere *sphere = DCAST(BoundingSphere, &vol); + + GeomSphere geom; + PTA_Vertexf verts; + LPoint3f center = sphere->get_center(); + verts.push_back(center); + center[0] += sphere->get_radius(); + verts.push_back(center); + geom.set_coords(verts, G_PER_VERTEX); + geom.set_num_prims(1); + + gsg->draw_sphere(&geom); + gsg->set_state(_inside_attrib, false); + gsg->draw_sphere(&geom); + + } else { + sgattrib_cat.warning() + << "Don't know how to draw a representation of " + << vol.get_class_type() << "\n"; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: DrawBoundsTransition::has_sub_render +// Access: Public, Virtual +// Description: Should be redefined to return true if the function +// sub_render(), above, expects to be called during +// traversal. +//////////////////////////////////////////////////////////////////// +bool DrawBoundsTransition:: +has_sub_render() const { + return true; +} diff --git a/panda/src/sgattrib/drawBoundsTransition.h b/panda/src/sgattrib/drawBoundsTransition.h new file mode 100644 index 0000000000..3e5d1c4217 --- /dev/null +++ b/panda/src/sgattrib/drawBoundsTransition.h @@ -0,0 +1,61 @@ +// Filename: drawBoundsTransition.h +// Created by: drose (26Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DRAWBOUNDSTRANSITION_H +#define DRAWBOUNDSTRANSITION_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : DrawBoundsTransition +// Description : This is a special transition that does not change +// rendering state, but instead causes a representation +// of the arc's bounding volume to be rendered +// immediately. It's intended primarily for debugging +// purposes; it's not designed to be terribly efficient. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DrawBoundsTransition : public ImmediateTransition { +public: + DrawBoundsTransition(); + +public: + virtual NodeTransition *make_copy() const; + + virtual bool sub_render(NodeRelation *arc, + const AllAttributesWrapper &attrib, + AllTransitionsWrapper &trans, + GraphicsStateGuardianBase *gsgbase); + virtual bool has_sub_render() const; + + NodeAttributes _outside_attrib; + NodeAttributes _inside_attrib; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ImmediateTransition::init_type(); + register_type(_type_handle, "DrawBoundsTransition", + ImmediateTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class DrawBoundsAttribute; +}; + +#include "drawBoundsTransition.I" + +#endif + + diff --git a/panda/src/sgattrib/fogAttribute.I b/panda/src/sgattrib/fogAttribute.I new file mode 100644 index 0000000000..c0c5dae91d --- /dev/null +++ b/panda/src/sgattrib/fogAttribute.I @@ -0,0 +1,38 @@ +// Filename: fogAttribute.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: FogAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FogAttribute:: +FogAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: FogAttribute::set_on +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void FogAttribute:: +set_on(Fog *fog) { + nassertv(fog != (Fog *)NULL); + _value = fog; + OnOffAttribute::set_on(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FogAttribute::get_fog +// Access: Public +// Description: Returns the fog that the FogAttribute represents. It +// is only valid to call this if is_on() has returned +// true. +//////////////////////////////////////////////////////////////////// +INLINE PT(Fog) FogAttribute:: +get_fog() const { + nassertr(is_on(), NULL); + return _value; +} diff --git a/panda/src/sgattrib/fogAttribute.cxx b/panda/src/sgattrib/fogAttribute.cxx new file mode 100644 index 0000000000..409c61d103 --- /dev/null +++ b/panda/src/sgattrib/fogAttribute.cxx @@ -0,0 +1,107 @@ +// Filename: fogAttribute.cxx +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "fogAttribute.h" +#include "fogTransition.h" + +#include +#include + +TypeHandle FogAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: FogAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle FogAttribute:: +get_handle() const { + return FogTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FogAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated FogAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *FogAttribute:: +make_copy() const { + return new FogAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: FogAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated FogAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *FogAttribute:: +make_initial() const { + return new FogAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: FogAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void FogAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_fog(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: FogAttribute::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the indicated transition +// pointer, which is guaranteed to be of type +// FogTransition. +//////////////////////////////////////////////////////////////////// +void FogAttribute:: +set_value_from(const OnOffTransition *other) { + const FogTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; + nassertv(_value != (Fog *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: FogAttribute::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int FogAttribute:: +compare_values(const OnOffAttribute *other) const { + const FogAttribute *ot; + DCAST_INTO_R(ot, other, false); + return (int)(_value.p() - ot->_value.p()); +} + +//////////////////////////////////////////////////////////////////// +// Function: FogAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void FogAttribute:: +output_value(ostream &out) const { + nassertv(_value != (Fog *)NULL); + out << *_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: FogAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void FogAttribute:: +write_value(ostream &out, int indent_level) const { + nassertv(_value != (Fog *)NULL); + indent(out, indent_level) << *_value << "\n"; +} diff --git a/panda/src/sgattrib/fogAttribute.h b/panda/src/sgattrib/fogAttribute.h new file mode 100644 index 0000000000..2150d51892 --- /dev/null +++ b/panda/src/sgattrib/fogAttribute.h @@ -0,0 +1,59 @@ +// Filename: fogAttribute.h +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FOGATTRIBUTE_H +#define FOGATTRIBUTE_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : FogAttribute +// Description : See FogTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA FogAttribute : public OnOffAttribute { +public: + INLINE FogAttribute(); + + INLINE void set_on(Fog *fog); + INLINE PT(Fog) get_fog() const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void set_value_from(const OnOffTransition *other); + virtual int compare_values(const OnOffAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + PT(Fog) _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnOffAttribute::init_type(); + register_type(_type_handle, "FogAttribute", + OnOffAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "fogAttribute.I" + +#endif diff --git a/panda/src/sgattrib/fogTransition.I b/panda/src/sgattrib/fogTransition.I new file mode 100644 index 0000000000..711a763cf0 --- /dev/null +++ b/panda/src/sgattrib/fogTransition.I @@ -0,0 +1,64 @@ +// Filename: fogTransition.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: FogTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FogTransition:: +FogTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: FogTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FogTransition:: +FogTransition(Fog *fog) : + OnOffTransition(TD_on), + _value(fog) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: FogTransition::off +// Access: Public +// Description: This named constructor returns a FogTransition that +// turns off fog. +//////////////////////////////////////////////////////////////////// +INLINE FogTransition FogTransition:: +off() { + FogTransition result; + result.set_off(); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: FogTransition::set_on +// Access: Public +// Description: Changes the FogTransition to render the particular +// fog. +//////////////////////////////////////////////////////////////////// +INLINE void FogTransition:: +set_on(Fog *fog) { + nassertv(fog != (Fog *)NULL); + _value = fog; + OnOffTransition::set_on(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FogTransition::get_fog +// Access: Public +// Description: Returns the fog that the FogTransition represents. +// It is only valid to call this if is_on() has returned +// true. +//////////////////////////////////////////////////////////////////// +INLINE PT(Fog) FogTransition:: +get_fog() const { + nassertr(is_on(), NULL); + return _value; +} diff --git a/panda/src/sgattrib/fogTransition.cxx b/panda/src/sgattrib/fogTransition.cxx new file mode 100644 index 0000000000..ffb76c1cbc --- /dev/null +++ b/panda/src/sgattrib/fogTransition.cxx @@ -0,0 +1,81 @@ +// Filename: fogTransition.cxx +// Created by: mike (06Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "fogTransition.h" +#include "fogAttribute.h" + +#include + +TypeHandle FogTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: FogTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated FogTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *FogTransition:: +make_copy() const { + return new FogTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: FogTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated FogAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *FogTransition:: +make_attrib() const { + return new FogAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: FogTransition::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the other transition pointer, +// which is guaranteed to be another FogTransition. +//////////////////////////////////////////////////////////////////// +void FogTransition:: +set_value_from(const OnOffTransition *other) { + const FogTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; + nassertv(_value != (Fog *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: FogTransition::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int FogTransition:: +compare_values(const OnOffTransition *other) const { + const FogTransition *ot; + DCAST_INTO_R(ot, other, false); + return (int)(_value.p() - ot->_value.p()); +} + +//////////////////////////////////////////////////////////////////// +// Function: FogTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void FogTransition:: +output_value(ostream &out) const { + nassertv(_value != (Fog *)NULL); + out << *_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: FogTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void FogTransition:: +write_value(ostream &out, int indent_level) const { + nassertv(_value != (Fog *)NULL); + indent(out, indent_level) << *_value << "\n"; +} diff --git a/panda/src/sgattrib/fogTransition.h b/panda/src/sgattrib/fogTransition.h new file mode 100644 index 0000000000..5d5ea858c7 --- /dev/null +++ b/panda/src/sgattrib/fogTransition.h @@ -0,0 +1,61 @@ +// Filename: fogTransition.h +// Created by: mike (19Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FOGTRANSITION_H +#define FOGTRANSITION_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : FogTransition +// Description : This controls the enabling of fog. This typically +// attenuates the colors based on the distance from the +// observer (e.g. the z depth). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA FogTransition : public OnOffTransition { +public: + INLINE FogTransition(); + INLINE FogTransition(Fog *fog); + INLINE static FogTransition off(); + + INLINE void set_on(Fog *fog); + INLINE PT(Fog) get_fog() const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + virtual void set_value_from(const OnOffTransition *other); + virtual int compare_values(const OnOffTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + PT(Fog) _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnOffTransition::init_type(); + register_type(_type_handle, "FogTransition", + OnOffTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class FogAttribute; +}; + +#include "fogTransition.I" + +#endif diff --git a/panda/src/sgattrib/linesmoothAttribute.I b/panda/src/sgattrib/linesmoothAttribute.I new file mode 100644 index 0000000000..0334b2d060 --- /dev/null +++ b/panda/src/sgattrib/linesmoothAttribute.I @@ -0,0 +1,13 @@ +// Filename: linesmoothAttribute.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: LinesmoothAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LinesmoothAttribute:: +LinesmoothAttribute() { +} diff --git a/panda/src/sgattrib/linesmoothAttribute.cxx b/panda/src/sgattrib/linesmoothAttribute.cxx new file mode 100644 index 0000000000..c5e6ebf7af --- /dev/null +++ b/panda/src/sgattrib/linesmoothAttribute.cxx @@ -0,0 +1,56 @@ +// Filename: linesmoothAttribute.cxx +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "linesmoothAttribute.h" +#include "linesmoothTransition.h" + +#include + +TypeHandle LinesmoothAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LinesmoothAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle LinesmoothAttribute:: +get_handle() const { + return LinesmoothTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LinesmoothAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated LinesmoothAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *LinesmoothAttribute:: +make_copy() const { + return new LinesmoothAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: LinesmoothAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated LinesmoothAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *LinesmoothAttribute:: +make_initial() const { + return new LinesmoothAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: LinesmoothAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void LinesmoothAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_linesmooth(this); +} diff --git a/panda/src/sgattrib/linesmoothAttribute.h b/panda/src/sgattrib/linesmoothAttribute.h new file mode 100644 index 0000000000..b857864108 --- /dev/null +++ b/panda/src/sgattrib/linesmoothAttribute.h @@ -0,0 +1,47 @@ +// Filename: linesmoothAttribute.h +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINESMOOTHATTRIBUTE_H +#define LINESMOOTHATTRIBUTE_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : LinesmoothAttribute +// Description : See LinesmoothTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA LinesmoothAttribute : public OnOffAttribute { +public: + INLINE LinesmoothAttribute(); + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnOffAttribute::init_type(); + register_type(_type_handle, "LinesmoothAttribute", + OnOffAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "linesmoothAttribute.I" + +#endif diff --git a/panda/src/sgattrib/linesmoothTransition.I b/panda/src/sgattrib/linesmoothTransition.I new file mode 100644 index 0000000000..70a878bfd4 --- /dev/null +++ b/panda/src/sgattrib/linesmoothTransition.I @@ -0,0 +1,26 @@ +// Filename: linesmoothTransition.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: LinesmoothTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LinesmoothTransition:: +LinesmoothTransition() { + set_on(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LinesmoothTransition::off +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LinesmoothTransition LinesmoothTransition:: +off() { + LinesmoothTransition result; + result.set_off(); + return result; +} diff --git a/panda/src/sgattrib/linesmoothTransition.cxx b/panda/src/sgattrib/linesmoothTransition.cxx new file mode 100644 index 0000000000..02bf7deae4 --- /dev/null +++ b/panda/src/sgattrib/linesmoothTransition.cxx @@ -0,0 +1,30 @@ +// Filename: linesmoothTransition.cxx +// Created by: mike (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "linesmoothTransition.h" +#include "linesmoothAttribute.h" + +TypeHandle LinesmoothTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LinesmoothTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated LinesmoothTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *LinesmoothTransition:: +make_copy() const { + return new LinesmoothTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: LinesmoothTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated LinesmoothAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *LinesmoothTransition:: +make_attrib() const { + return new LinesmoothAttribute; +} diff --git a/panda/src/sgattrib/linesmoothTransition.h b/panda/src/sgattrib/linesmoothTransition.h new file mode 100644 index 0000000000..03281d2f83 --- /dev/null +++ b/panda/src/sgattrib/linesmoothTransition.h @@ -0,0 +1,48 @@ +// Filename: linesmoothTransition.h +// Created by: mike (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LINESMOOTHTRANSITION_H +#define LINESMOOTHTRANSITION_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : LinesmoothTransition +// Description : This enables or disables the antialiasing of lines. +// It has no additional properties; it's simply an +// on-or-off thing. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA LinesmoothTransition : public OnOffTransition { +public: + INLINE LinesmoothTransition(); + INLINE static LinesmoothTransition off(); + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnOffTransition::init_type(); + register_type(_type_handle, "LinesmoothTransition", + OnOffTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class LinesmoothAttribute; +}; + +#include "linesmoothTransition.I" + +#endif diff --git a/panda/src/sgattrib/materialAttribute.I b/panda/src/sgattrib/materialAttribute.I new file mode 100644 index 0000000000..387d6eae7d --- /dev/null +++ b/panda/src/sgattrib/materialAttribute.I @@ -0,0 +1,38 @@ +// Filename: materialAttribute.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: MaterialAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MaterialAttribute:: +MaterialAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialAttribute::set_on +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void MaterialAttribute:: +set_on(Material *material) { + nassertv(material != (Material *)NULL); + _value = material; + OnOffAttribute::set_on(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialAttribute::get_material +// Access: Public +// Description: Returns the material that the MaterialAttribute +// represents. It is only valid to call this if is_on() +// has returned true. +//////////////////////////////////////////////////////////////////// +INLINE PT(Material) MaterialAttribute:: +get_material() const { + nassertr(is_on(), NULL); + return _value; +} diff --git a/panda/src/sgattrib/materialAttribute.cxx b/panda/src/sgattrib/materialAttribute.cxx new file mode 100644 index 0000000000..40fd0ba72c --- /dev/null +++ b/panda/src/sgattrib/materialAttribute.cxx @@ -0,0 +1,107 @@ +// Filename: materialAttribute.cxx +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "materialAttribute.h" +#include "materialTransition.h" + +#include +#include + +TypeHandle MaterialAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: MaterialAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle MaterialAttribute:: +get_handle() const { + return MaterialTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated MaterialAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *MaterialAttribute:: +make_copy() const { + return new MaterialAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated MaterialAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *MaterialAttribute:: +make_initial() const { + return new MaterialAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void MaterialAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_material(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialAttribute::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the indicated transition +// pointer, which is guaranteed to be of type +// MaterialTransition. +//////////////////////////////////////////////////////////////////// +void MaterialAttribute:: +set_value_from(const OnOffTransition *other) { + const MaterialTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; + nassertv(_value != (Material *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialAttribute::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int MaterialAttribute:: +compare_values(const OnOffAttribute *other) const { + const MaterialAttribute *ot; + DCAST_INTO_R(ot, other, false); + return (int)(_value.p() - ot->_value.p()); +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void MaterialAttribute:: +output_value(ostream &out) const { + nassertv(_value != (Material *)NULL); + out << *_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void MaterialAttribute:: +write_value(ostream &out, int indent_level) const { + nassertv(_value != (Material *)NULL); + indent(out, indent_level) << *_value << "\n"; +} diff --git a/panda/src/sgattrib/materialAttribute.h b/panda/src/sgattrib/materialAttribute.h new file mode 100644 index 0000000000..6354642524 --- /dev/null +++ b/panda/src/sgattrib/materialAttribute.h @@ -0,0 +1,59 @@ +// Filename: materialAttribute.h +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MATERIALATTRIBUTE_H +#define MATERIALATTRIBUTE_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : MaterialAttribute +// Description : See MaterialTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA MaterialAttribute : public OnOffAttribute { +public: + INLINE MaterialAttribute(); + + INLINE void set_on(Material *material); + INLINE PT(Material) get_material() const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void set_value_from(const OnOffTransition *other); + virtual int compare_values(const OnOffAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + PT(Material) _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnOffAttribute::init_type(); + register_type(_type_handle, "MaterialAttribute", + OnOffAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "materialAttribute.I" + +#endif diff --git a/panda/src/sgattrib/materialTransition.I b/panda/src/sgattrib/materialTransition.I new file mode 100644 index 0000000000..22f7157de3 --- /dev/null +++ b/panda/src/sgattrib/materialTransition.I @@ -0,0 +1,63 @@ +// Filename: materialTransition.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: MaterialTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MaterialTransition:: +MaterialTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MaterialTransition:: +MaterialTransition(Material *material) : + OnOffTransition(TD_on), + _value(material) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialTransition::off +// Access: Public +// Description: This named constructor returns a MaterialTransition +// that disables materials. +//////////////////////////////////////////////////////////////////// +INLINE MaterialTransition MaterialTransition:: +off() { + MaterialTransition result; + result.set_off(); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialTransition::set_on +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void MaterialTransition:: +set_on(Material *material) { + nassertv(material != (Material *)NULL); + _value = material; + OnOffTransition::set_on(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialTransition::get_material +// Access: Public +// Description: Returns the material that the MaterialTransition +// represents. It is only valid to call this if is_on() +// has returned true. +//////////////////////////////////////////////////////////////////// +INLINE PT(Material) MaterialTransition:: +get_material() const { + nassertr(is_on(), NULL); + return _value; +} diff --git a/panda/src/sgattrib/materialTransition.cxx b/panda/src/sgattrib/materialTransition.cxx new file mode 100644 index 0000000000..85ede6a7ed --- /dev/null +++ b/panda/src/sgattrib/materialTransition.cxx @@ -0,0 +1,81 @@ +// Filename: materialTransition.cxx +// Created by: mike (06Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "materialTransition.h" +#include "materialAttribute.h" + +#include + +TypeHandle MaterialTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: MaterialTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated MaterialTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *MaterialTransition:: +make_copy() const { + return new MaterialTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated MaterialAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *MaterialTransition:: +make_attrib() const { + return new MaterialAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialTransition::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the other transition pointer, +// which is guaranteed to be another MaterialTransition. +//////////////////////////////////////////////////////////////////// +void MaterialTransition:: +set_value_from(const OnOffTransition *other) { + const MaterialTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; + nassertv(_value != (Material *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialTransition::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int MaterialTransition:: +compare_values(const OnOffTransition *other) const { + const MaterialTransition *ot; + DCAST_INTO_R(ot, other, false); + return (int)(_value.p() - ot->_value.p()); +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void MaterialTransition:: +output_value(ostream &out) const { + nassertv(_value != (Material *)NULL); + out << *_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: MaterialTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void MaterialTransition:: +write_value(ostream &out, int indent_level) const { + nassertv(_value != (Material *)NULL); + indent(out, indent_level) << *_value << "\n"; +} diff --git a/panda/src/sgattrib/materialTransition.h b/panda/src/sgattrib/materialTransition.h new file mode 100644 index 0000000000..70b893c078 --- /dev/null +++ b/panda/src/sgattrib/materialTransition.h @@ -0,0 +1,60 @@ +// Filename: materialTransition.h +// Created by: mike (19Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MATERIALTRANSITION_H +#define MATERIALTRANSITION_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : MaterialTransition +// Description : Applies a material, controlling subtle lighting +// parameters, to geometry. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA MaterialTransition : public OnOffTransition { +public: + INLINE MaterialTransition(); + INLINE MaterialTransition(Material *material); + INLINE static MaterialTransition off(); + + INLINE void set_on(Material *material); + INLINE PT(Material) get_material() const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + virtual void set_value_from(const OnOffTransition *other); + virtual int compare_values(const OnOffTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + PT(Material) _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnOffTransition::init_type(); + register_type(_type_handle, "MaterialTransition", + OnOffTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class MaterialAttribute; +}; + +#include "materialTransition.I" + +#endif diff --git a/panda/src/sgattrib/pointShapeAttribute.I b/panda/src/sgattrib/pointShapeAttribute.I new file mode 100644 index 0000000000..471b9f945a --- /dev/null +++ b/panda/src/sgattrib/pointShapeAttribute.I @@ -0,0 +1,34 @@ +// Filename: pointShapeAttribute.I +// Created by: charles (11Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: PointShapeAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PointShapeAttribute:: +PointShapeAttribute() : + _value(PointShapeProperty::M_square) { +} + +//////////////////////////////////////////////////////////////////// +// Function: PointShapeAttribute::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PointShapeAttribute:: +set_mode(PointShapeProperty::Mode mode) { + _value.set_mode(mode); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointShapeAttribute::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PointShapeProperty::Mode PointShapeAttribute:: +get_mode() const { + return _value.get_mode(); +} diff --git a/panda/src/sgattrib/pointShapeAttribute.cxx b/panda/src/sgattrib/pointShapeAttribute.cxx new file mode 100644 index 0000000000..2371a638ed --- /dev/null +++ b/panda/src/sgattrib/pointShapeAttribute.cxx @@ -0,0 +1,104 @@ +// Filename: pointShapeAttribute.cxx +// Created by: charles (11Jul00) +// (absurd abuse of cut n' paste from renderModeAttribute.cxx) +//////////////////////////////////////////////////////////////////// + +#include "pointShapeAttribute.h" +#include "pointShapeTransition.h" + +#include +#include + +TypeHandle PointShapeAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PointShapeAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle PointShapeAttribute:: +get_handle() const { + return PointShapeTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointShapeAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated PointShapeAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *PointShapeAttribute:: +make_copy() const { + return new PointShapeAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointShapeAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated PointShapeAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *PointShapeAttribute:: +make_initial() const { + return new PointShapeAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointShapeAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void PointShapeAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_point_shape(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointShapeAttribute::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the indicated transition +// pointer, which is guaranteed to be of type +// PointShapeTransition. +//////////////////////////////////////////////////////////////////// +void PointShapeAttribute:: +set_value_from(const OnTransition *other) { + const PointShapeTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointShapeAttribute::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int PointShapeAttribute:: +compare_values(const OnAttribute *other) const { + const PointShapeAttribute *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointShapeAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void PointShapeAttribute:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointShapeAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void PointShapeAttribute:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/pointShapeAttribute.h b/panda/src/sgattrib/pointShapeAttribute.h new file mode 100644 index 0000000000..4f15eb0e16 --- /dev/null +++ b/panda/src/sgattrib/pointShapeAttribute.h @@ -0,0 +1,60 @@ +// Filename: pointShapeAttribute.h +// Created by: charles (11Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef POINTSHAPEATTRIBUTE_H +#define POINTSHAPEATTRIBUTE_H + +#include + +#include "pointShapeProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PointShapeAttribute +// Description : See PointShapeTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PointShapeAttribute : public OnAttribute { +public: + INLINE PointShapeAttribute(); + + INLINE void set_mode(PointShapeProperty::Mode); + INLINE PointShapeProperty::Mode get_mode() const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + PointShapeProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnAttribute::init_type(); + register_type(_type_handle, "PointShapeAttribute", + OnAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "pointShapeAttribute.I" + +#endif // POINTSHAPEATTRIBUTE_H diff --git a/panda/src/sgattrib/pointShapeProperty.I b/panda/src/sgattrib/pointShapeProperty.I new file mode 100644 index 0000000000..59d58995f9 --- /dev/null +++ b/panda/src/sgattrib/pointShapeProperty.I @@ -0,0 +1,45 @@ +// Filename: pointShapeProperty.I +// Created by: charles (10Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function : PointShapeProperty +// Access : public +//////////////////////////////////////////////////////////////////// + +INLINE PointShapeProperty:: +PointShapeProperty(PointShapeProperty::Mode mode) : + _mode(mode) { +} + +//////////////////////////////////////////////////////////////////// +// Function : set_mode +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void PointShapeProperty:: +set_mode(PointShapeProperty::Mode mode) { + _mode = mode; +} + +//////////////////////////////////////////////////////////////////// +// Function : get_mode +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE PointShapeProperty::Mode PointShapeProperty:: +get_mode(void) const { + return _mode; +} + +//////////////////////////////////////////////////////////////////// +// Function : compare_to +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE int PointShapeProperty:: +compare_to(const PointShapeProperty& other) const { + if (_mode != other._mode) { + return (int) _mode - (int) other._mode; + } + + return 0; +} diff --git a/panda/src/sgattrib/pointShapeProperty.cxx b/panda/src/sgattrib/pointShapeProperty.cxx new file mode 100644 index 0000000000..883546de90 --- /dev/null +++ b/panda/src/sgattrib/pointShapeProperty.cxx @@ -0,0 +1,28 @@ +// Filename: pointShapeProperty.cxx +// Created by: charles (10Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "pointShapeProperty.h" + +ostream & +operator << (ostream &out, PointShapeProperty::Mode mode) { + switch (mode) { + case PointShapeProperty::M_square: + return out << "square"; + + case PointShapeProperty::M_circle: + return out << "circle"; + } + + return out << "**invalid**(" << (int)mode << ")"; +} + +//////////////////////////////////////////////////////////////////// +// Function : output +// Access : public +//////////////////////////////////////////////////////////////////// +void PointShapeProperty:: +output(ostream &out) const { + out << _mode; +} diff --git a/panda/src/sgattrib/pointShapeProperty.h b/panda/src/sgattrib/pointShapeProperty.h new file mode 100644 index 0000000000..6062288282 --- /dev/null +++ b/panda/src/sgattrib/pointShapeProperty.h @@ -0,0 +1,47 @@ +// Filename: pointShapeProperty.h +// Created by: charles (10Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef POINTSHAPEPROPERTY_H +#define POINTSHAPEPROPERTY_H + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PointShapeProperty +// Description : Affects the shapes of point primitives +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PointShapeProperty { +public: + + enum Mode { + M_square, + M_circle + }; + +public: + + INLINE PointShapeProperty(Mode mode); + + INLINE void set_mode(Mode mode); + INLINE Mode get_mode(void) const; + + INLINE int compare_to(const PointShapeProperty& other) const; + void output(ostream &out) const; + +private: + + Mode _mode; +}; + +ostream &operator << (ostream &out, PointShapeProperty::Mode mode); + +INLINE ostream &operator << (ostream &out, const PointShapeProperty &prop) { + prop.output(out); + return out; +} + +#include "pointShapeProperty.I" + +#endif // POINTSHAPEPROPERTY_H diff --git a/panda/src/sgattrib/pointShapeTransition.I b/panda/src/sgattrib/pointShapeTransition.I new file mode 100644 index 0000000000..3dba8aaca5 --- /dev/null +++ b/panda/src/sgattrib/pointShapeTransition.I @@ -0,0 +1,35 @@ +// Filename: pointShapeTransition.I +// Created by: charles (11Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: PointShapeTransition +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PointShapeTransition:: +PointShapeTransition(PointShapeProperty::Mode mode) : + _value(mode) { +} + +//////////////////////////////////////////////////////////////////// +// Function: set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PointShapeTransition:: +set_mode(PointShapeProperty::Mode mode) { + _value.set_mode(mode); + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PointShapeProperty::Mode PointShapeTransition:: +get_mode(void) const { + return _value.get_mode(); +} diff --git a/panda/src/sgattrib/pointShapeTransition.cxx b/panda/src/sgattrib/pointShapeTransition.cxx new file mode 100644 index 0000000000..a2fda45ab3 --- /dev/null +++ b/panda/src/sgattrib/pointShapeTransition.cxx @@ -0,0 +1,78 @@ +// Filename: pointShapeTransition.cxx +// Created by: charles (11Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "pointShapeTransition.h" +#include "pointShapeAttribute.h" + +#include + +TypeHandle PointShapeTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: Pointshapetransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated Pointshapetransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *PointShapeTransition:: +make_copy() const { + return new PointShapeTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: Pointshapetransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated pointShapeattribute +//////////////////////////////////////////////////////////////////// +NodeAttribute *PointShapeTransition:: +make_attrib() const { + return new PointShapeAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointShapeTransition +// Access: Protected, Virtual +// Description: Copies the value from the other transition pointer, +// which is guaranteed to be another PointShapeTransition. +//////////////////////////////////////////////////////////////////// +void PointShapeTransition:: +set_value_from(const OnTransition *other) { + const PointShapeTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointShapeTransition::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int PointShapeTransition:: +compare_values(const OnTransition *other) const { + const PointShapeTransition *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: PointShapeTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void PointShapeTransition:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointShapeTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void PointShapeTransition:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/pointShapeTransition.h b/panda/src/sgattrib/pointShapeTransition.h new file mode 100644 index 0000000000..d27cdffdff --- /dev/null +++ b/panda/src/sgattrib/pointShapeTransition.h @@ -0,0 +1,59 @@ +// Filename: pointShapeTransition.h +// Created by: charles (10Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef POINTSHAPETRANSITION_H +#define POINTSHAPETRANSITION_H + +#include + +#include "pointShapeProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PointShapeTransition +// Description : An arc that determines the shape of point primitives +// drawn beneath. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PointShapeTransition : public OnTransition { +public: + INLINE PointShapeTransition(PointShapeProperty::Mode mode); + + INLINE void set_mode(PointShapeProperty::Mode mode); + INLINE PointShapeProperty::Mode get_mode() const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + PointShapeProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnTransition::init_type(); + register_type(_type_handle, "PointShapeTransition", + OnTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class PointShapeAttribute; +}; + +#include "pointShapeTransition.I" + +#endif // POINTSHAPETRANSITION_H diff --git a/panda/src/sgattrib/polygonOffsetAttribute.I b/panda/src/sgattrib/polygonOffsetAttribute.I new file mode 100644 index 0000000000..1c9dc4e41c --- /dev/null +++ b/panda/src/sgattrib/polygonOffsetAttribute.I @@ -0,0 +1,54 @@ +// Filename: polygonOffsetAttribute.I +// Created by: jason (12Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PolygonOffsetAttribute:: +PolygonOffsetAttribute() +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetAttribute::set_units +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PolygonOffsetAttribute:: +set_units(int units) { + _state.set_units(units); +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetAttribute::get_units +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int PolygonOffsetAttribute:: +get_units() const { + return _state.get_units(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetAttribute::set_factor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PolygonOffsetAttribute:: +set_factor(int factor) { + _state.set_factor(factor); +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetAttribute::get_factor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int PolygonOffsetAttribute:: +get_factor() const { + return _state.get_factor(); +} diff --git a/panda/src/sgattrib/polygonOffsetAttribute.cxx b/panda/src/sgattrib/polygonOffsetAttribute.cxx new file mode 100644 index 0000000000..110fc6115c --- /dev/null +++ b/panda/src/sgattrib/polygonOffsetAttribute.cxx @@ -0,0 +1,106 @@ +// Filename: polygonOffsetAttribute.cxx +// Created by: jason (12Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "polygonOffsetAttribute.h" +#include "polygonOffsetTransition.h" + +#include +#include + +TypeHandle PolygonOffsetAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle PolygonOffsetAttribute:: +get_handle() const { + return PolygonOffsetTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated PolygonOffsetAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *PolygonOffsetAttribute:: +make_copy() const { + return new PolygonOffsetAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated PolygonOffsetAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *PolygonOffsetAttribute:: +make_initial() const { + return new PolygonOffsetAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void PolygonOffsetAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_polygon_offset(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetAttribute::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the indicated transition +// pointer, which is guaranteed to be of type +// PolygonOffsetTransition. +//////////////////////////////////////////////////////////////////// +void PolygonOffsetAttribute:: +set_value_from(const OnTransition *other) { + const PolygonOffsetTransition *ot; + DCAST_INTO_V(ot, other); + _state = ot->_state; +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetAttribute::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int PolygonOffsetAttribute:: +compare_values(const OnAttribute *other) const { + const PolygonOffsetAttribute *ot; + DCAST_INTO_R(ot, other, false); + return _state.compare_to(ot->_state); +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void PolygonOffsetAttribute:: +output_value(ostream &out) const { + out << _state; +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void PolygonOffsetAttribute:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _state << "\n"; +} + + diff --git a/panda/src/sgattrib/polygonOffsetAttribute.h b/panda/src/sgattrib/polygonOffsetAttribute.h new file mode 100644 index 0000000000..8d2c8657cb --- /dev/null +++ b/panda/src/sgattrib/polygonOffsetAttribute.h @@ -0,0 +1,62 @@ +// Filename: polygonOffsetAttribute.h +// Created by: jason (12Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef POLYGONOFFSETATTRIBUTE_H +#define POLYGONOFFSETATTRIBUTE_H + +#include + +#include "polygonOffsetProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PolygonOffsetAttribute +// Description : See PolygonOffsetTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PolygonOffsetAttribute : public OnAttribute { +public: + INLINE PolygonOffsetAttribute(); + + INLINE void set_units(int units); + INLINE int get_units() const; + INLINE void set_factor(int factor); + INLINE int get_factor() const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + PolygonOffsetProperty _state; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnAttribute::init_type(); + register_type(_type_handle, "PolygonOffsetAttribute", + OnAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "polygonOffsetAttribute.I" + +#endif diff --git a/panda/src/sgattrib/polygonOffsetProperty.I b/panda/src/sgattrib/polygonOffsetProperty.I new file mode 100644 index 0000000000..6dc7387d83 --- /dev/null +++ b/panda/src/sgattrib/polygonOffsetProperty.I @@ -0,0 +1,81 @@ +// Filename: polygonOffsetProperty.I +// Created by: jason (12Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetProperty::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PolygonOffsetProperty:: +PolygonOffsetProperty() : + _units(0), _factor(0) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetProperty::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PolygonOffsetProperty:: +PolygonOffsetProperty(int units, int factor) : + _units(units), _factor(factor) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetProperty::set_units +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PolygonOffsetProperty:: +set_units(int units) { + _units = units; +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetProperty::get_units +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int PolygonOffsetProperty:: +get_units() const { + return _units; +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetProperty::set_factor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PolygonOffsetProperty:: +set_factor(int factor) { + _factor = factor; +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetProperty::get_factor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int PolygonOffsetProperty:: +get_factor() const { + return _factor; +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetProperty::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int PolygonOffsetProperty:: +compare_to(const PolygonOffsetProperty &other) const { + if (_units != other._units) + { + return (other._units - _units); + } + + return (other._factor - _factor); +} diff --git a/panda/src/sgattrib/polygonOffsetProperty.cxx b/panda/src/sgattrib/polygonOffsetProperty.cxx new file mode 100644 index 0000000000..07107586d4 --- /dev/null +++ b/panda/src/sgattrib/polygonOffsetProperty.cxx @@ -0,0 +1,16 @@ +// Filename: polygonOffsetProperty.cxx +// Created by: jason (12Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "polygonOffsetProperty.h" + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetProperty::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void PolygonOffsetProperty:: +output(ostream &out) const { + out << "Units " << _units << " Factor " << _factor << endl; +} diff --git a/panda/src/sgattrib/polygonOffsetProperty.h b/panda/src/sgattrib/polygonOffsetProperty.h new file mode 100644 index 0000000000..678e0b3656 --- /dev/null +++ b/panda/src/sgattrib/polygonOffsetProperty.h @@ -0,0 +1,42 @@ +// Filename: polygonOffsetProperty.h +// Created by: jason (12Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef POLYGONOFFSETPROPERTY_H +#define POLYGONOFFSETPROPERTY_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PolygonOffsetProperty +// Description : This class defines the set state for polygon offseting +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PolygonOffsetProperty { +public: + INLINE PolygonOffsetProperty(); + INLINE PolygonOffsetProperty(int units, int factor); + + INLINE void set_units(int units); + INLINE int get_units() const; + INLINE void set_factor(int factor); + INLINE int get_factor() const; + + INLINE int compare_to(const PolygonOffsetProperty &other) const; + void output(ostream &out) const; + +private: + int _units; + int _factor; +}; + +INLINE ostream &operator << (ostream &out, const PolygonOffsetProperty &prop) { + prop.output(out); + return out; +} + +#include "polygonOffsetProperty.I" + +#endif diff --git a/panda/src/sgattrib/polygonOffsetTransition.I b/panda/src/sgattrib/polygonOffsetTransition.I new file mode 100644 index 0000000000..bb038a4633 --- /dev/null +++ b/panda/src/sgattrib/polygonOffsetTransition.I @@ -0,0 +1,55 @@ +// Filename: polygonOffsetTransition.I +// Created by: jason (12Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PolygonOffsetTransition:: +PolygonOffsetTransition(int units, int factor) : + _state(units, factor) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetTransition::set_units +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PolygonOffsetTransition:: +set_units(int units) { + _state.set_units(units); +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetTransition::get_units +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int PolygonOffsetTransition:: +get_units() const { + return _state.get_units(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetTransition::set_factor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PolygonOffsetTransition:: +set_factor(int factor) { + _state.set_factor(factor); +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetTransition::get_factor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int PolygonOffsetTransition:: +get_factor() const { + return _state.get_factor(); +} diff --git a/panda/src/sgattrib/polygonOffsetTransition.cxx b/panda/src/sgattrib/polygonOffsetTransition.cxx new file mode 100644 index 0000000000..6b448d023e --- /dev/null +++ b/panda/src/sgattrib/polygonOffsetTransition.cxx @@ -0,0 +1,85 @@ +// Filename: polygonOffsetTransition.cxx +// Created by: jason (12Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "polygonOffsetTransition.h" +#include "polygonOffsetAttribute.h" + +#include + +TypeHandle PolygonOffsetTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated PolygonOffsetTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *PolygonOffsetTransition:: +make_copy() const { + return new PolygonOffsetTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated PolygonOffsetAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *PolygonOffsetTransition:: +make_attrib() const { + return new PolygonOffsetAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetTransition::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the other transition pointer, +// which is guaranteed to be another PolygonOffsetTransition. +//////////////////////////////////////////////////////////////////// +void PolygonOffsetTransition:: +set_value_from(const OnTransition *other) { + const PolygonOffsetTransition *ot; + DCAST_INTO_V(ot, other); + _state = ot->_state; +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetTransition::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int PolygonOffsetTransition:: +compare_values(const OnTransition *other) const { + const PolygonOffsetTransition *ot; + DCAST_INTO_R(ot, other, false); + return _state.compare_to(ot->_state); +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void PolygonOffsetTransition:: +output_value(ostream &out) const { + out << _state; +} + +//////////////////////////////////////////////////////////////////// +// Function: PolygonOffsetTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void PolygonOffsetTransition:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _state << "\n"; +} + + + + + + + diff --git a/panda/src/sgattrib/polygonOffsetTransition.h b/panda/src/sgattrib/polygonOffsetTransition.h new file mode 100644 index 0000000000..2ed5f464c5 --- /dev/null +++ b/panda/src/sgattrib/polygonOffsetTransition.h @@ -0,0 +1,65 @@ +// Filename: polygonOffsetTransition.h +// Created by: jason (12Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef POLYGONOFFSETTRANSITION_H +#define POLYGONOFFSETTRANSITION_H + +#include + +#include "polygonOffsetProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : PolygonOffsetTransition +// Description : This controls the amount of offseting done for depth +// tests. The first value is the units, this is the +// fixed offseting to be used, and the second value +// allows for variable offsetting per polygon based on +// the ratio of depth relative to screen area of the polygon +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PolygonOffsetTransition : public OnTransition { +public: + INLINE PolygonOffsetTransition(int amount = 0, int factor = 0); + + INLINE void set_units(int units); + INLINE int get_units() const; + INLINE void set_factor(int factor); + INLINE int get_factor() const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + PolygonOffsetProperty _state; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnTransition::init_type(); + register_type(_type_handle, "PolygonOffsetTransition", + OnTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class PolygonOffsetAttribute; +}; + + +#include "polygonOffsetTransition.I" + +#endif diff --git a/panda/src/sgattrib/pruneTransition.I b/panda/src/sgattrib/pruneTransition.I new file mode 100644 index 0000000000..581975b143 --- /dev/null +++ b/panda/src/sgattrib/pruneTransition.I @@ -0,0 +1,13 @@ +// Filename: pruneTransition.I +// Created by: drose (26Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: PruneTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PruneTransition:: +PruneTransition() { +} diff --git a/panda/src/sgattrib/pruneTransition.cxx b/panda/src/sgattrib/pruneTransition.cxx new file mode 100644 index 0000000000..ce0edfca25 --- /dev/null +++ b/panda/src/sgattrib/pruneTransition.cxx @@ -0,0 +1,76 @@ +// Filename: pruneTransition.cxx +// Created by: drose (26Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "pruneTransition.h" +#include +#include +#include +#include + +TypeHandle PruneTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PruneTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated PruneTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *PruneTransition:: +make_copy() const { + return new PruneTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: PruneTransition::sub_render +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool PruneTransition:: +sub_render(NodeRelation *, const AllAttributesWrapper &, + AllTransitionsWrapper &, GraphicsStateGuardianBase *) { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PruneTransition::has_sub_render +// Access: Public, Virtual +// Description: Should be redefined to return true if the function +// sub_render(), above, expects to be called during +// traversal. +//////////////////////////////////////////////////////////////////// +bool PruneTransition:: +has_sub_render() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PruneTransition::make_PruneTransition +// Access: Protected +// Description: Factory method to generate a PruneTransition object +//////////////////////////////////////////////////////////////////// +TypedWriteable* PruneTransition:: +make_PruneTransition(const FactoryParams ¶ms) +{ + PruneTransition *me = new PruneTransition; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: PruneTransition::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a PruneTransition object +//////////////////////////////////////////////////////////////////// +void PruneTransition:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_PruneTransition); +} diff --git a/panda/src/sgattrib/pruneTransition.h b/panda/src/sgattrib/pruneTransition.h new file mode 100644 index 0000000000..82fb5ffcbb --- /dev/null +++ b/panda/src/sgattrib/pruneTransition.h @@ -0,0 +1,58 @@ +// Filename: pruneTransition.h +// Created by: drose (26Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PRUNETRANSITION_H +#define PRUNETRANSITION_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : PruneTransition +// Description : This transition, when encountered in the scene graph, +// causes rendering to stop at this point and not +// traverse anything below. In effect, it causes all +// the geometry at this level and below to become +// invisible. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PruneTransition : public ImmediateTransition { +public: + INLINE PruneTransition(); + + virtual NodeTransition *make_copy() const; + + virtual bool sub_render(NodeRelation *arc, + const AllAttributesWrapper &attrib, + AllTransitionsWrapper &trans, + GraphicsStateGuardianBase *gsgbase); + virtual bool has_sub_render() const; + +public: + static void register_with_read_factory(void); + static TypedWriteable *make_PruneTransition(const FactoryParams ¶ms); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ImmediateTransition::init_type(); + register_type(_type_handle, "PruneTransition", + ImmediateTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "pruneTransition.I" + +#endif diff --git a/panda/src/sgattrib/renderModeAttribute.I b/panda/src/sgattrib/renderModeAttribute.I new file mode 100644 index 0000000000..b6d4d88a16 --- /dev/null +++ b/panda/src/sgattrib/renderModeAttribute.I @@ -0,0 +1,55 @@ +// Filename: renderModeAttribute.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE RenderModeAttribute:: +RenderModeAttribute() : + _value(RenderModeProperty::M_filled, 1.0) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeAttribute::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void RenderModeAttribute:: +set_mode(RenderModeProperty::Mode mode) { + _value.set_mode(mode); +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeAttribute::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE RenderModeProperty::Mode RenderModeAttribute:: +get_mode() const { + return _value.get_mode(); +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeAttribute::set_line_width +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void RenderModeAttribute:: +set_line_width(double line_width) { + _value.set_line_width(line_width); +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeAttribute::get_line_width +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE double RenderModeAttribute:: +get_line_width() const { + return _value.get_line_width(); +} diff --git a/panda/src/sgattrib/renderModeAttribute.cxx b/panda/src/sgattrib/renderModeAttribute.cxx new file mode 100644 index 0000000000..9e6ee94104 --- /dev/null +++ b/panda/src/sgattrib/renderModeAttribute.cxx @@ -0,0 +1,104 @@ +// Filename: renderModeAttribute.cxx +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "renderModeAttribute.h" +#include "renderModeTransition.h" + +#include +#include + +TypeHandle RenderModeAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle RenderModeAttribute:: +get_handle() const { + return RenderModeTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated RenderModeAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *RenderModeAttribute:: +make_copy() const { + return new RenderModeAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated RenderModeAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *RenderModeAttribute:: +make_initial() const { + return new RenderModeAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void RenderModeAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_render_mode(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeAttribute::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the indicated transition +// pointer, which is guaranteed to be of type +// RenderModeTransition. +//////////////////////////////////////////////////////////////////// +void RenderModeAttribute:: +set_value_from(const OnTransition *other) { + const RenderModeTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeAttribute::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int RenderModeAttribute:: +compare_values(const OnAttribute *other) const { + const RenderModeAttribute *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void RenderModeAttribute:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void RenderModeAttribute:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/renderModeAttribute.h b/panda/src/sgattrib/renderModeAttribute.h new file mode 100644 index 0000000000..f06106e9aa --- /dev/null +++ b/panda/src/sgattrib/renderModeAttribute.h @@ -0,0 +1,63 @@ +// Filename: renderModeAttribute.h +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef RENDERMODEATTRIBUTE_H +#define RENDERMODEATTRIBUTE_H + +#include + +#include "renderModeProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : RenderModeAttribute +// Description : See RenderModeTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA RenderModeAttribute : public OnAttribute { +public: + INLINE RenderModeAttribute(); + + INLINE void set_mode(RenderModeProperty::Mode); + INLINE RenderModeProperty::Mode get_mode() const; + + INLINE void set_line_width(double line_width); + INLINE double get_line_width() const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + RenderModeProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnAttribute::init_type(); + register_type(_type_handle, "RenderModeAttribute", + OnAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "renderModeAttribute.I" + +#endif diff --git a/panda/src/sgattrib/renderModeProperty.I b/panda/src/sgattrib/renderModeProperty.I new file mode 100644 index 0000000000..f76abd8f89 --- /dev/null +++ b/panda/src/sgattrib/renderModeProperty.I @@ -0,0 +1,73 @@ +// Filename: renderModeProperty.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeProperty::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE RenderModeProperty:: +RenderModeProperty(Mode mode, double line_width) : + _mode(mode), + _line_width(line_width) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeProperty::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void RenderModeProperty:: +set_mode(RenderModeProperty::Mode mode) { + _mode = mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeProperty::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE RenderModeProperty::Mode RenderModeProperty:: +get_mode() const { + return _mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeProperty::set_line_width +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void RenderModeProperty:: +set_line_width(double line_width) { + _line_width = line_width; +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeProperty::get_line_width +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE double RenderModeProperty:: +get_line_width() const { + return _line_width; +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeProperty::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int RenderModeProperty:: +compare_to(const RenderModeProperty &other) const { + if (_mode != other._mode) { + return (int)_mode - (int)other._mode; + } + if (_line_width != other._line_width) { + return _line_width < other._line_width ? -1 : 1; + } + return 0; +} + diff --git a/panda/src/sgattrib/renderModeProperty.cxx b/panda/src/sgattrib/renderModeProperty.cxx new file mode 100644 index 0000000000..7f5a262d19 --- /dev/null +++ b/panda/src/sgattrib/renderModeProperty.cxx @@ -0,0 +1,29 @@ +// Filename: renderModeProperty.cxx +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "renderModeProperty.h" + +ostream & +operator << (ostream &out, RenderModeProperty::Mode mode) { + switch (mode) { + case RenderModeProperty::M_filled: + return out << "filled"; + + case RenderModeProperty::M_wireframe: + return out << "wireframe"; + } + + return out << "**invalid**(" << (int)mode << ")"; +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeProperty::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void RenderModeProperty:: +output(ostream &out) const { + out << _mode << ":" << _line_width; +} diff --git a/panda/src/sgattrib/renderModeProperty.h b/panda/src/sgattrib/renderModeProperty.h new file mode 100644 index 0000000000..a41523e7d1 --- /dev/null +++ b/panda/src/sgattrib/renderModeProperty.h @@ -0,0 +1,51 @@ +// Filename: renderModeProperty.h +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef RENDERMODEPROPERTY_H +#define RENDERMODEPROPERTY_H + +#include + +//////////////////////////////////////////////////////////////////// +// Class : RenderModeProperty +// Description : This defines the types of renderMode testing we can +// enable. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA RenderModeProperty { +public: + enum Mode { + M_filled, + M_wireframe, + + // Perhaps others to be added later. + }; + +public: + INLINE RenderModeProperty(Mode mode, double line_width); + + INLINE void set_mode(Mode mode); + INLINE Mode get_mode() const; + + INLINE void set_line_width(double line_width); + INLINE double get_line_width() const; + + INLINE int compare_to(const RenderModeProperty &other) const; + void output(ostream &out) const; + +private: + Mode _mode; + double _line_width; +}; + +ostream &operator << (ostream &out, RenderModeProperty::Mode mode); + +INLINE ostream &operator << (ostream &out, const RenderModeProperty &prop) { + prop.output(out); + return out; +} + +#include "renderModeProperty.I" + +#endif diff --git a/panda/src/sgattrib/renderModeTransition.I b/panda/src/sgattrib/renderModeTransition.I new file mode 100644 index 0000000000..67375ead03 --- /dev/null +++ b/panda/src/sgattrib/renderModeTransition.I @@ -0,0 +1,57 @@ +// Filename: renderModeTransition.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE RenderModeTransition:: +RenderModeTransition(RenderModeProperty::Mode mode, double line_width) : + _value(mode, line_width) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeTransition::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void RenderModeTransition:: +set_mode(RenderModeProperty::Mode mode) { + _value.set_mode(mode); + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeTransition::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE RenderModeProperty::Mode RenderModeTransition:: +get_mode() const { + return _value.get_mode(); +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeTransition::set_line_width +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void RenderModeTransition:: +set_line_width(double line_width) { + _value.set_line_width(line_width); + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeTransition::get_line_width +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE double RenderModeTransition:: +get_line_width() const { + return _value.get_line_width(); +} diff --git a/panda/src/sgattrib/renderModeTransition.cxx b/panda/src/sgattrib/renderModeTransition.cxx new file mode 100644 index 0000000000..8decd48fee --- /dev/null +++ b/panda/src/sgattrib/renderModeTransition.cxx @@ -0,0 +1,78 @@ +// Filename: renderModeTransition.cxx +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "renderModeTransition.h" +#include "renderModeAttribute.h" + +#include + +TypeHandle RenderModeTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated RenderModeTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *RenderModeTransition:: +make_copy() const { + return new RenderModeTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated RenderModeAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *RenderModeTransition:: +make_attrib() const { + return new RenderModeAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeTransition::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the other transition pointer, +// which is guaranteed to be another RenderModeTransition. +//////////////////////////////////////////////////////////////////// +void RenderModeTransition:: +set_value_from(const OnTransition *other) { + const RenderModeTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeTransition::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int RenderModeTransition:: +compare_values(const OnTransition *other) const { + const RenderModeTransition *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void RenderModeTransition:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderModeTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void RenderModeTransition:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/renderModeTransition.h b/panda/src/sgattrib/renderModeTransition.h new file mode 100644 index 0000000000..1c329822c8 --- /dev/null +++ b/panda/src/sgattrib/renderModeTransition.h @@ -0,0 +1,62 @@ +// Filename: renderModeTransition.h +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef RENDERMODETRANSITION_H +#define RENDERMODETRANSITION_H + +#include + +#include "renderModeProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : RenderModeTransition +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA RenderModeTransition : public OnTransition { +public: + INLINE RenderModeTransition(RenderModeProperty::Mode mode, + double line_width = 1.0); + + INLINE void set_mode(RenderModeProperty::Mode mode); + INLINE RenderModeProperty::Mode get_mode() const; + + INLINE void set_line_width(double line_width); + INLINE double get_line_width() const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + RenderModeProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnTransition::init_type(); + register_type(_type_handle, "RenderModeTransition", + OnTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class RenderModeAttribute; +}; + +#include "renderModeTransition.I" + +#endif diff --git a/panda/src/sgattrib/renderRelation.I b/panda/src/sgattrib/renderRelation.I new file mode 100644 index 0000000000..724f2bcac4 --- /dev/null +++ b/panda/src/sgattrib/renderRelation.I @@ -0,0 +1,41 @@ +// Filename: renderRelation.I +// Created by: mike (24Sep99) +// + +//////////////////////////////////////////////////////////////////// +// Function: RenderRelation::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE RenderRelation:: +RenderRelation(Node *from, Node *to, int sort) : + NodeRelation(from, to, sort, get_class_type()) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderRelation::Constructor +// Access: Protected +// Description: Creates a new, unattached arc. This constructor is +// only intended for passing through the factory to +// create an arc based on a particular type using +// create_typed_arc(), below. You shouldn't, in +// general, attempt to create an unattached arc. +//////////////////////////////////////////////////////////////////// +INLINE RenderRelation:: +RenderRelation() : + NodeRelation(get_class_type()) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderRelation::register_with_factory +// Access: Public, Static +// Description: Registers the type RenderRelation with the factory. +// This is only intended to be called at initialization +// time; don't call it directly. +//////////////////////////////////////////////////////////////////// +INLINE void RenderRelation:: +register_with_factory() { + get_factory().register_factory(get_class_type(), make_arc); +} diff --git a/panda/src/sgattrib/renderRelation.N b/panda/src/sgattrib/renderRelation.N new file mode 100644 index 0000000000..4899282a65 --- /dev/null +++ b/panda/src/sgattrib/renderRelation.N @@ -0,0 +1 @@ +ignoremember register_with_factory diff --git a/panda/src/sgattrib/renderRelation.cxx b/panda/src/sgattrib/renderRelation.cxx new file mode 100644 index 0000000000..d1fa5b2ff0 --- /dev/null +++ b/panda/src/sgattrib/renderRelation.cxx @@ -0,0 +1,100 @@ +// Filename: renderRelation.cxx +// Created by: drose (26Oct98) +// + +#include "renderRelation.h" +#include "transformTransition.h" + +#include + +TypeHandle RenderRelation::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: RenderRelation::changed_transition +// Access: Public, Virtual +// Description: This is called by set_transition() or +// clear_transition() whenever a transition is added, +// updated, or removed from the arc. It is just a +// callback to the arc so it can decide whether +// it needs to update any internal data as a response to +// this adjustment (for instance, by marking the +// bounding sphere stale). +//////////////////////////////////////////////////////////////////// +void RenderRelation:: +changed_transition(TypeHandle trans_type) { + if (trans_type == TransformTransition::get_class_type()) { + // Changing the transform means changing our bounding volume. + mark_bound_stale(); + + // And we also might want to tell our node below that the + // transform has changed. + nassertv(get_child() != (Node *)NULL); + get_child()->transform_changed(this); + } + NodeRelation::changed_transition(trans_type); +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderRelation::recompute_bound +// Access: Protected, Virtual +// Description: Recomputes the dynamic bounding volume for this arc +// (and all of its descendants). +//////////////////////////////////////////////////////////////////// +void RenderRelation:: +recompute_bound() { + // First, compute the bounding volume around all of our children. + NodeRelation::recompute_bound(); + assert(_bound != (BoundingVolume *)NULL); + + // Now, if we have a transform transition on the arc, apply it to + // the bounding volume. + GeometricBoundingVolume *gbv; + DCAST_INTO_V(gbv, _bound); + + const TransformTransition *tt; + if (get_transition_into(tt, this)) { + gbv->xform(tt->get_matrix()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderRelation::make_arc +// Access: Public, Static +// Description: This function is passed to the Factory to make a new +// RenderRelation by type. Don't try to call this +// function directly. +//////////////////////////////////////////////////////////////////// +NodeRelation *RenderRelation:: +make_arc(const FactoryParams &) { + return new RenderRelation; +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderRelation::make_RenderRelation +// Access: Protected +// Description: Factory method to generate a RenderRelation object +//////////////////////////////////////////////////////////////////// +TypedWriteable* RenderRelation:: +make_RenderRelation(const FactoryParams ¶ms) +{ + RenderRelation *me = new RenderRelation; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderRelation::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a RenderRelation object +//////////////////////////////////////////////////////////////////// +void RenderRelation:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_RenderRelation); +} diff --git a/panda/src/sgattrib/renderRelation.h b/panda/src/sgattrib/renderRelation.h new file mode 100644 index 0000000000..60f117e173 --- /dev/null +++ b/panda/src/sgattrib/renderRelation.h @@ -0,0 +1,65 @@ +// Filename: renderRelation.h +// Created by: drose (26Oct98) +// + +#ifndef RENDERRELATION_H +#define RENDERRELATION_H + +#include + +#include +#include + +/////////////////////////////////////////////////////////////////// +// Class : RenderRelation +// Description : The arc type specific to renderable scene graphs. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA RenderRelation : public NodeRelation { +public: + INLINE RenderRelation(Node *from, Node *to, int sort = 0); + +protected: + // Normally, this should only be used for passing to the factory. + // Don't attempt to create an unattached arc directly. + INLINE RenderRelation(); + +public: + virtual void changed_transition(TypeHandle transition_type); + +protected: + virtual void recompute_bound(); + +public: + // This is just to be called at initialization time; don't try to + // call this directly. + INLINE static void register_with_factory(); + +private: + static NodeRelation *make_arc(const FactoryParams ¶ms); + +public: + static void register_with_read_factory(void); + + static TypedWriteable *make_RenderRelation(const FactoryParams ¶ms); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NodeRelation::init_type(); + register_type(_type_handle, "RenderRelation", + NodeRelation::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "renderRelation.I" + +#endif diff --git a/panda/src/sgattrib/showHideAttribute.I b/panda/src/sgattrib/showHideAttribute.I new file mode 100644 index 0000000000..646fc0c515 --- /dev/null +++ b/panda/src/sgattrib/showHideAttribute.I @@ -0,0 +1,13 @@ +// Filename: showHideAttribute.I +// Created by: drose (26Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ShowHideAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ShowHideAttribute:: +ShowHideAttribute() { +} diff --git a/panda/src/sgattrib/showHideAttribute.cxx b/panda/src/sgattrib/showHideAttribute.cxx new file mode 100644 index 0000000000..ec2a8df8f1 --- /dev/null +++ b/panda/src/sgattrib/showHideAttribute.cxx @@ -0,0 +1,66 @@ +// Filename: showHideAttribute.cxx +// Created by: drose (26Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "showHideAttribute.h" +#include "showHideTransition.h" + +#include +#include + +TypeHandle ShowHideAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ShowHideAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle ShowHideAttribute:: +get_handle() const { + return ShowHideTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ShowHideAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated ShowHideAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ShowHideAttribute:: +make_copy() const { + return new ShowHideAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ShowHideAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated ShowHideAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ShowHideAttribute:: +make_initial() const { + return new ShowHideAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ShowHideAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void ShowHideAttribute:: +output_property(ostream &out, const PT(Camera) &prop) const { + out << *prop; +} + +//////////////////////////////////////////////////////////////////// +// Function: ShowHideAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void ShowHideAttribute:: +write_property(ostream &out, const PT(Camera) &prop, + int indent_level) const { + indent(out, indent_level) << *prop << "\n"; +} diff --git a/panda/src/sgattrib/showHideAttribute.h b/panda/src/sgattrib/showHideAttribute.h new file mode 100644 index 0000000000..06f67e1dee --- /dev/null +++ b/panda/src/sgattrib/showHideAttribute.h @@ -0,0 +1,59 @@ +// Filename: showHideAttribute.h +// Created by: drose (26Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SHOWHIDEATTRIBUTE_H +#define SHOWHIDEATTRIBUTE_H + +#include + +#include "showHideNameClass.h" + +#include +#include +#include + +// We need to define this temporary macro so we can pass a parameter +// containing a comma through the macro. +#define MULTIATTRIBUTE_CAMERA MultiAttribute +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, MULTIATTRIBUTE_CAMERA); + +//////////////////////////////////////////////////////////////////// +// Class : ShowHideAttribute +// Description : See ShowHideTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ShowHideAttribute : public MultiAttribute { +public: + INLINE ShowHideAttribute(); + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + +protected: + virtual void output_property(ostream &out, const PT(Camera) &prop) const; + virtual void write_property(ostream &out, const PT(Camera) &prop, + int indent_level) const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MultiAttribute::init_type(); + register_type(_type_handle, "ShowHideAttribute", + MultiAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "showHideAttribute.I" + +#endif diff --git a/panda/src/sgattrib/showHideNameClass.h b/panda/src/sgattrib/showHideNameClass.h new file mode 100644 index 0000000000..2577a6ab21 --- /dev/null +++ b/panda/src/sgattrib/showHideNameClass.h @@ -0,0 +1,27 @@ +// Filename: showHideNameClass.h +// Created by: drose (26Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SHOWHIDENAMECLASS_H +#define SHOWHIDENAMECLASS_H + +#include + +//////////////////////////////////////////////////////////////////// +// Class : ShowHideNameClass +// Description : This is a stupid little class that's used by +// ShowHideTransition to define the name of its +// PT(Camera) class, so the MultiTransition it +// inherits from can initialize itself properly. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ShowHideNameClass { +public: + static string get_class_name() { + return "PT(Camera)"; + } +}; + +#endif + + diff --git a/panda/src/sgattrib/showHideTransition.I b/panda/src/sgattrib/showHideTransition.I new file mode 100644 index 0000000000..366ae1bcfc --- /dev/null +++ b/panda/src/sgattrib/showHideTransition.I @@ -0,0 +1,41 @@ +// Filename: showHideTransition.I +// Created by: drose (26Apr00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ShowHideTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ShowHideTransition:: +ShowHideTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ShowHideTransition::all_off +// Access: Public +// Description: This named constructor returns a ShowHideTransition +// that's preconfigured to disable visibility for all +// cameras. +//////////////////////////////////////////////////////////////////// +INLINE ShowHideTransition ShowHideTransition:: +all_off() { + ShowHideTransition t; + t.set_default_dir(TD_off); + return t; +} + +//////////////////////////////////////////////////////////////////// +// Function: ShowHideTransition::all_on +// Access: Public +// Description: This named constructor returns a ShowHideTransition +// that's preconfigured to enable visibility for all +// cameras. +//////////////////////////////////////////////////////////////////// +INLINE ShowHideTransition ShowHideTransition:: +all_on() { + ShowHideTransition t; + t.set_default_dir(TD_on); + return t; +} diff --git a/panda/src/sgattrib/showHideTransition.cxx b/panda/src/sgattrib/showHideTransition.cxx new file mode 100644 index 0000000000..a53579fdab --- /dev/null +++ b/panda/src/sgattrib/showHideTransition.cxx @@ -0,0 +1,65 @@ +// Filename: showHideTransition.cxx +// Created by: drose (26Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "showHideTransition.h" +#include "showHideAttribute.h" + +#include + +TypeHandle ShowHideTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ShowHideTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated ShowHideTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *ShowHideTransition:: +make_copy() const { + return new ShowHideTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ShowHideTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated ShowHideAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *ShowHideTransition:: +make_attrib() const { + return new ShowHideAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: ShowHideTransition::make_identity +// Access: Public, Virtual +// Description: Returns a newly allocated ShowHideTransition in the +// initial state. +//////////////////////////////////////////////////////////////////// +NodeTransition *ShowHideTransition:: +make_identity() const { + return new ShowHideTransition; +} + +//////////////////////////////////////////////////////////////////// +// Function: ShowHideTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void ShowHideTransition:: +output_property(ostream &out, const PT(Camera) &prop) const { + out << *prop; +} + +//////////////////////////////////////////////////////////////////// +// Function: ShowHideTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void ShowHideTransition:: +write_property(ostream &out, const PT(Camera) &prop, + int indent_level) const { + indent(out, indent_level) << *prop << "\n"; +} diff --git a/panda/src/sgattrib/showHideTransition.h b/panda/src/sgattrib/showHideTransition.h new file mode 100644 index 0000000000..8525086369 --- /dev/null +++ b/panda/src/sgattrib/showHideTransition.h @@ -0,0 +1,68 @@ +// Filename: showHideTransition.h +// Created by: drose (26Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SHOWHIDETRANSITION_H +#define SHOWHIDETRANSITION_H + +#include + +#include "showHideNameClass.h" + +#include +#include +#include + +// We need to define this temporary macro so we can pass a parameter +// containing a comma through the macro. +#define MULTITRANSITION_CAMERA MultiTransition +EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, MULTITRANSITION_CAMERA); + +//////////////////////////////////////////////////////////////////// +// Class : ShowHideTransition +// Description : This transition controls which cameras can view the +// geometry. By default, all cameras see everything; +// you can restrict the visibility of a particular +// subgraph to an arbitrary subset of cameras (or to no +// cameras at all). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ShowHideTransition : public MultiTransition { +public: + INLINE ShowHideTransition(); + INLINE static ShowHideTransition all_off(); + INLINE static ShowHideTransition all_on(); + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + virtual NodeTransition *make_identity() const; + +protected: + virtual void output_property(ostream &out, const PT(Camera) &prop) const; + virtual void write_property(ostream &out, const PT(Camera) &prop, + int indent_level) const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MultiTransition::init_type(); + register_type(_type_handle, "ShowHideTransition", + MultiTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class ShowHideAttribute; +}; + +#include "showHideTransition.I" + +#endif + + diff --git a/panda/src/sgattrib/stencilAttribute.I b/panda/src/sgattrib/stencilAttribute.I new file mode 100644 index 0000000000..27bffc606d --- /dev/null +++ b/panda/src/sgattrib/stencilAttribute.I @@ -0,0 +1,55 @@ +// Filename: stencilAttribute.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: StencilAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE StencilAttribute:: +StencilAttribute() : + _value(StencilProperty::M_none, StencilProperty::A_keep) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilAttribute::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void StencilAttribute:: +set_mode(StencilProperty::Mode mode) { + _value.set_mode(mode); +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilAttribute::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE StencilProperty::Mode StencilAttribute:: +get_mode() const { + return _value.get_mode(); +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilAttribute::set_action +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void StencilAttribute:: +set_action(StencilProperty::Action action) { + _value.set_action(action); +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilAttribute::get_action +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE StencilProperty::Action StencilAttribute:: +get_action() const { + return _value.get_action(); +} diff --git a/panda/src/sgattrib/stencilAttribute.cxx b/panda/src/sgattrib/stencilAttribute.cxx new file mode 100644 index 0000000000..29a0b18896 --- /dev/null +++ b/panda/src/sgattrib/stencilAttribute.cxx @@ -0,0 +1,104 @@ +// Filename: stencilAttribute.cxx +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "stencilAttribute.h" +#include "stencilTransition.h" + +#include +#include + +TypeHandle StencilAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: StencilAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle StencilAttribute:: +get_handle() const { + return StencilTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated StencilAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *StencilAttribute:: +make_copy() const { + return new StencilAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated StencilAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *StencilAttribute:: +make_initial() const { + return new StencilAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void StencilAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_stencil(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilAttribute::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the indicated transition +// pointer, which is guaranteed to be of type +// StencilTransition. +//////////////////////////////////////////////////////////////////// +void StencilAttribute:: +set_value_from(const OnTransition *other) { + const StencilTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilAttribute::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int StencilAttribute:: +compare_values(const OnAttribute *other) const { + const StencilAttribute *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void StencilAttribute:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void StencilAttribute:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/stencilAttribute.h b/panda/src/sgattrib/stencilAttribute.h new file mode 100644 index 0000000000..b8e958044a --- /dev/null +++ b/panda/src/sgattrib/stencilAttribute.h @@ -0,0 +1,64 @@ +// Filename: stencilAttribute.h +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef STENCILATTRIBUTE_H +#define STENCILATTRIBUTE_H + +#include + +#include "stencilProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : StencilAttribute +// Description : See StencilTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA StencilAttribute : public OnAttribute { +public: + INLINE StencilAttribute(); + + INLINE void set_mode(StencilProperty::Mode mode); + INLINE StencilProperty::Mode get_mode() const; + + INLINE void set_action(StencilProperty::Action action); + INLINE StencilProperty::Action get_action() const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + StencilProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnAttribute::init_type(); + register_type(_type_handle, "StencilAttribute", + OnAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "stencilAttribute.I" + +#endif + diff --git a/panda/src/sgattrib/stencilProperty.I b/panda/src/sgattrib/stencilProperty.I new file mode 100644 index 0000000000..a9bf58cf58 --- /dev/null +++ b/panda/src/sgattrib/stencilProperty.I @@ -0,0 +1,69 @@ +// Filename: stencilProperty.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: StencilProperty::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE StencilProperty:: +StencilProperty(StencilProperty::Mode mode, StencilProperty::Action action) : + _mode(mode), + _action(action) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilProperty::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void StencilProperty:: +set_mode(StencilProperty::Mode mode) { + _mode = mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilProperty::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE StencilProperty::Mode StencilProperty:: +get_mode() const { + return _mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilProperty::set_action +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void StencilProperty:: +set_action(StencilProperty::Action action) { + _action = action; +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilProperty::get_action +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE StencilProperty::Action StencilProperty:: +get_action() const { + return _action; +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilProperty::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int StencilProperty:: +compare_to(const StencilProperty &other) const { + if (_mode != other._mode) { + return (int)_mode - (int)other._mode; + } + return (int)_action - (int)other._action; +} diff --git a/panda/src/sgattrib/stencilProperty.cxx b/panda/src/sgattrib/stencilProperty.cxx new file mode 100644 index 0000000000..e0ec09662f --- /dev/null +++ b/panda/src/sgattrib/stencilProperty.cxx @@ -0,0 +1,78 @@ +// Filename: stencilProperty.cxx +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "stencilProperty.h" + +ostream & +operator << (ostream &out, StencilProperty::Mode mode) { + switch (mode) { + case StencilProperty::M_none: + return out << "none"; + + case StencilProperty::M_never: + return out << "never"; + + case StencilProperty::M_less: + return out << "less"; + + case StencilProperty::M_equal: + return out << "equal"; + + case StencilProperty::M_less_equal: + return out << "less_equal"; + + case StencilProperty::M_greater: + return out << "greater"; + + case StencilProperty::M_not_equal: + return out << "not_equal"; + + case StencilProperty::M_greater_equal: + return out << "greater_equal"; + + case StencilProperty::M_always: + return out << "always"; + } + + return out << "**invalid**(" << (int)mode << ")"; +} + +ostream & +operator << (ostream &out, StencilProperty::Action action) { + switch (action) { + case StencilProperty::A_keep: + return out << "keep"; + + case StencilProperty::A_zero: + return out << "zero"; + + case StencilProperty::A_replace: + return out << "replace"; + + case StencilProperty::A_increment: + return out << "increment"; + + case StencilProperty::A_decrement: + return out << "decrement"; + + case StencilProperty::A_invert: + return out << "invert"; + } + + return out << "**invalid**(" << (int)action << ")"; +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilProperty::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void StencilProperty:: +output(ostream &out) const { + out << _mode; + if (_mode != M_none) { + out << ":" << _action; + } +} diff --git a/panda/src/sgattrib/stencilProperty.h b/panda/src/sgattrib/stencilProperty.h new file mode 100644 index 0000000000..8316d6311d --- /dev/null +++ b/panda/src/sgattrib/stencilProperty.h @@ -0,0 +1,66 @@ +// Filename: stencilProperty.h +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef STENCILPROPERTY_H +#define STENCILPROPERTY_H + +#include + +//////////////////////////////////////////////////////////////////// +// Class : StencilProperty +// Description : Defines how we can write to and stencil via the +// stencil buffer. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA StencilProperty { +public: + enum Mode { + M_none, // No stencil test, and no write to stencil buffer. + M_never, // Never draw. + M_less, // incoming < stored + M_equal, // incoming == stored + M_less_equal, // incoming <= stored + M_greater, // incoming > stored + M_not_equal, // incoming != stored + M_greater_equal, // incoming >= stored + M_always // Always draw. + }; + + enum Action { // What to do to the stencil when the above test fails. + A_keep, + A_zero, + A_replace, + A_increment, + A_decrement, + A_invert + }; + +public: + INLINE StencilProperty(Mode mode, Action action); + + INLINE void set_mode(Mode mode); + INLINE Mode get_mode() const; + + INLINE void set_action(Action action); + INLINE Action get_action() const; + + INLINE int compare_to(const StencilProperty &other) const; + void output(ostream &out) const; + +private: + Mode _mode; + Action _action; +}; + +ostream &operator << (ostream &out, StencilProperty::Mode mode); +ostream &operator << (ostream &out, StencilProperty::Action action); + +INLINE ostream &operator << (ostream &out, const StencilProperty &prop) { + prop.output(out); + return out; +} + +#include "stencilProperty.I" + +#endif diff --git a/panda/src/sgattrib/stencilTransition.I b/panda/src/sgattrib/stencilTransition.I new file mode 100644 index 0000000000..b2a60c0155 --- /dev/null +++ b/panda/src/sgattrib/stencilTransition.I @@ -0,0 +1,58 @@ +// Filename: stencilTransition.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: StencilTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE StencilTransition:: +StencilTransition(StencilProperty::Mode mode, + StencilProperty::Action action) : + _value(mode, action) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilTransition::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void StencilTransition:: +set_mode(StencilProperty::Mode mode) { + _value.set_mode(mode); + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilTransition::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE StencilProperty::Mode StencilTransition:: +get_mode() const { + return _value.get_mode(); +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilTransition::set_action +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void StencilTransition:: +set_action(StencilProperty::Action action) { + _value.set_action(action); + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilTransition::get_action +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE StencilProperty::Action StencilTransition:: +get_action() const { + return _value.get_action(); +} diff --git a/panda/src/sgattrib/stencilTransition.cxx b/panda/src/sgattrib/stencilTransition.cxx new file mode 100644 index 0000000000..96d27aeae8 --- /dev/null +++ b/panda/src/sgattrib/stencilTransition.cxx @@ -0,0 +1,78 @@ +// Filename: stencilTransition.cxx +// Created by: mike (28Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "stencilTransition.h" +#include "stencilAttribute.h" + +#include + +TypeHandle StencilTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: StencilTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated StencilTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *StencilTransition:: +make_copy() const { + return new StencilTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated StencilAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *StencilTransition:: +make_attrib() const { + return new StencilAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilTransition::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the other transition pointer, +// which is guaranteed to be another StencilTransition. +//////////////////////////////////////////////////////////////////// +void StencilTransition:: +set_value_from(const OnTransition *other) { + const StencilTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilTransition::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int StencilTransition:: +compare_values(const OnTransition *other) const { + const StencilTransition *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void StencilTransition:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: StencilTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void StencilTransition:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/stencilTransition.h b/panda/src/sgattrib/stencilTransition.h new file mode 100644 index 0000000000..357f699154 --- /dev/null +++ b/panda/src/sgattrib/stencilTransition.h @@ -0,0 +1,68 @@ +// Filename: stencilTransition.h +// Created by: mike (18Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef STENCILTRANSITION_H +#define STENCILTRANSITION_H + +#include + +#include "stencilProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : StencilTransition +// Description : This is scene graph stencil (which is not to be +// confused with geometry stencil). By setting a +// StencilTransition on the scene graph, you can override +// the stencil at the object level, but not at the +// individual primitive or vertex level. If a scene +// graph stencil is set, it overrides the primitive stencil; +// otherwise, the primitive stencil shows through. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA StencilTransition : public OnTransition { +public: + INLINE StencilTransition(StencilProperty::Mode mode, + StencilProperty::Action action = StencilProperty::A_keep); + + INLINE void set_mode(StencilProperty::Mode mode); + INLINE StencilProperty::Mode get_mode() const; + + INLINE void set_action(StencilProperty::Action action); + INLINE StencilProperty::Action get_action() const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + StencilProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnTransition::init_type(); + register_type(_type_handle, "StencilTransition", + OnTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class StencilAttribute; +}; + +#include "stencilTransition.I" + +#endif diff --git a/panda/src/sgattrib/test_attrib.cxx b/panda/src/sgattrib/test_attrib.cxx new file mode 100644 index 0000000000..5975632376 --- /dev/null +++ b/panda/src/sgattrib/test_attrib.cxx @@ -0,0 +1,153 @@ +// Filename: test_attrib.cxx +// Created by: drose (18Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "colorTransition.h" +#include "textureTransition.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +class PrintNodes : + public TraverserVisitor { +public: + PrintNodes() { + _indent_level = 0; + } + bool reached_node(Node *node, AttributeWrapper &state, + NullLevelState &) { + indent(cerr, _indent_level) + << "\nReached " << *node << ", state is:\n"; + state.write(cerr, _indent_level); + return true; + } + bool forward_arc(NodeRelation *arc, TransitionWrapper &trans, + AttributeWrapper &pre, AttributeWrapper &post, + NullLevelState &) { + _indent_level += 2; + return true; + } + void backward_arc(NodeRelation *arc, TransitionWrapper &trans, + AttributeWrapper &pre, AttributeWrapper &post, + const NullLevelState &) { + _indent_level -= 2; + } + int _indent_level; +}; + + +int +main(int argc, char *argv[]) { + PT_NamedNode r = new NamedNode("r"); + + PT_NamedNode a = new NamedNode("a"); + PT_NamedNode b = new NamedNode("b"); + + PT_NamedNode aa = new NamedNode("aa"); + PT_NamedNode ab = new NamedNode("ab"); + PT_NamedNode ba = new NamedNode("ba"); + + NodeRelation *r_a = + new NodeRelation(r, a, 0, NodeRelation::get_class_type()); + NodeRelation *r_b = + new NodeRelation(r, b, 0, NodeRelation::get_class_type()); + + NodeRelation *a_aa = + new NodeRelation(a, aa, 0, NodeRelation::get_class_type()); + NodeRelation *a_ab = + new NodeRelation(a, ab, 0, NodeRelation::get_class_type()); + NodeRelation *b_ba = + new NodeRelation(b, ba, 0, NodeRelation::get_class_type()); + + PT(Texture) alpha = new Texture; + alpha->set_name("alpha"); + PT(Texture) beta = new Texture; + beta->set_name("beta"); + + r_a->set_transition(new ColorTransition(1.0, 1.0, 1.0, 1.0)); + r_a->set_transition(new TextureTransition(alpha)); + a_aa->set_transition(new ColorTransition(0.5, 1.0, 0.5, 0.2)); + a_ab->set_transition(new TextureTransition(beta)); + + cerr << "\nr to a has:\n"; + r_a->write_transitions(cerr, 2); + cerr << "\nr to b has:\n"; + r_b->write_transitions(cerr, 2); + cerr << "\na to aa has:\n"; + a_aa->write_transitions(cerr, 2); + cerr << "\na to ab has:\n"; + a_ab->write_transitions(cerr, 2); + cerr << "\nb to ba has:\n"; + b_ba->write_transitions(cerr, 2); + cerr << "\n"; + + { + cerr << "\n"; + PrintNodes pn; + df_traverse(r, pn, + AllAttributesWrapper(), + NullLevelState(), + NodeRelation::get_class_type()); + cerr << "\n"; + + AllTransitionsWrapper result; + wrt(r, aa, result, NodeRelation::get_class_type()); + cerr << "\nwrt of r to aa is:\n"; + result.write(cerr, 2); + + wrt(aa, r, result, NodeRelation::get_class_type()); + cerr << "\nwrt of aa to r is:\n"; + result.write(cerr, 2); + + wrt(r, ab, result, NodeRelation::get_class_type()); + cerr << "\nwrt of r to ab is:\n"; + result.write(cerr, 2); + + wrt(ab, r, result, NodeRelation::get_class_type()); + cerr << "\nwrt of ab to r is:\n"; + result.write(cerr, 2); + + wrt(ab, aa, result, NodeRelation::get_class_type()); + cerr << "\nwrt of ab to aa is:\n"; + result.write(cerr, 2); + + wrt(aa, ab, result, NodeRelation::get_class_type()); + cerr << "\nwrt of aa to ab is:\n"; + result.write(cerr, 2); + + wrt(r, ba, result, NodeRelation::get_class_type()); + cerr << "\nwrt of r to ba is:\n"; + result.write(cerr, 2); + + wrt(ba, r, result, NodeRelation::get_class_type()); + cerr << "\nwrt of ba to r is:\n"; + result.write(cerr, 2); + + wrt(aa, ba, result, NodeRelation::get_class_type()); + cerr << "\nwrt of aa to ba is:\n"; + result.write(cerr, 2); + + wrt(ba, aa, result, NodeRelation::get_class_type()); + cerr << "\nwrt of ba to aa is:\n"; + result.write(cerr, 2); + + wrt(ab, ba, result, NodeRelation::get_class_type()); + cerr << "\nwrt of ab to ba is:\n"; + result.write(cerr, 2); + + wrt(ba, ab, result, NodeRelation::get_class_type()); + cerr << "\nwrt of ba to ab is:\n"; + result.write(cerr, 2); + } + + return (0); +} + diff --git a/panda/src/sgattrib/texGenAttribute.I b/panda/src/sgattrib/texGenAttribute.I new file mode 100644 index 0000000000..6c75055d71 --- /dev/null +++ b/panda/src/sgattrib/texGenAttribute.I @@ -0,0 +1,66 @@ +// Filename: texGenAttribute.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TexGenAttribute:: +TexGenAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttribute::set_none +// Access: Public +// Description: Sets the attribute to disable texture coordinate +// generation. +//////////////////////////////////////////////////////////////////// +INLINE void TexGenAttribute:: +set_none() { + _value = TexGenProperty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttribute::set_texture_projector +// Access: Public +// Description: Sets the attribute to project textures through a +// perpective frustum. +//////////////////////////////////////////////////////////////////// +INLINE void TexGenAttribute:: +set_texture_projector() { + _value = TexGenProperty::texture_projector(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttribute::set_sphere_map +// Access: Public +// Description: Sets the attribute to project textures as sphere +// maps. +//////////////////////////////////////////////////////////////////// +INLINE void TexGenAttribute:: +set_sphere_map() { + _value = TexGenProperty::sphere_map(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttribute::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TexGenProperty::Mode TexGenAttribute:: +get_mode() const { + return _value.get_mode(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttribute::get_plane +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LMatrix4f TexGenAttribute:: +get_plane() const { + return _value.get_plane(); +} diff --git a/panda/src/sgattrib/texGenAttribute.cxx b/panda/src/sgattrib/texGenAttribute.cxx new file mode 100644 index 0000000000..bc567dcc49 --- /dev/null +++ b/panda/src/sgattrib/texGenAttribute.cxx @@ -0,0 +1,104 @@ +// Filename: texGenAttribute.cxx +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "texGenAttribute.h" +#include "texGenTransition.h" + +#include +#include + +TypeHandle TexGenAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle TexGenAttribute:: +get_handle() const { + return TexGenTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated TexGenAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TexGenAttribute:: +make_copy() const { + return new TexGenAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated TexGenAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TexGenAttribute:: +make_initial() const { + return new TexGenAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void TexGenAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_tex_gen(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttribute::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the indicated transition +// pointer, which is guaranteed to be of type +// TexGenTransition. +//////////////////////////////////////////////////////////////////// +void TexGenAttribute:: +set_value_from(const OnTransition *other) { + const TexGenTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttribute::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int TexGenAttribute:: +compare_values(const OnAttribute *other) const { + const TexGenAttribute *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void TexGenAttribute:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void TexGenAttribute:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/texGenAttribute.h b/panda/src/sgattrib/texGenAttribute.h new file mode 100644 index 0000000000..1465afab5c --- /dev/null +++ b/panda/src/sgattrib/texGenAttribute.h @@ -0,0 +1,64 @@ +// Filename: texGenAttribute.h +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TEXGENATTRIBUTE_H +#define TEXGENATTRIBUTE_H + +#include + +#include "texGenProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : TexGenAttribute +// Description : See TexGenTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TexGenAttribute : public OnAttribute { +public: + INLINE TexGenAttribute(); + + INLINE void set_none(); + INLINE void set_texture_projector(); + INLINE void set_sphere_map(); + + INLINE TexGenProperty::Mode get_mode() const; + INLINE LMatrix4f get_plane() const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + TexGenProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnAttribute::init_type(); + register_type(_type_handle, "TexGenAttribute", + OnAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "texGenAttribute.I" + +#endif diff --git a/panda/src/sgattrib/texGenProperty.I b/panda/src/sgattrib/texGenProperty.I new file mode 100644 index 0000000000..55125e7d22 --- /dev/null +++ b/panda/src/sgattrib/texGenProperty.I @@ -0,0 +1,74 @@ +// Filename: texGenProperty.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TexGenProperty::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TexGenProperty:: +TexGenProperty() : + _mode(M_none) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenProperty::texture_projector +// Access: Public +// Description: This named constructor creates a TexGenProperty +// suitable for projecting textures through a +// perspective frustum. +//////////////////////////////////////////////////////////////////// +INLINE TexGenProperty TexGenProperty:: +texture_projector() { + TexGenProperty p; + p._mode = M_texture_projector; + p._plane = LMatrix4f::ident_mat(); + return p; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenProperty::sphere_map +// Access: Public +// Description: This named constructor creates a TexGenProperty +// suitable for applying textures as a sphere map. +//////////////////////////////////////////////////////////////////// +INLINE TexGenProperty TexGenProperty:: +sphere_map() { + TexGenProperty p; + p._mode = M_sphere_map; + return p; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenProperty::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TexGenProperty::Mode TexGenProperty:: +get_mode() const { + return _mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenProperty::get_plane +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const LMatrix4f &TexGenProperty:: +get_plane() const { + return _plane; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenProperty::Equality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int TexGenProperty:: +compare_to(const TexGenProperty &other) const { + return (int)_mode - (int)other._mode; + // Should we compare planes also? +} diff --git a/panda/src/sgattrib/texGenProperty.cxx b/panda/src/sgattrib/texGenProperty.cxx new file mode 100644 index 0000000000..f8fb9218ce --- /dev/null +++ b/panda/src/sgattrib/texGenProperty.cxx @@ -0,0 +1,35 @@ +// Filename: texGenProperty.cxx +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "texGenProperty.h" + +ostream & +operator << (ostream &out, TexGenProperty::Mode mode) { + switch (mode) { + case TexGenProperty::M_none: + return out << "none"; + + case TexGenProperty::M_eye_linear: + return out << "eye_linear"; + + case TexGenProperty::M_texture_projector: + return out << "texture_projector"; + + case TexGenProperty::M_sphere_map: + return out << "sphere_map"; + } + + return out << "**invalid**(" << (int)mode << ")"; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenProperty::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void TexGenProperty:: +output(ostream &out) const { + out << _mode; +} diff --git a/panda/src/sgattrib/texGenProperty.h b/panda/src/sgattrib/texGenProperty.h new file mode 100644 index 0000000000..e2b8db270e --- /dev/null +++ b/panda/src/sgattrib/texGenProperty.h @@ -0,0 +1,53 @@ +// Filename: texGenProperty.h +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TEXGENPROPERTY_H +#define TEXGENPROPERTY_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : TexGenProperty +// Description : This defines the kinds of texture coordinates that +// may be automatically generated at render time. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TexGenProperty { +public: + enum Mode { + M_none, + M_eye_linear, + M_texture_projector, + M_sphere_map, + }; + +public: + INLINE TexGenProperty(); + INLINE static TexGenProperty texture_projector(); + INLINE static TexGenProperty sphere_map(); + + INLINE Mode get_mode() const; + INLINE const LMatrix4f &get_plane() const; + + INLINE int compare_to(const TexGenProperty &other) const; + + void output(ostream &out) const; + +private: + Mode _mode; + LMatrix4f _plane; +}; + +ostream &operator << (ostream &out, TexGenProperty::Mode mode); + +INLINE ostream &operator << (ostream &out, const TexGenProperty &prop) { + prop.output(out); + return out; +} + +#include "texGenProperty.I" + +#endif diff --git a/panda/src/sgattrib/texGenTransition.I b/panda/src/sgattrib/texGenTransition.I new file mode 100644 index 0000000000..e9cfdc7e50 --- /dev/null +++ b/panda/src/sgattrib/texGenTransition.I @@ -0,0 +1,95 @@ +// Filename: texGenTransition.I +// Created by: drose (24Sep99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TexGenTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TexGenTransition:: +TexGenTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenTransition::texture_projector +// Access: Public +// Description: This named constructor creates a transition to +// project textures through a perpective frustum. +//////////////////////////////////////////////////////////////////// +INLINE TexGenTransition TexGenTransition:: +texture_projector() { + TexGenTransition result; + result.set_texture_projector(); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenTransition::sphere_map +// Access: Public +// Description: This named constructor creates a transition to +// project textures as sphere maps. +//////////////////////////////////////////////////////////////////// +INLINE TexGenTransition TexGenTransition:: +sphere_map() { + TexGenTransition result; + result.set_sphere_map(); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenTransition::set_none +// Access: Public +// Description: Sets the transition to disable texture coordinate +// generation. +//////////////////////////////////////////////////////////////////// +INLINE void TexGenTransition:: +set_none() { + _value = TexGenProperty(); + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenTransition::set_texture_projector +// Access: Public +// Description: Sets the transition to project textures through a +// perpective frustum. +//////////////////////////////////////////////////////////////////// +INLINE void TexGenTransition:: +set_texture_projector() { + _value = TexGenProperty::texture_projector(); + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenTransition::set_sphere_map +// Access: Public +// Description: Sets the transition to project textures as sphere +// maps. +//////////////////////////////////////////////////////////////////// +INLINE void TexGenTransition:: +set_sphere_map() { + _value = TexGenProperty::sphere_map(); + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenTransition::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TexGenProperty::Mode TexGenTransition:: +get_mode() const { + return _value.get_mode(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenTransition::get_plane +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LMatrix4f TexGenTransition:: +get_plane() const { + return _value.get_plane(); +} diff --git a/panda/src/sgattrib/texGenTransition.cxx b/panda/src/sgattrib/texGenTransition.cxx new file mode 100644 index 0000000000..17304671ed --- /dev/null +++ b/panda/src/sgattrib/texGenTransition.cxx @@ -0,0 +1,78 @@ +// Filename: texGenTransition.cxx +// Created by: mike (28Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "texGenTransition.h" +#include "texGenAttribute.h" + +#include + +TypeHandle TexGenTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: TexGenTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated TexGenTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *TexGenTransition:: +make_copy() const { + return new TexGenTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated TexGenAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TexGenTransition:: +make_attrib() const { + return new TexGenAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenTransition::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the other transition pointer, +// which is guaranteed to be another TexGenTransition. +//////////////////////////////////////////////////////////////////// +void TexGenTransition:: +set_value_from(const OnTransition *other) { + const TexGenTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenTransition::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int TexGenTransition:: +compare_values(const OnTransition *other) const { + const TexGenTransition *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void TexGenTransition:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void TexGenTransition:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/texGenTransition.h b/panda/src/sgattrib/texGenTransition.h new file mode 100644 index 0000000000..1e2a50add6 --- /dev/null +++ b/panda/src/sgattrib/texGenTransition.h @@ -0,0 +1,65 @@ +// Filename: texGenTransition.h +// Created by: mike (18Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TEXGENTRANSITION_H +#define TEXGENTRANSITION_H + +#include + +#include "texGenProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : TexGenTransition +// Description : This controls the kinds of blending between colors +// being rendered and the existing frame buffer. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TexGenTransition : public OnTransition { +public: + INLINE TexGenTransition(); + INLINE static TexGenTransition texture_projector(); + INLINE static TexGenTransition sphere_map(); + + INLINE void set_none(); + INLINE void set_texture_projector(); + INLINE void set_sphere_map(); + + INLINE TexGenProperty::Mode get_mode() const; + INLINE LMatrix4f get_plane() const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + TexGenProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnTransition::init_type(); + register_type(_type_handle, "TexGenTransition", + OnTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class TexGenAttribute; +}; + +#include "texGenTransition.I" + +#endif diff --git a/panda/src/sgattrib/texMatrixAttribute.I b/panda/src/sgattrib/texMatrixAttribute.I new file mode 100644 index 0000000000..9d01641d32 --- /dev/null +++ b/panda/src/sgattrib/texMatrixAttribute.I @@ -0,0 +1,13 @@ +// Filename: texMatrixAttribute.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TexMatrixAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TexMatrixAttribute:: +TexMatrixAttribute() { +} diff --git a/panda/src/sgattrib/texMatrixAttribute.cxx b/panda/src/sgattrib/texMatrixAttribute.cxx new file mode 100644 index 0000000000..a171f83670 --- /dev/null +++ b/panda/src/sgattrib/texMatrixAttribute.cxx @@ -0,0 +1,57 @@ +// Filename: texMatrixAttribute.cxx +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "texMatrixAttribute.h" +#include "texMatrixTransition.h" + +#include +#include + +TypeHandle TexMatrixAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: TexMatrixAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle TexMatrixAttribute:: +get_handle() const { + return TexMatrixTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexMatrixAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated TexMatrixAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TexMatrixAttribute:: +make_copy() const { + return new TexMatrixAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexMatrixAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated TexMatrixAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TexMatrixAttribute:: +make_initial() const { + return new TexMatrixAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexMatrixAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void TexMatrixAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_tex_matrix(this); +} diff --git a/panda/src/sgattrib/texMatrixAttribute.h b/panda/src/sgattrib/texMatrixAttribute.h new file mode 100644 index 0000000000..842daa177f --- /dev/null +++ b/panda/src/sgattrib/texMatrixAttribute.h @@ -0,0 +1,48 @@ +// Filename: texMatrixAttribute.h +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TEXMATRIXATTRIBUTE_H +#define TEXMATRIXATTRIBUTE_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : TexMatrixAttribute +// Description : See TexMatrixTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TexMatrixAttribute : public LMatrix4fAttribute { +public: + INLINE TexMatrixAttribute(); + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + LMatrix4fAttribute::init_type(); + register_type(_type_handle, "TexMatrixAttribute", + LMatrix4fAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "texMatrixAttribute.I" + +#endif diff --git a/panda/src/sgattrib/texMatrixTransition.I b/panda/src/sgattrib/texMatrixTransition.I new file mode 100644 index 0000000000..16314e9b90 --- /dev/null +++ b/panda/src/sgattrib/texMatrixTransition.I @@ -0,0 +1,24 @@ +// Filename: texMatrixTransition.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TexMatrixTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TexMatrixTransition:: +TexMatrixTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: TexMatrixTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TexMatrixTransition:: +TexMatrixTransition(const LMatrix4f &matrix) : + LMatrix4fTransition(matrix) +{ +} diff --git a/panda/src/sgattrib/texMatrixTransition.cxx b/panda/src/sgattrib/texMatrixTransition.cxx new file mode 100644 index 0000000000..fe1cec3872 --- /dev/null +++ b/panda/src/sgattrib/texMatrixTransition.cxx @@ -0,0 +1,42 @@ +// Filename: texMatrixTransition.cxx +// Created by: mike (19Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "texMatrixTransition.h" +#include "texMatrixAttribute.h" + +#include + +TypeHandle TexMatrixTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: TexMatrixTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated TexMatrixTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *TexMatrixTransition:: +make_copy() const { + return new TexMatrixTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexMatrixTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated TexMatrixAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TexMatrixTransition:: +make_attrib() const { + return new TexMatrixAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexMatrixTransition::make_with_matrix +// Access: Protected, Virtual +// Description: Returns a new transition with the indicated matrix. +//////////////////////////////////////////////////////////////////// +MatrixTransition *TexMatrixTransition:: +make_with_matrix(const LMatrix4f &matrix) const { + return new TexMatrixTransition(matrix); +} diff --git a/panda/src/sgattrib/texMatrixTransition.h b/panda/src/sgattrib/texMatrixTransition.h new file mode 100644 index 0000000000..c6a24ced56 --- /dev/null +++ b/panda/src/sgattrib/texMatrixTransition.h @@ -0,0 +1,54 @@ +// Filename: texMatrixTransition.h +// Created by: mike (19Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TEXMATRIXTRANSITION_H +#define TEXMATRIXTRANSITION_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : TexMatrixTransition +// Description : If present, this modifies the texture coordinates on +// geometry as it is rendered. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TexMatrixTransition : public LMatrix4fTransition { +public: + INLINE TexMatrixTransition(); + INLINE TexMatrixTransition(const LMatrix4f &matrix); + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + virtual MatrixTransition * + make_with_matrix(const LMatrix4f &matrix) const; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + LMatrix4fTransition::init_type(); + register_type(_type_handle, "TexMatrixTransition", + LMatrix4fTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class TexMatrixAttribute; +}; + +#include "texMatrixTransition.I" + +#endif + + diff --git a/panda/src/sgattrib/textureApplyAttribute.I b/panda/src/sgattrib/textureApplyAttribute.I new file mode 100644 index 0000000000..33754b0296 --- /dev/null +++ b/panda/src/sgattrib/textureApplyAttribute.I @@ -0,0 +1,35 @@ +// Filename: textureApplyAttribute.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TextureApplyAttribute:: +TextureApplyAttribute() : + _value(TextureApplyProperty::M_modulate) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyAttribute::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TextureApplyAttribute:: +set_mode(TextureApplyProperty::Mode mode) { + _value.set_mode(mode); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyAttribute::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TextureApplyProperty::Mode TextureApplyAttribute:: +get_mode() const { + return _value.get_mode(); +} diff --git a/panda/src/sgattrib/textureApplyAttribute.cxx b/panda/src/sgattrib/textureApplyAttribute.cxx new file mode 100644 index 0000000000..06f4d36d3a --- /dev/null +++ b/panda/src/sgattrib/textureApplyAttribute.cxx @@ -0,0 +1,104 @@ +// Filename: textureApplyAttribute.cxx +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "textureApplyAttribute.h" +#include "textureApplyTransition.h" + +#include +#include + +TypeHandle TextureApplyAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle TextureApplyAttribute:: +get_handle() const { + return TextureApplyTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated TextureApplyAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TextureApplyAttribute:: +make_copy() const { + return new TextureApplyAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated TextureApplyAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TextureApplyAttribute:: +make_initial() const { + return new TextureApplyAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void TextureApplyAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_texture_apply(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyAttribute::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the indicated transition +// pointer, which is guaranteed to be of type +// TextureApplyTransition. +//////////////////////////////////////////////////////////////////// +void TextureApplyAttribute:: +set_value_from(const OnTransition *other) { + const TextureApplyTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyAttribute::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int TextureApplyAttribute:: +compare_values(const OnAttribute *other) const { + const TextureApplyAttribute *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void TextureApplyAttribute:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void TextureApplyAttribute:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/textureApplyAttribute.h b/panda/src/sgattrib/textureApplyAttribute.h new file mode 100644 index 0000000000..d402a1db5c --- /dev/null +++ b/panda/src/sgattrib/textureApplyAttribute.h @@ -0,0 +1,60 @@ +// Filename: textureApplyAttribute.h +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TEXTUREAPPLYATTRIBUTE_H +#define TEXTUREAPPLYATTRIBUTE_H + +#include + +#include "textureApplyProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : TextureApplyAttribute +// Description : See TextureApplyTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TextureApplyAttribute : public OnAttribute { +public: + INLINE TextureApplyAttribute(); + + INLINE void set_mode(TextureApplyProperty::Mode mode); + INLINE TextureApplyProperty::Mode get_mode() const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + TextureApplyProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnAttribute::init_type(); + register_type(_type_handle, "TextureApplyAttribute", + OnAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "textureApplyAttribute.I" + +#endif diff --git a/panda/src/sgattrib/textureApplyProperty.I b/panda/src/sgattrib/textureApplyProperty.I new file mode 100644 index 0000000000..c0f4c8d9ec --- /dev/null +++ b/panda/src/sgattrib/textureApplyProperty.I @@ -0,0 +1,60 @@ +// Filename: textureApplyProperty.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyProperty::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TextureApplyProperty:: +TextureApplyProperty(TextureApplyProperty::Mode mode) : + _mode(mode) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyProperty::Constructor +// Access: Public +// Description: This should only be called in the Protected +// constructor of TextureApplyTransition. It is made +// public, only for that to access, and is necessary +// for creating an object in make_TextureApplyTransition +// before any information has been read +//////////////////////////////////////////////////////////////////// +INLINE TextureApplyProperty:: +TextureApplyProperty(void) +{ +} + + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyProperty::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TextureApplyProperty:: +set_mode(TextureApplyProperty::Mode mode) { + _mode = mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyProperty::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TextureApplyProperty::Mode TextureApplyProperty:: +get_mode() const { + return _mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyProperty::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int TextureApplyProperty:: +compare_to(const TextureApplyProperty &other) const { + return (int)_mode - (int)other._mode; +} diff --git a/panda/src/sgattrib/textureApplyProperty.cxx b/panda/src/sgattrib/textureApplyProperty.cxx new file mode 100644 index 0000000000..ca033789c5 --- /dev/null +++ b/panda/src/sgattrib/textureApplyProperty.cxx @@ -0,0 +1,67 @@ +// Filename: textureApplyProperty.cxx +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "textureApplyProperty.h" +#include +#include + +ostream & +operator << (ostream &out, TextureApplyProperty::Mode mode) { + switch (mode) { + case TextureApplyProperty::M_modulate: + return out << "modulate"; + + case TextureApplyProperty::M_decal: + return out << "decal"; + + case TextureApplyProperty::M_blend: + return out << "blend"; + + case TextureApplyProperty::M_replace: + return out << "replace"; + + case TextureApplyProperty::M_add: + return out << "add"; + } + + return out << "**invalid**(" << (int)mode << ")"; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyProperty::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void TextureApplyProperty:: +output(ostream &out) const { + out << _mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyProperty::write_datagram +// Access: Public +// Description: Function to write the important information in +// this object to a Datagram +//////////////////////////////////////////////////////////////////// +void TextureApplyProperty:: +write_datagram(Datagram &destination) +{ + destination.add_uint8(_mode); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyProperty::read_datagram +// Access: Public +// Description: Function to write the important information into +// this object out of a Datagram +//////////////////////////////////////////////////////////////////// +void TextureApplyProperty:: +read_datagram(DatagramIterator &source) +{ + _mode = (enum Mode) source.get_uint8(); +} + + + diff --git a/panda/src/sgattrib/textureApplyProperty.h b/panda/src/sgattrib/textureApplyProperty.h new file mode 100644 index 0000000000..1056bade14 --- /dev/null +++ b/panda/src/sgattrib/textureApplyProperty.h @@ -0,0 +1,57 @@ +// Filename: textureApplyProperty.h +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TEXTUREAPPLYPROPERTY_H +#define TEXTUREAPPLYPROPERTY_H + +#include + +class Datagram; +class DatagramIterator; + +//////////////////////////////////////////////////////////////////// +// Class : TextureApplyProperty +// Description : Defines the way texture colors modify existing +// geometry colors. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TextureApplyProperty { +public: + enum Mode { + M_modulate, + M_decal, + M_blend, + M_replace, + M_add + }; + +public: + INLINE TextureApplyProperty(Mode mode); + INLINE TextureApplyProperty(void); + + INLINE void set_mode(Mode mode); + INLINE Mode get_mode() const; + + INLINE int compare_to(const TextureApplyProperty &other) const; + + void output(ostream &out) const; + +public: + void write_datagram(Datagram &destination); + void read_datagram(DatagramIterator &source); + +private: + Mode _mode; +}; + +ostream &operator << (ostream &out, TextureApplyProperty::Mode mode); + +INLINE ostream &operator << (ostream &out, const TextureApplyProperty &prop) { + prop.output(out); + return out; +} + +#include "textureApplyProperty.I" + +#endif diff --git a/panda/src/sgattrib/textureApplyTransition.I b/panda/src/sgattrib/textureApplyTransition.I new file mode 100644 index 0000000000..72133033b4 --- /dev/null +++ b/panda/src/sgattrib/textureApplyTransition.I @@ -0,0 +1,46 @@ +// Filename: textureApplyTransition.I +// Created by: drose (23Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TextureApplyTransition:: +TextureApplyTransition(TextureApplyProperty::Mode mode) : + _value(mode) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyTransition::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TextureApplyTransition:: +TextureApplyTransition(void) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyTransition::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TextureApplyTransition:: +set_mode(TextureApplyProperty::Mode mode) { + _value.set_mode(mode); + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyTransition::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TextureApplyProperty::Mode TextureApplyTransition:: +get_mode() const { + return _value.get_mode(); +} diff --git a/panda/src/sgattrib/textureApplyTransition.cxx b/panda/src/sgattrib/textureApplyTransition.cxx new file mode 100644 index 0000000000..a413c03cf4 --- /dev/null +++ b/panda/src/sgattrib/textureApplyTransition.cxx @@ -0,0 +1,143 @@ +// Filename: textureApplyTransition.cxx +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "textureApplyTransition.h" +#include "textureApplyAttribute.h" + +#include +#include +#include +#include +#include + +TypeHandle TextureApplyTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated TextureApplyTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *TextureApplyTransition:: +make_copy() const { + return new TextureApplyTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated TextureApplyAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TextureApplyTransition:: +make_attrib() const { + return new TextureApplyAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyTransition::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the other transition pointer, +// which is guaranteed to be another TextureApplyTransition. +//////////////////////////////////////////////////////////////////// +void TextureApplyTransition:: +set_value_from(const OnTransition *other) { + const TextureApplyTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyTransition::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int TextureApplyTransition:: +compare_values(const OnTransition *other) const { + const TextureApplyTransition *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void TextureApplyTransition:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void TextureApplyTransition:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyTransition::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void TextureApplyTransition:: +write_datagram(BamWriter *manager, Datagram &me) +{ + OnTransition::write_datagram(manager, me); + _value.write_datagram(me); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyTransition::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void TextureApplyTransition:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + OnTransition::fillin(scan, manager); + _value.read_datagram(scan); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyTransition::make_TextureApplyTransition +// Access: Protected +// Description: Factory method to generate a TextureApplyTransition object +//////////////////////////////////////////////////////////////////// +TypedWriteable* TextureApplyTransition:: +make_TextureApplyTransition(const FactoryParams ¶ms) +{ + TextureApplyTransition *me = new TextureApplyTransition; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureApplyTransition::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a TextureApplyTransition object +//////////////////////////////////////////////////////////////////// +void TextureApplyTransition:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_TextureApplyTransition); +} + + + diff --git a/panda/src/sgattrib/textureApplyTransition.h b/panda/src/sgattrib/textureApplyTransition.h new file mode 100644 index 0000000000..5914c8e3ad --- /dev/null +++ b/panda/src/sgattrib/textureApplyTransition.h @@ -0,0 +1,72 @@ +// Filename: textureApplyTransition.h +// Created by: drose (06Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TEXTUREAPPLYTRANSITION_H +#define TEXTUREAPPLYTRANSITION_H + +#include + +#include "textureApplyProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : TextureApplyTransition +// Description : This controls the way textures modify the colors +// assigned to geometry. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TextureApplyTransition : public OnTransition { +public: + INLINE TextureApplyTransition(TextureApplyProperty::Mode mode); + + INLINE void set_mode(TextureApplyProperty::Mode mode); + INLINE TextureApplyProperty::Mode get_mode() const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + INLINE TextureApplyTransition(void); + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + TextureApplyProperty _value; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + + static TypedWriteable *make_TextureApplyTransition(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnTransition::init_type(); + register_type(_type_handle, "TextureApplyTransition", + OnTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class TextureApplyAttribute; +}; + +#include "textureApplyTransition.I" + +#endif + + + diff --git a/panda/src/sgattrib/textureAttribute.I b/panda/src/sgattrib/textureAttribute.I new file mode 100644 index 0000000000..991fcc1e5d --- /dev/null +++ b/panda/src/sgattrib/textureAttribute.I @@ -0,0 +1,39 @@ +// Filename: textureAttribute.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TextureAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TextureAttribute:: +TextureAttribute() { +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureAttribute::set_on +// Access: Public +// Description: Changes the TextureAttribute to render the particular +// texture. +//////////////////////////////////////////////////////////////////// +INLINE void TextureAttribute:: +set_on(Texture *texture) { + nassertv(texture != (Texture *)NULL); + _value = texture; + OnOffAttribute::set_on(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureAttribute::get_texture +// Access: Public +// Description: Returns the texture that the TextureAttribute +// represents. It is only valid to call this if is_on() +// has returned true. +//////////////////////////////////////////////////////////////////// +INLINE PT(Texture) TextureAttribute:: +get_texture() const { + nassertr(is_on(), NULL); + return _value; +} diff --git a/panda/src/sgattrib/textureAttribute.cxx b/panda/src/sgattrib/textureAttribute.cxx new file mode 100644 index 0000000000..f06b143d8b --- /dev/null +++ b/panda/src/sgattrib/textureAttribute.cxx @@ -0,0 +1,107 @@ +// Filename: textureAttribute.cxx +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "textureAttribute.h" +#include "textureTransition.h" + +#include +#include + +TypeHandle TextureAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: TextureAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle TextureAttribute:: +get_handle() const { + return TextureTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated TextureAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TextureAttribute:: +make_copy() const { + return new TextureAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated TextureAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TextureAttribute:: +make_initial() const { + return new TextureAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void TextureAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_texture(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureAttribute::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the indicated transition +// pointer, which is guaranteed to be of type +// TextureTransition. +//////////////////////////////////////////////////////////////////// +void TextureAttribute:: +set_value_from(const OnOffTransition *other) { + const TextureTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; + nassertv(_value != (Texture *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureAttribute::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int TextureAttribute:: +compare_values(const OnOffAttribute *other) const { + const TextureAttribute *ot; + DCAST_INTO_R(ot, other, false); + return (int)(_value - ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void TextureAttribute:: +output_value(ostream &out) const { + nassertv(_value != (Texture *)NULL); + out << *_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void TextureAttribute:: +write_value(ostream &out, int indent_level) const { + nassertv(_value != (Texture *)NULL); + indent(out, indent_level) << *_value << "\n"; +} diff --git a/panda/src/sgattrib/textureAttribute.h b/panda/src/sgattrib/textureAttribute.h new file mode 100644 index 0000000000..f162368af7 --- /dev/null +++ b/panda/src/sgattrib/textureAttribute.h @@ -0,0 +1,59 @@ +// Filename: textureAttribute.h +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TEXTUREATTRIBUTE_H +#define TEXTUREATTRIBUTE_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : TextureAttribute +// Description : See TextureTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TextureAttribute : public OnOffAttribute { +public: + INLINE TextureAttribute(); + + INLINE void set_on(Texture *texture); + INLINE PT(Texture) get_texture() const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void set_value_from(const OnOffTransition *other); + virtual int compare_values(const OnOffAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + PT(Texture) _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnOffAttribute::init_type(); + register_type(_type_handle, "TextureAttribute", + OnOffAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "textureAttribute.I" + +#endif diff --git a/panda/src/sgattrib/textureTransition.I b/panda/src/sgattrib/textureTransition.I new file mode 100644 index 0000000000..8c99220f3b --- /dev/null +++ b/panda/src/sgattrib/textureTransition.I @@ -0,0 +1,65 @@ +// Filename: textureTransition.I +// Created by: drose (22Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TextureTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TextureTransition:: +TextureTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TextureTransition:: +TextureTransition(Texture *texture) : + OnOffTransition(TD_on), + _value(texture) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureTransition::off +// Access: Public +// Description: This named constructor returns a TextureTransition that +// turns off texturing, thus rendering geometry +// untextured. +//////////////////////////////////////////////////////////////////// +INLINE TextureTransition TextureTransition:: +off() { + TextureTransition result; + result.set_off(); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureTransition::set_on +// Access: Public +// Description: Changes the TextureTransition to render the particular +// texture. +//////////////////////////////////////////////////////////////////// +INLINE void TextureTransition:: +set_on(Texture *texture) { + nassertv(texture != (Texture *)NULL); + _value = texture; + OnOffTransition::set_on(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureTransition::get_texture +// Access: Public +// Description: Returns the texture that the TextureTransition +// represents. It is only valid to call this if is_on() +// has returned true. +//////////////////////////////////////////////////////////////////// +INLINE PT(Texture) TextureTransition:: +get_texture() const { + nassertr(is_on(), NULL); + return _value; +} diff --git a/panda/src/sgattrib/textureTransition.cxx b/panda/src/sgattrib/textureTransition.cxx new file mode 100644 index 0000000000..9cf37ad5de --- /dev/null +++ b/panda/src/sgattrib/textureTransition.cxx @@ -0,0 +1,173 @@ +// Filename: textureTransition.cxx +// Created by: drose (06Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_sgattrib.h" + +#include "textureTransition.h" +#include "textureAttribute.h" + +#include +#include +#include +#include +#include + +TypeHandle TextureTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: TextureTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated TextureTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *TextureTransition:: +make_copy() const { + return new TextureTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated TextureAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TextureTransition:: +make_attrib() const { + return new TextureAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureTransition::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the other transition pointer, +// which is guaranteed to be another TextureTransition. +//////////////////////////////////////////////////////////////////// +void TextureTransition:: +set_value_from(const OnOffTransition *other) { + const TextureTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; + nassertv(_value != (Texture *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureTransition::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int TextureTransition:: +compare_values(const OnOffTransition *other) const { + const TextureTransition *ot; + DCAST_INTO_R(ot, other, false); + return (int)(_value - ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void TextureTransition:: +output_value(ostream &out) const { + nassertv(_value != (Texture *)NULL); + out << *_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void TextureTransition:: +write_value(ostream &out, int indent_level) const { + nassertv(_value != (Texture *)NULL); + indent(out, indent_level) << *_value << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureTransition::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void TextureTransition:: +write_datagram(BamWriter *manager, Datagram &me) +{ + OnOffTransition::write_datagram(manager, me); + manager->write_pointer(me, _value); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureTransition::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void TextureTransition:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + OnOffTransition::fillin(scan, manager); + manager->read_pointer(scan, this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureTransition::complete_pointers +// Access: Public +// Description: Takes in a vector of pointes to TypedWriteable +// objects that correspond to all the requests for +// pointers that this object made to BamReader. +//////////////////////////////////////////////////////////////////// +int TextureTransition:: +complete_pointers(vector_typedWriteable &plist, BamReader*) +{ + if(plist[0] == TypedWriteable::Null) + { + if (sgattrib_cat->is_debug()) { + sgattrib_cat->debug() + << get_type().get_name() << " received null Texture," + << " turning off" << endl; + } + _value = (Texture *)NULL; + set_off(); + } + else + { + _value = DCAST(Texture, plist[0]); + } + + return 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureTransition::make_TextureTransition +// Access: Protected +// Description: Factory method to generate a TextureTransition object +//////////////////////////////////////////////////////////////////// +TypedWriteable* TextureTransition:: +make_TextureTransition(const FactoryParams ¶ms) +{ + TextureTransition *me = new TextureTransition; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureTransition::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a TextureTransition object +//////////////////////////////////////////////////////////////////// +void TextureTransition:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_TextureTransition); +} diff --git a/panda/src/sgattrib/textureTransition.h b/panda/src/sgattrib/textureTransition.h new file mode 100644 index 0000000000..72439fc92d --- /dev/null +++ b/panda/src/sgattrib/textureTransition.h @@ -0,0 +1,85 @@ +// Filename: textureTransition.h +// Created by: drose (18Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TEXTURETRANSITION_H +#define TEXTURETRANSITION_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : TextureTransition +// Description : This controls the assignment of the primary texture +// to a piece of geometry. If there is no +// TextureTransition or the transition is off, the +// geometry is rendered untextured; if there is an 'on' +// TextureTransition affecting a piece of geometry it +// will be rendered with the indicated texture. +// +// At one time this was a MultiTransition because we +// were thinking of using this interface to support +// multitexturing. But on reflection that is a flawed +// idea because MultiTransitions don't support any +// ordering, and we don't yet have an interface for +// specifying multiple layers of UV coordinates anyway. +// Until this is fully worked out, this will be a simple +// OnOffTransition: either there is a texture or there +// isn't. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TextureTransition : public OnOffTransition { +public: + INLINE TextureTransition(); + INLINE TextureTransition(Texture *texture); + INLINE static TextureTransition off(); + + INLINE void set_on(Texture *texture); + INLINE PT(Texture) get_texture() const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + virtual void set_value_from(const OnOffTransition *other); + virtual int compare_values(const OnOffTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + PT(Texture) _value; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + virtual int complete_pointers(vector_typedWriteable &plist, + BamReader *manager); + + static TypedWriteable *make_TextureTransition(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnOffTransition::init_type(); + register_type(_type_handle, "TextureTransition", + OnOffTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class TextureAttribute; +}; + +#include "textureTransition.I" + +#endif diff --git a/panda/src/sgattrib/transformAttribute.I b/panda/src/sgattrib/transformAttribute.I new file mode 100644 index 0000000000..6723a37263 --- /dev/null +++ b/panda/src/sgattrib/transformAttribute.I @@ -0,0 +1,13 @@ +// Filename: transformAttribute.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TransformAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TransformAttribute:: +TransformAttribute() { +} diff --git a/panda/src/sgattrib/transformAttribute.cxx b/panda/src/sgattrib/transformAttribute.cxx new file mode 100644 index 0000000000..e367674af1 --- /dev/null +++ b/panda/src/sgattrib/transformAttribute.cxx @@ -0,0 +1,57 @@ +// Filename: transformAttribute.cxx +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "transformAttribute.h" +#include "transformTransition.h" + +#include +#include + +TypeHandle TransformAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: TransformAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle TransformAttribute:: +get_handle() const { + return TransformTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated TransformAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TransformAttribute:: +make_copy() const { + return new TransformAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated TransformAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TransformAttribute:: +make_initial() const { + return new TransformAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void TransformAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_transform(this); +} diff --git a/panda/src/sgattrib/transformAttribute.h b/panda/src/sgattrib/transformAttribute.h new file mode 100644 index 0000000000..34f5069559 --- /dev/null +++ b/panda/src/sgattrib/transformAttribute.h @@ -0,0 +1,48 @@ +// Filename: transformAttribute.h +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TRANSFORMATTRIBUTE_H +#define TRANSFORMATTRIBUTE_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : TransformAttribute +// Description : See TransformTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TransformAttribute : public LMatrix4fAttribute { +public: + INLINE TransformAttribute(); + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + LMatrix4fAttribute::init_type(); + register_type(_type_handle, "TransformAttribute", + LMatrix4fAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "transformAttribute.I" + +#endif diff --git a/panda/src/sgattrib/transformTransition.I b/panda/src/sgattrib/transformTransition.I new file mode 100644 index 0000000000..654b8673a8 --- /dev/null +++ b/panda/src/sgattrib/transformTransition.I @@ -0,0 +1,25 @@ +// Filename: transformTransition.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TransformTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TransformTransition:: +TransformTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TransformTransition:: +TransformTransition(const LMatrix4f &matrix) : + LMatrix4fTransition(matrix) +{ +} + diff --git a/panda/src/sgattrib/transformTransition.cxx b/panda/src/sgattrib/transformTransition.cxx new file mode 100644 index 0000000000..363e61def5 --- /dev/null +++ b/panda/src/sgattrib/transformTransition.cxx @@ -0,0 +1,76 @@ +// Filename: transformTransition.cxx +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "transformTransition.h" +#include "transformAttribute.h" + +#include +#include +#include +#include +#include + +TypeHandle TransformTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: TransformTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated TransformTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *TransformTransition:: +make_copy() const { + return new TransformTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated TransformAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TransformTransition:: +make_attrib() const { + return new TransformAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformTransition::make_with_matrix +// Access: Protected, Virtual +// Description: Returns a new transition with the indicated matrix. +//////////////////////////////////////////////////////////////////// +MatrixTransition *TransformTransition:: +make_with_matrix(const LMatrix4f &matrix) const { + return new TransformTransition(matrix); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformTransition::make_TransformTransition +// Access: Protected +// Description: Factory method to generate a TransformTransition object +//////////////////////////////////////////////////////////////////// +TypedWriteable* TransformTransition:: +make_TransformTransition(const FactoryParams ¶ms) +{ + TransformTransition *me = new TransformTransition; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformTransition::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a TransformTransition object +//////////////////////////////////////////////////////////////////// +void TransformTransition:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_TransformTransition); +} diff --git a/panda/src/sgattrib/transformTransition.h b/panda/src/sgattrib/transformTransition.h new file mode 100644 index 0000000000..a12f72c264 --- /dev/null +++ b/panda/src/sgattrib/transformTransition.h @@ -0,0 +1,60 @@ +// Filename: transformTransition.h +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TRANSFORMTRANSITION_H +#define TRANSFORMTRANSITION_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : TransformTransition +// Description : This defines a new coordinate system. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TransformTransition : public LMatrix4fTransition { +public: + INLINE TransformTransition(); + INLINE TransformTransition(const LMatrix4f &matrix); + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + virtual MatrixTransition * + make_with_matrix(const LMatrix4f &matrix) const; + +public: + static void register_with_read_factory(void); + + static TypedWriteable *make_TransformTransition(const FactoryParams ¶ms); + +protected: + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + LMatrix4fTransition::init_type(); + register_type(_type_handle, "TransformTransition", + LMatrix4fTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class TransformAttribute; +}; + +#include "transformTransition.I" + +#endif + + diff --git a/panda/src/sgattrib/transparencyAttribute.I b/panda/src/sgattrib/transparencyAttribute.I new file mode 100644 index 0000000000..dbac17cf15 --- /dev/null +++ b/panda/src/sgattrib/transparencyAttribute.I @@ -0,0 +1,35 @@ +// Filename: transparencyAttribute.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyAttribute::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TransparencyAttribute:: +TransparencyAttribute() : + _value(TransparencyProperty::M_none) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyAttribute::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TransparencyProperty::Mode TransparencyAttribute:: +get_mode() const { + return _value.get_mode(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyAttribute::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TransparencyAttribute:: +set_mode(TransparencyProperty::Mode mode) { + _value.set_mode(mode); +} diff --git a/panda/src/sgattrib/transparencyAttribute.cxx b/panda/src/sgattrib/transparencyAttribute.cxx new file mode 100644 index 0000000000..d622bb1ea5 --- /dev/null +++ b/panda/src/sgattrib/transparencyAttribute.cxx @@ -0,0 +1,104 @@ +// Filename: transparencyAttribute.cxx +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "transparencyAttribute.h" +#include "transparencyTransition.h" + +#include +#include + +TypeHandle TransparencyAttribute::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyAttribute::get_handle +// Access: Public, Virtual +// Description: Returns the handle of the associated transition. +//////////////////////////////////////////////////////////////////// +TypeHandle TransparencyAttribute:: +get_handle() const { + return TransparencyTransition::get_class_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyAttribute::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated TransparencyAttribute just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TransparencyAttribute:: +make_copy() const { + return new TransparencyAttribute(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyAttribute::make_initial +// Access: Public, Virtual +// Description: Returns a newly allocated TransparencyAttribute +// corresponding to the default initial state. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TransparencyAttribute:: +make_initial() const { + return new TransparencyAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyAttribute::issue +// Access: Public, Virtual +// Description: This is called on scene graph rendering attributes +// when it is time to issue the particular attribute to +// the graphics engine. It should call the appropriate +// method on GraphicsStateGuardianBase. +//////////////////////////////////////////////////////////////////// +void TransparencyAttribute:: +issue(GraphicsStateGuardianBase *gsgbase) { + gsgbase->issue_transparency(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyAttribute::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the indicated transition +// pointer, which is guaranteed to be of type +// TransparencyTransition. +//////////////////////////////////////////////////////////////////// +void TransparencyAttribute:: +set_value_from(const OnTransition *other) { + const TransparencyTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyAttribute::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int TransparencyAttribute:: +compare_values(const OnAttribute *other) const { + const TransparencyAttribute *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyAttribute::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void TransparencyAttribute:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyAttribute::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void TransparencyAttribute:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} diff --git a/panda/src/sgattrib/transparencyAttribute.h b/panda/src/sgattrib/transparencyAttribute.h new file mode 100644 index 0000000000..fc1dbd9248 --- /dev/null +++ b/panda/src/sgattrib/transparencyAttribute.h @@ -0,0 +1,60 @@ +// Filename: transparencyAttribute.h +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TRANSPARENCYATTRIBUTE_H +#define TRANSPARENCYATTRIBUTE_H + +#include + +#include "transparencyProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : TransparencyAttribute +// Description : See TransparencyTransition. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TransparencyAttribute : public OnAttribute { +public: + INLINE TransparencyAttribute(); + + INLINE void set_mode(TransparencyProperty::Mode mode); + INLINE TransparencyProperty::Mode get_mode() const; + + virtual TypeHandle get_handle() const; + virtual NodeAttribute *make_copy() const; + virtual NodeAttribute *make_initial() const; + + virtual void issue(GraphicsStateGuardianBase *gsgbase); + +protected: + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnAttribute *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + TransparencyProperty _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnAttribute::init_type(); + register_type(_type_handle, "TransparencyAttribute", + OnAttribute::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "transparencyAttribute.I" + +#endif diff --git a/panda/src/sgattrib/transparencyProperty.I b/panda/src/sgattrib/transparencyProperty.I new file mode 100644 index 0000000000..27d13afa76 --- /dev/null +++ b/panda/src/sgattrib/transparencyProperty.I @@ -0,0 +1,59 @@ +// Filename: transparencyProperty.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyProperty::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TransparencyProperty:: +TransparencyProperty(TransparencyProperty::Mode mode) : + _mode(mode) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyProperty::Constructor +// Access: Public +// Description: This should only be called in the Protected +// constructor of TransparencyTransition. It is made +// public, only for that to access, and is necessary +// for creating an object in make_TransparencyTransition +// before any information has been read +//////////////////////////////////////////////////////////////////// +INLINE TransparencyProperty:: +TransparencyProperty(void) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyProperty::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TransparencyProperty:: +set_mode(TransparencyProperty::Mode mode) { + _mode = mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyProperty::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TransparencyProperty::Mode TransparencyProperty:: +get_mode() const { + return _mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyProperty::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int TransparencyProperty:: +compare_to(const TransparencyProperty &other) const { + return (int)_mode - (int)other._mode; +} diff --git a/panda/src/sgattrib/transparencyProperty.cxx b/panda/src/sgattrib/transparencyProperty.cxx new file mode 100644 index 0000000000..069f5a9a4b --- /dev/null +++ b/panda/src/sgattrib/transparencyProperty.cxx @@ -0,0 +1,68 @@ +// Filename: transparencyProperty.cxx +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "transparencyProperty.h" +#include +#include + +ostream & +operator << (ostream &out, TransparencyProperty::Mode mode) { + switch (mode) { + case TransparencyProperty::M_none: + return out << "none"; + + case TransparencyProperty::M_alpha: + return out << "alpha"; + + case TransparencyProperty::M_alpha_sorted: + return out << "alpha sorted"; + + case TransparencyProperty::M_multisample: + return out << "multisample"; + + case TransparencyProperty::M_multisample_mask: + return out << "multisample mask"; + + case TransparencyProperty::M_binary: + return out << "binary"; + } + + return out << "**invalid**(" << (int)mode << ")"; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyProperty::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void TransparencyProperty:: +output(ostream &out) const { + out << _mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyProperty::write_datagram +// Access: Public +// Description: Function to write the important information in +// this object to a Datagram +//////////////////////////////////////////////////////////////////// +void TransparencyProperty:: +write_datagram(Datagram &destination) +{ + destination.add_uint8(_mode); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyProperty::read_datagram +// Access: Public +// Description: Function to write the important information into +// this object out of a Datagram +//////////////////////////////////////////////////////////////////// +void TransparencyProperty:: +read_datagram(DatagramIterator &source) +{ + _mode = (enum Mode) source.get_uint8(); +} + diff --git a/panda/src/sgattrib/transparencyProperty.h b/panda/src/sgattrib/transparencyProperty.h new file mode 100644 index 0000000000..17457adb8d --- /dev/null +++ b/panda/src/sgattrib/transparencyProperty.h @@ -0,0 +1,57 @@ +// Filename: transparencyProperty.h +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TRANSPARENCYPROPERTY_H +#define TRANSPARENCYPROPERTY_H + +#include + +class Datagram; +class DatagramIterator; + +//////////////////////////////////////////////////////////////////// +// Class : TransparencyProperty +// Description : This defines the types of transparency that can be +// enabled. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TransparencyProperty { +public: + enum Mode { + M_none, // No transparency in effect. + M_alpha, // Writes to depth buffer of transp objects disabled + M_alpha_sorted, // Assumes transp objects are depth sorted + M_multisample, // Source alpha values modified to 1.0 before writing + M_multisample_mask, // Source alpha values not modified + M_binary, // Only writes pixels with alpha = 1.0 + }; + +public: + INLINE TransparencyProperty(Mode mode); + INLINE TransparencyProperty(void); + + INLINE void set_mode(Mode mode); + INLINE Mode get_mode() const; + + INLINE int compare_to(const TransparencyProperty &other) const; + void output(ostream &out) const; + +public: + void write_datagram(Datagram &destination); + void read_datagram(DatagramIterator &source); + +private: + Mode _mode; +}; + +ostream &operator << (ostream &out, TransparencyProperty::Mode mode); + +INLINE ostream &operator << (ostream &out, const TransparencyProperty &prop) { + prop.output(out); + return out; +} + +#include "transparencyProperty.I" + +#endif diff --git a/panda/src/sgattrib/transparencyTransition.I b/panda/src/sgattrib/transparencyTransition.I new file mode 100644 index 0000000000..2e0aeec4e5 --- /dev/null +++ b/panda/src/sgattrib/transparencyTransition.I @@ -0,0 +1,46 @@ +// Filename: transparencyTransition.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TransparencyTransition:: +TransparencyTransition(TransparencyProperty::Mode mode) : + _value(mode) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TransparencyTransition:: +TransparencyTransition(void) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyTransition::set_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TransparencyTransition:: +set_mode(TransparencyProperty::Mode mode) { + _value.set_mode(mode); + state_changed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyTransition::get_mode +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TransparencyProperty::Mode TransparencyTransition:: +get_mode() const { + return _value.get_mode(); +} diff --git a/panda/src/sgattrib/transparencyTransition.cxx b/panda/src/sgattrib/transparencyTransition.cxx new file mode 100644 index 0000000000..0bc848f396 --- /dev/null +++ b/panda/src/sgattrib/transparencyTransition.cxx @@ -0,0 +1,142 @@ +// Filename: transparencyTransition.cxx +// Created by: mike (28Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "transparencyTransition.h" +#include "transparencyAttribute.h" + +#include +#include +#include +#include +#include + +TypeHandle TransparencyTransition::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated TransparencyTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *TransparencyTransition:: +make_copy() const { + return new TransparencyTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyTransition::make_attrib +// Access: Public, Virtual +// Description: Returns a newly allocated TransparencyAttribute. +//////////////////////////////////////////////////////////////////// +NodeAttribute *TransparencyTransition:: +make_attrib() const { + return new TransparencyAttribute; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyTransition::set_value_from +// Access: Protected, Virtual +// Description: Copies the value from the other transition pointer, +// which is guaranteed to be another TransparencyTransition. +//////////////////////////////////////////////////////////////////// +void TransparencyTransition:: +set_value_from(const OnTransition *other) { + const TransparencyTransition *ot; + DCAST_INTO_V(ot, other); + _value = ot->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyTransition::compare_values +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +int TransparencyTransition:: +compare_values(const OnTransition *other) const { + const TransparencyTransition *ot; + DCAST_INTO_R(ot, other, false); + return _value.compare_to(ot->_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyTransition::output_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on one line. +//////////////////////////////////////////////////////////////////// +void TransparencyTransition:: +output_value(ostream &out) const { + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyTransition::write_value +// Access: Protected, Virtual +// Description: Formats the value for human consumption on multiple +// lines if necessary. +//////////////////////////////////////////////////////////////////// +void TransparencyTransition:: +write_value(ostream &out, int indent_level) const { + indent(out, indent_level) << _value << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyTransition::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void TransparencyTransition:: +write_datagram(BamWriter *manager, Datagram &me) +{ + OnTransition::write_datagram(manager, me); + _value.write_datagram(me); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyTransition::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void TransparencyTransition:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + OnTransition::fillin(scan, manager); + _value.read_datagram(scan); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyTransition::make_TransparencyTransition +// Access: Protected +// Description: Factory method to generate a TransparencyTransition object +//////////////////////////////////////////////////////////////////// +TypedWriteable* TransparencyTransition:: +make_TransparencyTransition(const FactoryParams ¶ms) +{ + TransparencyTransition *me = new TransparencyTransition; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransparencyTransition::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a TransparencyTransition object +//////////////////////////////////////////////////////////////////// +void TransparencyTransition:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_TransparencyTransition); +} + + diff --git a/panda/src/sgattrib/transparencyTransition.h b/panda/src/sgattrib/transparencyTransition.h new file mode 100644 index 0000000000..68baf93c3a --- /dev/null +++ b/panda/src/sgattrib/transparencyTransition.h @@ -0,0 +1,76 @@ +// Filename: transparencyTransition.h +// Created by: mike (18Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TRANSPARENCYTRANSITION_H +#define TRANSPARENCYTRANSITION_H + +#include + +#include "transparencyProperty.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : TransparencyTransition +// Description : This controls the enabling of transparency. Simply +// setting an alpha component to non-1 does not in +// itself make an object transparent; you must also +// enable transparency mode with a suitable +// TransparencyTransition. Similarly, it is wasteful to +// render an object with a TransparencyTransition in +// effect unless you actually want it to be at least +// partially transparent (and it has alpha components +// less than 1). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TransparencyTransition : public OnTransition { +public: + INLINE TransparencyTransition(TransparencyProperty::Mode mode); + + INLINE void set_mode(TransparencyProperty::Mode mode); + INLINE TransparencyProperty::Mode get_mode() const; + + virtual NodeTransition *make_copy() const; + virtual NodeAttribute *make_attrib() const; + +protected: + INLINE TransparencyTransition(void); + virtual void set_value_from(const OnTransition *other); + virtual int compare_values(const OnTransition *other) const; + virtual void output_value(ostream &out) const; + virtual void write_value(ostream &out, int indent_level) const; + + TransparencyProperty _value; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + + static TypedWriteable *make_TransparencyTransition(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + OnTransition::init_type(); + register_type(_type_handle, "TransparencyTransition", + OnTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class TransparencyAttribute; +}; + +#include "transparencyTransition.I" + +#endif diff --git a/panda/src/sgidisplay/Sources.pp b/panda/src/sgidisplay/Sources.pp new file mode 100644 index 0000000000..a925c76cee --- /dev/null +++ b/panda/src/sgidisplay/Sources.pp @@ -0,0 +1,18 @@ +#define DIRECTORY_IF_SGIGL yes + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET sgidisplay + #define UNIX_SYS_LIBS \ + Xsgivc + + #define SOURCES \ + config_sgidisplay.cxx config_sgidisplay.h sgiGraphicsPipe.cxx \ + sgiGraphicsPipe.h sgiHardwareChannel.cxx sgiHardwareChannel.h + + #define INSTALL_HEADERS \ + sgiGraphicsPipe.h sgiHardwareChannel.h + +#end lib_target + diff --git a/panda/src/sgidisplay/config_sgidisplay.cxx b/panda/src/sgidisplay/config_sgidisplay.cxx new file mode 100644 index 0000000000..bb4af9fcd7 --- /dev/null +++ b/panda/src/sgidisplay/config_sgidisplay.cxx @@ -0,0 +1,17 @@ +// Filename: config_sgidisplay.cxx +// Created by: cary (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_sgidisplay.h" +#include "sgiGraphicsPipe.h" +#include "sgiHardwareChannel.h" + +#include + +Configure(config_sgidisplay); + +ConfigureFn(config_sgidisplay) { + sgiGraphicsPipe::init_type(); + sgiHardwareChannel::init_type(); +} diff --git a/panda/src/sgidisplay/config_sgidisplay.h b/panda/src/sgidisplay/config_sgidisplay.h new file mode 100644 index 0000000000..b7abf07769 --- /dev/null +++ b/panda/src/sgidisplay/config_sgidisplay.h @@ -0,0 +1,11 @@ +// Filename: config_sgidisplay.h +// Created by: cary (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __CONFIG_SGIDISPLAY_H__ +#define __CONFIG_SGIDISPLAY_H__ + +// nothing to include here yet + +#endif /* __CONFIG_SGIDISPLAY_H__ */ diff --git a/panda/src/sgidisplay/sgiGraphicsPipe.cxx b/panda/src/sgidisplay/sgiGraphicsPipe.cxx new file mode 100644 index 0000000000..4fff7673d6 --- /dev/null +++ b/panda/src/sgidisplay/sgiGraphicsPipe.cxx @@ -0,0 +1,111 @@ +// Filename: sgiGraphicsPipe.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "sgiGraphicsPipe.h" +#include +#include +#include +#include "sgiHardwareChannel.h" + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle sgiGraphicsPipe::_type_handle; + +sgiGraphicsPipe::sgiGraphicsPipe(const PipeSpecifier& spec) + : InteractiveGraphicsPipe(spec) { + _num_channels = -1; +} + +sgiGraphicsPipe::~sgiGraphicsPipe(void) { + return; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_num_hw_channels +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +int sgiGraphicsPipe::get_num_hw_channels( void ) +{ + if ( _num_channels == -1 ) + { + Display* display = (Display *)_display; + // Is there a better way to get the display??? (I hope so) + display = glXGetCurrentDisplayEXT(); + + // For now, screen is the same as display + _screen = DefaultScreen( display ); + + XSGIvcScreenInfo server; + XSGIvcQueryVideoScreenInfo( display, _screen, &server ); + + // Not all of these will be active necessarily + _num_channels = server.numChannels; + } + + return _num_channels; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_hw_channel +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +HardwareChannel *sgiGraphicsPipe:: +get_hw_channel( GraphicsWindow* window, int index ) { + if ( _num_channels == -1 ) + get_num_hw_channels(); + + if ( index >= _num_channels ) + { + cerr << "sgiGraphicsPipe::get_hw_channel() - invalid index: " + << index << endl; + return NULL; + } + + Channels::iterator i; + i = _hw_chans.find( index ); + if (i != _hw_chans.end()) { + return (*i).second.p(); + } + + sgiHardwareChannel* hw_chan; + hw_chan = new sgiHardwareChannel( window, index ); + _hw_chans[index] = hw_chan; + + return hw_chan; +} + +TypeHandle sgiGraphicsPipe::get_class_type(void) { + return _type_handle; +} + +void sgiGraphicsPipe::init_type(void) { + InteractiveGraphicsPipe::init_type(); + register_type(_type_handle, "sgiGraphicsPipe", + InteractiveGraphicsPipe::get_class_type()); +} + +TypeHandle sgiGraphicsPipe::get_type(void) const { + return get_class_type(); +} + +sgiGraphicsPipe::sgiGraphicsPipe(void) { + cerr << "sgiGraphicsPipes should not be created with default constructor" + << endl; +} + +sgiGraphicsPipe::sgiGraphicsPipe(const sgiGraphicsPipe&) { + cerr << "sgiGraphicsPipes should not be copied" << endl; +} + +sgiGraphicsPipe& sgiGraphicsPipe::operator=(const sgiGraphicsPipe&) { + cerr << "sgiGraphicsPipes should not be assigned" << endl; + return *this; +} diff --git a/panda/src/sgidisplay/sgiGraphicsPipe.h b/panda/src/sgidisplay/sgiGraphicsPipe.h new file mode 100644 index 0000000000..31de8a1028 --- /dev/null +++ b/panda/src/sgidisplay/sgiGraphicsPipe.h @@ -0,0 +1,69 @@ +// Filename: sgiGraphicsPipe.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef SGIGRAPHICSPIPE_H +#define SGIGRAPHICSPIPE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "sgiHardwareChannel.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : sgiGraphicsPipe +// Description : +//////////////////////////////////////////////////////////////////// +class sgiGraphicsPipe : public InteractiveGraphicsPipe { +public: + + sgiGraphicsPipe(const PipeSpecifier&); + virtual ~sgiGraphicsPipe() = 0; + + INLINE void* get_display() const { return _display; } + INLINE int get_screen() const { return _screen; } + +protected: + + typedef map Channels; + Channels _hw_chans; + + virtual int get_num_hw_channels(); + virtual HardwareChannel *get_hw_channel(GraphicsWindow* window, + int index); + +public: + + static TypeHandle get_class_type(); + static void init_type(); + virtual TypeHandle get_type() const; + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +protected: + + void *_display; + int _screen; + int _num_channels; + +private: + + static TypeHandle _type_handle; + +protected: + + sgiGraphicsPipe(); + sgiGraphicsPipe(const sgiGraphicsPipe&); + sgiGraphicsPipe& operator=(const sgiGraphicsPipe&); +}; + +#endif diff --git a/panda/src/sgidisplay/sgiHardwareChannel.cxx b/panda/src/sgidisplay/sgiHardwareChannel.cxx new file mode 100644 index 0000000000..e4c17ce8d6 --- /dev/null +++ b/panda/src/sgidisplay/sgiHardwareChannel.cxx @@ -0,0 +1,54 @@ +// Filename: sgiHardwareChannel.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "sgiHardwareChannel.h" +#include "sgiGraphicsPipe.h" +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle sgiHardwareChannel::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +sgiHardwareChannel::sgiHardwareChannel( GraphicsWindow* window, int id ) : + HardwareChannel( window ) +{ + _id = id; + const GraphicsPipe* pipe = window->get_pipe(); + const sgiGraphicsPipe* sgipipe; + if ( pipe->get_class_type() == sgiGraphicsPipe::get_class_type() ) + sgipipe = (sgiGraphicsPipe *)pipe; + else + { + cerr << "sgiHardwareChannel::Constructor() - window does not have " + << "an sgiGraphicsPipe!!!" << endl; + return; + } + + XSGIvcChannelInfo* channel_info = (XSGIvcChannelInfo *)_channel_info; + + XSGIvcQueryChannelInfo( (Display *)sgipipe->get_display(), + sgipipe->get_screen(), _id, &channel_info ); + if ( channel_info->active ) { + int screeny = DisplayHeight( (Display *)sgipipe->get_display(), + sgipipe->get_screen() ); + _xsize = channel_info->source.width; + _ysize = channel_info->source.height; + _xorg = channel_info->source.x; + _yorg = screeny - ( channel_info->source.y + _ysize ); + set_active(true); + } else { + set_active(false); + } +} diff --git a/panda/src/sgidisplay/sgiHardwareChannel.h b/panda/src/sgidisplay/sgiHardwareChannel.h new file mode 100644 index 0000000000..33eb348d43 --- /dev/null +++ b/panda/src/sgidisplay/sgiHardwareChannel.h @@ -0,0 +1,54 @@ +// Filename: graphicsChannel.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef SGIHARDWARECHANNEL_H +#define SGIHARDWARECHANNEL_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : sgiHardwareChannel +// Description : +//////////////////////////////////////////////////////////////////// +class sgiHardwareChannel : public HardwareChannel +{ + public: + + sgiHardwareChannel( GraphicsWindow* window, int id ); + + protected: + + void *_channel_info; + + public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + HardwareChannel::init_type(); + register_type(_type_handle, "sgiHardwareChannel", + HardwareChannel::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + + private: + + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/sgiglutdisplay/Sources.pp b/panda/src/sgiglutdisplay/Sources.pp new file mode 100644 index 0000000000..5ff921ac78 --- /dev/null +++ b/panda/src/sgiglutdisplay/Sources.pp @@ -0,0 +1,19 @@ +#define DIRECTORY_IF_SGIGL yes +#define DIRECTORY_IF_GLUT yes + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET sgiglutdisplay + #define LOCAL_LIBS \ + sgidisplay glutdisplay + + #define SOURCES \ + config_sgiglutdisplay.cxx config_sgiglutdisplay.h \ + sgiglutGraphicsPipe.cxx sgiglutGraphicsPipe.h + + #define INSTALL_HEADERS \ + sgiglutGraphicsPipe.h + +#end lib_target + diff --git a/panda/src/sgiglutdisplay/config_sgiglutdisplay.cxx b/panda/src/sgiglutdisplay/config_sgiglutdisplay.cxx new file mode 100644 index 0000000000..854ec3528e --- /dev/null +++ b/panda/src/sgiglutdisplay/config_sgiglutdisplay.cxx @@ -0,0 +1,18 @@ +// Filename: config_sgiglutdisplay.cxx +// Created by: cary (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_sgiglutdisplay.h" +#include "sgiglutGraphicsPipe.h" + +#include + +Configure(config_sgiglutdisplay); +NotifyCategoryDef(sgiglutdisplay, "display"); + +ConfigureFn(config_sgiglutdisplay) { + sgiglutGraphicsPipe::init_type(); + GraphicsPipe::_factory.register_factory(sgiglutGraphicsPipe::get_class_type(), + sgiglutGraphicsPipe::make_sgiglutGraphicsPipe); +} diff --git a/panda/src/sgiglutdisplay/config_sgiglutdisplay.h b/panda/src/sgiglutdisplay/config_sgiglutdisplay.h new file mode 100644 index 0000000000..b7e049ed99 --- /dev/null +++ b/panda/src/sgiglutdisplay/config_sgiglutdisplay.h @@ -0,0 +1,14 @@ +// Filename: config_sgiglutdisplay.h +// Created by: cary (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_SGIGLUTDISPLAY_H +#define CONFIG_SGIGLUTDISPLAY_H + +#include +#include + +NotifyCategoryDecl(sgiglutdisplay, EXPCL_PANDAGL, EXPTP_PANDAGL); + +#endif diff --git a/panda/src/sgiglutdisplay/sgiglutGraphicsPipe.cxx b/panda/src/sgiglutdisplay/sgiglutGraphicsPipe.cxx new file mode 100644 index 0000000000..a77abc9303 --- /dev/null +++ b/panda/src/sgiglutdisplay/sgiglutGraphicsPipe.cxx @@ -0,0 +1,53 @@ +// Filename: sgiglutGraphicsPipe.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "sgiglutGraphicsPipe.h" +#include "config_sgiglutdisplay.h" + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle sgiglutGraphicsPipe::_type_handle; + +sgiglutGraphicsPipe::sgiglutGraphicsPipe(const PipeSpecifier& spec) + : sgiGraphicsPipe(spec) {} + +//////////////////////////////////////////////////////////////////// +// Function: sgiglutGraphicsPipe::get_window_type +// Access: Public, Virtual +// Description: Returns the TypeHandle of the kind of window +// preferred by this kind of pipe. +//////////////////////////////////////////////////////////////////// +TypeHandle sgiglutGraphicsPipe:: +get_window_type() const { + return glutGraphicsWindow::get_class_type(); +} + +GraphicsPipe *sgiglutGraphicsPipe:: +make_sgiglutGraphicsPipe(const FactoryParams ¶ms) { + GraphicsPipe::PipeSpec *pipe_param; + if (!get_param_into(pipe_param, params)) { + return new sgiglutGraphicsPipe(PipeSpecifier()); + } else { + return new sgiglutGraphicsPipe(pipe_param->get_specifier()); + } +} + +TypeHandle sgiglutGraphicsPipe::get_class_type(void) { + return _type_handle; +} + +void sgiglutGraphicsPipe::init_type(void) { + sgiGraphicsPipe::init_type(); + register_type(_type_handle, "sgiglutGraphicsPipe", + sgiGraphicsPipe::get_class_type()); +} + +TypeHandle sgiglutGraphicsPipe::get_type(void) const { + return get_class_type(); +} diff --git a/panda/src/sgiglutdisplay/sgiglutGraphicsPipe.h b/panda/src/sgiglutdisplay/sgiglutGraphicsPipe.h new file mode 100644 index 0000000000..63b9ca3fd1 --- /dev/null +++ b/panda/src/sgiglutdisplay/sgiglutGraphicsPipe.h @@ -0,0 +1,47 @@ +// Filename: sgiglutGraphicsPipe.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef SGIGLUTGRAPHICSPIPE_H +#define SGIGLUTGRAPHICSPIPE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : sgiglutGraphicsPipe +// Description : This kind of pipe can create glut windows but +// supports the functionality of an sgi pipe with +// hardware channels +//////////////////////////////////////////////////////////////////// +class sgiglutGraphicsPipe : public sgiGraphicsPipe +{ +public: + sgiglutGraphicsPipe( const PipeSpecifier& ); + + virtual TypeHandle get_window_type() const; + +public: + static GraphicsPipe* make_sgiglutGraphicsPipe(const FactoryParams ¶ms); + + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/sgiglxdisplay/Sources.pp b/panda/src/sgiglxdisplay/Sources.pp new file mode 100644 index 0000000000..86df04ccca --- /dev/null +++ b/panda/src/sgiglxdisplay/Sources.pp @@ -0,0 +1,19 @@ +#define DIRECTORY_IF_SGIGL yes +#define DIRECTORY_IF_GLX yes + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET sgiglxdisplay + #define LOCAL_LIBS \ + sgidisplay glxdisplay + + #define SOURCES \ + config_sgiglxdisplay.cxx config_sgiglxdisplay.h \ + sgiglxGraphicsPipe.cxx sgiglxGraphicsPipe.h + + #define INSTALL_HEADERS \ + sgiglxGraphicsPipe.h + +#end lib_target + diff --git a/panda/src/sgiglxdisplay/config_sgiglxdisplay.cxx b/panda/src/sgiglxdisplay/config_sgiglxdisplay.cxx new file mode 100644 index 0000000000..8a323da3c0 --- /dev/null +++ b/panda/src/sgiglxdisplay/config_sgiglxdisplay.cxx @@ -0,0 +1,18 @@ +// Filename: config_sgiglxdisplay.cxx +// Created by: cary (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_sgiglxdisplay.h" +#include "sgiglxGraphicsPipe.h" + +#include + +Configure(config_sgiglxdisplay); +NotifyCategoryDef(sgiglxdisplay, "display"); + +ConfigureFn(config_sgiglxdisplay) { + SgiGlxGraphicsPipe::init_type(); + GraphicsPipe::_factory.register_factory(SgiGlxGraphicsPipe::get_class_type(), + SgiGlxGraphicsPipe::make_sgiglxgraphicspipe); +} diff --git a/panda/src/sgiglxdisplay/config_sgiglxdisplay.h b/panda/src/sgiglxdisplay/config_sgiglxdisplay.h new file mode 100644 index 0000000000..e6551ade2f --- /dev/null +++ b/panda/src/sgiglxdisplay/config_sgiglxdisplay.h @@ -0,0 +1,14 @@ +// Filename: config_sgiglxdisplay.h +// Created by: cary (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_SGIGLXDISPLAY_H +#define CONFIG_SGIGLXDISPLAY_H + +#include +#include + +NotifyCategoryDecl(sgiglxdisplay, EXPCL_PANDAGL, EXPTP_PANDAGL); + +#endif diff --git a/panda/src/sgiglxdisplay/sgiglxGraphicsPipe.cxx b/panda/src/sgiglxdisplay/sgiglxGraphicsPipe.cxx new file mode 100644 index 0000000000..2884e842f9 --- /dev/null +++ b/panda/src/sgiglxdisplay/sgiglxGraphicsPipe.cxx @@ -0,0 +1,56 @@ +// Filename: sgiglxGraphicsPipe.cxx +// Created by: cary (01Oct99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "sgiglxGraphicsPipe.h" +#include "config_sgiglxdisplay.h" + +TypeHandle SgiGlxGraphicsPipe::_type_handle; + +SgiGlxGraphicsPipe::SgiGlxGraphicsPipe(const PipeSpecifier& spec) + : sgiGraphicsPipe(spec) {} + + +//////////////////////////////////////////////////////////////////// +// Function: SgiGlxGraphicsPipe::get_window_type +// Access: Public, Virtual +// Description: Returns the TypeHandle of the kind of window +// preferred by this kind of pipe. +//////////////////////////////////////////////////////////////////// +TypeHandle SgiGlxGraphicsPipe:: +get_window_type() const { + return glxGraphicsWindow::get_class_type(); +} + +GraphicsPipe *SgiGlxGraphicsPipe:: +make_sgiglxgraphicspipe(const FactoryParams ¶ms) { + GraphicsPipe::PipeSpec *pipe_param; + if (!get_param_into(pipe_param, params)) { + return new SgiGlxGraphicsPipe(PipeSpecifier()); + } else { + return new SgiGlxGraphicsPipe(pipe_param->get_specifier()); + } +} + +TypeHandle SgiGlxGraphicsPipe::get_class_type(void) { + return _type_handle; +} + +void SgiGlxGraphicsPipe::init_type(void) { + sgiGraphicsPipe::init_type(); + register_type(_type_handle, "SgiGlxGraphicsPipe", + sgiGraphicsPipe::get_class_type()); +} + +TypeHandle SgiGlxGraphicsPipe::get_type(void) const { + return get_class_type(); +} + +TypeHandle SgiGlxGraphicsPipe::force_init_type(void) { + init_type(); + return get_class_type(); +} diff --git a/panda/src/sgiglxdisplay/sgiglxGraphicsPipe.h b/panda/src/sgiglxdisplay/sgiglxGraphicsPipe.h new file mode 100644 index 0000000000..4abf295633 --- /dev/null +++ b/panda/src/sgiglxdisplay/sgiglxGraphicsPipe.h @@ -0,0 +1,41 @@ +// Filename: sgiglxGraphicsPipe.h +// Created by: cary (01Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __SGIGLXGRAPHICSPIPE_H__ +#define __SGIGLXGRAPHICSPIPE_H__ + +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : SgiGlxGraphicsPipe +// Description : This kind of pipe can create glx windows but +// supports the functionality of an sgi pipe with +// hardware channels +//////////////////////////////////////////////////////////////////// +class SgiGlxGraphicsPipe : public sgiGraphicsPipe +{ +public: + SgiGlxGraphicsPipe( const PipeSpecifier& ); + + virtual TypeHandle get_window_type() const; + + static GraphicsPipe* make_sgiglxgraphicspipe(const FactoryParams ¶ms); + + static TypeHandle get_class_type( void ); + static void init_type( void ); + virtual TypeHandle get_type( void ) const; + virtual TypeHandle force_init_type( void ); + +private: + static TypeHandle _type_handle; +}; + +#endif /* __SGIGLXGRAPHICSPIPE_H__ */ diff --git a/panda/src/sgmanip/Sources.pp b/panda/src/sgmanip/Sources.pp new file mode 100644 index 0000000000..e8052afe9e --- /dev/null +++ b/panda/src/sgmanip/Sources.pp @@ -0,0 +1,33 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET sgmanip + #define LOCAL_LIBS \ + dgraph loader sgraphutil sgattrib sgraph linmath lerp + + #define SOURCES \ + config_sgmanip.cxx config_sgmanip.h findApproxLevel.I \ + findApproxLevel.cxx findApproxLevel.h findApproxPath.I \ + findApproxPath.cxx findApproxPath.h nodePath.I nodePath.cxx \ + nodePath.h nodePathBase.I nodePathBase.cxx nodePathBase.h \ + nodePathCollection.I nodePathCollection.cxx nodePathCollection.h \ + nodePathLerps.cxx nodePathLerps.h + + #define INSTALL_HEADERS \ + nodePath.I nodePath.h nodePathBase.I nodePathBase.h \ + nodePathCollection.I nodePathCollection.h + + #define IGATESCAN all + +#end lib_target + +#begin test_bin_target + #define TARGET test_sgmanip + #define LOCAL_LIBS \ + sgmanip + + #define SOURCES \ + test_sgmanip.cxx + +#end test_bin_target + diff --git a/panda/src/sgmanip/config_sgmanip.cxx b/panda/src/sgmanip/config_sgmanip.cxx new file mode 100644 index 0000000000..a168fcddae --- /dev/null +++ b/panda/src/sgmanip/config_sgmanip.cxx @@ -0,0 +1,23 @@ +// Filename: config_sgmanip.cxx +// Created by: drose (05Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_sgmanip.h" + +#include + +#include "nodePathLerps.h" + +Configure(config_sgmanip); +NotifyCategoryDef(sgmanip, ""); + +ConfigureFn(config_sgmanip) { + PosLerpFunctor::init_type(); + HprLerpFunctor::init_type(); + ScaleLerpFunctor::init_type(); + PosHprLerpFunctor::init_type(); + PosHprScaleLerpFunctor::init_type(); + ColorLerpFunctor::init_type(); + ColorScaleLerpFunctor::init_type(); +} diff --git a/panda/src/sgmanip/config_sgmanip.h b/panda/src/sgmanip/config_sgmanip.h new file mode 100644 index 0000000000..bfaa07827c --- /dev/null +++ b/panda/src/sgmanip/config_sgmanip.h @@ -0,0 +1,15 @@ +// Filename: config_sgmanip.h +// Created by: drose (05Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_SGMANIP_H +#define CONFIG_SGMANIP_H + +#include + +#include + +NotifyCategoryDecl(sgmanip, EXPCL_PANDA, EXPTP_PANDA); + +#endif diff --git a/panda/src/sgmanip/findApproxLevel.I b/panda/src/sgmanip/findApproxLevel.I new file mode 100644 index 0000000000..d314fc0019 --- /dev/null +++ b/panda/src/sgmanip/findApproxLevel.I @@ -0,0 +1,68 @@ +// Filename: findApproxLevel.I +// Created by: drose (18Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxLevelEntry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FindApproxLevelEntry:: +FindApproxLevelEntry(const NodePath &node_path, FindApproxPath &approx_path) : + _node_path(node_path), + _approx_path(approx_path) +{ + _i = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxLevelEntry::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FindApproxLevelEntry:: +FindApproxLevelEntry(const FindApproxLevelEntry ©) : + _node_path(copy._node_path), + _i(copy._i), + _approx_path(copy._approx_path) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxLevelEntry::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void FindApproxLevelEntry:: +operator = (const FindApproxLevelEntry ©) { + _node_path = copy._node_path; + _i = copy._i; + nassertv(&_approx_path == ©._approx_path); +} + + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxLevelEntry::is_solution +// Access: Public +// Description: Returns true if this entry represents a solution to +// the search; i.e. all the components of the path have +// been successfully matched. +//////////////////////////////////////////////////////////////////// +INLINE bool FindApproxLevelEntry:: +is_solution() const { + return (_i >= _approx_path.get_num_components()); +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxLevel::add_entry +// Access: Public +// Description: Adds a new entry to the level. +//////////////////////////////////////////////////////////////////// +INLINE void FindApproxLevel:: +add_entry(const FindApproxLevelEntry &entry) { + _v.push_back(entry); +} + diff --git a/panda/src/sgmanip/findApproxLevel.cxx b/panda/src/sgmanip/findApproxLevel.cxx new file mode 100644 index 0000000000..ce9ce210e0 --- /dev/null +++ b/panda/src/sgmanip/findApproxLevel.cxx @@ -0,0 +1,104 @@ +// Filename: findApproxLevel.cxx +// Created by: drose (18Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "findApproxLevel.h" +#include "nodePathCollection.h" + +#include +#include +#include +#include + + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxLevelEntry::output +// Access: Public +// Description: Formats the entry for meaningful output. For +// debugging only. +//////////////////////////////////////////////////////////////////// +void FindApproxLevelEntry:: +output(ostream &out) const { + out << "(" << _node_path << "):"; + if (is_solution()) { + out << " solution!"; + } else { + out << "("; + _approx_path.output_component(out, _i); + out << ")," << _i; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxLevelEntry::consider_next_step +// Access: Public +// Description: Compares the indicated child node (which is assumed +// to be a child of _node, or a parent of _node if we +// happen to be searching upwards) with the next +// component of the path. If it matches, generates +// whatever additional entries are appropriate and +// stores them in next_level. +// +// If a complete solution is found, returns the solution +// node; otherwise, returns NULL. +//////////////////////////////////////////////////////////////////// +void FindApproxLevelEntry:: +consider_next_step(NodePathCollection &result, + NodeRelation *arc, FindApproxLevel &next_level, + int max_matches) const { + nassertv(_i < _approx_path.get_num_components()); + + FindApproxLevelEntry next(*this); + bool eb = next._node_path.extend_by(arc); + nassertv(eb); + + Node *child = arc->get_child(); + if (_approx_path.is_component_match_many(_i)) { + // Match any number, zero or more, levels of nodes. This is the + // tricky case that requires this whole nutty breadth-first thing. + + // This means we must reconsider our own entry with the next + // path entry, before we consider the next entry. + FindApproxLevelEntry reconsider(*this); + ++reconsider._i; + if (reconsider.is_solution()) { + // Does this now represent a solution? + bool eb = reconsider._node_path.extend_by(arc); + nassertv(eb); + result.add_path(reconsider._node_path); + } else { + reconsider.consider_next_step(result, arc, next_level, max_matches); + } + + if (max_matches > 0 && result.get_num_paths() >= max_matches) { + return; + } + + // And now we just add the next entry without incrementing its + // path entry. + next_level.add_entry(next); + + } else { + if (_approx_path.matches_component(_i, child)) { + // That matched, and it consumes one path entry. + ++next._i; + next_level.add_entry(next); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxLevel::write +// Access: Public +// Description: Shows the entire contents of the level, one entry per +// line. For debugging only. +//////////////////////////////////////////////////////////////////// +void FindApproxLevel:: +write(ostream &out) const { + Vec::const_iterator vi; + for (vi = _v.begin(); vi != _v.end(); ++vi) { + (*vi).output(out); + out << "\n"; + } +} diff --git a/panda/src/sgmanip/findApproxLevel.h b/panda/src/sgmanip/findApproxLevel.h new file mode 100644 index 0000000000..7c50676140 --- /dev/null +++ b/panda/src/sgmanip/findApproxLevel.h @@ -0,0 +1,79 @@ +// Filename: findApproxLevel.h +// Created by: drose (18Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FINDAPPROXLEVEL_H +#define FINDAPPROXLEVEL_H + +#include + +#include "findApproxPath.h" +#include "nodePath.h" + +#include + +class Node; +class FindApproxLevel; +class NodeFinder; +class NodePathCollection; + +//////////////////////////////////////////////////////////////////// +// Class : FindApproxLevelEntry +// Description : This class is local to this package only; it doesn't +// get exported. It represents a single node under +// consideration for matching at a single point in the +// breadth-first search. +//////////////////////////////////////////////////////////////////// +class FindApproxLevelEntry { +public: + INLINE FindApproxLevelEntry(const NodePath &node_path, + FindApproxPath &approx_path); + INLINE FindApproxLevelEntry(const FindApproxLevelEntry ©); + INLINE void operator = (const FindApproxLevelEntry ©); + + void consider_next_step(NodePathCollection &result, + NodeRelation *arc, FindApproxLevel &next_level, + int max_matches) const; + INLINE bool is_solution() const; + + void output(ostream &out) const; + + // _node_path represents the most recent node that we have + // previously accepted as being a partial solution. + NodePath _node_path; + + // _i represents the next component in the approx_path that must be + // matched against all of the children of _node_path, above. If _i + // refers to the end of the approx_path, then _node_path is a + // solution. + int _i; + FindApproxPath &_approx_path; +}; + +INLINE ostream & +operator << (ostream &out, const FindApproxLevelEntry &entry) { + entry.output(out); + return out; +} + +//////////////////////////////////////////////////////////////////// +// Class : FindApproxLevel +// Description : This class is local to this package only; it doesn't +// get exported. It maintains the list of nodes +// find_approx() considers for each level of the scene +// graph it visits, in its breadth-first search. +//////////////////////////////////////////////////////////////////// +class FindApproxLevel { +public: + INLINE void add_entry(const FindApproxLevelEntry &entry); + + void write(ostream &out) const; + + typedef vector Vec; + Vec _v; +}; + +#include "findApproxLevel.I" + +#endif diff --git a/panda/src/sgmanip/findApproxPath.I b/panda/src/sgmanip/findApproxPath.I new file mode 100644 index 0000000000..043a6b629c --- /dev/null +++ b/panda/src/sgmanip/findApproxPath.I @@ -0,0 +1,157 @@ +// Filename: findApproxPath.I +// Created by: drose (06Mar00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxPath::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FindApproxPath:: +FindApproxPath() { +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxPath::add_match_name +// Access: Public +// Description: Adds a component that must match the name of a node +// exactly. +//////////////////////////////////////////////////////////////////// +INLINE void FindApproxPath:: +add_match_name(const string &name) { + Component comp; + comp._type = CT_match_name; + comp._name = name; + _path.push_back(comp); +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxPath::add_match_name_glob +// Access: Public +// Description: Adds a component that must match the name of a node +// using standard shell globbing rules, with wildcard +// characters accepted. +//////////////////////////////////////////////////////////////////// +INLINE void FindApproxPath:: +add_match_name_glob(const string &name) { + Component comp; + comp._type = CT_match_name_glob; + comp._name = name; + _path.push_back(comp); +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxPath::add_match_exact_type +// Access: Public +// Description: Adds a component that must match the type of a node +// exactly, with no derived types matching. +//////////////////////////////////////////////////////////////////// +INLINE void FindApproxPath:: +add_match_exact_type(TypeHandle type) { + Component comp; + comp._type = CT_match_exact_type; + comp._type_handle = type; + _path.push_back(comp); +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxPath::add_match_inexact_type +// Access: Public +// Description: Adds a component that must match the type of a node +// or be a base class of the node's type. +//////////////////////////////////////////////////////////////////// +INLINE void FindApproxPath:: +add_match_inexact_type(TypeHandle type) { + Component comp; + comp._type = CT_match_inexact_type; + comp._type_handle = type; + _path.push_back(comp); +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxPath::add_match_one +// Access: Public +// Description: Adds a component that will match any node (but not a +// chain of many nodes). +//////////////////////////////////////////////////////////////////// +INLINE void FindApproxPath:: +add_match_one() { + Component comp; + comp._type = CT_match_one; + _path.push_back(comp); +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxPath::add_match_many +// Access: Public +// Description: Adds a component that will match a chain of zero or +// more consecutive nodes. +//////////////////////////////////////////////////////////////////// +INLINE void FindApproxPath:: +add_match_many() { + Component comp; + comp._type = CT_match_many; + _path.push_back(comp); +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxPath::add_match_pointer +// Access: Public +// Description: Adds a component that must match a particular node +// exactly, by pointer. +//////////////////////////////////////////////////////////////////// +INLINE void FindApproxPath:: +add_match_pointer(Node *pointer) { + Component comp; + comp._type = CT_match_pointer; + comp._pointer = pointer; + _path.push_back(comp); +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxPath::get_num_components +// Access: Public +// Description: Returns the number of components in the path. +//////////////////////////////////////////////////////////////////// +INLINE int FindApproxPath:: +get_num_components() const { + return _path.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxPath::is_component_match_many +// Access: Public +// Description: Returns true if the nth component is of type +// match_many, which will require special handling. +//////////////////////////////////////////////////////////////////// +INLINE bool FindApproxPath:: +is_component_match_many(int index) const { + nassertr(index >= 0 && index < (int)_path.size(), false); + return (_path[index]._type == CT_match_many); +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxPath::matches_component +// Access: Public +// Description: Returns true if the nth component of the path matches +// the indicated node, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool FindApproxPath:: +matches_component(int index, Node *node) const { + nassertr(index >= 0 && index < (int)_path.size(), false); + return (_path[index].matches(node)); +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxPath::output_component +// Access: Public +// Description: Formats the nth component of the path to the +// indicated output stream. +//////////////////////////////////////////////////////////////////// +INLINE void FindApproxPath:: +output_component(ostream &out, int index) const { + nassertv(index >= 0 && index < (int)_path.size()); + out << _path[index]; +} diff --git a/panda/src/sgmanip/findApproxPath.cxx b/panda/src/sgmanip/findApproxPath.cxx new file mode 100644 index 0000000000..c6f93e56bb --- /dev/null +++ b/panda/src/sgmanip/findApproxPath.cxx @@ -0,0 +1,203 @@ +// Filename: findApproxPath.cxx +// Created by: drose (18Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "findApproxPath.h" +#include "config_sgmanip.h" + +#include +#include +#include + + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxPath::Component::matches +// Access: Public +// Description: Returns true if the indicated node matches this +// component, false otherwise. +//////////////////////////////////////////////////////////////////// +bool FindApproxPath::Component:: +matches(Node *node) const { + string node_name; + + switch (_type) { + case CT_match_name: + // Match the node's name exactly. + if (node->is_of_type(NamedNode::get_class_type())) { + node_name = DCAST(NamedNode, node)->get_name(); + } + return (_name == node_name); + + case CT_match_name_glob: + // Match the node's name according to filename globbing rules. + if (node->is_of_type(NamedNode::get_class_type())) { + node_name = DCAST(NamedNode, node)->get_name(); + } + { + GlobPattern pattern(_name); + return (pattern.matches(node_name)); + } + + case CT_match_exact_type: + // Match the node's type exactly. + return (node->is_exact_type(_type_handle)); + + case CT_match_inexact_type: + // Match the node's type inexactly: it's a match if the node + // is the type, or is derived from the type. + return (node->is_of_type(_type_handle)); + + case CT_match_one: + case CT_match_many: + // Match any node. + return true; + + case CT_match_pointer: + // Match only this one particular node. + return (_pointer == node); + } + + sgmanip_cat.error() + << "Invalid component in FindApproxPath\n"; + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxPath::Component::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void FindApproxPath::Component:: +output(ostream &out) const { + out << _type; + if (_type == CT_match_name || _type == CT_match_name_glob) { + out << " \"" << _name << "\""; + + } else if (_type == CT_match_exact_type || _type == CT_match_inexact_type) { + out << " " << _type_handle; + + } else if (_type == CT_match_pointer) { + out << " (" << *_pointer << ")"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxPath::add_string +// Access: Public +// Description: Adds a sequence of components separated by slashes to +// the path sequence. Returns true if successful, false +// if the string contained an error. +//////////////////////////////////////////////////////////////////// +bool FindApproxPath:: +add_string(const string &str_path) { + size_t start = 0; + size_t slash = str_path.find('/'); + while (slash != string::npos) { + if (!add_component(str_path.substr(start, slash - start))) { + return false; + } + start = slash + 1; + slash = str_path.find('/', start); + } + return add_component(str_path.substr(start)); +} + + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxPath::add_component +// Access: Public +// Description: Adds a single component to the path sequence, defined +// by a string as might appear between slashes in the +// path string. Returns true if successful, false if +// the string component was in some way invalid. +//////////////////////////////////////////////////////////////////// +bool FindApproxPath:: +add_component(const string &str_component) { + if (str_component == "*") { + add_match_one(); + + } else if (str_component == "**") { + add_match_many(); + + } else if (!str_component.empty() && str_component[0] == '-') { + string type_name = str_component.substr(1); + TypeHandle handle = TypeRegistry::ptr()->find_type(type_name); + + if (handle == TypeHandle::none()) { + sgmanip_cat.error() + << "Invalid type name: " + type_name; + return false; + + } else { + add_match_exact_type(handle); + } + + } else if (!str_component.empty() && str_component[0] == '+') { + string type_name = str_component.substr(1); + TypeHandle handle = TypeRegistry::ptr()->find_type(type_name); + + if (handle == TypeHandle::none()) { + sgmanip_cat.error() + << "Invalid type name: " + type_name; + return false; + + } else { + add_match_inexact_type(handle); + } + + } else { + add_match_name_glob(str_component); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: FindApproxPath::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void FindApproxPath:: +output(ostream &out) const { + out << "("; + if (!_path.empty()) { + Path::const_iterator pi = _path.begin(); + out << *pi; + ++pi; + while (pi != _path.end()) { + out << " / " << *pi; + ++pi; + } + } + out << ")"; +} + +ostream & +operator << (ostream &out, FindApproxPath::ComponentType type) { + switch (type) { + case FindApproxPath::CT_match_name: + return out << "match_name"; + + case FindApproxPath::CT_match_name_glob: + return out << "match_name_glob"; + + case FindApproxPath::CT_match_exact_type: + return out << "match_exact_type"; + + case FindApproxPath::CT_match_inexact_type: + return out << "match_inexact_type"; + + case FindApproxPath::CT_match_one: + return out << "match_one"; + + case FindApproxPath::CT_match_many: + return out << "match_many"; + + case FindApproxPath::CT_match_pointer: + return out << "match_pointer"; + }; + + return out << "**invalid**"; +}; + diff --git a/panda/src/sgmanip/findApproxPath.h b/panda/src/sgmanip/findApproxPath.h new file mode 100644 index 0000000000..8a57b7a8d4 --- /dev/null +++ b/panda/src/sgmanip/findApproxPath.h @@ -0,0 +1,97 @@ +// Filename: findApproxPath.h +// Created by: drose (18Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FINDAPPROXPATH_H +#define FINDAPPROXPATH_H + +#include + +#include + +#include +#include + +class Node; + +//////////////////////////////////////////////////////////////////// +// Class : FindApproxPath +// Description : This class is local to this package only; it doesn't +// get exported. It chops a string path, as supplied to +// find_up() or find_down(), and breaks it up into its +// component pieces. +//////////////////////////////////////////////////////////////////// +class FindApproxPath { +public: + INLINE FindApproxPath(); + + bool add_string(const string &str_path); + bool add_component(const string &str_component); + + INLINE void add_match_name(const string &name); + INLINE void add_match_name_glob(const string &glob); + INLINE void add_match_exact_type(TypeHandle type); + INLINE void add_match_inexact_type(TypeHandle type); + INLINE void add_match_one(); + INLINE void add_match_many(); + INLINE void add_match_pointer(Node *pointer); + + INLINE int get_num_components() const; + INLINE bool is_component_match_many(int index) const; + INLINE bool matches_component(int index, Node *node) const; + + void output(ostream &out) const; + INLINE void output_component(ostream &out, int index) const; + +#ifndef WIN32_VC +// Visual C++ won't let us define the ostream operator functions for +// these guys if they're private--even though we declare them friends. +private: +#endif + enum ComponentType { + CT_match_name, + CT_match_name_glob, + CT_match_exact_type, + CT_match_inexact_type, + CT_match_one, + CT_match_many, + CT_match_pointer + }; + + class Component { + public: + bool matches(Node *node) const; + void output(ostream &out) const; + + ComponentType _type; + string _name; + TypeHandle _type_handle; + Node *_pointer; + }; + + typedef vector Path; + Path _path; + +friend ostream &operator << (ostream &, FindApproxPath::ComponentType); +friend INLINE ostream &operator << (ostream &, const FindApproxPath::Component &); +}; + +ostream & +operator << (ostream &out, FindApproxPath::ComponentType type); + +INLINE ostream & +operator << (ostream &out, const FindApproxPath::Component &component) { + component.output(out); + return out; +} + +INLINE ostream & +operator << (ostream &out, const FindApproxPath &path) { + path.output(out); + return out; +} + +#include "findApproxPath.I" + +#endif diff --git a/panda/src/sgmanip/get_cout.h b/panda/src/sgmanip/get_cout.h new file mode 100644 index 0000000000..919a4b4cb7 --- /dev/null +++ b/panda/src/sgmanip/get_cout.h @@ -0,0 +1,34 @@ +// Filename: get_cout.h +// Created by: drose (23Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GET_COUT_H +#define GET_COUT_H + +// This file defines some general object-placement functions for scene +// graph operations. These are all higher-level wrappers around more +// fundamental scene graph manipulations, and are most useful from a +// scripting-language point of view. + +#include + +// These are just handy hooks to export these so a non-C++ scripting +// language can access iostream objects for input and output. When +// interrogate supports exporting system globals directly, the need +// for these will go away. + +INLINE ostream &get_cout() { + return cout; +} + +INLINE ostream &get_cerr() { + return cerr; +} + +INLINE istream &get_cin() { + return cin; +} + +#endif + diff --git a/panda/src/sgmanip/nodePath.I b/panda/src/sgmanip/nodePath.I new file mode 100644 index 0000000000..0e19eddaed --- /dev/null +++ b/panda/src/sgmanip/nodePath.I @@ -0,0 +1,1479 @@ +// Filename: nodePath.I +// Created by: drose (06Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::ForwardIterator::Constructor +// Access: Public +// Description: This is an STL-style iterator that can be used to +// traverse the full list of arcs in the NodePath. Its +// primary purpose is as a parameter to wrt() so we can +// compute a correct relative wrt to the particular +// instance referenced by the NodePath. +//////////////////////////////////////////////////////////////////// +INLINE NodePath::ForwardIterator:: +ForwardIterator(NodePath::ArcComponent *comp) : _comp(comp) { +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::ForwardIterator::Dereference Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodeRelation *NodePath::ForwardIterator:: +operator * () const { + nassertr(_comp != (ArcComponent *)NULL, NULL); + return _comp->_arc; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::ForwardIterator::Increment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodePath::ForwardIterator:: +operator ++() { + nassertv(_comp != (ArcComponent *)NULL); + _comp = _comp->_next; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::ForwardIterator::Equality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodePath::ForwardIterator:: +operator == (const NodePath::ForwardIterator &other) const { + return _comp == other._comp; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::ForwardIterator::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodePath::ForwardIterator:: +operator != (const NodePath::ForwardIterator &other) const { + return _comp != other._comp; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::Default Constructor +// Access: Public +// Description: Creates an empty NodePath. This does not refer to +// any nodes, and is equivalent to a NULL Node pointer; +// it's an error to attempt to do any node operations on +// this object. +//////////////////////////////////////////////////////////////////// +INLINE NodePath:: +NodePath(TypeHandle graph_type) : NodePathBase(graph_type) { +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::Constructor +// Access: Public +// Description: Creates a NodePath that contains just one node: the +// top node. It's not yet much of a path, but it does +// reference at least the one node and can be used as an +// ordinary node pointer. +//////////////////////////////////////////////////////////////////// +INLINE NodePath:: +NodePath(Node *top_node, TypeHandle graph_type) : NodePathBase(graph_type) { + _top_node = top_node; + nassertv(verify_connectivity()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::Constructor +// Access: Public +// Description: Creates a NodePath that contains two nodes and one +// arc: the top arc. +//////////////////////////////////////////////////////////////////// +INLINE NodePath:: +NodePath(NodeRelation *top_arc) : NodePathBase(top_arc->get_type()) { + _top_node = top_arc->get_parent(); + _head = new ArcComponent(top_arc, NULL); + nassertv(verify_connectivity()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodePath:: +NodePath(const NodePathBase ©) : NodePathBase(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::Copy-and-shorten constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodePath:: +NodePath(const NodePath ©, int shorten_count) : NodePathBase(copy) { + shorten(shorten_count); + nassertv(verify_connectivity()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::Copy-and-extend constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodePath:: +NodePath(const NodePath ©, NodeRelation *arc) : + NodePathBase(copy) +{ + nassertv(verify_connectivity()); + if (!extend_by(arc)) { + clear(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::Copy-and-extend-down constructor +// Access: Public +// Description: This implicitly does an extend_down_to() on the +// indicated node, so that it is not necessary that the +// node be an immediate child of the path. +//////////////////////////////////////////////////////////////////// +INLINE NodePath:: +NodePath(const NodePath ©, Node *node) : + NodePathBase(copy) +{ + nassertv(verify_connectivity()); + if (!extend_down_to(node)) { + clear(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::Copy-and-extend constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodePath:: +NodePath(const NodePath ©, const string &path) : + NodePathBase(copy) +{ + nassertv(verify_connectivity()); + if (!extend_by(path)) { + clear(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +operator = (const NodePath ©) { + NodePathBase::operator = (copy); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodePath:: +~NodePath() { +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::Equality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodePath:: +operator == (const NodePath &other) const { + // If either is a singleton, they must both be the same singleton. + if (_head == (ArcComponent *)NULL || + other._head == (ArcComponent *)NULL) { + if (_head != (ArcComponent *)NULL || + other._head != (ArcComponent *)NULL || + _top_node != other._top_node) { + return false; + } + } + + // If both has at least one arc, check the list of arcs. + return r_equiv(_head, other._head); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool NodePath:: +operator != (const NodePath &other) const { + return !operator == (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_graph_type +// Access: Public +// Description: Changes the type of graph that the NodePath will +// search for. By default, this is RenderRelation. +// This may only be called when the NodePath contains no +// arcs (e.g. is_empty() || is_singleton()). +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_graph_type(TypeHandle graph_type) { + nassertv(_head == (ArcComponent *)NULL); + _graph_type = graph_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_graph_type +// Access: Public +// Description: Returns the type of graph that the NodePath is +// currently set to. +//////////////////////////////////////////////////////////////////// +INLINE TypeHandle NodePath:: +get_graph_type() const { + return _graph_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_max_search_depth +// Access: Public, Static +// Description: Certain operations, such as extend_down_to() or +// find_all_matches(), require a traversal of the scene +// graph to search for the target node or nodes. This +// traversal does not attempt to detect cycles, so an +// arbitrary cap is set on the depth of the traversal as +// a poor man's cycle detection, in the event that a +// cycle has inadvertently been introduced into the +// scene graph. +// +// There may be other reasons you'd want to truncate a +// search before the bottom of the scene graph has been +// reached. In any event, this function sets the limit +// on the number of levels that a traversal will +// continue, and hence the maximum length of a path that +// may be returned by a traversal. +// +// This is a static method, and so changing this +// parameter affects all of the NodePaths in the +// universe. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_max_search_depth(int max_search_depth) { + _max_search_depth = max_search_depth; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_max_search_depth +// Access: Public, Static +// Description: Returns the current setting of the search depth +// limit. See set_max_search_depth. +//////////////////////////////////////////////////////////////////// +INLINE int NodePath:: +get_max_search_depth() { + return _max_search_depth; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::is_empty +// Access: Public +// Description: Returns true if the NodePath contains no nodes and no +// arcs. +//////////////////////////////////////////////////////////////////// +INLINE bool NodePath:: +is_empty() const { + return (_head == (ArcComponent *)NULL && _top_node == (Node *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::is_singleton +// Access: Public +// Description: Returns true if the NodePath contains exactly one +// node, and no arcs. +//////////////////////////////////////////////////////////////////// +INLINE bool NodePath:: +is_singleton() const { + return (_head == (ArcComponent *)NULL && _top_node != (Node *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::has_arcs +// Access: Public +// Description: Returns true if the NodePath contains at least one +// arc, and therefore at least two nodes. This is the +// same thing as asking get_num_arcs() > 0, but is +// easier to compute. +//////////////////////////////////////////////////////////////////// +INLINE bool NodePath:: +has_arcs() const { + return (_head != (ArcComponent *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_num_nodes +// Access: Public +// Description: Returns the number of nodes in the path, including +// the top node. +//////////////////////////////////////////////////////////////////// +INLINE int NodePath:: +get_num_nodes() const { + if (is_empty()) { + return 0; + } + return get_num_arcs() + 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_bottom_node +// Access: Public +// Description: Returns the bottom node of the path, or NULL if the +// path is empty. +//////////////////////////////////////////////////////////////////// +INLINE Node *NodePath:: +get_bottom_node() const { + if (_head == (ArcComponent *)NULL) { + // A singleton or empty list. + return _top_node; + } + return _head->_arc->get_child(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::node +// Access: Public +// Description: A synonym of get_bottom_node(), this returns the +// bottom node of the path, or NULL if the path is +// empty. It's redefined for brevity because this +// function will be called fairly often. +//////////////////////////////////////////////////////////////////// +INLINE Node *NodePath:: +node() const { + return get_bottom_node(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_bottom_arc +// Access: Public +// Description: Returns the bottom arc of the path, or NULL if the +// path is empty or is a singleton. +//////////////////////////////////////////////////////////////////// +INLINE NodeRelation *NodePath:: +get_bottom_arc() const { + if (_head == (ArcComponent *)NULL) { + // A singleton or empty list. + return (NodeRelation *)NULL; + } + return _head->_arc; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_num_children +// Access: Public +// Description: Returns the number of children of the bottom node of +// the NodePath. This will be the same as the number of +// paths in the collection returned by get_children(). +//////////////////////////////////////////////////////////////////// +INLINE int NodePath:: +get_num_children() const { + nassertr(verify_connectivity(), 0); + nassertr(!is_empty(), 0); + + return get_bottom_node()->get_num_children(_graph_type); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_child +// Access: Public +// Description: Returns the nth of child of the bottom node of +// the NodePath. This will be the same as nth path in +// the collection returned by get_children(). +//////////////////////////////////////////////////////////////////// +INLINE NodePath NodePath:: +get_child(int n) const { + nassertr(verify_connectivity(), NodePath()); + nassertr(!is_empty(), NodePath()); + nassertr(n >= 0 && n < get_num_children(), NodePath()); + + return NodePath(*this, get_bottom_node()->get_child(_graph_type, n)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::has_parent +// Access: Public +// Description: Returns true if the node at the bottom of the +// NodePath has a parent; i.e. the NodePath contains at +// least two nodes. +//////////////////////////////////////////////////////////////////// +INLINE bool NodePath:: +has_parent() const { + return has_arcs(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_parent +// Access: Public +// Description: Returns the NodePath to the parent node of the bottom +// arc: that is, this NodePath, shortened by one node. +//////////////////////////////////////////////////////////////////// +INLINE NodePath NodePath:: +get_parent() const { + nassertr(has_parent(), NodePath()); + NodePath parent(*this); + parent.shorten(); + return parent; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::find_path_down_to +// Access: Public +// Description: Returns a NodePath that represents the extension of +// this NodePath down to the indicated node along the +// shortest possible path, if any, or an empty NodePath +// if there is no connection to the indicated node. +//////////////////////////////////////////////////////////////////// +INLINE NodePath NodePath:: +find_path_down_to(Node *node) const { + return NodePath(*this, node); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::find +// Access: Public +// Description: Searches for a node below this NodePath's bottom node +// that matches the indicated string. Returns the +// shortest match found, if any, or an empty NodePath if +// no match can be found. +//////////////////////////////////////////////////////////////////// +INLINE NodePath NodePath:: +find(const string &path) const { + return NodePath(*this, path); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::attach_new_node +// Access: Public +// Description: Creates a new NamedNode and attaches it below the +// current NodePath, returning a new NodePath that +// references it. +//////////////////////////////////////////////////////////////////// +INLINE NodePath NodePath:: +attach_new_node(const string &name) const { + nassertr(verify_connectivity(), NodePath()); + nassertr(!is_empty(), NodePath(_graph_type)); + + return attach_new_node(new NamedNode(name)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +output(ostream &out) const { + out << as_string(0); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::ls +// Access: Public +// Description: Lists all the nodes at and below the current path +// hierarchically. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +ls() const { + ls(nout); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::ls +// Access: Public +// Description: Lists all the nodes at and below the current path +// hierarchically. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +ls(ostream &out, int indent_level) const { + nassertv(verify_connectivity()); + nassertv(!is_empty()); + + r_list_descendants(out, indent_level); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::ls_transitions +// Access: Public +// Description: Lists all the nodes at and below the current path +// hierarchically, along with all the transitions on the +// arcs between them. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +ls_transitions() const { + ls_transitions(nout); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::ls_transitions +// Access: Public +// Description: Lists all the nodes at and below the current path +// hierarchically, along with all the transitions on the +// arcs between them. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +ls_transitions(ostream &out, int indent_level) const { + nassertv(verify_connectivity()); + nassertv(!is_empty()); + + r_list_transitions(out, indent_level); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_pos +// Access: Public +// Description: Sets the translation component of the transform, +// leaving rotation and scale untouched. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_pos(float x, float y, float z) { + set_pos(LPoint3f(x, y, z)); +} + +INLINE float NodePath:: +get_x() const { + return get_pos()[0]; +} + +INLINE float NodePath:: +get_y() const { + return get_pos()[1]; +} + +INLINE float NodePath:: +get_z() const { + return get_pos()[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_hpr +// Access: Public +// Description: Sets the rotation component of the transform, +// leaving translation and scale untouched. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_hpr(float h, float p, float r) { + set_hpr(LVecBase3f(h, p, r)); +} + +INLINE float NodePath:: +get_h() const { + return get_hpr()[0]; +} + +INLINE float NodePath:: +get_p() const { + return get_hpr()[1]; +} + +INLINE float NodePath:: +get_r() const { + return get_hpr()[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_scale +// Access: Public +// Description: Sets the scale component of the transform, +// leaving translation and rotation untouched. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_scale(float scale) { + set_scale(LVecBase3f(scale, scale, scale)); +} + +INLINE void NodePath:: +set_scale(float sx, float sy, float sz) { + set_scale(LVecBase3f(sx, sy, sz)); +} + +INLINE float NodePath:: +get_sx() const { + return get_scale()[0]; +} + +INLINE float NodePath:: +get_sy() const { + return get_scale()[1]; +} + +INLINE float NodePath:: +get_sz() const { + return get_scale()[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_color_scale +// Access: Public +// Description: Sets the color scale component of the transform +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_color_scale(float sr, float sg, float sb, float sa) { + set_color_scale(LVecBase4f(sr, sg, sb, sa)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_sr +// Access: Public +// Description: Sets the red scale component of the transform +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_sr(float sr) { + LVecBase4f new_scale = get_color_scale(); + new_scale[0] = sr; + + set_color_scale(new_scale); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_sg +// Access: Public +// Description: Sets the alpha scale component of the transform +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_sg(float sg) { + LVecBase4f new_scale = get_color_scale(); + new_scale[1] = sg; + + set_color_scale(new_scale); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_sb +// Access: Public +// Description: Sets the blue scale component of the transform +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_sb(float sb) { + LVecBase4f new_scale = get_color_scale(); + new_scale[2] = sb; + + set_color_scale(new_scale); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_sa +// Access: Public +// Description: Sets the alpha scale component of the transform +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_sa(float sa) { + LVecBase4f new_scale = get_color_scale(); + new_scale[3] = sa; + + set_color_scale(new_scale); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_sr +// Access: Public +// Description: Gets the red scale component of the transform +//////////////////////////////////////////////////////////////////// +INLINE float NodePath:: +get_sr() const { + return get_color_scale()[0]; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_sg +// Access: Public +// Description: Gets the green scale component of the transform +//////////////////////////////////////////////////////////////////// +INLINE float NodePath:: +get_sg() const { + return get_color_scale()[1]; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_sb +// Access: Public +// Description: Gets the blue scale component of the transform +//////////////////////////////////////////////////////////////////// +INLINE float NodePath:: +get_sb() const { + return get_color_scale()[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_sa +// Access: Public +// Description: Gets the alpha scale component of the transform +//////////////////////////////////////////////////////////////////// +INLINE float NodePath:: +get_sa() const { + return get_color_scale()[3]; +} + + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::clear_color_scale +// Access: Public +// Description: Completely removes any color scale from the bottom arc. +// This is preferable to simply setting the color scale to +// identity, as it also removes the overhead associated +// with having a color scale at all. +// +// This method is not strictly accurate as it clears +// both a color matrix and alpha transform, and either of +// those could have only offset components +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +clear_color_scale() { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + _head->_arc->clear_transition(ColorMatrixTransition::get_class_type()); + _head->_arc->clear_transition(AlphaTransformTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::has_color_scale +// Access: Public +// Description: Returns true if a color scale has been applied +// to the bottom arc, false otherwise. +// +// This method is not strictly accurate as it checks +// for a color matrix or alpha transform, and either of +// those could have only offset components +//////////////////////////////////////////////////////////////////// +INLINE bool NodePath:: +has_color_scale() const { + nassertr(has_arcs(), false); + nassertr(_head != (ArcComponent *)NULL, false); + return _head->_arc->has_transition(ColorMatrixTransition::get_class_type()) || + _head->_arc->has_transition(AlphaTransformTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_pos_hpr +// Access: Public +// Description: Sets the translation and rotation component of the +// transform, leaving scale untouched. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_pos_hpr(float x, float y, float z, float h, float p, float r) { + set_pos_hpr(LVecBase3f(x, y, z), LVecBase3f(h, p, r)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_pos_hpr_scale +// Access: Public +// Description: Completely replaces the transform with new +// translation, rotation, and scale components. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_pos_hpr_scale(float x, float y, float z, float h, float p, float r, + float sx, float sy, float sz) { + set_pos_hpr_scale(LVecBase3f(x, y, z), LVecBase3f(h, p, r), + LVecBase3f(sx, sy, sz)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::clear_mat +// Access: Public +// Description: Completely removes any transform from the bottom arc. +// This is preferable to simply setting the matrix to +// identity, as it also removes the overhead associated +// with having a matrix at all. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +clear_mat() { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + _head->_arc->clear_transition(TransformTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::has_mat +// Access: Public +// Description: Returns true if a transform matrix has been applied +// to the bottom arc, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool NodePath:: +has_mat() const { + nassertr(has_arcs(), false); + nassertr(_head != (ArcComponent *)NULL, false); + return _head->_arc->has_transition(TransformTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_mat +// Access: Public +// Description: Returns the complete transform matrix that has been +// applied to the bottom arc, or the identity matrix if +// no matrix has been applied. +//////////////////////////////////////////////////////////////////// +INLINE LMatrix4f NodePath:: +get_mat() const { + nassertr(has_arcs(), LMatrix4f::ident_mat()); + nassertr(_head != (ArcComponent *)NULL, LMatrix4f::ident_mat()); + + const TransformTransition *tt; + if (!get_transition_into(tt, _head->_arc)) { + // No relative transform. + return LMatrix4f::ident_mat(); + } + + return tt->get_matrix(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::look_at +// Access: Public +// Description: Sets the transform on this NodePath so that it +// rotates to face the indicated point in space. This +// will overwrite any previously existing scale on the +// node, although it will preserve any translation. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +look_at(float x, float y, float z) { + look_at(LPoint3f(x, y, z)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::heads_up +// Access: Public +// Description: Behaves like look_at(), but with a strong preference +// to keeping the up vector oriented in the indicated +// "up" direction. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +heads_up(float x, float y, float z) { + heads_up(LPoint3f(x, y, z)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::print_pos +// Access: Public +// Description: Displays the translation component. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +print_pos() const { + nout << get_pos() << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::print_hpr +// Access: Public +// Description: Displays the rotation component. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +print_hpr() const { + nout << get_hpr() << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::print_scale +// Access: Public +// Description: Displays the scale component. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +print_scale() const { + nout << get_scale() << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::print_pos_hpr +// Access: Public +// Description: Displays the translation and rotation component. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +print_pos_hpr() const { + nout << get_pos() << "\n" + << get_hpr() << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::print_pos_hpr_scale +// Access: Public +// Description: Displays the translation, rotation, and scale +// components. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +print_pos_hpr_scale() const { + nout << get_pos() << "\n" + << get_hpr() << "\n" + << get_scale() << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::print_mat +// Access: Public +// Description: Displays the complete transform matrix. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +print_mat() const { + nout << get_mat() << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::print_color_scale +// Access: Public +// Description: Displays the scale component. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +print_color_scale() const { + nout << get_color_scale() << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_pos +// Access: Public +// Description: Sets the translation component of the transform, +// relative to the other node. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_pos(const NodePath &other, float x, float y, float z) { + set_pos(other, LPoint3f(x, y, z)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_pos +// Access: Public +// Description: Returns the relative position of the bottom node as +// seen from the other node. +//////////////////////////////////////////////////////////////////// +INLINE LPoint3f NodePath:: +get_pos(const NodePath &other) const { + LMatrix4f mat = get_mat(other); + return mat.get_row3(3); +} + +INLINE float NodePath:: +get_x(const NodePath &other) const { + return get_pos(other)[0]; +} + +INLINE float NodePath:: +get_y(const NodePath &other) const { + return get_pos(other)[1]; +} + +INLINE float NodePath:: +get_z(const NodePath &other) const { + return get_pos(other)[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_hpr +// Access: Public +// Description: Sets the rotation component of the transform, +// relative to the other node. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_hpr(const NodePath &other, float h, float p, float r) { + set_hpr(other, LPoint3f(h, p, r)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_hpr +// Access: Public +// Description: Returns the relative orientation of the bottom node +// as seen from the other node. +//////////////////////////////////////////////////////////////////// +INLINE LVecBase3f NodePath:: +get_hpr(const NodePath &other) const { + LMatrix4f mat = get_mat(other); + LVector3f scale, hpr, pos; + decompose_matrix(mat, scale, hpr, pos); + return hpr; +} + +INLINE float NodePath:: +get_h(const NodePath &other) const { + return get_hpr(other)[0]; +} + +INLINE float NodePath:: +get_p(const NodePath &other) const { + return get_hpr(other)[1]; +} + +INLINE float NodePath:: +get_r(const NodePath &other) const { + return get_hpr(other)[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_scale +// Access: Public +// Description: Sets the scale component of the transform, +// relative to the other node. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_scale(const NodePath &other, float sx, float sy, float sz) { + set_scale(other, LPoint3f(sx, sy, sz)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_scale +// Access: Public +// Description: Returns the relative scale of the bottom node +// as seen from the other node. +//////////////////////////////////////////////////////////////////// +INLINE float NodePath:: +get_sx(const NodePath &other) const { + return get_scale(other)[0]; +} + +INLINE float NodePath:: +get_sy(const NodePath &other) const { + return get_scale(other)[1]; +} + +INLINE float NodePath:: +get_sz(const NodePath &other) const { + return get_scale(other)[2]; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_pos_hpr +// Access: Public +// Description: Sets the translation and rotation component of the +// transform, relative to the other node. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_pos_hpr(const NodePath &other, + float x, float y, float z, + float h, float p, float r) { + set_pos_hpr(other, LVecBase3f(x, y, z), LVecBase3f(h, p, r)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_pos_hpr_scale +// Access: Public +// Description: Completely replaces the transform with new +// translation, rotation, and scale components, relative +// to the other node. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_pos_hpr_scale(const NodePath &other, + float x, float y, float z, + float h, float p, float r, + float sx, float sy, float sz) { + set_pos_hpr_scale(other, LVecBase3f(x, y, z), LVecBase3f(h, p, r), + LVecBase3f(sx, sy, sz)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::look_at +// Access: Public +// Description: Sets the transform on this NodePath so that it +// rotates to face the indicated point in space, which +// is relative to the other NodePath. This +// will overwrite any previously existing scale on the +// node, although it will preserve any translation. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +look_at(const NodePath &other, float x, float y, float z) { + look_at(other, LPoint3f(x, y, z)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::heads_up +// Access: Public +// Description: Behaves like look_at(), but with a strong preference +// to keeping the up vector oriented in the indicated +// "up" direction. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +heads_up(const NodePath &other, float x, float y, float z) { + heads_up(other, LPoint3f(x, y, z)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::print_pos +// Access: Public +// Description: Displays the translation component. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +print_pos(const NodePath &other) const { + nout << get_pos(other) << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::print_hpr +// Access: Public +// Description: Displays the rotation component. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +print_hpr(const NodePath &other) const { + nout << get_hpr(other) << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::print_scale +// Access: Public +// Description: Displays the scale component. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +print_scale(const NodePath &other) const { + nout << get_scale(other) << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::print_pos_hpr +// Access: Public +// Description: Displays the translation and rotation component. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +print_pos_hpr(const NodePath &other) const { + nout << get_pos(other) << "\n" + << get_hpr(other) << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::print_pos_hpr_scale +// Access: Public +// Description: Displays the translation, rotation, and scale +// components. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +print_pos_hpr_scale(const NodePath &other) const { + nout << get_pos(other) << "\n" + << get_hpr(other) << "\n" + << get_scale(other) << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::print_mat +// Access: Public +// Description: Displays the complete transform matrix. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +print_mat(const NodePath &other) const { + nout << get_mat(other) << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_distance +// Access: Public +// Description: Returns the straight-line distance between this +// bottom node's coordinate frame's origin, and that of +// the other node's origin. +//////////////////////////////////////////////////////////////////// +INLINE float NodePath:: +get_distance(const NodePath &other) const { + LPoint3f pos = get_pos(other); + return length(LVector3f(pos)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_color +// Access: Public +// Description: Sets the color transition for a render relation +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_color(float r, float g, float b, float a, + int priority) { + set_color(Colorf(r, g, b, a), priority); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::clear_color +// Access: Public +// Description: Completely removes any color adjustment from the arc. +// This allows the natural color of the geometry, or +// whatever color transitions might be otherwise +// affecting the geometry, to show instead. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +clear_color() { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + _head->_arc->clear_transition(ColorTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::has_color +// Access: Public +// Description: Returns true if a color has been applied to the given +// arc, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool NodePath:: +has_color() const { + nassertr(has_arcs(), false); + nassertr(_head != (ArcComponent *)NULL, false); + return _head->_arc->has_transition(ColorTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::clear_texture +// Access: Public +// Description: Completely removes any texture adjustment that may +// have been set via set_texture() or set_texture_off() +// from this particular arc. This allows whatever +// textures might be otherwise affecting the geometry to +// show instead. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +clear_texture() { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + _head->_arc->clear_transition(TextureTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::clear_fog +// Access: Public +// Description: Completely removes any fog adjustment that may +// have been set via set_fog() or set_fog_off() +// from this particular arc. This allows whatever +// fogs might be otherwise affecting the geometry to +// show instead. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +clear_fog() { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + _head->_arc->clear_transition(FogTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::clear_render_mode +// Access: Public +// Description: Completely removes any render mode adjustment that +// may have been set on this arc via +// set_render_mode_wireframe() or +// set_render_mode_filled(). +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +clear_render_mode() { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + _head->_arc->clear_transition(RenderModeTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::has_render_mode +// Access: Public +// Description: Returns true if a render mode has been explicitly set +// on this particular arc via +// set_render_mode_wireframe() or +// set_render_mode_filled(), false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool NodePath:: +has_render_mode() const { + nassertr(has_arcs(), false); + nassertr(_head != (ArcComponent *)NULL, false); + return _head->_arc->has_transition(RenderModeTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::clear_two_sided +// Access: Public +// Description: Completely removes any two-sided adjustment that +// may have been set on this arc via set_two_sided(). +// The geometry at this level and below will +// subsequently be rendered either two-sided or +// one-sided, according to whatever other arcs may have +// had set_two_sided() on it, or according to the +// initial state otherwise. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +clear_two_sided() { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + _head->_arc->clear_transition(CullFaceTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::has_two_sided +// Access: Public +// Description: Returns true if a two-sided adjustment has been +// explicitly set on this particular arc via +// set_two_sided(). If this returns true, then +// get_two_sided() may be called to determine which has +// been set. +//////////////////////////////////////////////////////////////////// +INLINE bool NodePath:: +has_two_sided() const { + nassertr(has_arcs(), false); + nassertr(_head != (ArcComponent *)NULL, false); + return _head->_arc->has_transition(CullFaceTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_billboard_axis +// Access: Public +// Description: Puts a billboard transition on the arc such that it +// will rotate in two dimensions around the up axis. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_billboard_axis() { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + _head->_arc->set_transition(new BillboardTransition(BillboardTransition::axis())); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_billboard_point_eye +// Access: Public +// Description: Puts a billboard transition on the arc such that it +// will rotate in three dimensions about the origin, +// keeping its up vector oriented to the top of the +// camera. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_billboard_point_eye() { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + _head->_arc->set_transition(new BillboardTransition(BillboardTransition::point_eye())); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_billboard_point_world +// Access: Public +// Description: Puts a billboard transition on the arc such that it +// will rotate in three dimensions about the origin, +// keeping its up vector oriented to the sky. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_billboard_point_world() { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + _head->_arc->set_transition(new BillboardTransition(BillboardTransition::point_world())); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::clear_billboard +// Access: Public +// Description: Removes any billboard transition from the arc. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +clear_billboard() { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + _head->_arc->clear_transition(BillboardTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::has_billboard +// Access: Public +// Description: Returns true if there is any billboard transition on +// the arc. +//////////////////////////////////////////////////////////////////// +INLINE bool NodePath:: +has_billboard() const { + nassertr(has_arcs(), false); + nassertr(_head != (ArcComponent *)NULL, false); + + return _head->_arc->has_transition(BillboardTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::clear_transparency +// Access: Public +// Description: Completely removes any transparency adjustment that +// may have been set on this arc via set_transparency(). +// The geometry at this level and below will +// subsequently be rendered either transparent or not, +// to whatever other arcs may have had +// set_transparency() on them, or according to the +// initial state otherwise. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +clear_transparency() { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + _head->_arc->clear_transition(TransparencyTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::has_transparency +// Access: Public +// Description: Returns true if a transparent-rendering adjustment +// has been explicitly set on this particular arc via +// set_transparency(). If this returns true, then +// get_transparency() may be called to determine whether +// transparency has been explicitly enabled or +// explicitly disabled for this arc. +//////////////////////////////////////////////////////////////////// +INLINE bool NodePath:: +has_transparency() const { + nassertr(has_arcs(), false); + nassertr(_head != (ArcComponent *)NULL, false); + return _head->_arc->has_transition(TransparencyTransition::get_class_type()); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::show +// Access: Public +// Description: Removes any PruneTransition on this bottom arc so +// that the geometry at this level and below will once +// again be visible--assuming no ancestor arc has a +// PruneTransition. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +show() { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + _head->_arc->clear_transition(PruneTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::hide +// Access: Public +// Description: Puts a PruneTransition on this bottom arc so that the +// geometry at this level and below will be invisible. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +hide() { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + _head->_arc->set_transition(new PruneTransition); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::show_collision_solids +// Access: Public +// Description: Reveals all the collision solids at or below this +// node. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +show_collision_solids() { + find_all_matches("**/+CollisionNode").show(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::hide_collision_solids +// Access: Public +// Description: Hides all the collision solids at or below this +// node. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +hide_collision_solids() { + find_all_matches("**/+CollisionNode").hide(); +} diff --git a/panda/src/sgmanip/nodePath.cxx b/panda/src/sgmanip/nodePath.cxx new file mode 100644 index 0000000000..1e9b34d3d7 --- /dev/null +++ b/panda/src/sgmanip/nodePath.cxx @@ -0,0 +1,2550 @@ +// Filename: nodePath.cxx +// Created by: drose (05Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nodePath.h" +#include "config_sgmanip.h" +#include "findApproxLevel.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::extend_by +// Access: Public +// Description: Appends a new child node onto the bottom end of the +// path. This will search for an existing arc between +// the current bottom node and the indicated node; +// returns true if the arc is found and appended, false +// if no such arc exists. +// +// This operation may be ambiguous if there are multiple +// arcs connecting the bottom node of the path with the +// indicated node. In such case, the first arc found +// will be quietly chosen. Use append_arc() when +// unambiguous behavior is required. +//////////////////////////////////////////////////////////////////// +bool NodePath:: +extend_by(Node *node) { + nassertr(verify_connectivity(), false); + nassertr(node != (Node *)NULL, false); + + if (is_empty()) { + nassertr(_head == (ArcComponent *)NULL, false); + _top_node = node; + return true; + } + + Node *bottom_node = get_bottom_node(); + NodeRelation *arc = find_arc(bottom_node, node, _graph_type); + if (arc == (NodeRelation *)NULL) { + if (sgmanip_cat.is_debug()) { + sgmanip_cat.debug() + << "Cannot extend " << *this << " by " + << *node << "; no connection.\n"; + } + return false; + } + + _head = new ArcComponent(arc, _head); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::extend_by +// Access: Public +// Description: Adds the indicated arc to the end of the path. The +// arc must connect the bottom node of the path to some +// other node. Returns true if this is so, false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool NodePath:: +extend_by(NodeRelation *arc) { + nassertr(verify_connectivity(), false); + nassertr(arc != (NodeRelation *)NULL, false); + // Make sure the graph types are consistent. + nassertr(arc->get_type() == _graph_type, false); + + if (is_empty()) { + nassertr(_head == (ArcComponent *)NULL, false); + _top_node = arc->get_parent(); + } + + if (arc->get_parent() != get_bottom_node()) { + if (sgmanip_cat.is_debug()) { + sgmanip_cat.debug() + << "Cannot extend " << *this << " by arc " << *arc << "\n"; + } + return false; + } + + _head = new ArcComponent(arc, _head); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::extend_by +// Access: Public +// Description: Adds the indicated NodePath to the end of this path. +// The top node of the other NodePath must be the same +// node as the bottom node of this path. Returns true +// if this is so, false otherwise or if the other path +// is no longer accurately connected. +//////////////////////////////////////////////////////////////////// +bool NodePath:: +extend_by(const NodePath &other) { + nassertr(verify_connectivity(), false); + if (is_empty()) { + // A special case: extending an empty path by another path is just + // an assignment. + (*this) = other; + return true; + } + + return r_extend_by(other._head); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::extend_by +// Access: Public +// Description: Extends the NodePath by the NodePath represented by +// the given approximate string. If the match is +// ambiguous, returns the first match found (which will +// be the shortest path). Returns true if the extension +// was made, false if the node could not be found. +//////////////////////////////////////////////////////////////////// +bool NodePath:: +extend_by(const string &path) { + nassertr(verify_connectivity(), false); + NodePathCollection col; + find_matches(col, path, 1); + + if (col.is_empty()) { + if (sgmanip_cat.is_debug()) { + sgmanip_cat.debug() + << "Could not extend " << *this << " by " + << path << "; no match found.\n"; + } + return false; + } + + (*this) = col.get_path(0); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::extend_down_to +// Access: Public +// Description: Extends the NodePath down along the shortest +// available path to the indicated node, even if the +// node is several levels below the NodePath's bottom +// node. Returns true if the extension was made, false +// if the node was not below this NodePath. +//////////////////////////////////////////////////////////////////// +bool NodePath:: +extend_down_to(Node *node) { + if (!is_empty() && get_bottom_node() == node) { + // We're already there! + return true; + } + + nassertr(verify_connectivity(), false); + NodePathCollection col; + FindApproxPath approx_path; + approx_path.add_match_many(); + approx_path.add_match_pointer(node); + find_matches(col, approx_path, -1); + + if (col.is_empty()) { + if (sgmanip_cat.is_debug()) { + sgmanip_cat.debug() + << "Could not extend " << *this << " down to " + << *node << "; no connection found.\n"; + } + return false; + } + + (*this) = col.get_path(0); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::shorten +// Access: Public +// Description: Shortens the NodePath by removing the indicated +// number of nodes from the bottom of the path. It +// is an error to shorten the path to less than zero +// nodes; thus, num_nodes must be <= get_num_nodes(). +//////////////////////////////////////////////////////////////////// +void NodePath:: +shorten(int num_nodes) { + nassertv(num_nodes >= 0 && num_nodes <= get_num_nodes()); + + if (is_singleton()) { + // A special case: shortening a singleton path by (presumably) one + // node. + _top_node = (Node *)NULL; + return; + } + + int count = num_nodes; + while (count > 0) { + nassertv(_head != (ArcComponent *)NULL); + if (_head->_next == (ArcComponent *)NULL) { + // If we're about to shorten the path to a singleton, identify + // the top node first. + _top_node = _head->_arc->get_parent(); + } + _head = _head->_next; + count--; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::clear +// Access: Public +// Description: Resets the NodePath to an empty path. +//////////////////////////////////////////////////////////////////// +void NodePath:: +clear() { + _head.clear(); + _top_node.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_children +// Access: Public +// Description: Returns the set of all child nodes of the bottom +// node. +//////////////////////////////////////////////////////////////////// +NodePathCollection NodePath:: +get_children() const { + NodePathCollection result; + nassertr(verify_connectivity(), result); + nassertr(!is_empty(), result); + + Node *node = get_bottom_node(); + DownRelations::const_iterator dri; + dri = node->_children.find(_graph_type); + if (dri != node->_children.end()) { + const DownRelationPointers &drp = (*dri).second; + + DownRelationPointers::const_iterator drpi; + for (drpi = drp.begin(); drpi != drp.end(); ++drpi) { + NodeRelation *arc = (*drpi); + result.add_path(NodePath(*this, arc)); + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_siblings +// Access: Public +// Description: Returns the set of all child nodes of the bottom +// node's parent, except the bottom node itself. +//////////////////////////////////////////////////////////////////// +NodePathCollection NodePath:: +get_siblings() const { + NodePathCollection result; + nassertr(verify_connectivity(), result); + nassertr(has_arcs(), result); + nassertr(_head != (ArcComponent *)NULL, result); + NodeRelation *my_arc = _head->_arc; + + NodePath parent = *this; + parent.shorten(1); + + Node *node = parent.get_bottom_node(); + DownRelations::const_iterator dri; + dri = node->_children.find(_graph_type); + if (dri != node->_children.end()) { + const DownRelationPointers &drp = (*dri).second; + + DownRelationPointers::const_iterator drpi; + for (drpi = drp.begin(); drpi != drp.end(); ++drpi) { + NodeRelation *arc = (*drpi); + if (arc != my_arc) { + result.add_path(NodePath(parent, arc)); + } + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::find_all_paths_down_to +// Access: Public +// Description: Returns the set of all NodePaths that extend from +// this NodePath down to the indicated node. The +// shortest paths will be listed first. +//////////////////////////////////////////////////////////////////// +NodePathCollection NodePath:: +find_all_paths_down_to(Node *node) const { + NodePathCollection col; + nassertr(verify_connectivity(), col); + nassertr(node != (Node *)NULL, col); + FindApproxPath approx_path; + approx_path.add_match_many(); + approx_path.add_match_pointer(node); + find_matches(col, approx_path, -1); + return col; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::find_all_matches +// Access: Public +// Description: Returns the complete set of all NodePaths that begin +// with this NodePath and can be extended by +// path. The shortest paths will be listed +// first. +//////////////////////////////////////////////////////////////////// +NodePathCollection NodePath:: +find_all_matches(const string &path) const { + NodePathCollection col; + nassertr(verify_connectivity(), col); + find_matches(col, path, -1); + return col; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_node +// Access: Public +// Description: Returns the nth node of the path, where 0 is the +// bottom node and get_num_nodes() - 1 is the top node. +// This requires iterating through the path. +//////////////////////////////////////////////////////////////////// +Node *NodePath:: +get_node(int index) const { + nassertr(index >= 0 && index < get_num_nodes(), NULL); + + if (index == 0) { + if (_head == (ArcComponent *)NULL) { + // A singleton or empty list. + return _top_node; + } + return _head->_arc->get_child(); + } + + ArcComponent *comp = _head; + index--; + while (index > 0) { + // If this assertion fails, the index was out of range. + nassertr(comp != (ArcComponent *)NULL, NULL); + comp = comp->_next; + index--; + } + + // If this assertion fails, the index was out of range. + nassertr(comp != (ArcComponent *)NULL, NULL); + return comp->_arc->get_parent(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_num_arcs +// Access: Public +// Description: Returns the number of arcs in the path. This is +// always one less than the number of nodes (except for +// a completely empty path). +//////////////////////////////////////////////////////////////////// +int NodePath:: +get_num_arcs() const { + int num = 0; + ArcComponent *comp = _head; + while (comp != (ArcComponent *)NULL) { + num++; + comp = comp->_next; + } + return num; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_arc +// Access: Public +// Description: Returns the nth arc of the path, where 0 is the arc +// above the bottom node node and get_num_arcs() - 1 is +// the arc below the top node. This requires iterating +// through the path. +//////////////////////////////////////////////////////////////////// +NodeRelation *NodePath:: +get_arc(int index) const { + nassertr(index >= 0 && index < get_num_arcs(), NULL); + + ArcComponent *comp = _head; + while (index > 0) { + // If this assertion fails, the index was out of range. + nassertr(comp != (ArcComponent *)NULL, NULL); + comp = comp->_next; + index--; + } + + // If this assertion fails, the index was out of range. + nassertr(comp != (ArcComponent *)NULL, NULL); + return comp->_arc; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_top_node +// Access: Public +// Description: Returns the top node of the path, or NULL if the path +// is empty. This requires iterating through the path. +//////////////////////////////////////////////////////////////////// +Node *NodePath:: +get_top_node() const { + if (_head == (ArcComponent *)NULL) { + // A singleton or empty list. + return _top_node; + } + + ArcComponent *comp = _head; + while (comp->_next != (ArcComponent *)NULL) { + comp = comp->_next; + } + + // This assertion should not fail unless there is a logic error in + // the above. + nassertr(comp != (ArcComponent *)NULL, NULL); + return comp->_arc->get_parent(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::share_with +// Access: Public +// Description: Adjusts this NodePath so that it shares components +// with the other NodePath, even if the two paths were +// independently generated. +// +// Normally, if two NodePaths will share arcs, you +// should use the first one to derive the second; this +// will ensure that if one NodePath is reparented, the +// second will automatically be updated to reflect the +// change. If the two NodePaths are computed +// independently of each other, but end up sharing arcs, +// then reparenting one could invalidate the second. +// +// This operation can be applied to a NodePath that is +// known to share arcs with another NodePath, even if it +// was generated separately, so that both NodePaths will +// be considered related in the future. +// +// The return value is the number of arcs from the top +// of the path that both NodePaths had in common. +//////////////////////////////////////////////////////////////////// +int NodePath:: +share_with(const NodePath &other) { + typedef list Arcs; + Arcs other_arcs; + Arcs this_arcs; + + // First, we have to reverse the lists of this path and the other + // path, so we can compare their initial subsets. + ArcComponent *comp = other._head; + while (comp != (ArcComponent *)NULL) { + other_arcs.push_front(comp); + comp = comp->_next; + } + + comp = _head; + while (comp != (ArcComponent *)NULL) { + this_arcs.push_front(comp); + comp = comp->_next; + } + + // Now determine how many arcs this and the other path have in + // common. + int in_common = 0; + Arcs::const_iterator oi = other_arcs.begin(); + Arcs::const_iterator ti = this_arcs.begin(); + while (oi != other_arcs.end() && + ti != this_arcs.end() && + (*oi)->_arc == (*ti)->_arc) { + ++oi; + ++ti; + ++in_common; + } + + if (in_common > 0) { + ArcComponent *dest; + if (oi == other_arcs.end()) { + dest = other._head; + } else { + dest = (*oi); + } + + if (ti == this_arcs.end()) { + _head = dest; + } else { + (*ti)->_next = dest; + } + } + + return in_common; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::verify_connectivity +// Access: Public +// Description: Returns true if all of the arcs described in the +// NodePath are still connected, false otherwise. +//////////////////////////////////////////////////////////////////// +bool NodePath:: +verify_connectivity() const { + if (!has_arcs()) { + return true; + } + + const ArcComponent *comp = _head; + nassertr(comp != (const ArcComponent *)NULL, false); + + Node *parent = comp->_arc->get_parent(); + Node *child = comp->_arc->get_child(); + if (parent == (Node *)NULL || child == (Node *)NULL) { + return false; + } + + // We also want to verify that all of the arcs are of the proper + // type. + if (comp->_arc->get_type() != _graph_type) { + return false; + } + + comp = comp->_next; + while (comp != (const ArcComponent *)NULL) { + Node *next_parent = comp->_arc->get_parent(); + Node *next_child = comp->_arc->get_child(); + if (next_parent == (Node *)NULL || next_child == (Node *)NULL) { + return false; + } + if (next_child != parent) { + return false; + } + if (comp->_arc->get_type() != _graph_type) { + return false; + } + + parent = next_parent; + child = next_child; + comp = comp->_next; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::amputate_badness +// Access: Public +// Description: If a NodePath is inadvertantly broken +// (i.e. verify_connectivity() returns false), this +// function may be called to attempt to contain the +// damage. It does this by lopping off the top of the +// NodePath above the lowest disconnected arc. You +// might end up with a very short NodePath indeed, but +// at least it will be fully connected. It returns the +// same value as verify_connectivity(): true if the path +// was already connected, false if it had been broken +// (and has therefore been truncated). +// +// See also repair_connectivity(). +//////////////////////////////////////////////////////////////////// +bool NodePath:: +amputate_badness() { + if (!has_arcs()) { + return true; + } + + ArcComponent *comp = _head; + nassertr(comp != (ArcComponent *)NULL, false); + + Node *parent = comp->_arc->get_parent(); + Node *child = comp->_arc->get_child(); + if (parent == (Node *)NULL || child == (Node *)NULL) { + // Eek! The bottom arc is broken! + _top_node = child; + _head = (ArcComponent *)NULL; + return false; + } + + // We also want to verify that all of the arcs are of the proper + // type. + if (comp->_arc->get_type() != _graph_type) { + // Eek! The bottom arc is broken! + _top_node = child; + _head = (ArcComponent *)NULL; + return false; + } + + ArcComponent *prev = comp; + comp = comp->_next; + while (comp != (const ArcComponent *)NULL) { + Node *next_parent = comp->_arc->get_parent(); + Node *next_child = comp->_arc->get_child(); + if (next_parent == (Node *)NULL || next_child == (Node *)NULL) { + prev->_next = (ArcComponent *)NULL; + return false; + } + if (next_child != parent) { + prev->_next = (ArcComponent *)NULL; + return false; + } + if (comp->_arc->get_type() != _graph_type) { + prev->_next = (ArcComponent *)NULL; + return false; + } + + parent = next_parent; + child = next_child; + prev = comp; + comp = comp->_next; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::repair_connectivity +// Access: Public +// Description: When a NodePath has been inadvertantly disconnected, +// this attempts to repair the damage by finding a path +// from the bottom of the top NodePath to the top of the +// still-connected part of this NodePath. It returns +// true if the connection can be found, false if not (in +// which case the NodePath is unchanged). +//////////////////////////////////////////////////////////////////// +bool NodePath:: +repair_connectivity(const NodePath &top) { + NodePath new_path(*this); + new_path.amputate_badness(); + if (new_path.is_empty()) { + return false; + } + + Node *top_node = new_path.get_top_node(); + NodePath full_path(top); + if (!full_path.extend_down_to(top_node)) { + return false; + } + + if (!full_path.extend_by(new_path)) { + return false; + } + + (*this) = full_path; + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::reparent_to +// Access: Public +// Description: Removes the bottom node of the NodePath from its +// current parent and attaches it to the bottom node of +// the indicated NodePath. The arc above the bottom +// node is moved without destroying any transitions +// attached to it. +// +// It is an error to call this method on a NodePath that +// refers to just a single node, with no arcs. If you +// really want to parent just a single node somewhere, +// use instance_to() or attach_new_node(). +// +// This also updates the NodePath itself, as well as all +// NodePaths that were derived from this one, to reflect +// the new path to the node. +//////////////////////////////////////////////////////////////////// +void NodePath:: +reparent_to(const NodePath &other) { + nassertv(other.verify_connectivity()); + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + nassertv(!other.is_empty()); + + NodeRelation *arc = _head->_arc; + + arc->change_parent(other.get_bottom_node()); + + // Move our head pointer to the bottom of the new chain. This will + // update our own path, as well as all paths that share the same + // head pointer (i.e. all paths derived from this one). + _head->_next = other._head; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::wrt_reparent_to +// Access: Public +// Description: This functions identically to reparent_to(), except +// the transform above this node is also adjusted so +// that the node remains in the same place in world +// coordinates, even if it is reparented into a +// different coordinate system. +//////////////////////////////////////////////////////////////////// +void NodePath:: +wrt_reparent_to(const NodePath &other) { + nassertv(other.verify_connectivity()); + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + nassertv(!other.is_empty()); + + LMatrix4f mat = get_mat(other); + set_mat(mat); + + reparent_to(other); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::instance_to +// Access: Public +// Description: Adds the bottom node of the NodePath as a child of +// the bottom node of the indicated other NodePath. Any +// other parent-child relations of the node are +// unchanged; in particular, the node is not removed +// from its existing parent, if any. +// +// A new arc is created, and its transitions are copied +// from the bottom arc of this NodePath, if any. +// +// If the node already had an existing parent, this +// method will create a new instance of the node within +// the scene graph. +// +// This does not change the NodePath itself, but does +// return a new NodePath that reflects the new instance +// node. +//////////////////////////////////////////////////////////////////// +NodePath NodePath:: +instance_to(const NodePath &other) const { + nassertr(verify_connectivity(), NodePath()); + nassertr(!is_empty(), NodePath()); + nassertr(!other.is_empty(), NodePath()); + + Node *node = get_bottom_node(); + NodeRelation *arc = + NodeRelation::create_typed_arc(_graph_type, other.get_bottom_node(), node); + nassertr(arc != (NodeRelation *)NULL, NodePath()); + nassertr(arc->is_exact_type(_graph_type), NodePath()); + + if (has_arcs()) { + // Copy the transitions from this one's bottom arc, so the + // instance will inherit the same local state by default. + arc->copy_transitions_from(get_bottom_arc()); + } + + NodePath instance(*this); + instance._head = new ArcComponent(arc, other._head); + return instance; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::copy_to +// Access: Public +// Description: Functions exactly like instance_to(), except a deep +// copy is made of the bottom node and all of its +// descendents, which is then parented to the indicated +// node. A NodePath to the newly created copy is +// returned. +// +// Certain kinds of nodes may not be copied; if one of +// these is encountered, the node will be "copied" as +// the nearest copyable base class. For instance, a +// Camera node in the graph will become a simple +// NamedNode. +//////////////////////////////////////////////////////////////////// +NodePath NodePath:: +copy_to(const NodePath &other) const { + nassertr(verify_connectivity(), NodePath()); + nassertr(!is_empty(), NodePath()); + nassertr(!other.is_empty(), NodePath()); + + Node *source_node = get_bottom_node(); + PT_Node copy_node = source_node->copy_subgraph(_graph_type); + nassertr(copy_node != (Node *)NULL, NodePath()); + + NodeRelation *arc = + NodeRelation::create_typed_arc(_graph_type, other.get_bottom_node(), + copy_node); + nassertr(arc != (NodeRelation *)NULL, NodePath()); + nassertr(arc->is_exact_type(_graph_type), NodePath()); + + if (has_arcs()) { + // Copy the transitions from this one's bottom arc, so the + // duplicate will inherit the same local state by default. + arc->copy_transitions_from(get_bottom_arc()); + } + + NodePath instance(*this); + instance._head = new ArcComponent(arc, other._head); + return instance; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::attach_new_node +// Access: Public +// Description: Attaches a new node, with or without existing +// parents, to the scene graph below the bottom node of +// this NodePath. This is the preferred way to add +// nodes to the graph. +// +// This does *not* automatically extend the current +// NodePath to reflect the attachment; however, a +// NodePath that does reflect this extension is +// returned. +//////////////////////////////////////////////////////////////////// +NodePath NodePath:: +attach_new_node(Node *node) const { + nassertr(verify_connectivity(), NodePath()); + nassertr(!is_empty(), NodePath(_graph_type)); + nassertr(node != (Node *)NULL, NodePath(_graph_type)); + + NodePath path(node, _graph_type); + return path.instance_to(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::remove_node +// Access: Public +// Description: Disconnects the bottom node from the scene graph and +// destroys its arc (along with all associated +// transitions). This will also delete the node if +// there are no other pointers to it. +// +// Normally, this should be called only when you are +// really done with the node. If you want to remove a +// node from the scene graph but keep it around for +// later, you should probably use reparent_to() and put +// it under a holding node instead. +// +// It is an error to call this method on a NodePath that +// refers to just a single node, with no arcs. +// +// After the node is removed, the NodePath will have +// been cleared. +//////////////////////////////////////////////////////////////////// +void NodePath:: +remove_node() { + nassertv(verify_connectivity()); + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + PT(NodeRelation) arc = _head->_arc; + clear(); + remove_arc(arc); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::as_string +// Access: Public +// Description: Formats the NodePath as a string, showing each node +// name (or possibly type, if the node is unnamed), +// separated by slashes. +// +// The resulting string is not necessarily unique to +// this NodePath. Some nodes may be unnamed; different +// nodes in the graph may share the same name. Even if +// this is not the case, there might be multiple +// different arcs between the same two nodes, which +// cannot be described in this string. +// +// start_at_node is the node number, zero-based, at +// which to start the string. Normally this would be 0 +// to format the entire path, or 1 to format the path +// beginning at the node below the top node (which could +// then be used to restore the path later, subject to +// the ambiguities described above). +//////////////////////////////////////////////////////////////////// +string NodePath:: +as_string(int start_at_node) const { + nassertr(start_at_node >= 0, string()); + + if (is_empty()) { + // An empty path always returns an empty string. + return string(); + + } else if (is_singleton()) { + // A singleton path is a special case. We either return the name + // of the singleton, if start_at_node is 0, or the empty string in + // any other case. + + if (start_at_node == 0) { + return format_node_name(_top_node); + } + return string(); + } + + // In the normal case, we have at least one arc. We must walk to + // the end of the list, then back up start_at_node times, and start + // formatting names from there. + string result; + r_as_string(_head, result, start_at_node); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::write_transitions +// Access: Public +// Description: Writes to the indicated output stream a list of all +// transitions encountered on all arcs along the +// NodePath. +//////////////////////////////////////////////////////////////////// +void NodePath:: +write_transitions(ostream &out, int indent_level) const { + if (is_empty()) { + out << "Empty NodePath, no transitions.\n"; + } else if (is_singleton()) { + out << "Singleton NodePath, no transitions.\n"; + } else { + r_write_transitions(_head, out, indent_level); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_net_transitions +// Access: Public +// Description: Returns the set of all accumulated transitions along +// the NodePath. +//////////////////////////////////////////////////////////////////// +AllTransitionsWrapper NodePath:: +get_net_transitions() const { + AllTransitionsWrapper trans; + + if (has_arcs()) { + r_get_net_transitions(_head, trans); + } + + return trans; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::analyze +// Access: Public +// Description: Reports some statistics about the scene graph +// properties at and below this node. +//////////////////////////////////////////////////////////////////// +void NodePath:: +analyze() const { + nassertv(!is_empty()); + SceneGraphAnalyzer sga(_graph_type); + sga.add_node(get_bottom_node()); + sga.write(nout); +} + + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::flatten_light +// Access: Public +// Description: Lightly flattens out the hierarchy below this node by +// removing unneeded grouping nodes--nodes that have +// exactly one child, for instance, but have no special +// properties in themselves. This will not affect any +// transforms or other transitions. +// +// This is the same level of flattening that is normally +// automatically applied by the egg loader. It's +// generally completely safe (except that it may remove +// a node you expected to be there). +// +// The return value is the number of arcs removed. +//////////////////////////////////////////////////////////////////// +int NodePath:: +flatten_light() { + nassertr(!is_empty(), 0); + SceneGraphReducer gr(_graph_type); + int num_removed = gr.flatten(get_bottom_node(), false); + + if (sgmanip_cat.is_debug()) { + sgmanip_cat.debug() + << "flatten_light() removed " << num_removed << " arcs.\n"; + } + + return num_removed; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::flatten_medium +// Access: Public +// Description: A more thorough flattening than flatten_light(), this +// first applies all the transforms, colors, and texture +// matrices from the arcs onto the vertices, and then +// performs a flatten. This results in substantially +// improved perforamance over flatten_light() because +// (a) there are now fewer transforms, and (b) once the +// transforms are gone, it can safely remove more nodes. +// +// Since this operation will remove transforms from the +// scene graph, it may be dangerous to apply to arcs +// where you expect to dynamically modify the transform, +// or where you expect the geometry to remain in a +// particular local coordinate system. +//////////////////////////////////////////////////////////////////// +int NodePath:: +flatten_medium() { + nassertr(!is_empty(), 0); + SceneGraphReducer gr(_graph_type); + gr.apply_transitions(get_bottom_node()); + int num_removed = gr.flatten(get_bottom_node(), false); + + if (sgmanip_cat.is_debug()) { + sgmanip_cat.debug() + << "flatten_medium() removed " << num_removed << " arcs.\n"; + } + + return num_removed; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::flatten_strong +// Access: Public +// Description: The strongest possible flattening. This first +// applies all of the transforms to the vertices, as in +// flatten_medium(), but then it will combine sibling +// nodes together when possible, in addition to removing +// unnecessary parent-child nodes. This can result in +// substantially fewer nodes, but any nicely-grouped +// hierachical bounding volumes may be lost. +// +// It is generally a good idea to apply this kind of +// flattening only to nodes that will be culled largely +// as a single unit, like a car. Applying this to an +// entire scene may result in overall poorer performance +// because of less-effective culling. +//////////////////////////////////////////////////////////////////// +int NodePath:: +flatten_strong() { + nassertr(!is_empty(), 0); + SceneGraphReducer gr(_graph_type); + gr.apply_transitions(get_bottom_node()); + int num_removed = gr.flatten(get_bottom_node(), true); + + if (sgmanip_cat.is_debug()) { + sgmanip_cat.debug() + << "flatten_strong() removed " << num_removed << " arcs.\n"; + } + + return num_removed; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::write_bam_file +// Access: Public +// Description: Writes the contents of this node and below out to a +// bam file with the indicated filename. This file may +// then be read in again, as is, at some later point. +// Returns true if successful, false on some kind of +// error. +//////////////////////////////////////////////////////////////////// +bool NodePath:: +write_bam_file(const string &filename) const { + nassertr(!is_empty(), false); + BamFile bam_file; + + bool okflag = false; + + if (bam_file.open_write(filename)) { + if (bam_file.write_object(get_bottom_node())) { + okflag = true; + } + bam_file.close(); + } + + return okflag; +} + + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_pos +// Access: Public +// Description: Sets the translation component of the transform, +// leaving rotation and scale untouched. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_pos(const LVecBase3f &pos) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(); + mat.set_row(3, pos); + set_mat(mat); +} + +void NodePath:: +set_x(float x) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(); + mat(3, 0) = x; + set_mat(mat); +} + +void NodePath:: +set_y(float y) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(); + mat(3, 1) = y; + set_mat(mat); +} + +void NodePath:: +set_z(float z) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(); + mat(3, 2) = z; + set_mat(mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_pos +// Access: Public +// Description: Retrieves the translation component of the transform. +//////////////////////////////////////////////////////////////////// +LPoint3f NodePath:: +get_pos() const { + nassertr(has_arcs(), LPoint3f(0.0, 0.0, 0.0)); + nassertr(_head != (ArcComponent *)NULL, LPoint3f(0.0, 0.0, 0.0)); + LMatrix4f mat = get_mat(); + return mat.get_row3(3); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_hpr +// Access: Public +// Description: Sets the rotation component of the transform, +// leaving translation and scale untouched. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_hpr(const LVecBase3f &hpr) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(); + LVecBase3f scale, old_hpr, pos; + decompose_matrix(mat, scale, old_hpr, pos); + compose_matrix(mat, scale, hpr, pos); + set_mat(mat); +} + +void NodePath:: +set_h(float h) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(); + LVecBase3f scale, old_hpr, pos; + decompose_matrix(mat, scale, old_hpr, pos); + old_hpr[0] = h; + compose_matrix(mat, scale, old_hpr, pos); + set_mat(mat); +} + +void NodePath:: +set_p(float p) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(); + LVecBase3f scale, old_hpr, pos; + decompose_matrix(mat, scale, old_hpr, pos); + old_hpr[1] = p; + compose_matrix(mat, scale, old_hpr, pos); + set_mat(mat); +} + +void NodePath:: +set_r(float r) { + LMatrix4f mat = get_mat(); + LVecBase3f scale, old_hpr, pos; + decompose_matrix(mat, scale, old_hpr, pos); + old_hpr[2] = r; + compose_matrix(mat, scale, old_hpr, pos); + set_mat(mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_hpr +// Access: Public +// Description: Retrieves the rotation component of the transform. +//////////////////////////////////////////////////////////////////// +LVecBase3f NodePath:: +get_hpr() const { + nassertr(has_arcs(), LVecBase3f(0.0, 0.0, 0.0)); + nassertr(_head != (ArcComponent *)NULL, LVecBase3f(0.0, 0.0, 0.0)); + LMatrix4f mat = get_mat(); + LVecBase3f scale, hpr, pos; + decompose_matrix(mat, scale, hpr, pos); + return hpr; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_scale +// Access: Public +// Description: Sets the scale component of the transform, +// leaving translation and rotation untouched. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_scale(const LVecBase3f &sv3) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(); + LVecBase3f old_scale, hpr, pos; + decompose_matrix(mat, old_scale, hpr, pos); + compose_matrix(mat, sv3, hpr, pos); + set_mat(mat); +} + +void NodePath:: +set_sx(float sx) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(); + LVecBase3f old_scale, hpr, pos; + decompose_matrix(mat, old_scale, hpr, pos); + old_scale[0] = sx; + compose_matrix(mat, old_scale, hpr, pos); + set_mat(mat); +} + +void NodePath:: +set_sy(float sy) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(); + LVecBase3f old_scale, hpr, pos; + decompose_matrix(mat, old_scale, hpr, pos); + old_scale[1] = sy; + compose_matrix(mat, old_scale, hpr, pos); + set_mat(mat); +} + +void NodePath:: +set_sz(float sz) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(); + LVecBase3f old_scale, hpr, pos; + decompose_matrix(mat, old_scale, hpr, pos); + old_scale[2] = sz; + compose_matrix(mat, old_scale, hpr, pos); + set_mat(mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_scale +// Access: Public +// Description: Retrieves the scale component of the transform. +//////////////////////////////////////////////////////////////////// +LVecBase3f NodePath:: +get_scale() const { + nassertr(has_arcs(), LVecBase3f(1.0, 1.0, 1.0)); + nassertr(_head != (ArcComponent *)NULL, LVecBase3f(1.0, 1.0, 1.0)); + LMatrix4f mat = get_mat(); + LVecBase3f scale, hpr, pos; + decompose_matrix(mat, scale, hpr, pos); + return scale; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_color_scale +// Access: Public +// Description: Sets the color scale component of the transform, +// leaving translation and rotation untouched. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_color_scale(const LVecBase4f &sv4) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + +#ifndef NDEBUG + if (_graph_type == DataRelation::get_class_type()) { + sgmanip_cat.warning() + << "Setting color scale on data graph arc.\n" + << "(This is meaningless. Did you mean to do this to the bottom node?)\n"; + } +#endif + + if (sv4[0] != 1 || sv4[1] != 1 || sv4[2] != 1) { + LMatrix4f mat = LMatrix4f::scale_mat(sv4[0], sv4[1], sv4[2]); + _head->_arc->set_transition(new ColorMatrixTransition(mat)); + } + else { + _head->_arc->clear_transition(ColorMatrixTransition::get_class_type()); + } + + if (sv4[3] != 1) { + _head->_arc->set_transition(new AlphaTransformTransition(0, sv4[3])); + } + else { + _head->_arc->clear_transition(AlphaTransformTransition::get_class_type()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_color_scale +// Access: Public +// Description: Returns the complete transform vector that has been +// applied to the bottom arc, or the all 1's if no +// scale has been applied +//////////////////////////////////////////////////////////////////// +LVecBase4f NodePath:: +get_color_scale() const { + nassertr(has_arcs(), LVecBase4f(1,1,1,1)); + nassertr(_head != (ArcComponent *)NULL, LVecBase4f(1,1,1,1)); + + LVecBase4f scale; + + const ColorMatrixTransition *ct; + if (!get_transition_into(ct, _head->_arc)) { + // No relative transform. + scale[0] = 1; + scale[1] = 1; + scale[2] = 1; + } + else + { + LVecBase3f old_scale, hpr, pos; + decompose_matrix(ct->get_matrix(), old_scale, hpr, pos); + scale[0] = old_scale[0]; + scale[1] = old_scale[1]; + scale[2] = old_scale[2]; + } + + const AlphaTransformTransition *att; + if (!get_transition_into(att, _head->_arc)) { + scale[3] = 1; + } + else { + scale[3] = att->get_scale(); + } + + return scale; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_pos_hpr +// Access: Public +// Description: Sets the translation and rotation component of the +// transform, leaving scale untouched. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_pos_hpr(const LVecBase3f &pos, const LVecBase3f &hpr) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(); + LVecBase3f scale, old_hpr, old_pos; + decompose_matrix(mat, scale, old_hpr, old_pos); + compose_matrix(mat, scale, hpr, pos); + set_mat(mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_pos_hpr_scale +// Access: Public +// Description: Completely replaces the transform with new +// translation, rotation, and scale components. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_pos_hpr_scale(const LVecBase3f &pos, const LVecBase3f &hpr, + const LVecBase3f &scale) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat; + compose_matrix(mat, scale, hpr, pos); + set_mat(mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_mat +// Access: Public +// Description: Directly sets an arbitrary 4x4 transform matrix. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_mat(const LMatrix4f &mat) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + +#ifndef NDEBUG + if (_graph_type == DataRelation::get_class_type()) { + sgmanip_cat.warning() + << "Setting transform on data graph arc.\n" + << "(This is probably meaningless. Did you mean to do this to the bottom node?)\n"; + } +#endif + + _head->_arc->set_transition(new TransformTransition(mat)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::look_at +// Access: Public +// Description: Sets the transform on this NodePath so that it +// rotates to face the indicated point in space. This +// will overwrite any previously existing scale on the +// node, although it will preserve any translation. +//////////////////////////////////////////////////////////////////// +void NodePath:: +look_at(const LPoint3f &point, const LVector3f &up) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + LPoint3f pos = get_pos(); + + LMatrix4f mat; + ::look_at(mat, point - pos, up); + mat.set_row(3, pos); + set_mat(mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::heads_up +// Access: Public +// Description: Behaves like look_at(), but with a strong preference +// to keeping the up vector oriented in the indicated +// "up" direction. +//////////////////////////////////////////////////////////////////// +void NodePath:: +heads_up(const LPoint3f &point, const LVector3f &up) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + LPoint3f pos = get_pos(); + + LMatrix4f mat; + ::heads_up(mat, point - pos, up); + mat.set_row(3, pos); + set_mat(mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_pos +// Access: Public +// Description: Sets the translation component of the transform, +// relative to the other node. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_pos(const NodePath &other, const LVecBase3f &pos) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(other); + mat.set_row(3, pos); + set_mat(other, mat); +} + +void NodePath:: +set_x(const NodePath &other, float x) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(other); + mat(3, 0) = x; + set_mat(other, mat); +} + +void NodePath:: +set_y(const NodePath &other, float y) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(other); + mat(3, 1) = y; + set_mat(other, mat); +} + +void NodePath:: +set_z(const NodePath &other, float z) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(other); + mat(3, 2) = z; + set_mat(other, mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_hpr +// Access: Public +// Description: Sets the rotation component of the transform, +// relative to the other node. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_hpr(const NodePath &other, const LVecBase3f &hpr) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(other); + LVecBase3f scale, old_hpr, pos; + decompose_matrix(mat, scale, old_hpr, pos); + compose_matrix(mat, scale, hpr, pos); + set_mat(other, mat); +} + +void NodePath:: +set_h(const NodePath &other, float h) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(other); + LVecBase3f scale, old_hpr, pos; + decompose_matrix(mat, scale, old_hpr, pos); + old_hpr[0] = h; + compose_matrix(mat, scale, old_hpr, pos); + set_mat(other, mat); +} + +void NodePath:: +set_p(const NodePath &other, float p) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(other); + LVecBase3f scale, old_hpr, pos; + decompose_matrix(mat, scale, old_hpr, pos); + old_hpr[1] = p; + compose_matrix(mat, scale, old_hpr, pos); + set_mat(other, mat); +} + +void NodePath:: +set_r(const NodePath &other, float r) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(other); + LVecBase3f scale, old_hpr, pos; + decompose_matrix(mat, scale, old_hpr, pos); + old_hpr[2] = r; + compose_matrix(mat, scale, old_hpr, pos); + set_mat(other, mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_scale +// Access: Public +// Description: Sets the scale component of the transform, +// relative to the other node. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_scale(const NodePath &other, const LVecBase3f &scale) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(other); + LVecBase3f old_scale, hpr, pos; + decompose_matrix(mat, old_scale, hpr, pos); + compose_matrix(mat, scale, hpr, pos); + set_mat(other, mat); +} + +void NodePath:: +set_sx(const NodePath &other, float sx) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(other); + LVecBase3f old_scale, hpr, pos; + decompose_matrix(mat, old_scale, hpr, pos); + old_scale[0] = sx; + compose_matrix(mat, old_scale, hpr, pos); + set_mat(other, mat); +} + +void NodePath:: +set_sy(const NodePath &other, float sy) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(other); + LVecBase3f old_scale, hpr, pos; + decompose_matrix(mat, old_scale, hpr, pos); + old_scale[1] = sy; + compose_matrix(mat, old_scale, hpr, pos); + set_mat(other, mat); +} + +void NodePath:: +set_sz(const NodePath &other, float sz) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(other); + LVecBase3f old_scale, hpr, pos; + decompose_matrix(mat, old_scale, hpr, pos); + old_scale[2] = sz; + compose_matrix(mat, old_scale, hpr, pos); + set_mat(other, mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_scale +// Access: Public +// Description: Returns the relative scale of the bottom node +// as seen from the other node. +//////////////////////////////////////////////////////////////////// +LVecBase3f NodePath:: +get_scale(const NodePath &other) const { + LMatrix4f mat = get_mat(other); + + // We decompose the scale directly instead of calling + // decompose_matrix(), since we don't care about the hpr and don't + // need to go through all the work of unrolling the axes. + + // Extract the axes from the matrix. + LVector3f x, y, z; + x = mat.get_row3(0); + y = mat.get_row3(1); + z = mat.get_row3(2); + + // Now return the lengths of these axes as the scale. + return LVecBase3f(length(x), length(y), length(z)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_pos_hpr +// Access: Public +// Description: Sets the translation and rotation component of the +// transform, relative to the other node. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_pos_hpr(const NodePath &other, const LVecBase3f &pos, + const LVecBase3f &hpr) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat = get_mat(other); + LVecBase3f scale, old_hpr, old_pos; + decompose_matrix(mat, scale, old_hpr, old_pos); + compose_matrix(mat, scale, hpr, pos); + set_mat(other, mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_pos_hpr_scale +// Access: Public +// Description: Completely replaces the transform with new +// translation, rotation, and scale components, relative +// to the other node. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_pos_hpr_scale(const NodePath &other, + const LVecBase3f &pos, const LVecBase3f &hpr, + const LVecBase3f &scale) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + LMatrix4f mat; + compose_matrix(mat, scale, hpr, pos); + set_mat(other, mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_mat +// Access: Public +// Description: Converts the indicated matrix from the other's +// coordinate space to the local coordinate space, and +// applies it to the arc. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_mat(const NodePath &other, const LMatrix4f &mat) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + +#ifndef NDEBUG + if (_graph_type == DataRelation::get_class_type()) { + sgmanip_cat.warning() + << "Setting transform on data graph arc.\n" + << "(This is probably meaningless. Did you mean to do this to the bottom node?)\n"; + } +#endif + + LMatrix4f new_mat; + + // First, we perform a wrt to the node's parent, to get the + // conversion matrix. + NodeTransitionWrapper ntw(TransformTransition::get_class_type()); + if (other.is_empty()) { + wrt(NULL, + _head->_arc->get_parent(), ForwardIterator(_head->_next), ForwardIterator(), + ntw, _graph_type); + } else { + wrt(other.get_bottom_node(), ForwardIterator(other._head), ForwardIterator(), + _head->_arc->get_parent(), ForwardIterator(_head->_next), ForwardIterator(), + ntw, _graph_type); + } + const TransformTransition *tt; + if (!get_transition_into(tt, ntw)) { + // No relative transform. + new_mat = mat; + } else { + new_mat = mat * tt->get_matrix(); + } + + _head->_arc->set_transition(new TransformTransition(new_mat)); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_mat +// Access: Public +// Description: Returns the matrix that describes the coordinate +// space of the bottom node, relative to the other +// path's bottom node's coordinate space. +//////////////////////////////////////////////////////////////////// +LMatrix4f NodePath:: +get_mat(const NodePath &other) const { + nassertr(!is_empty(), LMatrix4f::ident_mat()); + + NodeTransitionWrapper ntw(TransformTransition::get_class_type()); + if (other.is_empty()) { + wrt(get_bottom_node(), ForwardIterator(_head), ForwardIterator(), + (Node *)NULL, ntw, _graph_type); + } else { + wrt(get_bottom_node(), ForwardIterator(_head), ForwardIterator(), + other.get_bottom_node(), ForwardIterator(other._head), ForwardIterator(), + ntw, _graph_type); + } + const TransformTransition *tt; + if (!get_transition_into(tt, ntw)) { + // No relative transform. + return LMatrix4f::ident_mat(); + } else { + return tt->get_matrix(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::look_at +// Access: Public +// Description: Sets the transform on this NodePath so that it +// rotates to face the indicated point in space, which +// is relative to the other NodePath. This +// will overwrite any previously existing scale on the +// node, although it will preserve any translation. +//////////////////////////////////////////////////////////////////// +void NodePath:: +look_at(const NodePath &other, const LPoint3f &point, const LVector3f &up) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + LPoint3f pos = get_pos(); + + NodePath parent(*this, 1); + LPoint3f rel_point = point * other.get_mat(parent); + + LMatrix4f mat; + ::look_at(mat, rel_point - pos, up); + mat.set_row(3, pos); + set_mat(mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::heads_up +// Access: Public +// Description: Behaves like look_at(), but with a strong preference +// to keeping the up vector oriented in the indicated +// "up" direction. +//////////////////////////////////////////////////////////////////// +void NodePath:: +heads_up(const NodePath &other, const LPoint3f &point, const LVector3f &up) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + LPoint3f pos = get_pos(); + + NodePath parent(*this, 1); + LPoint3f rel_point = point * other.get_mat(parent); + + LMatrix4f mat; + ::heads_up(mat, rel_point - pos, up); + mat.set_row(3, pos); + set_mat(mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_color +// Access: Public +// Description: Sets the color transition for a render relation +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_color(const Colorf &color, int priority) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + ColorTransition *col_trans = new ColorTransition(color); + _head->_arc->set_transition(col_trans, priority); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_color +// Access: Public +// Description: Returns the color that has been assigned to the arc, +// or black if no color has been assigned. +//////////////////////////////////////////////////////////////////// +Colorf NodePath:: +get_color() const { + nassertr(has_arcs(), false); + nassertr(_head != (ArcComponent *)NULL, false); + + const ColorTransition *ct; + if (get_transition_into(ct, _head->_arc)) { + if (ct->is_on() && ct->is_real()) { + return ct->get_color(); + } + } + + sgmanip_cat.warning() + << "get_color() called on " << *this << " which has no color set.\n"; + + return Colorf(0.0, 0.0, 0.0, 0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_texture +// Access: Public +// Description: Sets the geometry at this level and below to render +// using the indicated texture. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_texture(Texture *tex, int priority) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + nassertv(tex != NULL); + + TextureTransition *tex_trans = new TextureTransition(tex); + _head->_arc->set_transition(tex_trans, priority); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_texture_off +// Access: Public +// Description: Sets the geometry at this level and below to render +// using no texture. This is normally the default, but +// it may be useful to use this to contradict +// set_texture() at a higher node level (or, with a +// priority, to override a set_texture() at a lower +// level). +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_texture_off(int priority) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + TextureTransition *tex_trans = + new TextureTransition(TextureTransition::off()); + _head->_arc->set_transition(tex_trans, priority); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::has_texture +// Access: Public +// Description: Returns true if a texture has been applied to this +// particular arc via set_texture(), false otherwise. +// This is not the same thing as asking whether the +// geometry at this node will be rendered with +// texturing, as there may be a texture in effect from a +// higher or lower level. +//////////////////////////////////////////////////////////////////// +bool NodePath:: +has_texture() const { + nassertr(has_arcs(), false); + nassertr(_head != (ArcComponent *)NULL, false); + + const TextureTransition *tt; + if (get_transition_into(tt, _head->_arc)) { + return tt->is_on(); + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::has_texture_off +// Access: Public +// Description: Returns true if a texture has been specifically +// disabled on this particular arc via +// set_texture_off(), false otherwise. This is not the +// same thing as asking whether the geometry at this +// node will be rendered untextured, as there may be a +// texture in effect from a higher or lower level. +//////////////////////////////////////////////////////////////////// +bool NodePath:: +has_texture_off() const { + nassertr(has_arcs(), false); + nassertr(_head != (ArcComponent *)NULL, false); + + const TextureTransition *tt; + if (get_transition_into(tt, _head->_arc)) { + return tt->is_off(); + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_texture +// Access: Public +// Description: Returns the texture that has been set on this +// particular arc, or NULL if no texture has been set. +// This is not necessarily the texture that will be +// applied to the geometry at or below this level, as +// another texture at a higher or lower level may +// override. +//////////////////////////////////////////////////////////////////// +Texture *NodePath:: +get_texture() const { + nassertr(has_arcs(), NULL); + nassertr(_head != (ArcComponent *)NULL, NULL); + + const TextureTransition *tt; + if (get_transition_into(tt, _head->_arc)) { + if (tt->is_on()) { + return tt->get_texture(); + } + } + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_fog +// Access: Public +// Description: Sets the geometry at this level and below to render +// using the indicated fog. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_fog(Fog *fog, int priority) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + nassertv(fog != NULL); + + FogTransition *fog_trans = new FogTransition(fog); + _head->_arc->set_transition(fog_trans, priority); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_fog_off +// Access: Public +// Description: Sets the geometry at this level and below to render +// using no fog. This is normally the default, but +// it may be useful to use this to contradict +// set_fog() at a higher node level (or, with a +// priority, to override a set_fog() at a lower +// level). +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_fog_off(int priority) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + FogTransition *fog_trans = + new FogTransition(FogTransition::off()); + _head->_arc->set_transition(fog_trans, priority); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::has_fog +// Access: Public +// Description: Returns true if a fog has been applied to this +// particular arc via set_fog(), false otherwise. +// This is not the same thing as asking whether the +// geometry at this node will be rendered with +// fog, as there may be a fog in effect from a higher or +// lower level. +//////////////////////////////////////////////////////////////////// +bool NodePath:: +has_fog() const { + nassertr(has_arcs(), false); + nassertr(_head != (ArcComponent *)NULL, false); + + const FogTransition *tt; + if (get_transition_into(tt, _head->_arc)) { + return tt->is_on(); + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::has_fog_off +// Access: Public +// Description: Returns true if a fog has been specifically +// disabled on this particular arc via +// set_fog_off(), false otherwise. This is not the +// same thing as asking whether the geometry at this +// node will be rendered unfogged, as there may be a +// fog in effect from a higher or lower level. +//////////////////////////////////////////////////////////////////// +bool NodePath:: +has_fog_off() const { + nassertr(has_arcs(), false); + nassertr(_head != (ArcComponent *)NULL, false); + + const FogTransition *tt; + if (get_transition_into(tt, _head->_arc)) { + return tt->is_off(); + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_fog +// Access: Public +// Description: Returns the fog that has been set on this +// particular arc, or NULL if no fog has been set. +// This is not necessarily the fog that will be +// applied to the geometry at or below this level, as +// another fog at a higher or lower level may +// override. +//////////////////////////////////////////////////////////////////// +Fog *NodePath:: +get_fog() const { + nassertr(has_arcs(), NULL); + nassertr(_head != (ArcComponent *)NULL, NULL); + + const FogTransition *tt; + if (get_transition_into(tt, _head->_arc)) { + if (tt->is_on()) { + return tt->get_fog(); + } + } + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_render_mode_wireframe +// Access: Public +// Description: Sets up the geometry at this level and below (unless +// overridden) to render in wireframe mode. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_render_mode_wireframe(int priority) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + _head->_arc->set_transition(new RenderModeTransition(RenderModeProperty::M_wireframe), priority); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_render_mode_filled +// Access: Public +// Description: Sets up the geometry at this level and below (unless +// overridden) to render in filled (i.e. not wireframe) +// mode. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_render_mode_filled(int priority) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + RenderModeTransition *rmt = + new RenderModeTransition(RenderModeProperty::M_filled); + _head->_arc->set_transition(rmt, priority); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_two_sided +// Access: Public +// Description: Specifically sets or disables two-sided rendering +// mode on this particular arc. If no other arcs +// override, this will cause backfacing polygons to be +// drawn (in two-sided mode, true) or culled (in +// one-sided mode, false). +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_two_sided(bool two_sided, int priority) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + CullFaceProperty::Mode mode = + two_sided ? + CullFaceProperty::M_cull_none : + CullFaceProperty::M_cull_clockwise; + + CullFaceTransition *cft = new CullFaceTransition(mode); + _head->_arc->set_transition(cft, priority); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_two_sided +// Access: Public +// Description: Returns true if two-sided rendering has been +// specifically set on this arc via set_two_sided(), or +// false if one-sided rendering has been specifically +// set, or if nothing has been specifically set. See +// also has_two_sided(). This does not necessarily +// imply that the geometry will or will not be rendered +// two-sided, as there may be other arcs that override. +//////////////////////////////////////////////////////////////////// +bool NodePath:: +get_two_sided() const { + nassertr(has_arcs(), NULL); + nassertr(_head != (ArcComponent *)NULL, NULL); + + const CullFaceTransition *cft; + if (get_transition_into(cft, _head->_arc)) { + return (cft->get_mode() == CullFaceProperty::M_cull_none); + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_transparency +// Access: Public +// Description: Specifically sets or disables transparent rendering +// mode on this particular arc. If no other arcs +// override, this will cause items with a non-1 value +// for alpha color to be rendered partially transparent. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_transparency(bool transparency, int priority) { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + TransparencyProperty::Mode mode = + transparency ? + TransparencyProperty::M_alpha : + TransparencyProperty::M_none; + + TransparencyTransition *tt = new TransparencyTransition(mode); + _head->_arc->set_transition(tt, priority); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_transparency +// Access: Public +// Description: Returns true if transparent rendering has been +// specifically set on this arc via set_transparency(), or +// false if nontransparent rendering has been specifically +// set, or if nothing has been specifically set. See +// also has_transparency(). This does not necessarily +// imply that the geometry will or will not be rendered +// transparent, as there may be other arcs that override. +//////////////////////////////////////////////////////////////////// +bool NodePath:: +get_transparency() const { + nassertr(has_arcs(), NULL); + nassertr(_head != (ArcComponent *)NULL, NULL); + + const TransparencyTransition *tt; + if (get_transition_into(tt, _head->_arc)) { + return (tt->get_mode() != TransparencyProperty::M_none); + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::is_hidden +// Access: Public +// Description: Returns true if some arc above this bottom node has +// been set to 'hide', false if it should be visible. +//////////////////////////////////////////////////////////////////// +bool NodePath:: +is_hidden() const { + return !get_hidden_ancestor().is_empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_hidden_ancestor +// Access: Public +// Description: Returns a NodePath indicating the lowest arc above +// this node which has been set to 'hide'. Calling +// show() on the NodePath returned by this function +// should make the node visible again (unless there is +// another arc further up that also has the 'hide' +// transition set). +// +// This function returns an empty NodePath if no +// ancestors have been set to 'hide'. +//////////////////////////////////////////////////////////////////// +NodePath NodePath:: +get_hidden_ancestor() const { + if (!has_arcs()) { + return NodePath(); + } + nassertr(_head != (ArcComponent *)NULL, NodePath()); + if (_head->_arc->has_transition(PruneTransition::get_class_type())) { + return *this; + } + + NodePath next(*this); + next.shorten(); + return next.get_hidden_ancestor(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::show_bounds +// Access: Public +// Description: Causes the bounding volume of the bottom node and all +// of its descendants (that is, the bounding volume +// associated with the the bottom arc) to be rendered, +// if possible. The rendering method is less than +// optimal; this is intended primarily for debugging. +//////////////////////////////////////////////////////////////////// +void NodePath:: +show_bounds() { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + _head->_arc->set_transition(new DrawBoundsTransition); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::hide_bounds +// Access: Public +// Description: Stops the rendering of the bounding volume begun with +// show_bounds(). +//////////////////////////////////////////////////////////////////// +void NodePath:: +hide_bounds() { + nassertv(has_arcs()); + nassertv(_head != (ArcComponent *)NULL); + + _head->_arc->clear_transition(DrawBoundsTransition::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_bounds +// Access: Public +// Description: Returns a newly-allocated bounding volume containing +// the bottom node and all of its descendants. This is +// the bounding volume on the bottom arc, converted to +// the local coordinate space of the node. +//////////////////////////////////////////////////////////////////// +PT(BoundingVolume) NodePath:: +get_bounds() const { + nassertr(has_arcs(), new BoundingSphere); + + PT(BoundingVolume) bv = _head->_arc->get_bound().make_copy(); + if (bv->is_of_type(GeometricBoundingVolume::get_class_type()) && + _head->_arc->has_transition(TransformTransition::get_class_type())) { + + // The bounding volume has already been transformed by the arc's + // matrix. We'd rather return a bounding volume in the node's + // space, so we have to untransform it now. Ick. + GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bv); + NodePath parent(*this, 1); + LMatrix4f mat = parent.get_mat(*this); + gbv->xform(mat); + } + + return bv; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::write_bounds +// Access: Public +// Description: Writes a description of the bounding volume +// containing the bottom node and all of its descendants +// to the indicated output stream. +//////////////////////////////////////////////////////////////////// +void NodePath:: +write_bounds(ostream &out) const { + get_bounds()->write(out); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::r_equiv +// Access: Private +// Description: The recursive implementation of operator ==. Returns +// true if the list of arcs is equivalent, false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool NodePath:: +r_equiv(const ArcComponent *next, const ArcComponent *other) const { + if (next == (const ArcComponent *)NULL) { + nassertr(other == (const ArcComponent *)NULL, false); + return true; + } + + nassertr(next != (const ArcComponent *)NULL, false); + nassertr(other != (const ArcComponent *)NULL, false); + + return (next == other || + (next->_arc == other->_arc && r_equiv(next->_next, other->_next))); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::r_extend +// Access: Private +// Description: The recursive implementation of extend_by(NodePath). +// This must walk to the end of the other path's +// component list and extend in forward order. +//////////////////////////////////////////////////////////////////// +bool NodePath:: +r_extend_by(const ArcComponent *other) { + if (other == (const ArcComponent *)NULL) { + return true; + } + if (!r_extend_by(other->_next)) { + return false; + } + + if (!extend_by(other->_arc)) { + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::r_as_string +// Access: Private +// Description: The recursive implementation of as_string. This +// must reverse the order of the path components. +//////////////////////////////////////////////////////////////////// +int NodePath:: +r_as_string(const ArcComponent *comp, string &result, int skip_nodes) const { + nassertr(comp != (const ArcComponent *)NULL, 0); + + if (comp->_next == (const ArcComponent *)NULL) { + // Here's the end of the chain. + if (skip_nodes == 0) { + // Skip no nodes; return the full name. + result = format_node_name(comp->_arc->get_parent()) + "/" + + format_node_name(comp->_arc->get_child()); + + } else if (skip_nodes == 1) { + // Skip the first node. + result = format_node_name(comp->_arc->get_child()); + } + + return 2; + + } else { + int nodes_before = r_as_string(comp->_next, result, skip_nodes); + if (skip_nodes <= nodes_before) { + if (skip_nodes != nodes_before) { + // This is not the first node, so format a slash between the + // previous node and this node. + + if (comp->_arc->get_parent() == comp->_next->_arc->get_child()) { + result += "/"; + } else { + // Unless the path is broken here. In this case, insert a + // visual indication of the break. + result += "/.../" + format_node_name(comp->_arc->get_parent()) + "/"; + } + } + result += format_node_name(comp->_arc->get_child()); + } + return nodes_before + 1; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::r_write_transitions +// Access: Private +// Description: The recursive implementation of write_transitions(). +//////////////////////////////////////////////////////////////////// +void NodePath:: +r_write_transitions(const ArcComponent *comp, + ostream &out, int indent_level) const { + nassertv(comp != (const ArcComponent *)NULL); + nassertv(comp->_arc != (NodeRelation *)NULL); + + if (comp->_next != (const ArcComponent *)NULL) { + r_write_transitions(comp->_next, out, indent_level); + } + + indent(out, indent_level) << *comp->_arc << "\n"; + comp->_arc->write_transitions(out, indent_level + 2); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::r_get_net_transitions +// Access: Private +// Description: The recursive implementation of get_net_transitions(). +//////////////////////////////////////////////////////////////////// +void NodePath:: +r_get_net_transitions(const ArcComponent *comp, + AllTransitionsWrapper &trans) const { + nassertv(comp != (const ArcComponent *)NULL); + nassertv(comp->_arc != (NodeRelation *)NULL); + + if (comp->_next != (const ArcComponent *)NULL) { + r_get_net_transitions(comp->_next, trans); + } + + AllTransitionsWrapper arc_trans; + arc_trans.extract_from(comp->_arc); + trans.compose_in_place(arc_trans); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::format_node_name +// Access: Private +// Description: Formats one component of the as_string string: a +// node, either unnamed as a type, or as a name. +//////////////////////////////////////////////////////////////////// +string NodePath:: +format_node_name(Node *node) const { + string name; + if (node->is_of_type(NamedNode::get_class_type())) { + name = DCAST(NamedNode, node)->get_name(); + } + + if (name.empty()) { + // No name. If the type isn't one of the trivial types (Node or + // NamedNode), use the type name instead, since it's likely to be + // more unique. + if (!node->is_of_type(Node::get_class_type()) && + !node->is_of_type(NamedNode::get_class_type())) { + return "-" + node->get_type().get_name(); + } + } + + // Use the node's name. + return name; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::find_matches +// Access: Private +// Description: Finds up to max_matches matches against the given +// path string from this node and deeper. The +// max_matches count indicates the maximum number of +// matches to return, or -1 not to limit the number +// returned. +//////////////////////////////////////////////////////////////////// +void NodePath:: +find_matches(NodePathCollection &result, const string &path, + int max_matches) const { + if (is_empty()) { + sgmanip_cat.warning() + << "Attempt to extend an empty NodePath by '" << path + << "'.\n"; + return; + } + FindApproxPath approx_path; + if (approx_path.add_string(path)) { + find_matches(result, approx_path, max_matches); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::find_matches +// Access: Private +// Description: Finds up to max_matches matches against the given +// approx_path from this node and deeper. The +// max_matches count indicates the maximum number of +// matches to return, or -1 not to limit the number +// returned. +//////////////////////////////////////////////////////////////////// +void NodePath:: +find_matches(NodePathCollection &result, FindApproxPath &approx_path, + int max_matches) const { + if (is_empty()) { + sgmanip_cat.warning() + << "Attempt to extend an empty NodePath by: " << approx_path << ".\n"; + return; + } + FindApproxLevelEntry start(*this, approx_path); + FindApproxLevel level; + level.add_entry(start); + r_find_matches(result, level, max_matches, _max_search_depth); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::r_find_matches +// Access: Private +// Description: The recursive implementation of find_matches. +//////////////////////////////////////////////////////////////////// +void NodePath:: +r_find_matches(NodePathCollection &result, + const FindApproxLevel &level, + int max_matches, int num_levels_remaining) const { + // Go on to the next level. If we exceeded the requested maximum + // depth, stop. + if (num_levels_remaining <= 0) { + return; + } + num_levels_remaining--; + + FindApproxLevel next_level; + + // For each node in the current level, build up the set of possible + // matches in the next level. + FindApproxLevel::Vec::const_iterator li; + for (li = level._v.begin(); li != level._v.end(); ++li) { + const FindApproxLevelEntry &entry = (*li); + if (entry.is_solution()) { + // Does this entry already represent a solution? + result.add_path(entry._node_path); + if (max_matches > 0 && result.get_num_paths() >= max_matches) { + return; + } + + } else { + Node *node = entry._node_path.get_bottom_node(); + nassertv(node != (Node *)NULL); + + DownRelations::const_iterator dri; + dri = node->_children.find(_graph_type); + if (dri != node->_children.end()) { + const DownRelationPointers &drp = (*dri).second; + + DownRelationPointers::const_iterator drpi; + for (drpi = drp.begin(); drpi != drp.end(); ++drpi) { + NodeRelation *arc = (*drpi); + entry.consider_next_step(result, arc, next_level, max_matches); + if (max_matches > 0 && result.get_num_paths() >= max_matches) { + return; + } + } + } + } + } + + // Now recurse on the next level. + r_find_matches(result, next_level, max_matches, num_levels_remaining); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::r_list_descendants +// Access: Private +// Description: The recursive implementation of ls(). +//////////////////////////////////////////////////////////////////// +void NodePath:: +r_list_descendants(ostream &out, int indent_level) const { + Node *node = get_bottom_node(); + nassertv(node != (Node *)NULL); + indent(out, indent_level) << *node << "\n"; + + DownRelations::const_iterator dri; + dri = node->_children.find(_graph_type); + if (dri != node->_children.end()) { + const DownRelationPointers &drp = (*dri).second; + + DownRelationPointers::const_iterator drpi; + for (drpi = drp.begin(); drpi != drp.end(); ++drpi) { + NodeRelation *arc = (*drpi); + NodePath next(*this, arc); + next.r_list_descendants(out, indent_level + 2); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::r_list_transitions +// Access: Private +// Description: The recursive implementation of ls_transitions(). +//////////////////////////////////////////////////////////////////// +void NodePath:: +r_list_transitions(ostream &out, int indent_level) const { + Node *node = get_bottom_node(); + nassertv(node != (Node *)NULL); + + out << "\n+"; + indent(out, indent_level + 1) << *node << "\n\n"; + + DownRelations::const_iterator dri; + dri = node->_children.find(_graph_type); + if (dri != node->_children.end()) { + const DownRelationPointers &drp = (*dri).second; + + DownRelationPointers::const_iterator drpi; + for (drpi = drp.begin(); drpi != drp.end(); ++drpi) { + NodeRelation *arc = (*drpi); + NodePath next(*this, arc); + + indent(out, indent_level + 2) << *arc << ":\n"; + arc->write_transitions(out, indent_level + 2); + next.r_list_transitions(out, indent_level + 2); + } + } +} diff --git a/panda/src/sgmanip/nodePath.h b/panda/src/sgmanip/nodePath.h new file mode 100644 index 0000000000..432b52f64e --- /dev/null +++ b/panda/src/sgmanip/nodePath.h @@ -0,0 +1,476 @@ + +// Filename: nodePath.h +// Created by: drose (05Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NODEPATH_H +#define NODEPATH_H + +#include + +#include "nodePathBase.h" +#include "nodePathCollection.h" + +#include +#include +#include + +#include + +class FindApproxLevel; +class FindApproxPath; +class Texture; +class Fog; +class Camera; +class AllTransitionsWrapper; + +// +// A NodePath is the fundamental unit of high-level interaction with +// the scene graph. It encapsulates the complete path down to a node +// from some other node, usually the root of the scene graph. This +// conveniently resolves ambiguities associated with instancing and +// protects the user from having to keep track of nodes and arcs +// separately. +// +// NodePath also contains a number of handy methods for common +// scene-graph manipulations, such as reparenting, and common state +// changes, such as repositioning. +// +// There are also a number of NodePath methods for finding nodes deep +// within the tree by name or by type. These take a path string, +// which at its simplest consists of a series of node names separated +// by slashes, like a directory pathname. +// +// Each component of the path string may optionally consist of one of +// the following special names, instead of a node name: +// +// * -- matches exactly one node, with any name. +// ** -- matches any sequence of zero or more nodes. +// +typename -- matches any node that is or derives from the given type. +// -typename -- matches any node that is the given type exactly. +// +// Furthermore, a node name may itself contain standard filename +// globbing characters, like *, ?, and [a-z], that will be accepted as +// a partial match. (In fact, the '*' special name may be seen as +// just a special case of this.) The globbing characters may not be +// used with the typename matches. +// +// Examples: +// +// "room//graph" will look for a node named "graph", which is a child +// of an unnamed node, which is a child of a node named "room", which +// is a child of the starting path. +// +// "**/red*" will look for any node anywhere in the tree (below the +// starting path) with a name that begins with "red". +// +// "**/+PartBundleNode/**/head" will look for a node named "head", +// somewhere below a PartBundleNode anywhere in the tree. +// +// +// The search is always potentially ambiguous, even if the special +// wildcard operators are not used, because there may be multiple +// nodes in the tree with the same name. In general, in the case of +// an ambiguity, the shortest path is preferred; when a method (such +// as extend_by) must choose only only one of several possible paths, +// it will choose the shortest available; on the other hand, when a +// method (such as find_all_matches) is to return all of the matching +// paths, it will sort them so that the shortest paths appear first in +// the output. +// + +//////////////////////////////////////////////////////////////////// +// Class : NodePath +// Description : A NodePath represents a complete path through the +// scene graph between two particular nodes (e.g. render +// to a GeomNode). It thus unambiguously defines +// instances, especially when it is rooted at the top of +// the graph. +// +// This class does not have any data members or +// additional virtual functions; it's just interface. +// All of the data is defined in the base class, +// NodePathBase. This is important so that +// NodePathCollection can work correctly without knowing +// exactly what a NodePath is. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NodePath : public NodePathBase { +public: + INLINE NodePath(TypeHandle graph_type = RenderRelation::get_class_type()); + INLINE NodePath(Node *top_node, TypeHandle graph_type = RenderRelation::get_class_type()); + INLINE NodePath(NodeRelation *top_arc); + INLINE NodePath(const NodePathBase ©); + INLINE NodePath(const NodePath ©, int shorten_count); + INLINE NodePath(const NodePath ©, NodeRelation *arc); + INLINE NodePath(const NodePath ©, Node *node); + INLINE NodePath(const NodePath ©, const string &path); + INLINE void operator = (const NodePath ©); + INLINE ~NodePath(); + + INLINE bool operator == (const NodePath &other) const; + INLINE bool operator != (const NodePath &other) const; + + INLINE void set_graph_type(TypeHandle graph_type); + INLINE TypeHandle get_graph_type() const; + + INLINE static void set_max_search_depth(int max_search_depth); + INLINE static int get_max_search_depth(); + + + // Methods to extend or shorten a NodePath. + + bool extend_by(Node *node); + bool extend_by(NodeRelation *arc); + bool extend_by(const NodePath &other); + bool extend_by(const string &path); + bool extend_down_to(Node *node); + + void shorten(int num_nodes = 1); + void clear(); + + + // Methods to query a NodePath's contents. + + INLINE bool is_empty() const; + INLINE bool is_singleton() const; + INLINE bool has_arcs() const; + INLINE int get_num_nodes() const; + Node *get_node(int index) const; + + int get_num_arcs() const; + NodeRelation *get_arc(int index) const; + + Node *get_top_node() const; + INLINE Node *get_bottom_node() const; + INLINE Node *node() const; + INLINE NodeRelation *get_bottom_arc() const; + + + // Methods to manage the disconnected NodePaths that can result if + // two NodePaths independently reference the same path, and one of + // them is reparented. + + int share_with(const NodePath &other); + bool verify_connectivity() const; + bool amputate_badness(); + bool repair_connectivity(const NodePath &top); + + + // Methods that return collections of NodePaths derived from or + // related to this one. + + NodePathCollection get_siblings() const; + NodePathCollection get_children() const; + INLINE int get_num_children() const; + INLINE NodePath get_child(int n) const; + + INLINE bool has_parent() const; + INLINE NodePath get_parent() const; + + INLINE NodePath find_path_down_to(Node *node) const; + INLINE NodePath find(const string &path) const; + + NodePathCollection + find_all_paths_down_to(Node *node) const; + + NodePathCollection + find_all_matches(const string &path) const; + + + // Methods that actually move nodes around in the scene graph. + + void reparent_to(const NodePath &other); + void wrt_reparent_to(const NodePath &other); + NodePath instance_to(const NodePath &other) const; + NodePath copy_to(const NodePath &other) const; + NodePath attach_new_node(Node *node) const; + INLINE NodePath attach_new_node(const string &name) const; + void remove_node(); + + + // Handy ways to look at what's there, and other miscellaneous + // operations. + + string as_string(int start_at_node = 0) const; + INLINE void output(ostream &out) const; + void write_transitions(ostream &out, int indent_level = 0) const; + + AllTransitionsWrapper get_net_transitions() const; + + INLINE void ls() const; + INLINE void ls(ostream &out, int indent_level = 0) const; + INLINE void ls_transitions() const; + INLINE void ls_transitions(ostream &out, int indent_level = 0) const; + void analyze() const; + + int flatten_light(); + int flatten_medium(); + int flatten_strong(); + + bool write_bam_file(const string &filename) const; + + // The remaining methods operate on scene graph attributes, + // according to the arc above the bottom node. In general, it is + // valid to call these only if !is_empty() && !is_singleton(). + + + // Methods that get and set the matrix transform: pos, hpr, scale, + // in the local coordinate system. These affect the transform at + // the bottom level only. + + INLINE void set_pos(float x, float y, float z); + void set_pos(const LVecBase3f &pos); + void set_x(float x); + void set_y(float y); + void set_z(float z); + LPoint3f get_pos() const; + INLINE float get_x() const; + INLINE float get_y() const; + INLINE float get_z() const; + + INLINE void set_hpr(float h, float p, float r); + void set_hpr(const LVecBase3f &hpr); + void set_h(float h); + void set_p(float p); + void set_r(float r); + LVecBase3f get_hpr() const; + INLINE float get_h() const; + INLINE float get_p() const; + INLINE float get_r() const; + + INLINE void set_scale(float scale); + INLINE void set_scale(float sx, float sy, float sz); + void set_scale(const LVecBase3f &sv3); + void set_sx(float sx); + void set_sy(float sy); + void set_sz(float sz); + LVecBase3f get_scale() const; + INLINE float get_sx() const; + INLINE float get_sy() const; + INLINE float get_sz() const; + + INLINE void set_pos_hpr(float x, float y, float z, + float h, float p, float r); + void set_pos_hpr(const LVecBase3f &pos, + const LVecBase3f &hpr); + INLINE void set_pos_hpr_scale(float x, float y, float z, + float h, float p, float r, + float sx, float sy, float sz); + void set_pos_hpr_scale(const LVecBase3f &pos, + const LVecBase3f &hpr, + const LVecBase3f &scale); + + void set_mat(const LMatrix4f &mat); + INLINE void clear_mat(); + INLINE bool has_mat() const; + INLINE LMatrix4f get_mat() const; + + INLINE bool has_color_scale() const; + INLINE void clear_color_scale(); + + void set_color_scale(const LVecBase4f &sv4); + INLINE void set_color_scale(float sx, float sy, float sz, float sa); + INLINE void set_sr(float sr); + INLINE void set_sg(float sg); + INLINE void set_sb(float sb); + INLINE void set_sa(float sa); + + LVecBase4f get_color_scale() const; + INLINE float get_sr() const; + INLINE float get_sg() const; + INLINE float get_sb() const; + INLINE float get_sa() const; + + INLINE void look_at(float x, float y, float z); + void look_at(const LPoint3f &point, const LVector3f &up = LVector3f::up()); + INLINE void heads_up(float x, float y, float z); + void heads_up(const LPoint3f &point, const LVector3f &up = LVector3f::up()); + + INLINE void print_pos() const; + INLINE void print_hpr() const; + INLINE void print_scale() const; + INLINE void print_pos_hpr() const; + INLINE void print_pos_hpr_scale() const; + INLINE void print_mat() const; + INLINE void print_color_scale() const; + + // Methods that get and set the matrix transforms relative to some + // other node in the scene graph. These perform an implicit wrt(). + + INLINE void set_pos(const NodePath &other, float x, float y, float z); + void set_pos(const NodePath &other, const LVecBase3f &pos); + void set_x(const NodePath &other, float x); + void set_y(const NodePath &other, float y); + void set_z(const NodePath &other, float z); + INLINE LPoint3f get_pos(const NodePath &other) const; + INLINE float get_x(const NodePath &other) const; + INLINE float get_y(const NodePath &other) const; + INLINE float get_z(const NodePath &other) const; + + INLINE void set_hpr(const NodePath &other, float h, float p, float r); + void set_hpr(const NodePath &other, const LVecBase3f &hpr); + void set_h(const NodePath &other, float h); + void set_p(const NodePath &other, float p); + void set_r(const NodePath &other, float r); + INLINE LVecBase3f get_hpr(const NodePath &other) const; + INLINE float get_h(const NodePath &other) const; + INLINE float get_p(const NodePath &other) const; + INLINE float get_r(const NodePath &other) const; + + INLINE void set_scale(const NodePath &other, float sx, float sy, float sz); + void set_scale(const NodePath &other, const LVecBase3f &scale); + void set_sx(const NodePath &other, float sx); + void set_sy(const NodePath &other, float sy); + void set_sz(const NodePath &other, float sz); + LVecBase3f get_scale(const NodePath &other) const; + INLINE float get_sx(const NodePath &other) const; + INLINE float get_sy(const NodePath &other) const; + INLINE float get_sz(const NodePath &other) const; + + INLINE void set_pos_hpr(const NodePath &other, + float x, float y, float z, + float h, float p, float r); + void set_pos_hpr(const NodePath &other, + const LVecBase3f &pos, + const LVecBase3f &hpr); + INLINE void set_pos_hpr_scale(const NodePath &other, + float x, float y, float z, + float h, float p, float r, + float sx, float sy, float sz); + void set_pos_hpr_scale(const NodePath &other, + const LVecBase3f &pos, + const LVecBase3f &hpr, + const LVecBase3f &scale); + + void set_mat(const NodePath &other, const LMatrix4f &mat); + LMatrix4f get_mat(const NodePath &other) const; + + INLINE void look_at(const NodePath &other, + float x, float y, float z); + void look_at(const NodePath &other, + const LPoint3f &point = LPoint3f(0.0, 0.0, 0.0), + const LVector3f &up = LVector3f::up()); + INLINE void heads_up(const NodePath &other, + float x, float y, float z); + void heads_up(const NodePath &other, + const LPoint3f &point = LPoint3f(0.0, 0.0, 0.0), + const LVector3f &up = LVector3f::up()); + + INLINE void print_pos(const NodePath &other) const; + INLINE void print_hpr(const NodePath &other) const; + INLINE void print_scale(const NodePath &other) const; + INLINE void print_pos_hpr(const NodePath &other) const; + INLINE void print_pos_hpr_scale(const NodePath &other) const; + INLINE void print_mat(const NodePath &other) const; + + INLINE float get_distance(const NodePath &other) const; + + + // Methods that affect appearance of geometry: color, texture, etc. + // These affect the state at the bottom level only. + + INLINE void set_color(float r, float g, float b, float a = 1.0, + int priority = 0); + void set_color(const Colorf &color, int priority = 0); + INLINE void clear_color(); + INLINE bool has_color() const; + Colorf get_color() const; + + void set_texture(Texture *tex, int priority = 0); + void set_texture_off(int priority = 0); + INLINE void clear_texture(); + bool has_texture() const; + bool has_texture_off() const; + Texture *get_texture() const; + + void set_fog(Fog *fog, int priority = 0); + void set_fog_off(int priority = 0); + INLINE void clear_fog(); + bool has_fog() const; + bool has_fog_off() const; + Fog *get_fog() const; + + void set_render_mode_wireframe(int priority = 0); + void set_render_mode_filled(int priority = 0); + INLINE void clear_render_mode(); + INLINE bool has_render_mode() const; + + void set_two_sided(bool two_sided, int priority = 0); + INLINE void clear_two_sided(); + INLINE bool has_two_sided() const; + bool get_two_sided() const; + + INLINE void set_billboard_axis(); + INLINE void set_billboard_point_eye(); + INLINE void set_billboard_point_world(); + INLINE void clear_billboard(); + INLINE bool has_billboard() const; + + void set_transparency(bool transparency, int priority = 0); + INLINE void clear_transparency(); + INLINE bool has_transparency() const; + bool get_transparency() const; + + INLINE void show(); + INLINE void hide(); + INLINE void show_collision_solids(); + INLINE void hide_collision_solids(); + bool is_hidden() const; + NodePath get_hidden_ancestor() const; + + void show_bounds(); + void hide_bounds(); + PT(BoundingVolume) get_bounds() const; + void write_bounds(ostream &out) const; + +public: + // This is a supporting class for passing the list of arcs to wrt(). + class ForwardIterator { + public: + INLINE ForwardIterator(ArcComponent *comp = NULL); + INLINE NodeRelation *operator * () const; + INLINE void operator ++(); + INLINE bool operator == (const ForwardIterator &other) const; + INLINE bool operator != (const ForwardIterator &other) const; + ArcComponent *_comp; + }; + +private: + bool r_equiv(const ArcComponent *next, const ArcComponent *other) const; + bool r_extend_by(const ArcComponent *other); + int r_as_string(const ArcComponent *comp, string &result, + int skip_nodes) const; + void r_write_transitions(const ArcComponent *comp, + ostream &out, int indent_level) const; + void r_get_net_transitions(const ArcComponent *comp, + AllTransitionsWrapper &trans) const; + string format_node_name(Node *node) const; + + void find_matches(NodePathCollection &result, + const string &approx_path_str, + int max_matches) const; + void find_matches(NodePathCollection &result, + FindApproxPath &approx_path, + int max_matches) const; + void r_find_matches(NodePathCollection &result, + const FindApproxLevel &level, + int max_matches, int num_levels_remaining) const; + + void r_list_descendants(ostream &out, int indent_level) const; + void r_list_transitions(ostream &out, int indent_level) const; + + // It's important that there are no data members in this class. Put + // them in NodePathBase instead. + + friend class NodePathCollection; +}; + +INLINE ostream &operator << (ostream &out, const NodePath &path) { + path.output(out); + return out; +} + +#include "nodePath.I" + +#endif diff --git a/panda/src/sgmanip/nodePathBase.I b/panda/src/sgmanip/nodePathBase.I new file mode 100644 index 0000000000..fafec9286b --- /dev/null +++ b/panda/src/sgmanip/nodePathBase.I @@ -0,0 +1,53 @@ +// Filename: nodePathBase.I +// Created by: drose (06Mar00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: NodePathBase::ArcComponent::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodePathBase::ArcComponent:: +ArcComponent(NodeRelation *arc, ArcComponent *next) : + _arc(arc), + _next(next) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathBase::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodePathBase:: +NodePathBase(TypeHandle graph_type) : + _graph_type(graph_type) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathBase::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NodePathBase:: +NodePathBase(const NodePathBase ©) : + _head(copy._head), + _top_node(copy._top_node), + _graph_type(copy._graph_type) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathBase::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void NodePathBase:: +operator = (const NodePathBase ©) { + _head = copy._head; + _top_node = copy._top_node; + _graph_type = copy._graph_type; +} diff --git a/panda/src/sgmanip/nodePathBase.cxx b/panda/src/sgmanip/nodePathBase.cxx new file mode 100644 index 0000000000..8e8754931a --- /dev/null +++ b/panda/src/sgmanip/nodePathBase.cxx @@ -0,0 +1,8 @@ +// Filename: nodePathBase.cxx +// Created by: drose (06Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nodePathBase.h" + +int NodePathBase::_max_search_depth = 10000; diff --git a/panda/src/sgmanip/nodePathBase.h b/panda/src/sgmanip/nodePathBase.h new file mode 100644 index 0000000000..5ec98b43d0 --- /dev/null +++ b/panda/src/sgmanip/nodePathBase.h @@ -0,0 +1,63 @@ +// Filename: nodePathBase.h +// Created by: drose (06Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NODEPATHBASE_H +#define NODEPATHBASE_H + +#include + +#include +#include +#include +#include +#include +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : NodePathBase +// Description : This is the base class for NodePath so we can defined +// NodePathCollections as collections of something. All +// of the interface for NodePath is defined at that +// level; this just defines the things that are stored +// here. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NodePathBase { +public: + INLINE NodePathBase(TypeHandle graph_type = RenderRelation::get_class_type()); + INLINE NodePathBase(const NodePathBase ©); + INLINE void operator = (const NodePathBase ©); + +protected: + // We maintain our own linked list structure here instead of using + // some STL structure, so we can efficiently copy-construct these + // things by sharing the initial part of the list. + + // We have a singly-linked list whose head is the bottom arc of the + // path, and whose tail is the top arc of the path. Thus, we can + // copy the entire path by simply copying the head pointer, and we + // can then append to or shorten our own path without affecting the + // paths we're sharing ArcComponents with. Very LISPy. + class ArcComponent : public ReferenceCount { + public: + INLINE ArcComponent(NodeRelation *arc, ArcComponent *next); + PT(NodeRelation) _arc; + PT(ArcComponent) _next; + }; + + PT(ArcComponent) _head; + + // This is only used when the NodePath is a singleton. If there is + // at least one arc, this is ignored. + PT_Node _top_node; + + TypeHandle _graph_type; + static int _max_search_depth; +}; + +#include "nodePathBase.I" + +#endif diff --git a/panda/src/sgmanip/nodePathCollection.I b/panda/src/sgmanip/nodePathCollection.I new file mode 100644 index 0000000000..4b6811efa1 --- /dev/null +++ b/panda/src/sgmanip/nodePathCollection.I @@ -0,0 +1,16 @@ +// Filename: nodePathCollection.I +// Created by: drose (06Mar00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::ls +// Access: Public +// Description: Lists all the nodes at and below each node in the +// collection hierarchically. +//////////////////////////////////////////////////////////////////// +INLINE void NodePathCollection:: +ls() const { + ls(nout); +} diff --git a/panda/src/sgmanip/nodePathCollection.cxx b/panda/src/sgmanip/nodePathCollection.cxx new file mode 100644 index 0000000000..240729d2db --- /dev/null +++ b/panda/src/sgmanip/nodePathCollection.cxx @@ -0,0 +1,363 @@ +// Filename: nodePathCollection.cxx +// Created by: drose (06Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "nodePathCollection.h" +#include "nodePath.h" +#include "findApproxPath.h" +#include "findApproxLevel.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +NodePathCollection:: +NodePathCollection() { +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +NodePathCollection:: +NodePathCollection(const NodePathCollection ©) : + _node_paths(copy._node_paths) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NodePathCollection:: +operator = (const NodePathCollection ©) { + _node_paths = copy._node_paths; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::add_path +// Access: Public +// Description: Adds a new NodePath to the collection. +//////////////////////////////////////////////////////////////////// +void NodePathCollection:: +add_path(const NodePath &node_path) { + // If the pointer to our internal array is shared by any other + // NodePathCollections, we have to copy the array now so we won't + // inadvertently modify any of our brethren NodePathCollection + // objects. + + if (_node_paths.get_count() > 1) { + PTA(NodePathBase) old_node_paths = _node_paths; + _node_paths = PTA(NodePathBase)(0); + _node_paths.v() = old_node_paths.v(); + } + + _node_paths.push_back(node_path); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::removes_path +// Access: Public +// Description: Removes a new NodePath from the collection. Returns +// true if the path was removed, false if it was not a +// member of the collection. +//////////////////////////////////////////////////////////////////// +bool NodePathCollection:: +remove_path(const NodePath &node_path) { + int path_index = -1; + for (int i = 0; path_index == -1 && i < (int)_node_paths.size(); i++) { + if ((const NodePath &)_node_paths[i] == node_path) { + path_index = i; + } + } + + if (path_index == -1) { + // The indicated path was not a member of the collection. + return false; + } + + // If the pointer to our internal array is shared by any other + // NodePathCollections, we have to copy the array now so we won't + // inadvertently modify any of our brethren NodePathCollection + // objects. + + if (_node_paths.get_count() > 1) { + PTA(NodePathBase) old_node_paths = _node_paths; + _node_paths = PTA(NodePathBase)(0); + _node_paths.v() = old_node_paths.v(); + } + + _node_paths.erase(_node_paths.begin() + path_index); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::add_paths_from +// Access: Public +// Description: Adds all the NodePaths indicated in the other +// collection to this path. The other paths are simply +// appended to the end of the paths in this list; +// duplicates are not automatically removed. +//////////////////////////////////////////////////////////////////// +void NodePathCollection:: +add_paths_from(const NodePathCollection &other) { + int other_num_paths = other.get_num_paths(); + for (int i = 0; i < other_num_paths; i++) { + add_path(other.get_path(i)); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::remove_paths_from +// Access: Public +// Description: Removes from this collection all of the NodePaths +// listed in the other collection. +//////////////////////////////////////////////////////////////////// +void NodePathCollection:: +remove_paths_from(const NodePathCollection &other) { + NodePaths new_paths; + int num_paths = get_num_paths(); + for (int i = 0; i < num_paths; i++) { + NodePath path = get_path(i); + if (!other.has_path(path)) { + new_paths.push_back(path); + } + } + _node_paths = new_paths; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::remove_duplicate_paths +// Access: Public +// Description: Removes any duplicate entries of the same NodePaths +// on this collection. If a NodePath appears multiple +// times, the first appearance is retained; subsequent +// appearances are removed. +//////////////////////////////////////////////////////////////////// +void NodePathCollection:: +remove_duplicate_paths() { + NodePaths new_paths; + + int num_paths = get_num_paths(); + for (int i = 0; i < num_paths; i++) { + NodePath path = get_path(i); + bool duplicated = false; + + for (int j = 0; j < i && !duplicated; j++) { + duplicated = (path == get_path(j)); + } + + if (!duplicated) { + new_paths.push_back(path); + } + } + + _node_paths = new_paths; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::has_path +// Access: Public +// Description: Returns true if the indicated NodePath appears in +// this collection, false otherwise. +//////////////////////////////////////////////////////////////////// +bool NodePathCollection:: +has_path(const NodePath &path) const { + for (int i = 0; i < get_num_paths(); i++) { + if (path == get_path(i)) { + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::clear +// Access: Public +// Description: Removes all NodePaths from the collection. +//////////////////////////////////////////////////////////////////// +void NodePathCollection:: +clear() { + _node_paths.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::is_empty +// Access: Public +// Description: Returns true if there are no NodePaths in the +// collection, false otherwise. +//////////////////////////////////////////////////////////////////// +bool NodePathCollection:: +is_empty() const { + return _node_paths.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::get_num_paths +// Access: Public +// Description: Returns the number of NodePaths in the collection. +//////////////////////////////////////////////////////////////////// +int NodePathCollection:: +get_num_paths() const { + return _node_paths.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::get_path +// Access: Public +// Description: Returns the nth NodePath in the collection. +//////////////////////////////////////////////////////////////////// +NodePath NodePathCollection:: +get_path(int index) const { + nassertr(index >= 0 && index < _node_paths.size(), NodePath()); + + return NodePath(_node_paths[index]); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::ls +// Access: Public +// Description: Lists all the nodes at and below each node in the +// collection hierarchically. +//////////////////////////////////////////////////////////////////// +void NodePathCollection:: +ls(ostream &out, int indent_level) const { + for (int i = 0; i < get_num_paths(); i++) { + NodePath path = get_path(i); + indent(out, indent_level) << path << "\n"; + path.ls(out, indent_level + 2); + out << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::find_all_matches +// Access: Public +// Description: Returns the complete set of all NodePaths that begin +// with any NodePath in this collection and can be +// extended by approx_path_str. The shortest paths will +// be listed first. +//////////////////////////////////////////////////////////////////// +NodePathCollection NodePathCollection:: +find_all_matches(const char *approx_path_str) const { + NodePathCollection result; + + FindApproxPath approx_path; + if (approx_path.add_string(approx_path_str)) { + if (!is_empty()) { + FindApproxLevel level; + for (int i = 0; i < get_num_paths(); i++) { + FindApproxLevelEntry start(get_path(i), approx_path); + level.add_entry(start); + } + get_path(0).r_find_matches(result, level, -1, + NodePath::get_max_search_depth()); + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::reparent_to +// Access: Public +// Description: Reparents all the NodePaths in the collection to the +// indicated node. +//////////////////////////////////////////////////////////////////// +void NodePathCollection:: +reparent_to(const NodePath &other) { + for (int i = 0; i < get_num_paths(); i++) { + get_path(i).reparent_to(other); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::wrt_reparent_to +// Access: Public +// Description: Reparents all the NodePaths in the collection to the +// indicated node, adjusting each transform so as not to +// move in world coordinates. +//////////////////////////////////////////////////////////////////// +void NodePathCollection:: +wrt_reparent_to(const NodePath &other) { + for (int i = 0; i < get_num_paths(); i++) { + get_path(i).wrt_reparent_to(other); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::instance_to +// Access: Public +// Description: Creates another instance for each NodePath in the +// collection under the indicated node; returns the +// collection of new instances. +//////////////////////////////////////////////////////////////////// +NodePathCollection NodePathCollection:: +instance_to(const NodePath &other) const { + NodePathCollection result; + + for (int i = 0; i < get_num_paths(); i++) { + result.add_path(get_path(i).instance_to(other)); + } + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::show +// Access: Public +// Description: Shows all NodePaths in the collection. +//////////////////////////////////////////////////////////////////// +void NodePathCollection:: +show() { + for (int i = 0; i < get_num_paths(); i++) { + get_path(i).show(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::show +// Access: Public +// Description: Hides all NodePaths in the collection. +//////////////////////////////////////////////////////////////////// +void NodePathCollection:: +hide() { + for (int i = 0; i < get_num_paths(); i++) { + get_path(i).hide(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::output +// Access: Public +// Description: Writes a brief one-line description of the +// NodePathCollection to the indicated output stream. +//////////////////////////////////////////////////////////////////// +void NodePathCollection:: +output(ostream &out) const { + if (get_num_paths() == 1) { + out << "1 NodePath"; + } else { + out << get_num_paths() << " NodePaths"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::write +// Access: Public +// Description: Writes a complete multi-line description of the +// NodePathCollection to the indicated output stream. +//////////////////////////////////////////////////////////////////// +void NodePathCollection:: +write(ostream &out, int indent_level) const { + for (int i = 0; i < get_num_paths(); i++) { + indent(out, indent_level) << get_path(i) << "\n"; + } +} diff --git a/panda/src/sgmanip/nodePathCollection.h b/panda/src/sgmanip/nodePathCollection.h new file mode 100644 index 0000000000..4acf9d86aa --- /dev/null +++ b/panda/src/sgmanip/nodePathCollection.h @@ -0,0 +1,73 @@ +// Filename: nodePathCollection.h +// Created by: drose (06Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NODEPATHCOLLECTION_H +#define NODEPATHCOLLECTION_H + +#include + +// We don't include NodePath in the header file, so NodePath can +// include us. +#include "nodePathBase.h" + +#include + +class NodePath; + +//////////////////////////////////////////////////////////////////// +// Class : NodePathCollection +// Description : This is a set of zero or more NodePaths. It's handy +// for returning from functions that need to return +// multiple NodePaths (for instance, +// NodePaths::get_children). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA NodePathCollection { +public: + NodePathCollection(); + NodePathCollection(const NodePathCollection ©); + void operator = (const NodePathCollection ©); + + void add_path(const NodePath &node_path); + bool remove_path(const NodePath &node_path); + void add_paths_from(const NodePathCollection &other); + void remove_paths_from(const NodePathCollection &other); + void remove_duplicate_paths(); + bool has_path(const NodePath &path) const; + void clear(); + + bool is_empty() const; + int get_num_paths() const; + NodePath get_path(int index) const; + + // Handy operations on many NodePaths at once. + INLINE void ls() const; + void ls(ostream &out, int indent_level = 0) const; + + NodePathCollection find_all_matches(const char *approx_path_str) const; + void reparent_to(const NodePath &other); + void wrt_reparent_to(const NodePath &other); + NodePathCollection instance_to(const NodePath &other) const; + + void show(); + void hide(); + + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + +private: + typedef PTA(NodePathBase) NodePaths; + NodePaths _node_paths; +}; + +INLINE ostream &operator << (ostream &out, const NodePathCollection &col) { + col.output(out); + return out; +} + +#include "nodePathCollection.I" + +#endif + + diff --git a/panda/src/sgmanip/nodePathLerps.cxx b/panda/src/sgmanip/nodePathLerps.cxx new file mode 100644 index 0000000000..b7220e7388 --- /dev/null +++ b/panda/src/sgmanip/nodePathLerps.cxx @@ -0,0 +1,173 @@ +// Filename: nodePathLerps.cxx +// Created by: frang (01Jun00) +// +//////////////////////////////////////////////////////////////////// + +#include "nodePathLerps.h" + +TypeHandle PosLerpFunctor::_type_handle; +TypeHandle HprLerpFunctor::_type_handle; +TypeHandle ScaleLerpFunctor::_type_handle; +TypeHandle ColorLerpFunctor::_type_handle; +TypeHandle PosHprLerpFunctor::_type_handle; +TypeHandle PosHprScaleLerpFunctor::_type_handle; +TypeHandle ColorScaleLerpFunctor::_type_handle; + + +PosLerpFunctor::PosLerpFunctor(const PosLerpFunctor& c) + : _node_path(c._node_path), LPoint3fLerpFunctor(c) {} + +PosLerpFunctor::~PosLerpFunctor(void) +{ +} + +PosLerpFunctor& PosLerpFunctor::operator=(const PosLerpFunctor& c) { + _node_path = c._node_path; + LPoint3fLerpFunctor::operator=(c); + return *this; +} + +void PosLerpFunctor::operator()(float t) { + if (_is_wrt) + _node_path.set_pos(_wrt_path, interpolate(t)); + else + _node_path.set_pos(interpolate(t)); +} + +HprLerpFunctor::HprLerpFunctor(const HprLerpFunctor& c) + : _node_path(c._node_path), LVecBase3fLerpFunctor(c) {} + +HprLerpFunctor::~HprLerpFunctor(void) +{ +} + +HprLerpFunctor& HprLerpFunctor::operator=(const HprLerpFunctor& c) { + _node_path = c._node_path; + LVecBase3fLerpFunctor::operator=(c); + return *this; +} + +void HprLerpFunctor::operator()(float t) { + if (_is_wrt) + _node_path.set_hpr(_wrt_path, interpolate(t)); + else + _node_path.set_hpr(interpolate(t)); +} + +ScaleLerpFunctor::ScaleLerpFunctor(const ScaleLerpFunctor& c) + : _node_path(c._node_path), LVecBase3fLerpFunctor(c) {} + +ScaleLerpFunctor::~ScaleLerpFunctor(void) +{ +} + +ScaleLerpFunctor& ScaleLerpFunctor::operator=(const ScaleLerpFunctor& c) { + _node_path = c._node_path; + LVecBase3fLerpFunctor::operator=(c); + return *this; +} + +void ScaleLerpFunctor::operator()(float t) { + if (_is_wrt) + _node_path.set_scale(_wrt_path, interpolate(t)); + else + _node_path.set_scale(interpolate(t)); +} + +ColorLerpFunctor::ColorLerpFunctor(const ColorLerpFunctor& c) + : _node_path(c._node_path), LVecBase4fLerpFunctor(c) {} + +ColorLerpFunctor::~ColorLerpFunctor(void) +{ +} + +ColorLerpFunctor& ColorLerpFunctor::operator=(const ColorLerpFunctor& c) { + _node_path = c._node_path; + LVecBase4fLerpFunctor::operator=(c); + return *this; +} + +void ColorLerpFunctor::operator()(float t) { + _node_path.set_color(interpolate(t)); +} + + +PosHprLerpFunctor::PosHprLerpFunctor(const PosHprLerpFunctor& c) + : _node_path(c._node_path), LerpFunctor(c) {} + +PosHprLerpFunctor::~PosHprLerpFunctor(void) +{ +} + +PosHprLerpFunctor& PosHprLerpFunctor::operator=(const PosHprLerpFunctor& c) { + _node_path = c._node_path; + _pstart = c._pstart; + _pend = c._pend; + _pdiff_cache = c._pdiff_cache; + _hstart = c._hstart; + _hend = c._hend; + _hdiff_cache = c._hdiff_cache; + LerpFunctor::operator=(c); + return *this; +} + +void PosHprLerpFunctor::operator()(float t) { + LPoint3f p = ((t * _pdiff_cache) + _pstart); + LVecBase3f h = ((t * _hdiff_cache) + _hstart); + if (_is_wrt) + _node_path.set_pos_hpr(_wrt_path, p, h); + else + _node_path.set_pos_hpr(p, h); +} + +PosHprScaleLerpFunctor::PosHprScaleLerpFunctor(const PosHprScaleLerpFunctor& c) + : _node_path(c._node_path), LerpFunctor(c) {} + +PosHprScaleLerpFunctor::~PosHprScaleLerpFunctor(void) +{ +} + +PosHprScaleLerpFunctor& +PosHprScaleLerpFunctor::operator=(const PosHprScaleLerpFunctor& c) { + _node_path = c._node_path; + _pstart = c._pstart; + _pend = c._pend; + _pdiff_cache = c._pdiff_cache; + _hstart = c._hstart; + _hend = c._hend; + _hdiff_cache = c._hdiff_cache; + _sstart = c._sstart; + _send = c._send; + _sdiff_cache = c._sdiff_cache; + LerpFunctor::operator=(c); + return *this; +} + +void PosHprScaleLerpFunctor::operator()(float t) { + LPoint3f p = ((t * _pdiff_cache) + _pstart); + LVecBase3f h = ((t * _hdiff_cache) + _hstart); + LVecBase3f s = ((t * _sdiff_cache) + _sstart); + if (_is_wrt) + _node_path.set_pos_hpr_scale(_wrt_path, p, h, s); + else + _node_path.set_pos_hpr_scale(p, h, s); +} + +ColorScaleLerpFunctor::ColorScaleLerpFunctor(const ColorScaleLerpFunctor& c) + : _node_path(c._node_path), LVecBase4fLerpFunctor(c) {} + +ColorScaleLerpFunctor::~ColorScaleLerpFunctor(void) +{ +} + +ColorScaleLerpFunctor& ColorScaleLerpFunctor::operator=(const ColorScaleLerpFunctor& c) { + _node_path = c._node_path; + LVecBase4fLerpFunctor::operator=(c); + return *this; +} + +void ColorScaleLerpFunctor::operator()(float t) { + _node_path.set_color_scale(interpolate(t)); +} + + diff --git a/panda/src/sgmanip/nodePathLerps.h b/panda/src/sgmanip/nodePathLerps.h new file mode 100644 index 0000000000..4141b74d7d --- /dev/null +++ b/panda/src/sgmanip/nodePathLerps.h @@ -0,0 +1,402 @@ +// Filename: nodePathLerps.h +// Created by: frang (01Jun00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __NODEPATHLERPS_H__ +#define __NODEPATHLERPS_H__ + +#include +#include "nodePath.h" + +//////////////////////////////////////////////////////////////////// +// Class : PosLerpFunctor +// Description : Class for Lerping between positions in space +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PosLerpFunctor : public LPoint3fLerpFunctor { +private: + NodePath _node_path; + NodePath _wrt_path; + bool _is_wrt; +public: + PosLerpFunctor(NodePath np, LPoint3f start, LPoint3f end) + : LPoint3fLerpFunctor(start, end), _node_path(np), _is_wrt(false) {} + PosLerpFunctor(NodePath np, float sx, float sy, float sz, float ex, float ey, + float ez) : LPoint3fLerpFunctor(LPoint3f(sx, sy, sz), + LPoint3f(ex, ey, ez)), + _node_path(np), _is_wrt(false) {} + PosLerpFunctor(NodePath np, LPoint3f start, LPoint3f end, NodePath wrt) + : LPoint3fLerpFunctor(start, end), _node_path(np), _is_wrt(true), + _wrt_path(wrt) {} + PosLerpFunctor(NodePath np, float sx, float sy, float sz, float ex, float ey, + float ez, NodePath wrt) + : LPoint3fLerpFunctor(LPoint3f(sx, sy, sz), LPoint3f(ex, ey, ez)), + _node_path(np), _is_wrt(true), _wrt_path(wrt) {} + PosLerpFunctor(const PosLerpFunctor&); + virtual ~PosLerpFunctor(void); + PosLerpFunctor& operator=(const PosLerpFunctor&); + virtual void operator()(float); +public: + // now for typehandle stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LPoint3fLerpFunctor::init_type(); + register_type(_type_handle, "PosLerpFunctor", + LPoint3fLerpFunctor::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + + +// evil bad bad evil HPR +//////////////////////////////////////////////////////////////////// +// Class : HprLerpFunctor +// Description : Class for Lerping between orientations in space +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA HprLerpFunctor : public LVecBase3fLerpFunctor { +private: + NodePath _node_path; + NodePath _wrt_path; + bool _is_wrt; +public: + HprLerpFunctor(NodePath np, LVecBase3f start, LVecBase3f end) + : LVecBase3fLerpFunctor(start, end), _node_path(np), _is_wrt(false) {} + HprLerpFunctor(NodePath np, float sx, float sy, float sz, float ex, float ey, + float ez) : LVecBase3fLerpFunctor(LVecBase3f(sx, sy, sz), + LVecBase3f(ex, ey, ez)), + _node_path(np), _is_wrt(false) {} + HprLerpFunctor(NodePath np, LVecBase3f start, LVecBase3f end, NodePath wrt) + : LVecBase3fLerpFunctor(start, end), _node_path(np), _is_wrt(true), + _wrt_path(wrt) {} + HprLerpFunctor(NodePath np, float sx, float sy, float sz, float ex, float ey, + float ez, NodePath wrt) + : LVecBase3fLerpFunctor(LVecBase3f(sx, sy, sz), LVecBase3f(ex, ey, ez)), + _node_path(np), _is_wrt(true), _wrt_path(wrt) {} + HprLerpFunctor(const HprLerpFunctor&); + virtual ~HprLerpFunctor(void); + HprLerpFunctor& operator=(const HprLerpFunctor&); + virtual void operator()(float); +public: + // now for typehandle stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LVecBase3fLerpFunctor::init_type(); + register_type(_type_handle, "HprLerpFunctor", + LVecBase3fLerpFunctor::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +//////////////////////////////////////////////////////////////////// +// Class : ColorLerpFunctor +// Description : Class for Lerping between scales +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ScaleLerpFunctor : public LVecBase3fLerpFunctor { +private: + NodePath _node_path; + NodePath _wrt_path; + bool _is_wrt; +public: + ScaleLerpFunctor(NodePath np, LVecBase3f start, LVecBase3f end) + : LVecBase3fLerpFunctor(start, end), _node_path(np), _is_wrt(false) {} + ScaleLerpFunctor(NodePath np, float sx, float sy, float sz, float ex, + float ey, float ez) + : LVecBase3fLerpFunctor(LVecBase3f(sx, sy, sz), LVecBase3f(ex, ey, ez)), + _node_path(np), _is_wrt(false) {} + ScaleLerpFunctor(NodePath np, LVecBase3f start, LVecBase3f end, NodePath wrt) + : LVecBase3fLerpFunctor(start, end), _node_path(np), _is_wrt(true), + _wrt_path(wrt) {} + ScaleLerpFunctor(NodePath np, float sx, float sy, float sz, float ex, + float ey, float ez, NodePath wrt) + : LVecBase3fLerpFunctor(LVecBase3f(sx, sy, sz), LVecBase3f(ex, ey, ez)), + _node_path(np), _is_wrt(true), _wrt_path(wrt) {} + ScaleLerpFunctor(const ScaleLerpFunctor&); + virtual ~ScaleLerpFunctor(void); + ScaleLerpFunctor& operator=(const ScaleLerpFunctor&); + virtual void operator()(float); +public: + // now for typehandle stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LVecBase3fLerpFunctor::init_type(); + register_type(_type_handle, "ScaleLerpFunctor", + LVecBase3fLerpFunctor::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +//////////////////////////////////////////////////////////////////// +// Class : ColorLerpFunctor +// Description : Class for Lerping between colors +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ColorLerpFunctor : public LVecBase4fLerpFunctor { +private: + NodePath _node_path; + NodePath _wrt_path; + bool _is_wrt; +public: + ColorLerpFunctor(NodePath np, LVecBase4f start, LVecBase4f end) + : LVecBase4fLerpFunctor(start, end), _node_path(np), _is_wrt(false) {} + ColorLerpFunctor(NodePath np, float sr, float sg, float sb, float sa, + float er, float eg, float eb, float ea) : LVecBase4fLerpFunctor(LVecBase4f(sr, sg, sb, sa), + LVecBase4f(er, eg, eb, ea)), _node_path(np), _is_wrt(false) {} + ColorLerpFunctor(NodePath np, LVecBase4f start, LVecBase4f end, NodePath wrt) + : LVecBase4fLerpFunctor(start, end), _node_path(np), _is_wrt(true), + _wrt_path(wrt) {} + ColorLerpFunctor(NodePath np, float sr, float sg, float sb, float sa, float er, float eg, + float eb, float ea, NodePath wrt) + : LVecBase4fLerpFunctor(LVecBase4f(sr, sg, sb, sa), LVecBase4f(er, eg, eb, ea)), + _node_path(np), _is_wrt(true), _wrt_path(wrt) {} + ColorLerpFunctor(const ColorLerpFunctor&); + virtual ~ColorLerpFunctor(void); + ColorLerpFunctor& operator=(const ColorLerpFunctor&); + virtual void operator()(float); +public: + // now for typehandle stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LVecBase4fLerpFunctor::init_type(); + register_type(_type_handle, "ColorLerpFunctor", + LVecBase4fLerpFunctor::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +//////////////////////////////////////////////////////////////////// +// Class : PosHprLerpFunctor +// Description : Class for Lerping between positions and orientations +// in space +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PosHprLerpFunctor : public LerpFunctor { +private: + NodePath _node_path; + LPoint3f _pstart; + LPoint3f _pend; + LPoint3f _pdiff_cache; + LVecBase3f _hstart; + LVecBase3f _hend; + LVecBase3f _hdiff_cache; + NodePath _wrt_path; + bool _is_wrt; +public: + PosHprLerpFunctor(NodePath np, LPoint3f pstart, LPoint3f pend, + LVecBase3f hstart, LVecBase3f hend) + : LerpFunctor(), _node_path(np), _pstart(pstart), _pend(pend), + _pdiff_cache(pend-pstart), _hstart(hstart), _hend(hend), + _hdiff_cache(hend-hstart), _is_wrt(false) {} + PosHprLerpFunctor(NodePath np, float psx, float psy, float psz, float pex, + float pey, float pez, float hsx, float hsy, float hsz, + float hex, float hey, float hez) + : LerpFunctor(), _node_path(np), _pstart(psx, psy, psz), + _pend(pex, pey, pez), _pdiff_cache(_pend-_pstart), + _hstart(hsx, hsy, hsz), _hend(hex, hey, hez), + _hdiff_cache(_hend - _hstart), _is_wrt(false) {} + PosHprLerpFunctor(NodePath np, LPoint3f pstart, LPoint3f pend, + LVecBase3f hstart, LVecBase3f hend, NodePath wrt) + : LerpFunctor(), _node_path(np), _pstart(pstart), _pend(pend), + _pdiff_cache(pend-pstart), _hstart(hstart), _hend(hend), + _hdiff_cache(hend-hstart), _is_wrt(true), _wrt_path(wrt) {} + PosHprLerpFunctor(NodePath np, float psx, float psy, float psz, float pex, + float pey, float pez, float hsx, float hsy, float hsz, + float hex, float hey, float hez, NodePath wrt) + : LerpFunctor(), _node_path(np), _pstart(psx, psy, psz), + _pend(pex, pey, pez), _pdiff_cache(_pend-_pstart), + _hstart(hsx, hsy, hsz), _hend(hex, hey, hez), + _hdiff_cache(_hend - _hstart), _is_wrt(true), _wrt_path(wrt) {} + PosHprLerpFunctor(const PosHprLerpFunctor&); + virtual ~PosHprLerpFunctor(void); + PosHprLerpFunctor& operator=(const PosHprLerpFunctor&); + virtual void operator()(float); +public: + // now for typehandle stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LerpFunctor::init_type(); + register_type(_type_handle, "PosHprLerpFunctor", + LerpFunctor::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +//////////////////////////////////////////////////////////////////// +// Class : PosHprScaleLerpFunctor +// Description : Class for Lerping between position, orientation, +// and scale +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PosHprScaleLerpFunctor : public LerpFunctor { +private: + NodePath _node_path; + LPoint3f _pstart; + LPoint3f _pend; + LPoint3f _pdiff_cache; + LVecBase3f _hstart; + LVecBase3f _hend; + LVecBase3f _hdiff_cache; + LVecBase3f _sstart; + LVecBase3f _send; + LVecBase3f _sdiff_cache; + NodePath _wrt_path; + bool _is_wrt; +public: + PosHprScaleLerpFunctor(NodePath np, LPoint3f pstart, LPoint3f pend, + LVecBase3f hstart, LVecBase3f hend, LVecBase3f sstart, + LVecBase3f send) + : LerpFunctor(), _node_path(np), _pstart(pstart), _pend(pend), + _pdiff_cache(pend-pstart), _hstart(hstart), _hend(hend), + _hdiff_cache(hend-hstart), _sstart(sstart), _send(send), + _sdiff_cache(send-sstart), _is_wrt(false) {} + PosHprScaleLerpFunctor(NodePath np, float psx, float psy, float psz, + float pex, float pey, float pez, float hsx, float hsy, + float hsz, float hex, float hey, float hez, float ssx, + float ssy, float ssz, float sex, float sey, float sez) + : LerpFunctor(), _node_path(np), _pstart(psx, psy, psz), + _pend(pex, pey, pez), _pdiff_cache(_pend-_pstart), + _hstart(hsx, hsy, hsz), _hend(hex, hey, hez), + _hdiff_cache(_hend-_hstart), _sstart(ssx, ssy, ssz), + _send(sex, sey, sez), _sdiff_cache(_send-_sstart), _is_wrt(false) {} + PosHprScaleLerpFunctor(NodePath np, LPoint3f pstart, LPoint3f pend, + LVecBase3f hstart, LVecBase3f hend, LVecBase3f sstart, + LVecBase3f send, NodePath wrt) + : LerpFunctor(), _node_path(np), _pstart(pstart), _pend(pend), + _pdiff_cache(pend-pstart), _hstart(hstart), _hend(hend), + _hdiff_cache(hend-hstart), _sstart(sstart), _send(send), + _sdiff_cache(send-sstart), _is_wrt(true), _wrt_path(wrt) {} + PosHprScaleLerpFunctor(NodePath np, float psx, float psy, float psz, + float pex, float pey, float pez, float hsx, float hsy, + float hsz, float hex, float hey, float hez, float ssx, + float ssy, float ssz, float sex, float sey, float sez, + NodePath wrt) + : LerpFunctor(), _node_path(np), _pstart(psx, psy, psz), + _pend(pex, pey, pez), _pdiff_cache(_pend-_pstart), + _hstart(hsx, hsy, hsz), _hend(hex, hey, hez), + _hdiff_cache(_hend-_hstart), _sstart(ssx, ssy, ssz), + _send(sex, sey, sez), _sdiff_cache(_send-_sstart), _is_wrt(true), + _wrt_path(wrt) {} + PosHprScaleLerpFunctor(const PosHprScaleLerpFunctor&); + virtual ~PosHprScaleLerpFunctor(void); + PosHprScaleLerpFunctor& operator=(const PosHprScaleLerpFunctor&); + virtual void operator()(float); +public: + // now for typehandle stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LerpFunctor::init_type(); + register_type(_type_handle, "PosHprScaleLerpFunctor", + LerpFunctor::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +//////////////////////////////////////////////////////////////////// +// Class : ColorScaleLerpFunctor +// Description : Class for Lerping between color scales +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ColorScaleLerpFunctor : public LVecBase4fLerpFunctor { +private: + NodePath _node_path; + NodePath _wrt_path; + bool _is_wrt; +public: + ColorScaleLerpFunctor(NodePath np, LVecBase4f start, LVecBase4f end) + : LVecBase4fLerpFunctor(start, end), _node_path(np), _is_wrt(false) {} + ColorScaleLerpFunctor(NodePath np, float sr, float sg, float sb, float sa, + float er, float eg, float eb, float ea) : LVecBase4fLerpFunctor(LVecBase4f(sr, sg, sb, sa), + LVecBase4f(er, eg, eb, ea)), _node_path(np), _is_wrt(false) {} + ColorScaleLerpFunctor(NodePath np, LVecBase4f start, LVecBase4f end, NodePath wrt) + : LVecBase4fLerpFunctor(start, end), _node_path(np), _is_wrt(true), + _wrt_path(wrt) {} + ColorScaleLerpFunctor(NodePath np, float sr, float sg, float sb, float sa, float er, float eg, + float eb, float ea, NodePath wrt) + : LVecBase4fLerpFunctor(LVecBase4f(sr, sg, sb, sa), LVecBase4f(er, eg, eb, ea)), + _node_path(np), _is_wrt(true), _wrt_path(wrt) {} + ColorScaleLerpFunctor(const ColorScaleLerpFunctor&); + virtual ~ColorScaleLerpFunctor(void); + ColorScaleLerpFunctor& operator=(const ColorScaleLerpFunctor&); + virtual void operator()(float); +public: + // now for typehandle stuff + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + LVecBase4fLerpFunctor::init_type(); + register_type(_type_handle, "ColorScaleLerpFunctor", + LVecBase4fLerpFunctor::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type(void) { + init_type(); + return get_class_type(); + } +private: + static TypeHandle _type_handle; +}; + +#endif /* __NODEPATHLERPS_H__ */ + + + + + diff --git a/panda/src/sgmanip/test_sgmanip.cxx b/panda/src/sgmanip/test_sgmanip.cxx new file mode 100644 index 0000000000..ed8bbe33a3 --- /dev/null +++ b/panda/src/sgmanip/test_sgmanip.cxx @@ -0,0 +1,120 @@ +// Filename: test_sgmanip.cxx +// Created by: drose (18Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "nodePath.h" + +#include +#include +#include +#include +#include + +int +main() { + PT_Node render = new NamedNode("render"); + + PT_Node apples = new NamedNode("apples"); + PT(RenderRelation) apples_arc = new RenderRelation(render, apples); + PT_Node mcintosh = new NamedNode("mcintosh"); + PT(RenderRelation) mcintosh_arc = new RenderRelation(apples, mcintosh); + PT_Node grannysmith = new NamedNode("grannysmith"); + PT(RenderRelation) grannysmith_arc = new RenderRelation(apples, grannysmith); + PT_Node delicious = new NamedNode("delicious"); + PT(RenderRelation) delicious_arc = new RenderRelation(apples, delicious); + PT_Node golden = new NamedNode("golden"); + PT(RenderRelation) golden_arc = new RenderRelation(delicious, golden); + PT_Node red = new NamedNode("red"); + PT(RenderRelation) red_arc = new RenderRelation(delicious, red); + + PT_Node oranges = new NamedNode("oranges"); + PT(RenderRelation) oranges_arc = new RenderRelation(render, oranges); + PT_Node navel = new NamedNode("navel"); + PT(RenderRelation) navel_arc = new RenderRelation(oranges, navel); + PT_Node red_orange = new NamedNode("red"); + PT(RenderRelation) red_orange_arc = new RenderRelation(oranges, red_orange); + + NodePath render_path(render); + render_path.ls(); + + NodePath del_path(render_path, "apples/delicious"); + nout << "\ndel_path = " << del_path << "\n"; + nout << "del_path.get_parent() = " << del_path.get_parent() << "\n"; + NodePath red_path(del_path, "red"); + nout << "red_path = " << red_path << "\n"; + NodePath del_path2(render_path, delicious); + nout << "del_path2 = " << del_path2 << "\n"; + NodePath mc_path(NodePath(del_path, 1), "mcintosh"); + nout << "mc_path = " << mc_path << "\n"; + NodePath apples_path(del_path, 1); + nout << "apples_path = " << apples_path << "\n"; + NodePath oranges_path(render_path, oranges); + nout << "oranges_path = " << oranges_path << "\n"; + + /* + nout << "\ndel_path2.share_with(del_path) = " + << del_path2.share_with(del_path) << "\n"; + nout << "del_path2 = " << del_path2 << "\n"; + */ + + nout << "\nChildren of del_path:\n"; + del_path.get_children().write(nout, 2); + nout << "Sibs:\n"; + del_path.get_siblings().write(nout, 2); + nout << "Descendants:\n"; + del_path.ls(nout); + nout << "Nodes:\n"; + int i; + for (i = 0; i < del_path.get_num_nodes(); i++) { + nout << " " << *del_path.get_node(i) << "\n"; + } + nout << "Arcs:\n"; + for (i = 0; i < del_path.get_num_arcs(); i++) { + nout << " " << *del_path.get_arc(i) << "\n"; + } + nout << "As strings:\n"; + for (i = 0; i <= del_path.get_num_nodes(); i++) { + nout << " " << i << ": " << del_path.as_string(i) << "\n"; + } + nout << "Connectivity = " << del_path.verify_connectivity() << "\n"; + + nout << "\nEquivalence to self = " << (del_path == del_path) << "\n"; + nout << "Equivalence to del_path2 = " << (del_path == del_path2) << "\n"; + nout << "Equivalence to mc_path = " << (del_path == mc_path) << "\n"; + + nout << "\nAll paths to red_orange:\n"; + render_path.find_all_paths_down_to(red_orange).write(nout, 2); + + nout << "\nMoving del_path to oranges\n"; + del_path.reparent_to(oranges_path); + nout << "del_path = " << del_path << "\n" + << "red_path = " << red_path << "\n" + << "del_path2 = " << del_path2 << "\n" + << "mc_path = " << mc_path << "\n" + << "apples_path = " << apples_path << "\n" + << "oranges_path = " << oranges_path << "\n"; + + render_path.ls(nout); + + nout << "\nInstancing del_path back under apples\n"; + NodePath new_del_path = del_path.instance_to(apples_path); + nout << "del_path = " << del_path << "\n" + << "new_del_path = " << new_del_path << "\n" + << "red_path = " << red_path << "\n" + << "del_path2 = " << del_path2 << "\n" + << "mc_path = " << mc_path << "\n" + << "apples_path = " << apples_path << "\n" + << "oranges_path = " << oranges_path << "\n"; + + render_path.ls(nout); + + nout << "\nRepairing del_path2, result is " + << del_path2.repair_connectivity(render_path) << "\n"; + nout << "del_path2 = " << del_path2 << "\n\n"; + + return (0); +} + diff --git a/panda/src/sgraph/Sources.pp b/panda/src/sgraph/Sources.pp new file mode 100644 index 0000000000..620a0fd5f0 --- /dev/null +++ b/panda/src/sgraph/Sources.pp @@ -0,0 +1,33 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET sgraph + #define LOCAL_LIBS \ + gobj putil graph mathutil linmath + + #define SOURCES \ + camera.I camera.cxx camera.h config_sgraph.cxx config_sgraph.h \ + geomNode.I geomNode.cxx geomNode.h geomTransformer.I \ + geomTransformer.cxx geomTransformer.h planeNode.I planeNode.cxx \ + planeNode.h projectionNode.I projectionNode.cxx projectionNode.h \ + renderTraverser.I renderTraverser.cxx renderTraverser.h + + #define INSTALL_HEADERS \ + camera.I camera.h geomNode.I geomNode.h geomTransformer.I \ + geomTransformer.h planeNode.I planeNode.h projectionNode.I \ + projectionNode.h renderTraverser.I renderTraverser.h + + #define IGATESCAN all + +#end lib_target + +#begin test_bin_target + #define TARGET test_sgraph + #define LOCAL_LIBS \ + sgraph mathutil graph putil + + #define SOURCES \ + test_sgraph.cxx + +#end test_bin_target + diff --git a/panda/src/sgraph/camera.I b/panda/src/sgraph/camera.I new file mode 100644 index 0000000000..9b438d4cbd --- /dev/null +++ b/panda/src/sgraph/camera.I @@ -0,0 +1,51 @@ +// Filename: camera.I +// Created by: drose (27Sep99) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: Camera::set_active +// Access: Public +// Description: Sets the active flag on the camera. When the camera +// is not active, nothing will be rendered. +//////////////////////////////////////////////////////////////////// +INLINE void Camera:: +set_active(bool active) { + _active = active; +} + +//////////////////////////////////////////////////////////////////// +// Function: Camera::is_active +// Access: Public +// Description: Returns the current setting of the active flag on the +// camera. +//////////////////////////////////////////////////////////////////// +INLINE bool Camera:: +is_active() const { + return _active; +} + +//////////////////////////////////////////////////////////////////// +// Function: Camera::set_scene +// Access: Public +// Description: Sets the scene that will be rendered by the camera. +// This is normally the root node of a scene graph, +// typically a node called 'render', although it could +// represent the root of any subgraph. +//////////////////////////////////////////////////////////////////// +INLINE void Camera:: +set_scene(Node *scene) { + _scene = scene; +} + +//////////////////////////////////////////////////////////////////// +// Function: Camera::get_scene +// Access: Public +// Description: Returns the scene that will be rendered by the +// camera. See set_scene(). +//////////////////////////////////////////////////////////////////// +INLINE Node *Camera:: +get_scene() const { + return _scene; +} diff --git a/panda/src/sgraph/camera.cxx b/panda/src/sgraph/camera.cxx new file mode 100644 index 0000000000..f7857b70c4 --- /dev/null +++ b/panda/src/sgraph/camera.cxx @@ -0,0 +1,345 @@ +// Filename: dCamera.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include +#include "camera.h" +#include +#include +#include +#include + +#include + + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle Camera::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: Camera::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Camera:: +Camera(const string &name) : + ProjectionNode(name), + _active(true) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: Camera::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Camera:: +Camera(const Camera ©) : + ProjectionNode(copy), + _active(copy._active), + _scene(copy._scene) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: Camera::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Camera:: +operator = (const Camera ©) { + ProjectionNode::operator = (copy); + _active = copy._active; + _scene = copy._scene; +} + +//////////////////////////////////////////////////////////////////// +// Function: Camera::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Node that is a shallow copy +// of this one. It will be a different Node pointer, +// but its internal data may or may not be shared with +// that of the original Node. +//////////////////////////////////////////////////////////////////// +Node *Camera:: +make_copy() const { + return new Camera(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: Camera::safe_to_flatten +// Access: Public, Virtual +// Description: Returns true if it is generally safe to flatten out +// this particular kind of Node by duplicating +// instances, false otherwise (for instance, a Camera +// cannot be safely flattened, because the Camera +// pointer itself is meaningful). +//////////////////////////////////////////////////////////////////// +bool Camera:: +safe_to_flatten() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: Camera::safe_to_transform +// Access: Public, Virtual +// Description: Returns true if it is generally safe to transform +// this particular kind of Node by calling the xform() +// method, false otherwise. For instance, it's usually +// a bad idea to attempt to xform a Character. +//////////////////////////////////////////////////////////////////// +bool Camera:: +safe_to_transform() const { + return false; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Camera::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Camera:: +~Camera(void) { + // We don't have to destroy the display regions associated with the + // camera; they're responsible for themselves. + + // We don't have to remove the camera from the global list, because + // it must have already been removed--otherwise we couldn't be + // deleting it! +} + +//////////////////////////////////////////////////////////////////// +// Function: Camera::get_num_drs +// Access: Public +// Description: Returns the number of display regions that share this +// camera. +//////////////////////////////////////////////////////////////////// +int Camera:: +get_num_drs() const { + return _display_regions.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Camera::get_dr +// Access: Public +// Description: Returns the nth display region that shares this +// camera. +//////////////////////////////////////////////////////////////////// +DisplayRegion *Camera:: +get_dr(int index) const { + nassertr(index >= 0 && index < get_num_drs(), NULL); + return _display_regions[index]; +} + +//////////////////////////////////////////////////////////////////// +// Function: Camera::is_in_view +// Access: Public +// Description: Returns true if the given point is within the bounds +// of the projection of the camera (i.e. if the camera +// can see the point). +//////////////////////////////////////////////////////////////////// +bool Camera:: +is_in_view(const LPoint3f &pos) { + BoundingVolume *bv = _projection->make_bounds(); + if (bv == NULL) + return false; + GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bv); + int ret = gbv->contains(pos); + delete bv; + return (ret != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: Camera::get_fov_near_far +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Camera:: +get_perspective_params(float &yfov, float &aspect, float &cnear, + float &cfar) const { + if (_projection->get_type() == PerspectiveProjection::get_class_type()) { + PerspectiveProjection *proj = DCAST(PerspectiveProjection, _projection); + proj->get_frustum().get_perspective_params(yfov, aspect, cnear, cfar); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Camera::get_fov_near_far +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Camera:: +get_perspective_params(float &xfov, float &yfov, float &aspect, float &cnear, + float &cfar) const { + if (_projection->get_type() == PerspectiveProjection::get_class_type()) { + PerspectiveProjection *proj = DCAST(PerspectiveProjection, _projection); + proj->get_frustum().get_perspective_params(xfov, yfov, aspect, cnear, + cfar); + } +} + +float Camera:: +get_hfov(void) const { + float xfov=0.0, yfov=0.0, aspect, cnear, cfar; + get_perspective_params(xfov, yfov, aspect, cnear, cfar); + return xfov; +} + +float Camera:: +get_vfov(void) const { + float xfov=0.0, yfov=0.0, aspect, cnear, cfar; + get_perspective_params(xfov, yfov, aspect, cnear, cfar); + return yfov; +} + +float Camera:: +get_aspect(void) const { + float yfov, aspect=0.0, cnear, cfar; + get_perspective_params(yfov, aspect, cnear, cfar); + return aspect; +} + +void Camera:: +get_near_far(float &cnear, float &cfar) const { + if (_projection->get_type() == PerspectiveProjection::get_class_type()) { + PerspectiveProjection *proj = DCAST(PerspectiveProjection, _projection); + cnear = proj->get_frustum()._fnear; + cfar = proj->get_frustum()._ffar; + } +} + +float Camera:: +get_near(void) const { + float cnear, cfar; + get_near_far(cnear, cfar); + return cnear; +} + +float Camera:: +get_far(void) const { + float cnear, cfar; + get_near_far(cnear, cfar); + return cfar; +} + +void Camera:: +set_fov(float hfov) { + if (_projection->get_type() == PerspectiveProjection::get_class_type()) { + PerspectiveProjection *proj = DCAST(PerspectiveProjection, _projection); + float tfov, ufov, aspect, cnear, cfar; + Frustumf frust = proj->get_frustum(); + frust.get_perspective_params(tfov, ufov, aspect, cnear, cfar); + frust.make_perspective_hfov(hfov, aspect, cnear, cfar); + proj->set_frustum(frust); + } +} + +void Camera:: +set_fov(float hfov, float vfov) { + if (_projection->get_type() == PerspectiveProjection::get_class_type()) { + PerspectiveProjection *proj = DCAST(PerspectiveProjection, _projection); + float tfov, ufov, aspect, cnear, cfar; + Frustumf frust = proj->get_frustum(); + frust.get_perspective_params(tfov, ufov, aspect, cnear, cfar); + frust.make_perspective(hfov, vfov, cnear, cfar); + proj->set_frustum(frust); + } +} + +void Camera:: +set_hfov(float hfov) { + if (_projection->get_type() == PerspectiveProjection::get_class_type()) { + PerspectiveProjection *proj = DCAST(PerspectiveProjection, _projection); + float tfov, ufov, aspect, cnear, cfar; + Frustumf frust = proj->get_frustum(); + frust.get_perspective_params(tfov, ufov, aspect, cnear, cfar); + frust.make_perspective(hfov, ufov, cnear, cfar); + proj->set_frustum(frust); + } +} + +void Camera:: +set_vfov(float vfov) { + if (_projection->get_type() == PerspectiveProjection::get_class_type()) { + PerspectiveProjection *proj = DCAST(PerspectiveProjection, _projection); + float tfov, ufov, aspect, cnear, cfar; + Frustumf frust = proj->get_frustum(); + frust.get_perspective_params(tfov, ufov, aspect, cnear, cfar); + frust.make_perspective(tfov, vfov, cnear, cfar); + proj->set_frustum(frust); + } +} + +void Camera:: +set_aspect(float aspect) { + if (_projection->get_type() == PerspectiveProjection::get_class_type()) { + PerspectiveProjection *proj = DCAST(PerspectiveProjection, _projection); + float xfov, yfov, taspect, cnear, cfar; + Frustumf frust = proj->get_frustum(); + frust.get_perspective_params(xfov, yfov, taspect, cnear, cfar); + // I don't know what to do when the aspect changes. I have arbitrarilly + // decided to preserved the horizontal FoV. The vertical will be + // recomputed based on the new aspect and the horizontal. + frust.make_perspective_hfov(xfov, aspect, cnear, cfar); + proj->set_frustum(frust); + } +} + +void Camera:: +set_near_far(float cnear, float cfar) { + if (_projection->get_type() == PerspectiveProjection::get_class_type()) { + PerspectiveProjection *proj = DCAST(PerspectiveProjection, _projection); + float xfov, yfov, aspect, tnear, tfar; + Frustumf frust = proj->get_frustum(); + frust.get_perspective_params(xfov, yfov, aspect, tnear, tfar); + frust.make_perspective(xfov, yfov, cnear, cfar); + proj->set_frustum(frust); + } +} + +void Camera:: +set_near(float cnear) { + float cfar = get_far(); + set_near_far(cnear, cfar); +} + +void Camera:: +set_far(float cfar) { + float cnear = get_near(); + set_near_far(cnear, cfar); +} + +//////////////////////////////////////////////////////////////////// +// Function: Camera::add_display_region +// Access: Private +// Description: Adds the indicated DisplayRegion to the set of +// DisplayRegions shared by the camera. This is only +// intended to be called from the DisplayRegion. +//////////////////////////////////////////////////////////////////// +void Camera:: +add_display_region(DisplayRegion *display_region) { + _display_regions.push_back(display_region); +} + +//////////////////////////////////////////////////////////////////// +// Function: Camera::remove_display_region +// Access: Private +// Description: Removes the indicated DisplayRegion from the set of +// DisplayRegions shared by the camera. This is only +// intended to be called from the DisplayRegion. +//////////////////////////////////////////////////////////////////// +void Camera:: +remove_display_region(DisplayRegion *display_region) { + DisplayRegions::iterator dri = + find(_display_regions.begin(), _display_regions.end(), display_region); + if (dri != _display_regions.end()) { + _display_regions.erase(dri); + } +} diff --git a/panda/src/sgraph/camera.h b/panda/src/sgraph/camera.h new file mode 100644 index 0000000000..15d23e7045 --- /dev/null +++ b/panda/src/sgraph/camera.h @@ -0,0 +1,105 @@ +// Filename: camera.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef CAMERA_H +#define CAMERA_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "projectionNode.h" + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +class DisplayRegion; + +//////////////////////////////////////////////////////////////////// +// Class : Camera +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Camera : public ProjectionNode { +public: + Camera(const string &name = ""); + Camera(const Camera ©); + void operator = (const Camera ©); + virtual ~Camera(); + + virtual Node *make_copy() const; + virtual bool safe_to_flatten() const; + virtual bool safe_to_transform() const; + + //virtual void output(ostream &out) const; + virtual void config() { } + + INLINE void set_active(bool active); + INLINE bool is_active() const; + + INLINE void set_scene(Node *scene); + INLINE Node *get_scene() const; + + int get_num_drs() const; + DisplayRegion *get_dr(int index) const; + + bool is_in_view(const LPoint3f &pos); + + void get_perspective_params(float &yfov, float &aspect, float &cnear, + float &cfar) const; + void get_perspective_params(float &xfov, float &yfov, float &aspect, + float &cnear, float &cfar) const; + float get_hfov(void) const; + float get_vfov(void) const; + void set_fov(float hfov); + void set_fov(float hfov, float vfov); + void set_hfov(float hfov); + void set_vfov(float vfov); + float get_aspect(void) const; + void set_aspect(float aspect); + void get_near_far(float &cnear, float &cfar) const; + void set_near_far(float cnear, float cfar); + float get_near(void) const; + void set_near(float cnear); + float get_far(void) const; + void set_far(float cfar); + +private: + void add_display_region(DisplayRegion *display_region); + void remove_display_region(DisplayRegion *display_region); + + bool _active; + PT_Node _scene; + + typedef vector DisplayRegions; + DisplayRegions _display_regions; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ProjectionNode::init_type(); + register_type(_type_handle, "Camera", + ProjectionNode::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + +friend class DisplayRegion; +}; + +#include "camera.I" + +#endif diff --git a/panda/src/sgraph/config_sgraph.cxx b/panda/src/sgraph/config_sgraph.cxx new file mode 100644 index 0000000000..f561b6d52e --- /dev/null +++ b/panda/src/sgraph/config_sgraph.cxx @@ -0,0 +1,28 @@ +// Filename: config_sgraph.cxx +// Created by: drose (12Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_sgraph.h" +#include "renderTraverser.h" +#include "geomNode.h" +#include "camera.h" +#include "planeNode.h" +#include "projectionNode.h" + +#include + +Configure(config_sgraph); +NotifyCategoryDef(sgraph, ""); + +ConfigureFn(config_sgraph) { + RenderTraverser::init_type(); + GeomNode::init_type(); + Camera::init_type(); + PlaneNode::init_type(); + ProjectionNode::init_type(); + + //Registration of writeable object's creation + //functions with BamReader's factory + GeomNode::register_with_read_factory(); +} diff --git a/panda/src/sgraph/config_sgraph.h b/panda/src/sgraph/config_sgraph.h new file mode 100644 index 0000000000..cf065da0c4 --- /dev/null +++ b/panda/src/sgraph/config_sgraph.h @@ -0,0 +1,14 @@ +// Filename: config_sgraph.h +// Created by: drose (12Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_SGRAPH_H +#define CONFIG_SGRAPH_H + +#include +#include + +NotifyCategoryDecl(sgraph, EXPCL_PANDA, EXPTP_PANDA); + +#endif diff --git a/panda/src/sgraph/geomNode.I b/panda/src/sgraph/geomNode.I new file mode 100644 index 0000000000..d30a0c1017 --- /dev/null +++ b/panda/src/sgraph/geomNode.I @@ -0,0 +1,4 @@ +// Filename: geomNode.I +// Created by: drose (16Apr00) +// +//////////////////////////////////////////////////////////////////// diff --git a/panda/src/sgraph/geomNode.cxx b/panda/src/sgraph/geomNode.cxx new file mode 100644 index 0000000000..92174a3011 --- /dev/null +++ b/panda/src/sgraph/geomNode.cxx @@ -0,0 +1,333 @@ +// Filename: geomNode.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "geomNode.h" +#include "geomTransformer.h" + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle GeomNode::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GeomNode:: +GeomNode(const string &name) : NamedNode(name), _num_geoms(0) { +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GeomNode:: +GeomNode(const GeomNode ©) : + NamedNode(copy), + _geoms(copy._geoms), + _num_geoms(0) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void GeomNode:: +operator = (const GeomNode ©) { + NamedNode::operator = (copy); + _geoms = copy._geoms; + _num_geoms = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +GeomNode:: +~GeomNode() { +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Node that is a shallow copy +// of this one. It will be a different Node pointer, +// but its internal data may or may not be shared with +// that of the original Node. +//////////////////////////////////////////////////////////////////// +Node *GeomNode:: +make_copy() const { + return new GeomNode(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::xform +// Access: Public, Virtual +// Description: Transforms the contents of this node by the indicated +// matrix, if it means anything to do so. For most +// kinds of nodes, this does nothing. +// +// For a GeomNode, this does the right thing, but it is +// better to use a GeomTransformer instead, since it +// will share the new arrays properly between different +// GeomNodes. +//////////////////////////////////////////////////////////////////// +void GeomNode:: +xform(const LMatrix4f &mat) { + GeomTransformer transformer; + transformer.transform_vertices(this, mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void GeomNode:: +write(ostream &out, int indent_level) const { + Geoms::const_iterator gi; + for (gi = _geoms.begin(); gi != _geoms.end(); ++gi) { + dDrawable *drawable = (*gi); + if (drawable->is_of_type(Geom::get_class_type())) { + indent(out, indent_level) << *DCAST(Geom, drawable) << "\n"; + } else { + indent(out, indent_level) << drawable->get_type() << "\n"; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::write_verbose +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void GeomNode:: +write_verbose(ostream &out) const { + Geoms::const_iterator gi; + for (gi = _geoms.begin(); gi != _geoms.end(); ++gi) { + dDrawable *drawable = (*gi); + if (drawable->is_of_type(Geom::get_class_type())) { + DCAST(Geom, drawable)->output_verbose(out); + } else { + out << drawable->get_type() << "\n"; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::draw +// Access: Public +// Description: Draws all the geometry belonging to the geom node, +// in the gsg's current state. +//////////////////////////////////////////////////////////////////// +void GeomNode:: +draw(GraphicsStateGuardianBase *gsg) { + Geoms::const_iterator gi; + for (gi = _geoms.begin(); gi != _geoms.end(); ++gi) { + (*gi)->draw(gsg); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::get_num_geoms +// Access: Public +// Description: Returns the number of geoms stored in the node. +//////////////////////////////////////////////////////////////////// +int GeomNode:: +get_num_geoms() const { + return _geoms.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::get_geom +// Access: Public +// Description: Returns a pointer to the nth Geom (actually, +// Drawable) stored in the node. +//////////////////////////////////////////////////////////////////// +dDrawable *GeomNode:: +get_geom(int n) const { + nassertr(n >= 0 && n < get_num_geoms(), (dDrawable *)NULL); + return _geoms[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::remove_geom +// Access: Public +// Description: Removes the nth Geom from the node. All subsequent +// index numbers are shifted down by one. +//////////////////////////////////////////////////////////////////// +void GeomNode:: +remove_geom(int n) { + nassertv(n >= 0 && n < get_num_geoms()); + _geoms.erase(_geoms.begin() + n); + mark_bound_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::clear +// Access: Public +// Description: Removes all the Geoms from the node. +//////////////////////////////////////////////////////////////////// +void GeomNode:: +clear() { + _geoms.clear(); + mark_bound_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::add_geom +// Access: Public +// Description: Adds a new Geom to the end of the list. Returns the +// new Geom's index number within the node. +//////////////////////////////////////////////////////////////////// +int GeomNode:: +add_geom(dDrawable *geom) { + _geoms.push_back(geom); + mark_bound_stale(); + return _geoms.size() - 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::add_geoms_from +// Access: Public +// Description: Adds all the Geoms in the indicated GeomNode to the +// end of the list. +//////////////////////////////////////////////////////////////////// +void GeomNode:: +add_geoms_from(const GeomNode *other) { + _geoms.insert(_geoms.end(), other->_geoms.begin(), other->_geoms.end()); + mark_bound_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::recompute_bound +// Access: Protected, Virtual +// Description: Recomputes the dynamic bounding volume for this node. +//////////////////////////////////////////////////////////////////// +void GeomNode:: +recompute_bound() { + // First, get ourselves a fresh, empty bounding volume. + BoundedObject::recompute_bound(); + assert(_bound != (BoundingVolume *)NULL); + + // Now actually compute the bounding volume by putting it around all + // of our drawable's bounding volumes. + vector child_volumes; + + Geoms::const_iterator gi; + for (gi = _geoms.begin(); gi != _geoms.end(); ++gi) { + child_volumes.push_back(&(*gi)->get_bound()); + } + + _bound->around(child_volumes.begin(), child_volumes.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::write_datagram +// Access: Public +// Description: Function to write the important information in +// the particular object to a Datagram +//////////////////////////////////////////////////////////////////// +void GeomNode:: +write_datagram(BamWriter *manager, Datagram &me) +{ + NamedNode::write_datagram(manager, me); + //Write out all of the Geom objects that this node + //stores + me.add_uint16(_geoms.size()); + for(int i = 0; i < _geoms.size(); i++) + { + manager->write_pointer(me, _geoms[i]); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::complete_pointers +// Access: Public +// Description: Takes in a vector of pointes to TypedWriteable +// objects that correspond to all the requests for +// pointers that this object made to BamReader. +//////////////////////////////////////////////////////////////////// +int GeomNode:: +complete_pointers(vector_typedWriteable &plist, BamReader* manager) +{ + int start = NamedNode::complete_pointers(plist, manager); + for(int i = start; i < _num_geoms+start; i++) + { + PT(dDrawable) temp = DCAST(dDrawable, plist[i]); + _geoms.push_back(temp); + } + return start+_num_geoms; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::make_GeomNode +// Access: Protected +// Description: Factory method to generate a GeomNode object +//////////////////////////////////////////////////////////////////// +TypedWriteable* GeomNode:: +make_GeomNode(const FactoryParams ¶ms) +{ + GeomNode *me = new GeomNode; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::fillin +// Access: Protected +// Description: Function that reads out of the datagram (or asks +// manager to read) all of the data that is needed to +// re-create this object and stores it in the appropiate +// place +//////////////////////////////////////////////////////////////////// +void GeomNode:: +fillin(DatagramIterator& scan, BamReader* manager) +{ + NamedNode::fillin(scan, manager); + _num_geoms = scan.get_uint16(); + for(int i = 0; i < _num_geoms; i++) + { + manager->read_pointer(scan, this); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a GeomNode object +//////////////////////////////////////////////////////////////////// +void GeomNode:: +register_with_read_factory(void) +{ + BamReader::get_factory()->register_factory(get_class_type(), make_GeomNode); +} + + + + + + + diff --git a/panda/src/sgraph/geomNode.h b/panda/src/sgraph/geomNode.h new file mode 100644 index 0000000000..92221d05d6 --- /dev/null +++ b/panda/src/sgraph/geomNode.h @@ -0,0 +1,94 @@ +// Filename: geomNode.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef GEOMNODE_H +#define GEOMNODE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include +#include + +class GraphicsStateGuardianBase; +class AllAttributesWrapper; + +//////////////////////////////////////////////////////////////////// +// Class : GeomNode +// Description : Scene graph node that holds drawable geometry +// elements +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomNode : public NamedNode { +public: + GeomNode(const string &name = ""); + GeomNode(const GeomNode ©); + void operator = (const GeomNode ©); + virtual ~GeomNode(); + + virtual Node *make_copy() const; + virtual void xform(const LMatrix4f &mat); + + void write(ostream &out, int indent_level = 0) const; + void write_verbose(ostream &out) const; + + void draw(GraphicsStateGuardianBase *gsg); + + int get_num_geoms() const; + dDrawable *get_geom(int n) const; + void remove_geom(int n); + void clear(); + int add_geom(dDrawable *geom); + void add_geoms_from(const GeomNode *other); + +protected: + virtual void recompute_bound(); + +private: + typedef PTA(PT(dDrawable)) Geoms; + Geoms _geoms; + +public: + static void register_with_read_factory(void); + virtual void write_datagram(BamWriter* manager, Datagram &me); + virtual int complete_pointers(vector_typedWriteable &plist, + BamReader *manager); + + static TypedWriteable *make_GeomNode(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator& scan, BamReader* manager); + +private: + //This value is only used for the process of re-construction + //from a binary source. DO NOT ACCESS. The value is only + //guaranteed to be accurate during that process + int _num_geoms; + +public: + static TypeHandle get_class_type(void) { + return _type_handle; + } + static void init_type(void) { + NamedNode::init_type(); + register_type(_type_handle, "GeomNode", + NamedNode::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + friend class GeomTransformer; +}; + +#include "geomNode.I" + +#endif diff --git a/panda/src/sgraph/geomTransformer.I b/panda/src/sgraph/geomTransformer.I new file mode 100644 index 0000000000..217c896faa --- /dev/null +++ b/panda/src/sgraph/geomTransformer.I @@ -0,0 +1,44 @@ +// Filename: geomTransformer.I +// Created by: drose (23May00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::SourceVertices::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool GeomTransformer::SourceVertices:: +operator < (const GeomTransformer::SourceVertices &other) const { + if (_coords != other._coords) { + return _coords < other._coords; + } + return (_mat.compare_to(other._mat) < 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::SourceNormals::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool GeomTransformer::SourceNormals:: +operator < (const GeomTransformer::SourceNormals &other) const { + if (_norms != other._norms) { + return _norms < other._norms; + } + return (_mat.compare_to(other._mat) < 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::SourceTexCoords::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool GeomTransformer::SourceTexCoords:: +operator < (const GeomTransformer::SourceTexCoords &other) const { + if (_texcoords != other._texcoords) { + return _texcoords < other._texcoords; + } + return (_mat.compare_to(other._mat) < 0); +} diff --git a/panda/src/sgraph/geomTransformer.cxx b/panda/src/sgraph/geomTransformer.cxx new file mode 100644 index 0000000000..c35d3fbd3e --- /dev/null +++ b/panda/src/sgraph/geomTransformer.cxx @@ -0,0 +1,298 @@ +// Filename: geomTransformer.cxx +// Created by: drose (23May00) +// +//////////////////////////////////////////////////////////////////// + +#include "geomTransformer.h" +#include "geomNode.h" + + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GeomTransformer:: +GeomTransformer() { +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GeomTransformer:: +~GeomTransformer() { +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::transform_vertices +// Access: Public +// Description: Transforms the vertices and the normals in the +// indicated Geom by the indicated matrix. Returns true +// if the Geom was changed, false otherwise. +//////////////////////////////////////////////////////////////////// +bool GeomTransformer:: +transform_vertices(Geom *geom, const LMatrix4f &mat) { + bool transformed = false; + + nassertr(geom != (Geom *)NULL, false); + + PTA_Vertexf coords; + GeomBindType bind; + PTA_ushort index; + + geom->get_coords(coords, bind, index); + + if (bind != G_OFF) { + // Look up the Geom's coords in our table--have we already + // transformed this array? + SourceVertices sv; + sv._mat = mat; + sv._coords = coords; + + PTA_Vertexf &new_coords = _vertices[sv]; + + if (new_coords.is_null()) { + // We have not transformed the array yet. Do so now. + new_coords.reserve(coords.size()); + PTA_Vertexf::const_iterator vi; + for (vi = coords.begin(); vi != coords.end(); ++vi) { + new_coords.push_back((*vi) * mat); + } + nassertr(new_coords.size() == coords.size(), false); + } + + geom->set_coords(new_coords, bind, index); + transformed = true; + } + + // Now do the same thing for normals. + PTA_Normalf norms; + + geom->get_normals(norms, bind, index); + + if (bind != G_OFF) { + SourceNormals sn; + sn._mat = mat; + sn._norms = norms; + + PTA_Normalf &new_norms = _normals[sn]; + + if (new_norms.is_null()) { + // We have not transformed the array yet. Do so now. + new_norms.reserve(norms.size()); + PTA_Normalf::const_iterator ni; + for (ni = norms.begin(); ni != norms.end(); ++ni) { + new_norms.push_back((*ni) * mat); + } + nassertr(new_norms.size() == norms.size(), false); + } + + geom->set_normals(new_norms, bind, index); + transformed = true; + } + + return transformed; +} + + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::transform_vertices +// Access: Public +// Description: Transforms the vertices and the normals in all of the +// Geoms within the indicated GeomNode by the indicated +// matrix. Does not destructively change Geoms; +// instead, a copy will be made of each Geom to be +// changed, in case multiple GeomNodes reference the +// same Geom. Returns true if the GeomNode was changed, +// false otherwise. +//////////////////////////////////////////////////////////////////// +bool GeomTransformer:: +transform_vertices(GeomNode *node, const LMatrix4f &mat) { + bool any_changed = false; + + GeomNode::Geoms new_geoms; + + GeomNode::Geoms::const_iterator gi; + for (gi = node->_geoms.begin(); gi != node->_geoms.end(); ++gi) { + dDrawable *drawable = (*gi); + if (drawable->is_of_type(Geom::get_class_type())) { + Geom *geom = DCAST(Geom, drawable); + PT(Geom) new_geom = geom->make_copy(); + if (transform_vertices(new_geom, mat)) { + new_geoms.push_back(new_geom.p()); + any_changed = true; + } else { + new_geoms.push_back(geom); + } + } else { + new_geoms.push_back(drawable); + } + } + + if (any_changed) { + node->_geoms = new_geoms; + node->mark_bound_stale(); + return true; + } + + return false; +} + + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::transform_texcoords +// Access: Public +// Description: Transforms the texture coordinates in the indicated +// Geom by the indicated matrix. Returns true if the +// Geom was changed, false otherwise. +//////////////////////////////////////////////////////////////////// +bool GeomTransformer:: +transform_texcoords(Geom *geom, const LMatrix4f &mat) { + bool transformed = false; + + nassertr(geom != (Geom *)NULL, false); + + PTA_TexCoordf texcoords; + GeomBindType bind; + PTA_ushort index; + + geom->get_texcoords(texcoords, bind, index); + + if (bind != G_OFF) { + // Look up the Geom's texcoords in our table--have we already + // transformed this array? + SourceTexCoords stc; + stc._mat = mat; + stc._texcoords = texcoords; + + PTA_TexCoordf &new_texcoords = _texcoords[stc]; + + if (new_texcoords.is_null()) { + // We have not transformed the array yet. Do so now. + new_texcoords.reserve(texcoords.size()); + PTA_TexCoordf::const_iterator tci; + for (tci = texcoords.begin(); tci != texcoords.end(); ++tci) { + const TexCoordf &tc = (*tci); + LVecBase4f v4(tc[0], tc[1], 0.0, 1.0); + v4 = v4 * mat; + new_texcoords.push_back(TexCoordf(v4[0] / v4[3], v4[1] / v4[3])); + } + nassertr(new_texcoords.size() == texcoords.size(), false); + } + + geom->set_texcoords(new_texcoords, bind, index); + transformed = true; + } + + return transformed; +} + + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::transform_texcoords +// Access: Public +// Description: Transforms the texcoords and the normals in all of the +// Geoms within the indicated GeomNode by the indicated +// matrix. Does not destructively change Geoms; +// instead, a copy will be made of each Geom to be +// changed, in case multiple GeomNodes reference the +// same Geom. Returns true if the GeomNode was changed, +// false otherwise. +//////////////////////////////////////////////////////////////////// +bool GeomTransformer:: +transform_texcoords(GeomNode *node, const LMatrix4f &mat) { + bool any_changed = false; + + GeomNode::Geoms new_geoms; + + GeomNode::Geoms::const_iterator gi; + for (gi = node->_geoms.begin(); gi != node->_geoms.end(); ++gi) { + dDrawable *drawable = (*gi); + if (drawable->is_of_type(Geom::get_class_type())) { + Geom *geom = DCAST(Geom, drawable); + PT(Geom) new_geom = geom->make_copy(); + if (transform_texcoords(new_geom, mat)) { + new_geoms.push_back(new_geom.p()); + any_changed = true; + } else { + new_geoms.push_back(geom); + } + } else { + new_geoms.push_back(drawable); + } + } + + if (any_changed) { + node->_geoms = new_geoms; + return true; + } + + return false; +} + + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::set_color +// Access: Public +// Description: Overrides the color indicated within the Geom with +// the given replacement color. Returns true if the +// Geom was changed, false otherwise. +//////////////////////////////////////////////////////////////////// +bool GeomTransformer:: +set_color(Geom *geom, const Colorf &color) { + // In this case, we always replace whatever color array was there + // with a new color array containing just this color. + + // We do want to share this one-element array between Geoms, though. + PTA_Colorf &new_colors = _colors[color]; + + if (new_colors.is_null()) { + // We haven't seen this color before; define a new color array. + new_colors.push_back(color); + } + + geom->set_colors(new_colors, G_OVERALL); + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::transform_texcoords +// Access: Public +// Description: Overrides the color indicated within the GeomNode +// with the given replacement color. Returns true if +// any Geom in the GeomNode was changed, false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool GeomTransformer:: +set_color(GeomNode *node, const Colorf &color) { + bool any_changed = false; + + GeomNode::Geoms new_geoms; + + GeomNode::Geoms::const_iterator gi; + for (gi = node->_geoms.begin(); gi != node->_geoms.end(); ++gi) { + dDrawable *drawable = (*gi); + if (drawable->is_of_type(Geom::get_class_type())) { + Geom *geom = DCAST(Geom, drawable); + PT(Geom) new_geom = geom->make_copy(); + if (set_color(new_geom, color)) { + new_geoms.push_back(new_geom.p()); + any_changed = true; + } else { + new_geoms.push_back(geom); + } + } else { + new_geoms.push_back(drawable); + } + } + + if (any_changed) { + node->_geoms = new_geoms; + return true; + } + + return false; +} diff --git a/panda/src/sgraph/geomTransformer.h b/panda/src/sgraph/geomTransformer.h new file mode 100644 index 0000000000..7bf51a6c1f --- /dev/null +++ b/panda/src/sgraph/geomTransformer.h @@ -0,0 +1,84 @@ +// Filename: geomTransformer.h +// Created by: drose (23May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMTRANSFORMER_H +#define GEOMTRANSFORMER_H + +#include + +#include +#include + +class GeomNode; + +/////////////////////////////////////////////////////////////////// +// Class : GeomTransformer +// Description : An object specifically designed to transform the +// vertices of a Geom without disturbing indexing or +// affecting any other Geoms that may share the same +// vertex arrays, and without needlessly wasting memory +// when different Geoms sharing the same vertex arrays +// are transformed by the same amount. +// +// If you create a single GeomTransformer and use it to +// transform a number of different Geoms by various +// transformations, then those Geoms which happen to +// share the same arrays and are transformed by the same +// amounts will still share the same arrays as each +// other (but different from the original arrays). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomTransformer { +public: + GeomTransformer(); + ~GeomTransformer(); + + bool transform_vertices(Geom *geom, const LMatrix4f &mat); + bool transform_vertices(GeomNode *node, const LMatrix4f &mat); + + bool transform_texcoords(Geom *geom, const LMatrix4f &mat); + bool transform_texcoords(GeomNode *node, const LMatrix4f &mat); + + bool set_color(Geom *geom, const Colorf &color); + bool set_color(GeomNode *node, const Colorf &color); + +private: + class SourceVertices { + public: + INLINE bool operator < (const SourceVertices &other) const; + + LMatrix4f _mat; + PTA_Vertexf _coords; + }; + typedef map Vertices; + Vertices _vertices; + + class SourceNormals { + public: + INLINE bool operator < (const SourceNormals &other) const; + + LMatrix4f _mat; + PTA_Normalf _norms; + }; + typedef map Normals; + Normals _normals; + + class SourceTexCoords { + public: + INLINE bool operator < (const SourceTexCoords &other) const; + + LMatrix4f _mat; + PTA_TexCoordf _texcoords; + }; + typedef map TexCoords; + TexCoords _texcoords; + + typedef map Colors; + Colors _colors; +}; + +#include "geomTransformer.I" + +#endif + diff --git a/panda/src/sgraph/planeNode.I b/panda/src/sgraph/planeNode.I new file mode 100644 index 0000000000..4ac74156dc --- /dev/null +++ b/panda/src/sgraph/planeNode.I @@ -0,0 +1,38 @@ +// Filename: planeNode.I +// Created by: drose (23May00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: PlaneNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PlaneNode:: +PlaneNode(const string &name) : + NamedNode(name) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PlaneNode::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PlaneNode:: +PlaneNode(const PlaneNode ©) : + NamedNode(copy), + _plane(copy._plane) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PlaneNode::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PlaneNode:: +operator = (const PlaneNode ©) { + NamedNode::operator = (copy); + _plane = copy._plane; +} diff --git a/panda/src/sgraph/planeNode.cxx b/panda/src/sgraph/planeNode.cxx new file mode 100644 index 0000000000..b50708f9f7 --- /dev/null +++ b/panda/src/sgraph/planeNode.cxx @@ -0,0 +1,48 @@ +// Filename: planeNode.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "planeNode.h" + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle PlaneNode::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: PlaneNode::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Node that is a shallow copy +// of this one. It will be a different Node pointer, +// but its internal data may or may not be shared with +// that of the original Node. +//////////////////////////////////////////////////////////////////// +Node *PlaneNode:: +make_copy() const { + return new PlaneNode(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: set_plane +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void PlaneNode::set_plane(const Planef& plane) +{ + _plane = plane; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_plane +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +const Planef& PlaneNode::get_plane(void) const +{ + return _plane; +} diff --git a/panda/src/sgraph/planeNode.h b/panda/src/sgraph/planeNode.h new file mode 100644 index 0000000000..ba986ddc1e --- /dev/null +++ b/panda/src/sgraph/planeNode.h @@ -0,0 +1,63 @@ +// Filename: planeNode.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef PLANENODE_H +#define PLANENODE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : PlaneNode +// Description : A node that has a plane +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PlaneNode : public NamedNode +{ +public: + INLINE PlaneNode(const string& name = ""); + INLINE PlaneNode(const PlaneNode ©); + INLINE void operator = (const PlaneNode ©); + + virtual Node *make_copy() const; + + void set_plane(const Planef& plane); + const Planef& get_plane(void) const; + +protected: + + Planef _plane; + +public: + + static TypeHandle get_class_type( void ) { + return _type_handle; + } + static void init_type(void) { + NamedNode::init_type(); + register_type(_type_handle, "PlaneNode", + NamedNode::get_class_type()); + } + virtual TypeHandle get_type(void) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + + static TypeHandle _type_handle; +}; + +#include "planeNode.I" + +#endif diff --git a/panda/src/sgraph/projectionNode.I b/panda/src/sgraph/projectionNode.I new file mode 100644 index 0000000000..925ce2c213 --- /dev/null +++ b/panda/src/sgraph/projectionNode.I @@ -0,0 +1,38 @@ +// Filename: projectionNode.I +// Created by: drose (23May00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: ProjectionNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ProjectionNode:: +ProjectionNode(const string &name) : + NamedNode(name) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ProjectionNode::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ProjectionNode:: +ProjectionNode(const ProjectionNode ©) : + NamedNode(copy), + _projection(copy._projection) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ProjectionNode::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ProjectionNode:: +operator = (const ProjectionNode ©) { + NamedNode::operator = (copy); + _projection = copy._projection; +} diff --git a/panda/src/sgraph/projectionNode.cxx b/panda/src/sgraph/projectionNode.cxx new file mode 100644 index 0000000000..e839690bbd --- /dev/null +++ b/panda/src/sgraph/projectionNode.cxx @@ -0,0 +1,55 @@ +// Filename: projectionNode.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "projectionNode.h" +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle ProjectionNode::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ProjectionNode::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Node that is a shallow copy +// of this one. It will be a different Node pointer, +// but its internal data may or may not be shared with +// that of the original Node. +//////////////////////////////////////////////////////////////////// +Node *ProjectionNode:: +make_copy() const { + return new ProjectionNode(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: set_projection +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void ProjectionNode::set_projection( const Projection& projection ) +{ + _projection = projection.make_copy(); +} + +//////////////////////////////////////////////////////////////////// +// Function: get_projection +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +const Projection* ProjectionNode::get_projection( void ) const +{ + if (_projection == NULL) { + // If we have no projection yet, just return a default projection. + Frustumf f; + static PerspectiveProjection default_projection(f); + return &default_projection; + } + return _projection; +} diff --git a/panda/src/sgraph/projectionNode.h b/panda/src/sgraph/projectionNode.h new file mode 100644 index 0000000000..4913ff067e --- /dev/null +++ b/panda/src/sgraph/projectionNode.h @@ -0,0 +1,62 @@ +// Filename: projectionNode.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef PROJECTIONNODE_H +#define PROJECTIONNODE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : ProjectionNode +// Description : A node that has a frustum +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ProjectionNode : public NamedNode { +public: + INLINE ProjectionNode(const string& name = ""); + INLINE ProjectionNode(const ProjectionNode ©); + INLINE void operator = (const ProjectionNode ©); + + virtual Node *make_copy() const; + + void set_projection( const Projection& projection ); + const Projection* get_projection( void ) const; + +protected: + + PT(Projection) _projection; + +public: + + static TypeHandle get_class_type( void ) { + return _type_handle; + } + static void init_type( void ) { + NamedNode::init_type(); + register_type( _type_handle, "ProjectionNode", + NamedNode::get_class_type() ); + } + virtual TypeHandle get_type( void ) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + + static TypeHandle _type_handle; +}; + +#include "projectionNode.I" + +#endif diff --git a/panda/src/sgraph/renderTraverser.I b/panda/src/sgraph/renderTraverser.I new file mode 100644 index 0000000000..588ac6feb6 --- /dev/null +++ b/panda/src/sgraph/renderTraverser.I @@ -0,0 +1,37 @@ +// Filename: renderTraverser.I +// Created by: drose (12Apr00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: RenderTraverser::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE RenderTraverser:: +RenderTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type) : + _gsg(gsg), + _graph_type(graph_type) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderTraverser::get_gsg +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GraphicsStateGuardian *RenderTraverser:: +get_gsg() const { + return _gsg; +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderTraverser::get_graph_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypeHandle RenderTraverser:: +get_graph_type() const { + return _graph_type; +} diff --git a/panda/src/sgraph/renderTraverser.cxx b/panda/src/sgraph/renderTraverser.cxx new file mode 100644 index 0000000000..10604bffac --- /dev/null +++ b/panda/src/sgraph/renderTraverser.cxx @@ -0,0 +1,31 @@ +// Filename: renderTraverser.cxx +// Created by: drose (12Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "renderTraverser.h" + +#include + +TypeHandle RenderTraverser::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: RenderTraverser::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RenderTraverser:: +output(ostream &out) const { + out << get_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderTraverser::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void RenderTraverser:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << *this << "\n"; +} diff --git a/panda/src/sgraph/renderTraverser.h b/panda/src/sgraph/renderTraverser.h new file mode 100644 index 0000000000..1aa2992824 --- /dev/null +++ b/panda/src/sgraph/renderTraverser.h @@ -0,0 +1,70 @@ +// Filename: renderTraverser.h +// Created by: drose (12Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef RENDERTRAVERSER_H +#define RENDERTRAVERSER_H + +#include + +#include +#include + +class GraphicsStateGuardian; +class Node; +class AllAttributesWrapper; +class AllTransitionsWrapper; + +//////////////////////////////////////////////////////////////////// +// Class : RenderTraverser +// Description : This is the abstract base class that defines the +// interface for any number of different kinds of +// specialized traversers that walk over the scene graph +// and render it with a given GraphicsStateGuardian, +// such as DirectRenderTraverser and CullTraverser. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA RenderTraverser : public TypedReferenceCount { +public: + INLINE RenderTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type); + + INLINE GraphicsStateGuardian *get_gsg() const; + INLINE TypeHandle get_graph_type() const; + + virtual void traverse(Node *root, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans)=0; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level = 0) const; + +protected: + GraphicsStateGuardian *_gsg; + TypeHandle _graph_type; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "RenderTraverser", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +INLINE ostream &operator << (ostream &out, const RenderTraverser &rt) { + rt.output(out); + return out; +} + +#include "renderTraverser.I" + +#endif diff --git a/panda/src/sgraph/test_sgraph.cxx b/panda/src/sgraph/test_sgraph.cxx new file mode 100644 index 0000000000..23965d5060 --- /dev/null +++ b/panda/src/sgraph/test_sgraph.cxx @@ -0,0 +1,14 @@ +// Filename: test_sgraph.cxx +// Created by: mike (02Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "camera.h" + +#include + +int main() { + nout << "running test_sgraph" << endl; + PT(Camera) cam = new Camera("camera"); + return 0; +} diff --git a/panda/src/sgraphutil/Sources.pp b/panda/src/sgraphutil/Sources.pp new file mode 100644 index 0000000000..9930d8c901 --- /dev/null +++ b/panda/src/sgraphutil/Sources.pp @@ -0,0 +1,27 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET sgraphutil + #define LOCAL_LIBS \ + graph sgraph sgattrib linmath putil gobj mathutil gsgbase display \ + pnmimage + + #define SOURCES \ + appTraverser.I appTraverser.cxx appTraverser.h \ + config_sgraphutil.cxx config_sgraphutil.h directRenderTraverser.I \ + directRenderTraverser.cxx directRenderTraverser.h get_rel_pos.I \ + get_rel_pos.cxx get_rel_pos.h sceneGraphAnalyzer.cxx \ + sceneGraphAnalyzer.h sceneGraphReducer.I sceneGraphReducer.cxx \ + sceneGraphReducer.h + + #define INSTALL_HEADERS \ + appTraverser.I appTraverser.h config_sgraphutil.h \ + directRenderLevelState.h directRenderTraverser.I \ + directRenderTraverser.h frustumCullTraverser.I \ + frustumCullTraverser.h get_rel_pos.I get_rel_pos.h \ + sceneGraphAnalyzer.h sceneGraphReducer.I sceneGraphReducer.h + + #define IGATESCAN all + +#end lib_target + diff --git a/panda/src/sgraphutil/appTraverser.I b/panda/src/sgraphutil/appTraverser.I new file mode 100644 index 0000000000..c0e497fa83 --- /dev/null +++ b/panda/src/sgraphutil/appTraverser.I @@ -0,0 +1,16 @@ +// Filename: appTraverser.I +// Created by: drose (25Apr00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: AppTraverser::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AppTraverser:: +AppTraverser(TypeHandle graph_type) : + _graph_type(graph_type) +{ +} diff --git a/panda/src/sgraphutil/appTraverser.cxx b/panda/src/sgraphutil/appTraverser.cxx new file mode 100644 index 0000000000..d620aaf8a9 --- /dev/null +++ b/panda/src/sgraphutil/appTraverser.cxx @@ -0,0 +1,34 @@ +// Filename: appTraverser.cxx +// Created by: drose (25Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "appTraverser.h" +#include "config_sgraphutil.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: AppTraverser::traverse +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void AppTraverser:: +traverse(Node *root) { + if (!implicit_app_traversal) { + df_traverse(root, *this, NullAttributeWrapper(), NullLevelState(), + _graph_type); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AppTraverser::reached_node +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool AppTraverser:: +reached_node(Node *node, NullAttributeWrapper &, NullLevelState &) { + node->app_traverse(); + + return true; +} diff --git a/panda/src/sgraphutil/appTraverser.h b/panda/src/sgraphutil/appTraverser.h new file mode 100644 index 0000000000..754459700d --- /dev/null +++ b/panda/src/sgraphutil/appTraverser.h @@ -0,0 +1,47 @@ +// Filename: appTraverser.h +// Created by: drose (25Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef APPTRAVERSER_H +#define APPTRAVERSER_H + +#include + +#include +#include +#include +#include +#include +#include + +class Node; + +//////////////////////////////////////////////////////////////////// +// Class : AppTraverser +// Description : This traverser is designed to make a per-frame pass +// over the scene graph before rendering, to update any +// internal nodes as appropriate. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA AppTraverser : + public TraverserVisitor { +public: + INLINE AppTraverser(TypeHandle graph_type); + + void traverse(Node *root); + +public: + // These methods, from parent class TraverserVisitor, define the + // behavior of the AppTraverser as it traverses the graph. + // Normally you would never call these directly. + bool reached_node(Node *node, NullAttributeWrapper &render_state, + NullLevelState &level_state); + +private: + TypeHandle _graph_type; +}; + +#include "appTraverser.I" + +#endif + diff --git a/panda/src/sgraphutil/config_sgraphutil.cxx b/panda/src/sgraphutil/config_sgraphutil.cxx new file mode 100644 index 0000000000..e5006424fa --- /dev/null +++ b/panda/src/sgraphutil/config_sgraphutil.cxx @@ -0,0 +1,35 @@ +// Filename: config_sgraphutil.cxx +// Created by: drose (21Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_sgraphutil.h" +#include "directRenderTraverser.h" + +#include + +Configure(config_sgraphutil); +NotifyCategoryDef(sgraphutil, ""); + +ConfigureFn(config_sgraphutil) { + DirectRenderTraverser::init_type(); +} + +// Set this true to enable simple view-frustum culling: the +// elimination of branches of the scene graph from the drawing +// pipeline, based on the intersection test of its bounding sphere +// with the viewing frustum. Usually you'd only want to turn it off +// if it misbehaved, although there are rare cases in whice the +// culling logic is more expensive than the cost of drawing more than +// you can see. +const bool view_frustum_cull = config_sgraphutil.GetBool("view-frustum-cull", true); + +// Set this to color everything outside of the frustum red instead of +// culling it. Presumably this is only useful for debugging culling. +const bool fake_view_frustum_cull = config_sgraphutil.GetBool("fake-view-frustum-cull", false); + +// Setting this true causes the app traversal to be done during the +// draw traversal, instead of explicitly when the AppTraverser is +// called. +const bool implicit_app_traversal = config_sgraphutil.GetBool("implicit-app-traversal", true); + diff --git a/panda/src/sgraphutil/config_sgraphutil.h b/panda/src/sgraphutil/config_sgraphutil.h new file mode 100644 index 0000000000..ada64ddcb4 --- /dev/null +++ b/panda/src/sgraphutil/config_sgraphutil.h @@ -0,0 +1,21 @@ +// Filename: config_sgraphutil.h +// Created by: drose (21Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_SGRAPHUTIL_H +#define CONFIG_SGRAPHUTIL_H + +#include +#include + +// Configure variables for sgraphutil package. + +NotifyCategoryDecl(sgraphutil, EXPCL_PANDA, EXPTP_PANDA); + +extern EXPCL_PANDA const bool view_frustum_cull; +extern EXPCL_PANDA const bool fake_view_frustum_cull; +extern EXPCL_PANDA const bool implicit_app_traversal; + + +#endif diff --git a/panda/src/sgraphutil/directRenderLevelState.h b/panda/src/sgraphutil/directRenderLevelState.h new file mode 100644 index 0000000000..ae0240cf0a --- /dev/null +++ b/panda/src/sgraphutil/directRenderLevelState.h @@ -0,0 +1,23 @@ +// Filename: directRenderLevelState.h +// Created by: drose (17Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DIRECTRENDERLEVELSTATE_H +#define DIRECTRENDERLEVELSTATE_H + +#include + +//////////////////////////////////////////////////////////////////// +// Class : DirectRenderLevelState +// Description : This is the state information the +// DirectRenderTraverser retains for each level during +// traversal. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DirectRenderLevelState { +public: + bool _decal_mode; +}; + +#endif + diff --git a/panda/src/sgraphutil/directRenderTraverser.I b/panda/src/sgraphutil/directRenderTraverser.I new file mode 100644 index 0000000000..0b9e1aca5a --- /dev/null +++ b/panda/src/sgraphutil/directRenderTraverser.I @@ -0,0 +1,5 @@ +// Filename: directRenderTraverser.I +// Created by: drose (02Apr00) +// +//////////////////////////////////////////////////////////////////// + diff --git a/panda/src/sgraphutil/directRenderTraverser.cxx b/panda/src/sgraphutil/directRenderTraverser.cxx new file mode 100644 index 0000000000..792f743cc6 --- /dev/null +++ b/panda/src/sgraphutil/directRenderTraverser.cxx @@ -0,0 +1,179 @@ +// Filename: directRenderTraverser.cxx +// Created by: drose (18Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "directRenderTraverser.h" +#include "config_sgraphutil.h" +#include "frustumCullTraverser.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TypeHandle DirectRenderTraverser::_type_handle; +PStatCollector DirectRenderTraverser::_draw_pcollector = + PStatCollector("Draw", RGBColorf(1,0,0), 20); + +//////////////////////////////////////////////////////////////////// +// Function: DirectRenderTraverser::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DirectRenderTraverser:: +DirectRenderTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type) : + RenderTraverser(gsg, graph_type) +{ +} + + +//////////////////////////////////////////////////////////////////// +// Function: DirectRenderTraverser::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +DirectRenderTraverser:: +~DirectRenderTraverser() { +} + + +//////////////////////////////////////////////////////////////////// +// Function: DirectRenderTraverser::traverse +// Access: Public, Virtual +// Description: This performs a normal, complete render traversal +// using this DirectRenderTraverser object. +//////////////////////////////////////////////////////////////////// +void DirectRenderTraverser:: +traverse(Node *root, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans) { + // Statistics + PStatTimer timer(_draw_pcollector); + + AllAttributesWrapper render_state; + render_state.apply_from(initial_state, net_trans); + + DirectRenderLevelState level_state; + + DecalAttribute *decal_attrib; + if (get_attribute_into(decal_attrib, render_state, + DecalTransition::get_class_type())) { + level_state._decal_mode = decal_attrib->is_on(); + } + + fc_traverse(root, *this, render_state, level_state, + _gsg, _graph_type); + + if (level_state._decal_mode && + root->is_of_type(GeomNode::get_class_type())) { + // Close the decal. + _gsg->end_decal(DCAST(GeomNode, root)); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: DirectRenderTraverser::reached_node +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +bool DirectRenderTraverser:: +reached_node(Node *node, AllAttributesWrapper &render_state, + DirectRenderLevelState &level_state) { + if (implicit_app_traversal) { + node->app_traverse(); + } + node->draw_traverse(); + + level_state._decal_mode = false; + + AllTransitionsWrapper new_trans; + + if (!node->sub_render(render_state, new_trans, _gsg)) { + return false; + } + render_state.apply_in_place(new_trans); + + if (node->is_of_type(GeomNode::get_class_type())) { + _gsg->set_state(render_state.get_attributes(), true); + // Make sure the current display region is still in effect. + _gsg->prepare_display_region(); + + GeomNode *geom = DCAST(GeomNode, node); + + // We must make decals a special case, because they're so strange. + DecalAttribute *decal_attrib; + if (get_attribute_into(decal_attrib, render_state, + DecalTransition::get_class_type())) { + level_state._decal_mode = decal_attrib->is_on(); + } + + if (level_state._decal_mode) { + _gsg->begin_decal(geom); + + } else { + geom->draw(_gsg); + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: DirectRenderTraverser::forward_arc +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +bool DirectRenderTraverser:: +forward_arc(NodeRelation *arc, AllTransitionsWrapper &trans, + AllAttributesWrapper &, AllAttributesWrapper &post, + DirectRenderLevelState &) { + bool carry_on = true; + + // Go through all the transitions on the arc and sub_render() on + // each one. For most transition types, this will be a no-op and + // return true. For Shader types, this will fire off another render + // and return false. + + AllTransitionsWrapper::const_iterator nti; + for (nti = trans.begin(); nti != trans.end(); ++nti) { + NodeTransition *t = (*nti).second.get_trans(); + AllTransitionsWrapper new_trans; + if (!t->sub_render(arc, post, new_trans, _gsg)) { + carry_on = false; + } + post.apply_in_place(new_trans); + } + + return carry_on; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DirectRenderTraverser::backward_arc +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void DirectRenderTraverser:: +backward_arc(NodeRelation *arc, AllTransitionsWrapper &, + AllAttributesWrapper &, AllAttributesWrapper &post, + const DirectRenderLevelState &level_state) { + if (level_state._decal_mode) { + // Reset the state to redraw the base geometry. + _gsg->set_state(post.get_attributes(), true); + _gsg->prepare_display_region(); + _gsg->end_decal(DCAST(GeomNode, arc->get_child())); + } +} + diff --git a/panda/src/sgraphutil/directRenderTraverser.h b/panda/src/sgraphutil/directRenderTraverser.h new file mode 100644 index 0000000000..155d2d8aae --- /dev/null +++ b/panda/src/sgraphutil/directRenderTraverser.h @@ -0,0 +1,87 @@ +// Filename: directRenderTraverser.h +// Created by: drose (18Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DIRECTRENDERTRAVERSER_H +#define DIRECTRENDERTRAVERSER_H + +#include + +#include "directRenderLevelState.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Node; +class GraphicsStateGuardian; +class GeometricBoundingVolume; +class NodeAttributes; + +//////////////////////////////////////////////////////////////////// +// Class : DirectRenderTraverser +// Description : A kind of RenderTraverser that renders each GeomNode +// it encounters immediately as it is encountered. No +// attempt is made to perform state-sorting or binning; +// however, view-frustum culling is performed. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DirectRenderTraverser : + public RenderTraverser, + public TraverserVisitor { +public: + DirectRenderTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type); + virtual ~DirectRenderTraverser(); + + virtual void traverse(Node *root, + const AllAttributesWrapper &initial_state, + const AllTransitionsWrapper &net_trans); + +public: + // These methods, from parent class TraverserVisitor, define the + // behavior of the DirectRenderTraverser as it traverses the graph. + // Normally you would never call these directly. + bool reached_node(Node *node, AllAttributesWrapper &render_state, + DirectRenderLevelState &level_state); + + bool forward_arc(NodeRelation *arc, AllTransitionsWrapper &trans, + AllAttributesWrapper &pre, AllAttributesWrapper &post, + DirectRenderLevelState &level_state); + + void backward_arc(NodeRelation *arc, AllTransitionsWrapper &trans, + AllAttributesWrapper &pre, AllAttributesWrapper &post, + const DirectRenderLevelState &level_state); + +private: + // Statistics + static PStatCollector _draw_pcollector; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + RenderTraverser::init_type(); + register_type(_type_handle, "DirectRenderTraverser", + RenderTraverser::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "directRenderTraverser.I" + +#endif + diff --git a/panda/src/sgraphutil/frustumCullTraverser.I b/panda/src/sgraphutil/frustumCullTraverser.I new file mode 100644 index 0000000000..de3c681b51 --- /dev/null +++ b/panda/src/sgraphutil/frustumCullTraverser.I @@ -0,0 +1,242 @@ +// Filename: frustumCullTraverser.I +// Created by: drose (14Apr00) +// +//////////////////////////////////////////////////////////////////// + +#include "frustumCullTraverser.h" +#include "get_rel_pos.h" +#include "config_sgraphutil.h" + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: FrustumCullTraverser::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +FrustumCullTraverser:: +FrustumCullTraverser(Node *root, + Visitor &visitor, + const AttributeWrapper &initial_render_state, + const LevelState &initial_level_state, + GraphicsStateGuardian *gsg, + TypeHandle graph_type) : + _visitor(visitor), + _initial_render_state(initial_render_state), + _gsg(gsg), + _graph_type(graph_type) +{ + _view_frustum = NULL; + PT(GeometricBoundingVolume) local_frustum; + + if (view_frustum_cull && _gsg != (GraphicsStateGuardian *)NULL) { + // If we're to be performing view-frustum culling, determine the + // bounding volume associated with the current viewing frustum. + + const ProjectionNode *camera = _gsg->get_current_projection_node(); + if (camera != (const ProjectionNode *)NULL) { + const Projection *proj = camera->get_projection(); + nassertv(proj != (const Projection *)NULL); + BoundingVolume *bv = proj->make_bounds(); + + if (bv->is_of_type(GeometricBoundingVolume::get_class_type())) { + _view_frustum = DCAST(GeometricBoundingVolume, bv); + } else { + delete bv; + } + } + + LMatrix4f mat; + get_rel_mat(_gsg->get_current_projection_node(), root, mat); + + local_frustum = DCAST(GeometricBoundingVolume, _view_frustum->make_copy()); + local_frustum->xform(mat); + + if (sgraphutil_cat.is_spam()) { + sgraphutil_cat.spam() + << "Beginning frustum cull, frustum is: " << *local_frustum << "\n" + << "Transform is:\n"; + mat.write(sgraphutil_cat.spam(false), 2); + } + } + + LevelState level_state(initial_level_state); + traverse(root, _initial_render_state, level_state, local_frustum, false); + + _view_frustum = NULL; + nassertv(_arc_stack.empty()); +} + + +//////////////////////////////////////////////////////////////////// +// Function: FrustumCullTraverser::Traverse +// Access: Public +// Description: Walks to the next arc of the graph. +//////////////////////////////////////////////////////////////////// +template +void FrustumCullTraverser:: +traverse(NodeRelation *arc, AttributeWrapper render_state, + LevelState level_state, PT(GeometricBoundingVolume) local_frustum, + bool all_in) { + // local_frustum, above, is a PT(GeometricBoundingVolume), instead + // of just an ordinary GeometricBoundingVolume*, because we might + // reassign it within this function to a locally-allocated volume + // which we expect to automatically destruct when the function + // terminates. + nassertv(arc->get_child() != (Node *)NULL); + + bool carry_on = true; + bool pushed_arc_stack = false; + + // First, if we're performing view-frustum culling, check the + // bounding volume associated with this arc (which bounds all of its + // children) against the bounding volume of our view frustum. + + if (_view_frustum != (GeometricBoundingVolume *)NULL) { + // If this arc's child has multiple parents, we need to save the + // arc on the arc stack, so we can unambiguously wrt(). + if (arc->get_child()->get_num_parents(_graph_type) > 1) { + _arc_stack.push_back(arc); + pushed_arc_stack = true; + } + + if (!all_in) { + // Now test for intersection with the bounding volume. + const BoundingVolume &arc_volume = arc->get_bound(); + if (arc_volume.is_of_type(GeometricBoundingVolume::get_class_type())) { + const GeometricBoundingVolume *arc_gbv = + DCAST(GeometricBoundingVolume, &arc_volume); + + nassertv(local_frustum != (GeometricBoundingVolume *)NULL); + int result = local_frustum->contains(arc_gbv); + if (result == BoundingVolume::IF_no_intersection) { + // No intersection at all. Cull. + if (sgraphutil_cat.is_spam() && !arc_volume.is_empty()) { + sgraphutil_cat.spam() + << "Culling " << *arc->get_child() << " with volume " + << arc_volume << "\n"; + } + + /* + if (fake_view_frustum_cull) { + // In fake mode, we just render everything in red + // wireframe instead of actually culling it. + + ColorTransition *c = + new ColorTransition(1.0, 0.0, 0.0, 1.0); + RenderModeTransition *w = + new RenderModeTransition(RenderModeProperty::M_wireframe); + TextureTransition *t = + new TextureTransition(TextureTransition::off()); + + c->set_priority(100); + w->set_priority(100); + t->set_priority(100); + AllTransitionsWrapper trans; + trans.set_transition(c); + trans.set_transition(w); + trans.set_transition(t); + + post.apply_in_place(trans); + all_in = true; + + } else */ { + carry_on = false; + } + + } else if ((result & BoundingVolume::IF_all) != 0) { + // The arc and its descendants are completely enclosed within + // the frustum. No need to cull further. + all_in = true; + } + } + + if (carry_on && + arc->has_transition(TransformTransition::get_class_type())) { + // Now apply the transform associated with this arc. We do + // this after the bounding volume test, because the bounding + // volume has already been transformed. + + local_frustum = + DCAST(GeometricBoundingVolume, _view_frustum->make_copy()); + + NodeTransitionWrapper ntw(TransformTransition::get_class_type()); + wrt(_gsg->get_current_projection_node(), + arc->get_child(), _arc_stack.begin(), _arc_stack.end(), + ntw, _graph_type); + + const TransformTransition *tt; + if (get_transition_into(tt, ntw)) { + // This frustum is transformed from the camera. Most will + // be. + nassertv(local_frustum != (GeometricBoundingVolume *)NULL); + local_frustum->xform(tt->get_matrix()); + + if (sgraphutil_cat.is_spam()) { + sgraphutil_cat.spam() + << "Transforming frustum into local space of " << *arc << ": " << *local_frustum << "\n" + << "Transform is:\n"; + tt->get_matrix().write(sgraphutil_cat.spam(false), 2); + } + } + } + } + } + + if (carry_on) { + // Now give the visitor a chance to veto this arc. + TransitionWrapper trans = + TransitionWrapper::init_from(_initial_render_state); + trans.extract_from(arc); + + AttributeWrapper post_state(render_state); + post_state.apply_in_place(trans); + if (_visitor.forward_arc(arc, trans, render_state, post_state, + level_state)) { + + traverse(arc->get_child(), post_state, level_state, + local_frustum, all_in); + _visitor.backward_arc(arc, trans, render_state, post_state, + level_state); + } + } + + if (pushed_arc_stack) { + _arc_stack.pop_back(); + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: FrustumCullTraverser::Traverse +// Access: Public +// Description: Walks to the next node of the graph. +//////////////////////////////////////////////////////////////////// +template +void FrustumCullTraverser:: +traverse(Node *node, AttributeWrapper &render_state, LevelState &level_state, + GeometricBoundingVolume *local_frustum, bool all_in) { + // Tell the visitor we reached the node, and give it a chance to + // veto our further progress. + if (_visitor.reached_node(node, render_state, level_state)) { + + // Look for further children of the given NodeRelation. + DownRelations::const_iterator dri; + dri = node->_children.find(_graph_type); + if (dri != node->_children.end()) { + // Here are some! + const DownRelationPointers &drp = (*dri).second; + + // Now visit each of the children in turn. + DownRelationPointers::const_iterator drpi; + for (drpi = drp.begin(); drpi != drp.end(); ++drpi) { + traverse(*drpi, render_state, level_state, local_frustum, all_in); + } + } + } +} + diff --git a/panda/src/sgraphutil/frustumCullTraverser.h b/panda/src/sgraphutil/frustumCullTraverser.h new file mode 100644 index 0000000000..1ee75e5157 --- /dev/null +++ b/panda/src/sgraphutil/frustumCullTraverser.h @@ -0,0 +1,81 @@ +// Filename: frustumCullTraverser.h +// Created by: drose (14Apr00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FRUSTUMCULLTRAVERSER_H +#define FRUSTUMCULLTRAVERSER_H + +#include + +#include +#include +#include +#include +#include + + +/////////////////////////////////////////////////////////////////// +// Class : FrustumCullTraverser +// Description : A special kind of depth-first traverser that can +// prune the graph based on a lack of intersection with +// a given bounding volume; i.e. it performs +// view-frustum culling. +//////////////////////////////////////////////////////////////////// +template +class FrustumCullTraverser { +public: + typedef TYPENAME Visitor::TransitionWrapper TransitionWrapper; + typedef TYPENAME Visitor::AttributeWrapper AttributeWrapper; + + FrustumCullTraverser(Node *root, + Visitor &visitor, + const AttributeWrapper &initial_render_state, + const LevelState &initial_level_state, + GraphicsStateGuardian *gsg, + TypeHandle graph_type); + +protected: + void traverse(NodeRelation *arc, + AttributeWrapper render_state, + LevelState level_state, + PT(GeometricBoundingVolume) local_frustum, + bool all_in); + void traverse(Node *node, + AttributeWrapper &render_state, + LevelState &level_state, + GeometricBoundingVolume *local_frustum, + bool all_in); + + Visitor &_visitor; + AttributeWrapper _initial_render_state; + GraphicsStateGuardian *_gsg; + TypeHandle _graph_type; + + // If we are performing view-frustum culling, this is a pointer to + // the bounding volume that encloses the view frustum, in its own + // coordinate space. If we are not performing view-frustum culling, + // this will be a NULL pointer. + PT(GeometricBoundingVolume) _view_frustum; + + // This is a list of arcs we have passed so we can perform + // unambiguous wrt's. + typedef vector ArcStack; + ArcStack _arc_stack; +}; + +// Convenience function. +template +INLINE void +fc_traverse(Node *root, Visitor &visitor, + const AttributeWrapper &initial_render_state, + const LevelState &initial_level_state, + GraphicsStateGuardian *gsg, TypeHandle graph_type) { + FrustumCullTraverser + fct(root, visitor, initial_render_state, + initial_level_state, gsg, graph_type); +} + +#include "frustumCullTraverser.I" + +#endif diff --git a/panda/src/sgraphutil/get_rel_pos.I b/panda/src/sgraphutil/get_rel_pos.I new file mode 100644 index 0000000000..e1932c44db --- /dev/null +++ b/panda/src/sgraphutil/get_rel_pos.I @@ -0,0 +1,138 @@ +// Filename: get_rel_pos.I +// Created by: drose (18Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: get_pos +// Description: Returns the position in space of the node's origin, +// relative to another node (such as render). +//////////////////////////////////////////////////////////////////// +INLINE LPoint3f +get_rel_pos(const Node *node, const Node *relative_to, + TypeHandle graph_type) { + NodeTransitionWrapper ntw(TransformTransition::get_class_type()); + wrt(node, relative_to, ntw, graph_type); + const TransformTransition *tt; + if (!get_transition_into(tt, ntw)) { + // No relative transform. + return LPoint3f(0.0, 0.0, 0.0); + } + + return tt->get_matrix().get_row3(3); +} + +//////////////////////////////////////////////////////////////////// +// Function: get_mat +// Description: Returns the net transform of the node, relative to +// another nose (such as render). +//////////////////////////////////////////////////////////////////// +INLINE void +get_rel_mat(const Node *node, const Node *relative_to, + LMatrix4f &mat, TypeHandle graph_type) { + NodeTransitionWrapper ntw(TransformTransition::get_class_type()); + wrt(node, relative_to, ntw, graph_type); + const TransformTransition *tt; + if (!get_transition_into(tt, ntw)) { + // No relative transform. + mat = LMatrix4f::ident_mat(); + return; + } + + mat = tt->get_matrix(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: get_up +// Description: Returns the vector which indicates "up" +// (e.g. the positive z axis, in a Z-up right-handed +// system) to the the indicated node, relative to +// another node (such as render). +//////////////////////////////////////////////////////////////////// +INLINE LVector3f +get_rel_up(const Node *node, const Node *relative_to, + CoordinateSystem cs) { + LMatrix4f mat; + get_rel_rot_mat(node, relative_to, mat); + return LVector3f::up(cs) * mat; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_right +// Description: Returns the vector which indicates "right" +// (e.g. the positive x axis, in a Z-up right-handed +// system) to the the indicated node, relative to +// another node (such as render). +//////////////////////////////////////////////////////////////////// +INLINE LVector3f +get_rel_right(const Node *node, const Node *relative_to, + CoordinateSystem cs) { + LMatrix4f mat; + get_rel_rot_mat(node, relative_to, mat); + return LVector3f::right(cs) * mat; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_forward +// Description: Returns the vector which indicates "forward" +// (e.g. the positive y axis, in a Z-up right-handed +// system) to the the indicated node, relative to +// another node (such as render). +//////////////////////////////////////////////////////////////////// +INLINE LVector3f +get_rel_forward(const Node *node, const Node *relative_to, + CoordinateSystem cs) { + LMatrix4f mat; + get_rel_rot_mat(node, relative_to, mat); + return LVector3f::forward(cs) * mat; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_down +// Description: Returns the vector which indicates "down" +// (e.g. the negative y axis, in a Z-up right-handed +// system) to the the indicated node, relative to +// another node (such as render). +//////////////////////////////////////////////////////////////////// +INLINE LVector3f +get_rel_down(const Node *node, const Node *relative_to, + CoordinateSystem cs) { + LMatrix4f mat; + get_rel_rot_mat(node, relative_to, mat); + return LVector3f::down(cs) * mat; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_left +// Description: Returns the vector which indicates "left" +// (e.g. the negative x axis, in a Z-up right-handed +// system) to the the indicated node, relative to +// another node (such as render). +//////////////////////////////////////////////////////////////////// +INLINE LVector3f +get_rel_left(const Node *node, const Node *relative_to, + CoordinateSystem cs) { + LMatrix4f mat; + get_rel_rot_mat(node, relative_to, mat); + return LVector3f::left(cs) * mat; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_back +// Description: Returns the vector which indicates "back" +// (e.g. the negative y axis, in a Z-up right-handed +// system) to the the indicated node, relative to +// another node (such as render). +//////////////////////////////////////////////////////////////////// +INLINE LVector3f +get_rel_back(const Node *node, const Node *relative_to, + CoordinateSystem cs) { + LMatrix4f mat; + get_rel_rot_mat(node, relative_to, mat); + return LVector3f::back(cs) * mat; +} diff --git a/panda/src/sgraphutil/get_rel_pos.cxx b/panda/src/sgraphutil/get_rel_pos.cxx new file mode 100644 index 0000000000..681c83d0e8 --- /dev/null +++ b/panda/src/sgraphutil/get_rel_pos.cxx @@ -0,0 +1,78 @@ +// Filename: get_rel_pos.cxx +// Created by: drose (18Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "get_rel_pos.h" + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: get_rot_mat +// Description: Returns a rotation-only matrix that corresponds to +// the position in space of the node's origin, relative +// to another node (such as render). +//////////////////////////////////////////////////////////////////// +void +get_rel_rot_mat(const Node *node, const Node *relative_to, + LMatrix4f &mat, TypeHandle graph_type) { + NodeTransitionWrapper ntw(TransformTransition::get_class_type()); + wrt(node, relative_to, ntw, graph_type); + const TransformTransition *tt; + if (!get_transition_into(tt, ntw)) { + // No relative transform. + mat = LMatrix4f::ident_mat(); + return; + } + + // Extract the axes from the matrix. + const LMatrix4f &rel_mat = tt->get_matrix(); + LVector3f x, y, z; + x = rel_mat.get_row3(0); + y = rel_mat.get_row3(1); + z = rel_mat.get_row3(2); + + // Normalize these axes to eliminate scale. + x = normalize(x); + y = normalize(y); + z = normalize(z); + + // Now build a new matrix which just represents these axes. + mat.set(x[0], x[1], x[2], 0.0, + y[0], y[1], y[2], 0.0, + z[0], z[1], z[2], 0.0, + 0.0, 0.0, 0.0, 1.0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: get_scale +// Description: Returns the relative scale between the indicated node +// and the other node. +//////////////////////////////////////////////////////////////////// +LVecBase3f +get_rel_scale(const Node *node, const Node *relative_to, + TypeHandle graph_type) { + NodeTransitionWrapper ntw(TransformTransition::get_class_type()); + wrt(node, relative_to, ntw, graph_type); + const TransformTransition *tt; + if (!get_transition_into(tt, ntw)) { + // No relative transform. + return LVecBase3f(1.0, 1.0, 1.0); + } + + // Extract the axes from the matrix. + const LMatrix4f &rel_mat = tt->get_matrix(); + LVector3f x, y, z; + x = rel_mat.get_row3(0); + y = rel_mat.get_row3(1); + z = rel_mat.get_row3(2); + + // Now return the lengths of these axes as the scale. + + return LVecBase3f(length(x), length(y), length(z)); +} + + diff --git a/panda/src/sgraphutil/get_rel_pos.h b/panda/src/sgraphutil/get_rel_pos.h new file mode 100644 index 0000000000..640a7ed12a --- /dev/null +++ b/panda/src/sgraphutil/get_rel_pos.h @@ -0,0 +1,57 @@ +// Filename: get_rel_pos.h +// Created by: drose (18Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef GET_REL_POS_H +#define GET_REL_POS_H + +#include + +#include +#include +#include +#include + +class Node; + +INLINE LPoint3f EXPCL_PANDA +get_rel_pos(const Node *node, const Node *relative_to, + TypeHandle graph_type = RenderRelation::get_class_type()); +INLINE void EXPCL_PANDA +get_rel_mat(const Node *node, const Node *relative_to, + LMatrix4f &mat, + TypeHandle graph_type = RenderRelation::get_class_type()); +void EXPCL_PANDA +get_rel_rot_mat(const Node *node, const Node *relative_to, + LMatrix4f &mat, + TypeHandle graph_type = RenderRelation::get_class_type()); + +LVecBase3f EXPCL_PANDA +get_rel_scale(const Node *node, const Node *relative_to, + TypeHandle graph_type = RenderRelation::get_class_type()); + + +INLINE LVector3f EXPCL_PANDA +get_rel_up(const Node *node, const Node *relative_to, + CoordinateSystem cs = CS_default); +INLINE LVector3f EXPCL_PANDA +get_rel_right(const Node *node, const Node *relative_to, + CoordinateSystem cs = CS_default); +INLINE LVector3f EXPCL_PANDA +get_rel_forward(const Node *node, const Node *relative_to, + CoordinateSystem cs = CS_default); +INLINE LVector3f EXPCL_PANDA +get_rel_down(const Node *node, const Node *relative_to, + CoordinateSystem cs = CS_default); +INLINE LVector3f EXPCL_PANDA +get_rel_left(const Node *node, const Node *relative_to, + CoordinateSystem cs = CS_default); +INLINE LVector3f EXPCL_PANDA +get_rel_back(const Node *node, const Node *relative_to, + CoordinateSystem cs = CS_default); + +#include "get_rel_pos.I" + +#endif + diff --git a/panda/src/sgraphutil/sceneGraphAnalyzer.cxx b/panda/src/sgraphutil/sceneGraphAnalyzer.cxx new file mode 100644 index 0000000000..c00e456a41 --- /dev/null +++ b/panda/src/sgraphutil/sceneGraphAnalyzer.cxx @@ -0,0 +1,372 @@ +// Filename: sceneGraphAnalyzer.cxx +// Created by: drose (02Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "sceneGraphAnalyzer.h" +#include "config_sgraphutil.h" + +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphAnalyzer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +SceneGraphAnalyzer:: +SceneGraphAnalyzer(TypeHandle graph_type) : + _graph_type(graph_type) +{ + clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphAnalyzer::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +SceneGraphAnalyzer:: +~SceneGraphAnalyzer() { +} + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphAnalyzer::clear +// Access: Public +// Description: Resets all of the data in the analyzer in preparation +// for a new run. +//////////////////////////////////////////////////////////////////// +void SceneGraphAnalyzer:: +clear() { + _nodes.clear(); + _textures.clear(); + + _num_nodes = 0; + _num_instances = 0; + _num_transforms = 0; + _num_arcs = 0; + _num_arcs_with_transitions = 0; + _num_geom_nodes = 0; + _num_geoms = 0; + _num_non_geoms = 0; + + _num_vertices = 0; + _num_normals = 0; + _num_texcoords = 0; + _num_tris = 0; + _num_quads = 0; + _num_polys = 0; + _num_lines = 0; + _num_points = 0; + _num_spheres = 0; + + _num_individual_tris = 0; + _num_tristrips = 0; + _num_triangles_in_strips = 0; + _num_trifans = 0; + _num_triangles_in_fans = 0; + + _texture_bytes = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphAnalyzer::add_node +// Access: Public +// Description: Adds a new node to the set of data for analysis. +// Normally, this would only be called once, and passed +// the top of the scene graph, but it's possible to +// repeatedly pass in subgraphs to get an analysis of +// all the graphs together. +//////////////////////////////////////////////////////////////////// +void SceneGraphAnalyzer:: +add_node(Node *node) { + collect_statistics(node, false); +} + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphAnalyzer::write +// Access: Public +// Description: Describes all the data collected. +//////////////////////////////////////////////////////////////////// +void SceneGraphAnalyzer:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) + << _num_nodes << " total nodes (including " + << _num_instances << " instances).\n"; + + indent(out, indent_level) + << _num_transforms << " transforms; " + << 100 * _num_arcs_with_transitions / _num_arcs + << "% of arcs have some transition.\n"; + + indent(out, indent_level) + << _num_geoms << " Geoms "; + if (_num_non_geoms != 0) { + out << "(plus " << _num_non_geoms << " non-Geom Drawables) "; + } + out << "appear on " << _num_geom_nodes << " GeomNodes.\n"; + + indent(out, indent_level) + << _num_vertices << " vertices, " << _num_normals << " normals, " + << _num_texcoords << " texture coordinates.\n"; + + indent(out, indent_level) + << _num_tris << " triangles:\n"; + indent(out, indent_level + 2) + << _num_triangles_in_strips + << " of these are on " << _num_tristrips << " tristrips"; + if (_num_tristrips != 0) { + out << " (" + << (double)_num_triangles_in_strips / (double)_num_tristrips + << " average tris per strip)"; + } + out << ".\n"; + + indent(out, indent_level + 2) + << _num_triangles_in_fans + << " of these are on " << _num_trifans << " trifans"; + if (_num_trifans != 0) { + out << " (" + << (double)_num_triangles_in_fans / (double)_num_trifans + << " average tris per fan)"; + } + out << ".\n"; + + indent(out, indent_level + 2) + << _num_individual_tris + << " of these are independent triangles.\n"; + + indent(out, indent_level) + << _num_quads << " quads, " << _num_polys << " general polygons, " + << _num_lines << " lines, " << _num_points << " points, " + << _num_spheres << " spheres.\n"; + + indent(out, indent_level) + << _textures.size() << " textures, estimated minimum " + << (_texture_bytes + 1023) / 1024 << "K texture memory required.\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphAnalyzer::collect_statistics +// Access: Private +// Description: Recursively visits each node, counting up the +// statistics. +//////////////////////////////////////////////////////////////////// +void SceneGraphAnalyzer:: +collect_statistics(Node *node, bool under_instance) { + _num_nodes++; + + if (!under_instance) { + Nodes::iterator ni = _nodes.find(node); + if (ni == _nodes.end()) { + // This is the first time this node has been encountered. + _nodes.insert(Nodes::value_type(node, 1)); + } else { + // This node has been encountered before; that makes it an + // instance. + (*ni).second++; + _num_instances++; + under_instance = true; + } + } + + if (node->is_of_type(GeomNode::get_class_type())) { + collect_statistics(DCAST(GeomNode, node)); + } + + int num_children = node->get_num_children(_graph_type); + for (int i = 0; i < num_children; i++) { + NodeRelation *arc = node->get_child(_graph_type, i); + collect_statistics(arc, under_instance); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphAnalyzer::collect_statistics +// Access: Private +// Description: Recursively visits each node, counting up the +// statistics. +//////////////////////////////////////////////////////////////////// +void SceneGraphAnalyzer:: +collect_statistics(NodeRelation *arc, bool under_instance) { + _num_arcs++; + if (arc->has_any_transition()) { + _num_arcs_with_transitions++; + } + if (arc->has_transition(TransformTransition::get_class_type())) { + _num_transforms++; + } + + TextureTransition *tt; + if (get_transition_into(tt, arc)) { + if (tt->is_on()) { + collect_statistics(tt->get_texture()); + } + } + + collect_statistics(arc->get_child(), under_instance); +} + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphAnalyzer::collect_statistics +// Access: Private +// Description: Recursively visits each node, counting up the +// statistics. +//////////////////////////////////////////////////////////////////// +void SceneGraphAnalyzer:: +collect_statistics(GeomNode *geom_node) { + nassertv(geom_node != (GeomNode *)NULL); + + _num_geom_nodes++; + + int num_geoms = geom_node->get_num_geoms(); + for (int i = 0; i < num_geoms; i++) { + dDrawable *geom = geom_node->get_geom(i); + + if (geom->is_of_type(Geom::get_class_type())) { + _num_geoms++; + + collect_statistics(DCAST(Geom, geom)); + } else { + _num_non_geoms++; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphAnalyzer::collect_statistics +// Access: Private +// Description: Recursively visits each node, counting up the +// statistics. +//////////////////////////////////////////////////////////////////// +void SceneGraphAnalyzer:: +collect_statistics(Geom *geom) { + int num_prims; + int num_verts; + int num_components; + + num_prims = geom->get_num_prims(); + if (geom->uses_components()) { + num_verts = 0; + num_components = 0; + for (int i = 0; i < num_prims; i++) { + num_verts += geom->get_length(i); + num_components += (geom->get_length(i) - geom->get_num_more_vertices_than_components()); + } + } else { + num_verts = num_prims * geom->get_num_vertices_per_prim(); + num_components = 1; + } + + _num_vertices += num_verts; + + switch (geom->get_binding(G_NORMAL)) { + case G_OVERALL: + _num_normals++; + break; + + case G_PER_PRIM: + _num_normals += num_prims; + break; + + case G_PER_COMPONENT: + _num_normals += num_components; + break; + + case G_PER_VERTEX: + _num_normals += num_verts; + break; + } + + if (geom->get_binding(G_TEXCOORD) == G_PER_VERTEX) { + _num_texcoords += num_verts; + } + + if (geom->is_of_type(GeomPoint::get_class_type())) { + _num_points += num_verts; + + } else if (geom->is_of_type(GeomLine::get_class_type())) { + _num_lines += num_prims; + + } else if (geom->is_of_type(GeomLinestrip::get_class_type())) { + _num_lines += num_components; + + } else if (geom->is_of_type(GeomPolygon::get_class_type())) { + _num_polys += num_prims; + + } else if (geom->is_of_type(GeomQuad::get_class_type())) { + _num_quads += num_prims; + + } else if (geom->is_of_type(GeomTri::get_class_type())) { + _num_tris += num_prims; + _num_individual_tris += num_prims; + + } else if (geom->is_of_type(GeomTristrip::get_class_type())) { + _num_tris += num_components; + _num_tristrips += num_prims; + _num_triangles_in_strips += num_components; + + } else if (geom->is_of_type(GeomTrifan::get_class_type())) { + _num_tris += num_components; + _num_trifans += num_prims; + _num_triangles_in_fans += num_components; + + } else if (geom->is_of_type(GeomSphere::get_class_type())) { + _num_spheres += num_prims; + + } else { + sgraphutil_cat.warning() + << "Unknown GeomType in SceneGraphAnalyzer: " + << geom->get_type() << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphAnalyzer::collect_statistics +// Access: Private +// Description: Recursively visits each node, counting up the +// statistics. +//////////////////////////////////////////////////////////////////// +void SceneGraphAnalyzer:: +collect_statistics(Texture *texture) { + nassertv(texture != (Texture *)NULL); + + Textures::iterator ti = _textures.find(texture); + if (ti == _textures.end()) { + // This is the first time this texture has been encountered. + _textures.insert(Textures::value_type(texture, 1)); + + // Attempt to guess how many bytes of texture memory this one + // requires. + PixelBuffer *pb = texture->_pbuffer; + if (pb != (PixelBuffer *)NULL) { + int bytes = + pb->get_xsize() * pb->get_ysize() * pb->get_num_components() * + pb->get_component_width(); + + bool is_mipmapped = false; + switch (texture->get_minfilter()) { + case Texture::FT_nearest_mipmap_nearest: + case Texture::FT_linear_mipmap_nearest: + case Texture::FT_nearest_mipmap_linear: + case Texture::FT_linear_mipmap_linear: + is_mipmapped = true; + } + + if (is_mipmapped) { + bytes *= 4/3; + } + + _texture_bytes += bytes; + } + + } else { + // This texture has been encountered before; don't count it again. + (*ti).second++; + } +} + diff --git a/panda/src/sgraphutil/sceneGraphAnalyzer.h b/panda/src/sgraphutil/sceneGraphAnalyzer.h new file mode 100644 index 0000000000..058e7f49da --- /dev/null +++ b/panda/src/sgraphutil/sceneGraphAnalyzer.h @@ -0,0 +1,80 @@ +// Filename: sceneGraphAnalyzer.h +// Created by: drose (02Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SCENEGRAPHANALYZER_H +#define SCENEGRAPHANALYZER_H + +#include + +#include +#include + +#include + +class Node; +class GeomNode; +class Geom; +class Texture; + +/////////////////////////////////////////////////////////////////// +// Class : SceneGraphAnalyzer +// Description : A handy class that can scrub over a scene graph and +// collect interesting statistics on it. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA SceneGraphAnalyzer { +public: + SceneGraphAnalyzer(TypeHandle graph_type = RenderRelation::get_class_type()); + ~SceneGraphAnalyzer(); + + void clear(); + void add_node(Node *node); + + void write(ostream &out, int indent_level = 0) const; + +private: + void collect_statistics(Node *node, bool under_instance); + void collect_statistics(NodeRelation *arc, bool under_instance); + void collect_statistics(GeomNode *geom_node); + void collect_statistics(Geom *geom); + void collect_statistics(Texture *texture); + + typedef map Nodes; + typedef map Textures; + + Nodes _nodes; + Textures _textures; + +public: + int _num_nodes; + int _num_instances; + int _num_transforms; + int _num_arcs; + int _num_arcs_with_transitions; + int _num_geom_nodes; + int _num_geoms; + int _num_non_geoms; + + int _num_vertices; + int _num_normals; + int _num_texcoords; + int _num_tris; + int _num_quads; + int _num_polys; + int _num_lines; + int _num_points; + int _num_spheres; + + int _num_individual_tris; + int _num_tristrips; + int _num_triangles_in_strips; + int _num_trifans; + int _num_triangles_in_fans; + + int _texture_bytes; + + TypeHandle _graph_type; +}; + +#endif diff --git a/panda/src/sgraphutil/sceneGraphReducer.I b/panda/src/sgraphutil/sceneGraphReducer.I new file mode 100644 index 0000000000..de1f0bb75a --- /dev/null +++ b/panda/src/sgraphutil/sceneGraphReducer.I @@ -0,0 +1,39 @@ +// Filename: sceneGraphReducer.I +// Created by: drose (22May00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphReducer::AccumulatedTransitions::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SceneGraphReducer::AccumulatedTransitions:: +AccumulatedTransitions() { +} + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphReducer::AccumulatedTransitions::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SceneGraphReducer::AccumulatedTransitions:: +AccumulatedTransitions(const SceneGraphReducer::AccumulatedTransitions ©) : + _transform(copy._transform), + _color(copy._color), + _texture_matrix(copy._texture_matrix) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphReducer::AccumulatedTransitions::Copy Assignment +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void SceneGraphReducer::AccumulatedTransitions:: +operator = (const SceneGraphReducer::AccumulatedTransitions ©) { + _transform = copy._transform; + _color = copy._color; + _texture_matrix = copy._texture_matrix; +} diff --git a/panda/src/sgraphutil/sceneGraphReducer.cxx b/panda/src/sgraphutil/sceneGraphReducer.cxx new file mode 100644 index 0000000000..1d126ebf62 --- /dev/null +++ b/panda/src/sgraphutil/sceneGraphReducer.cxx @@ -0,0 +1,324 @@ +// Filename: sceneGraphReducer.cxx +// Created by: drose (22May00) +// +//////////////////////////////////////////////////////////////////// + +#include "sceneGraphReducer.h" +#include "config_sgraphutil.h" + +#include +#include +#include +#include + + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphReducer::AccumulatedTransitions::apply_to_arc +// Access: Public +// Description: Stores the relevant transitions on the indicated arc, +// and clears the transitions for continuing. +//////////////////////////////////////////////////////////////////// +void SceneGraphReducer::AccumulatedTransitions:: +apply_to_arc(NodeRelation *arc, int transition_types) { + if ((transition_types & TT_transform) != 0) { + if (!_transform->get_matrix().almost_equal(LMatrix4f::ident_mat())) { + arc->set_transition(_transform); + } + _transform = new TransformTransition; + } + + if ((transition_types & TT_color) != 0) { + if (!_color->is_identity()) { + arc->set_transition(_color); + _color = new ColorTransition; + } + } + + if ((transition_types & TT_texture_matrix) != 0) { + if (!_texture_matrix->get_matrix().almost_equal(LMatrix4f::ident_mat())) { + arc->set_transition(_texture_matrix); + } + _texture_matrix = new TexMatrixTransition; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphReducer::AccumulatedTransitions::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void SceneGraphReducer::AccumulatedTransitions:: +write(ostream &out, int transition_types, int indent_level) const { + if ((transition_types & TT_transform) != 0) { + _transform->write(out, indent_level); + } + if ((transition_types & TT_color) != 0) { + _color->write(out, indent_level); + } + if ((transition_types & TT_texture_matrix) != 0) { + _texture_matrix->write(out, indent_level); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphReducer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +SceneGraphReducer:: +SceneGraphReducer(TypeHandle graph_type) : + GraphReducer(graph_type) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphReducer::apply_transitions +// Access: Public +// Description: Walks the scene graph, accumulating transitions of +// the indicated types, applying them to the vertices, +// and removing them from the scene graph. This has a +// performance optimization benefit in itself, but is +// especially useful to pave the way for a call to +// flatten() and greatly improve the effectiveness of +// the flattening operation. +// +// Multiply instanced geometry is duplicated before the +// transitions are applied. +// +// Of course, this operation does make certain dynamic +// operations impossible. +//////////////////////////////////////////////////////////////////// +void SceneGraphReducer:: +apply_transitions(Node *root, int transition_types) { + AccumulatedTransitions trans; + if ((transition_types & TT_transform) != 0) { + trans._transform = new TransformTransition; + } + if ((transition_types & TT_color) != 0) { + trans._color = new ColorTransition; + } + if ((transition_types & TT_texture_matrix) != 0) { + trans._texture_matrix = new TexMatrixTransition; + } + + DownRelations::const_iterator dri; + dri = root->_children.find(_graph_type); + if (dri != root->_children.end()) { + // We must make a temporary copy of the DownRelationPointers, + // because we'll be modifying this list as we go. + DownRelationPointers drp = (*dri).second; + + DownRelationPointers::const_iterator drpi; + for (drpi = drp.begin(); drpi != drp.end(); ++drpi) { + NodeRelation *child_arc = (*drpi); + + r_apply_transitions(child_arc, transition_types, trans, false); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphReducer::r_apply_transitions +// Access: Protected +// Description: The recursive implementation of apply_transitions(). +//////////////////////////////////////////////////////////////////// +void SceneGraphReducer:: +r_apply_transitions(NodeRelation *arc, int transition_types, + SceneGraphReducer::AccumulatedTransitions trans, + bool duplicate) { + if (sgraphutil_cat.is_debug()) { + sgraphutil_cat.debug() + << "r_apply_transitions(" << *arc << "), arc's transitions are:\n"; + arc->write_transitions(sgraphutil_cat.debug(false), 2); + } + + if ((transition_types & TT_transform) != 0) { + nassertv(trans._transform != (TransformTransition *)NULL); + TransformTransition *tt; + if (get_transition_into(tt, arc)) { + trans._transform = + DCAST(TransformTransition, trans._transform->compose(tt)); + arc->clear_transition(TransformTransition::get_class_type()); + } + } + + if ((transition_types & TT_color) != 0) { + nassertv(trans._color != (ColorTransition *)NULL); + ColorTransition *ct; + if (get_transition_into(ct, arc)) { + trans._color = + DCAST(ColorTransition, trans._color->compose(ct)); + arc->clear_transition(ColorTransition::get_class_type()); + } + } + + if ((transition_types & TT_texture_matrix) != 0) { + nassertv(trans._texture_matrix != (TexMatrixTransition *)NULL); + TexMatrixTransition *tmt; + if (get_transition_into(tmt, arc)) { + trans._texture_matrix = + DCAST(TexMatrixTransition, trans._texture_matrix->compose(tmt)); + arc->clear_transition(TexMatrixTransition::get_class_type()); + } + } + + PT(Node) node = arc->get_child(); + nassertv(node != (Node *)NULL); + + if (sgraphutil_cat.is_debug()) { + sgraphutil_cat.debug() + << "Applying transitions to " << *node << "\n" + << "Accumulated transitions are:\n"; + trans.write(sgraphutil_cat.debug(false), transition_types, 2); + } + + + if (node->get_num_parents(_graph_type) > 1) { + // This node has multiple instances; we need to duplicate + // everything in the graph from this point down. + duplicate = true; + } + + if (duplicate) { + // Make another copy of the Node. + + if (!node->safe_to_flatten()) { + // Oops! We *can't* flatten below this node. We'll have to stop + // here. Drop transitions onto this arc to reflect what's been + // accumulated so far. + if (sgraphutil_cat.is_debug()) { + sgraphutil_cat.debug() + << "Cannot duplicate nodes of type " << node->get_type() + << "; dropping transitions here and stopping.\n"; + } + + trans.apply_to_arc(arc, transition_types); + return; + } + + PT(Node) new_node = node->make_copy(); + if (new_node->get_type() != node->get_type()) { + sgraphutil_cat.error() + << "Cannot apply transitions to " << *node + << "; don't know how to copy nodes of this type.\n"; + + trans.apply_to_arc(arc, transition_types); + return; + } + + if (sgraphutil_cat.is_debug()) { + sgraphutil_cat.debug() + << "Duplicated " << *node << "\n"; + } + + copy_children(new_node, node); + node = new_node.p(); + arc->change_child(node); + } + + // Check to see if we can't propagate any of these transitions past + // this arc for some reason. A little bit kludgey, since we have to + // know about all the special kinds of transitions, but attempting + // to make this general and also work correctly is kind of tricky. + int apply_types = 0; + if (arc->has_transition(BillboardTransition::get_class_type())) { + if (sgraphutil_cat.is_debug()) { + sgraphutil_cat.debug() + << "Arc " << *arc + << " contains a BillboardTransition; leaving transform here.\n"; + } + apply_types |= TT_transform; + } + if (!node->safe_to_transform()) { + if (sgraphutil_cat.is_debug()) { + sgraphutil_cat.debug() + << "Cannot safely transform nodes of type " << node->get_type() + << "; leaving a transform here but carrying on otherwise.\n"; + } + apply_types |= TT_transform; + } + trans.apply_to_arc(arc, transition_types & apply_types); + + // Now apply what's left to the vertices. + if (node->is_of_type(GeomNode::get_class_type())) { + if (sgraphutil_cat.is_debug()) { + sgraphutil_cat.debug() + << "Transforming geometry.\n"; + } + + // We treat GeomNodes as a special case, since we can apply more + // than just a transform matrix and so we can share vertex arrays + // across different GeomNodes. + GeomNode *gnode = DCAST(GeomNode, node); + if ((transition_types & TT_transform) != 0) { + if (trans._transform->get_matrix() != LMatrix4f::ident_mat()) { + _transformer.transform_vertices(gnode, trans._transform->get_matrix()); + } + } + if ((transition_types & TT_color) != 0) { + if (trans._color->is_on() && trans._color->is_real()) { + _transformer.set_color(gnode, trans._color->get_color()); + } + } + if ((transition_types & TT_texture_matrix) != 0) { + if (trans._texture_matrix->get_matrix() != LMatrix4f::ident_mat()) { + _transformer.transform_texcoords(gnode, + trans._texture_matrix->get_matrix()); + } + } + + } else { + // This handles any kind of node other than a GeomNode. + if ((transition_types & TT_transform) != 0) { + node->xform(trans._transform->get_matrix()); + } + } + + DownRelations::const_iterator dri; + dri = node->_children.find(_graph_type); + if (dri != node->_children.end()) { + const DownRelationPointers &drp = (*dri).second; + DownRelationPointers drp_copy = drp; + + DownRelationPointers::const_iterator drpi; + for (drpi = drp_copy.begin(); drpi != drp_copy.end(); ++drpi) { + NodeRelation *child_arc = (*drpi); + + r_apply_transitions(child_arc, transition_types, trans, duplicate); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphReducer::collapse_nodes +// Access: Protected, Virtual +// Description: Collapses the two nodes into a single node, if +// possible. The 'siblings' flag is true if the two +// nodes are siblings nodes; otherwise, node1 is a +// parent of node2. The return value is the resulting +// node, which may be either one of the source nodes, or +// a new node altogether, or it may be NULL to indicate +// that the collapse operation could not take place. +// +// This function may be extended in a user class to +// handle combining special kinds of nodes. +//////////////////////////////////////////////////////////////////// +Node *SceneGraphReducer:: +collapse_nodes(Node *node1, Node *node2, bool siblings) { + // We can collapse two GeomNodes easily, if they're siblings. If + // they're parent-child, we'd probably better not (it might + // interfere with decaling). + if (siblings && + node1->is_exact_type(GeomNode::get_class_type()) && + node2->is_exact_type(GeomNode::get_class_type())) { + GeomNode *gnode1; + DCAST_INTO_R(gnode1, node1, NULL); + GeomNode *gnode2; + DCAST_INTO_R(gnode2, node2, NULL); + gnode1->add_geoms_from(gnode2); + return gnode1; + + } else { + return GraphReducer::collapse_nodes(node1, node2, siblings); + } +} diff --git a/panda/src/sgraphutil/sceneGraphReducer.h b/panda/src/sgraphutil/sceneGraphReducer.h new file mode 100644 index 0000000000..cc7bbb9031 --- /dev/null +++ b/panda/src/sgraphutil/sceneGraphReducer.h @@ -0,0 +1,68 @@ +// Filename: sceneGraphReducer.h +// Created by: drose (22May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SCENEGRAPHREDUCER_H +#define SCENEGRAPHREDUCER_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +class Geom; + +/////////////////////////////////////////////////////////////////// +// Class : SceneGraphReducer +// Description : A specialization on GraphReducer for reducing scene +// graphs. This GraphReducer knows about special kinds +// of nodes like GeomNodes and can combine them +// appropriately. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA SceneGraphReducer : public GraphReducer { +public: + SceneGraphReducer(TypeHandle graph_type = RenderRelation::get_class_type()); + + enum TransitionTypes { + TT_transform = 0x001, + TT_color = 0x002, + TT_texture_matrix = 0x004, + }; + + void apply_transitions(Node *root, int transition_types = ~0); + +protected: + class AccumulatedTransitions { + public: + INLINE AccumulatedTransitions(); + INLINE AccumulatedTransitions(const AccumulatedTransitions ©); + INLINE void operator = (const AccumulatedTransitions ©); + + void write(ostream &out, int transition_types, int indent_level) const; + void apply_to_arc(NodeRelation *arc, int transition_types); + + PT(TransformTransition) _transform; + PT(ColorTransition) _color; + PT(TexMatrixTransition) _texture_matrix; + }; + + void r_apply_transitions(NodeRelation *arc, int transition_types, + AccumulatedTransitions trans, + bool duplicate); + + virtual Node *collapse_nodes(Node *node1, Node *node2, bool siblings); + +private: + GeomTransformer _transformer; +}; + +#include "sceneGraphReducer.I" + +#endif diff --git a/panda/src/shader/Sources.pp b/panda/src/shader/Sources.pp new file mode 100644 index 0000000000..4d30c5dafb --- /dev/null +++ b/panda/src/shader/Sources.pp @@ -0,0 +1,25 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET shader + #define LOCAL_LIBS \ + putil express display graph sgattrib light sgraphutil + + #define SOURCES \ + casterShader.I casterShader.cxx casterShader.h config_shader.cxx \ + config_shader.h outlineShader.cxx outlineShader.h \ + planarReflector.cxx planarReflector.h projtexShader.cxx \ + projtexShader.h projtexShadower.cxx projtexShadower.h shader.I \ + shader.cxx shader.h shaderTransition.I shaderTransition.cxx \ + shaderTransition.h spheretexHighlighter.cxx spheretexHighlighter.h \ + spheretexReflector.cxx spheretexReflector.h spheretexShader.cxx \ + spheretexShader.h spotlightShader.cxx spotlightShader.h + + #define INSTALL_HEADERS \ + casterShader.I casterShader.h outlineShader.h planarReflector.h \ + projtexShader.h projtexShadower.h shader.I shader.h \ + shaderTransition.I shaderTransition.h spheretexHighlighter.h \ + spheretexReflector.h spheretexShader.h spotlightShader.h + +#end lib_target + diff --git a/panda/src/shader/casterShader.I b/panda/src/shader/casterShader.I new file mode 100644 index 0000000000..de3ad6642d --- /dev/null +++ b/panda/src/shader/casterShader.I @@ -0,0 +1,35 @@ +// Filename: casterShader.I +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Function: CasterShader::add_caster +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +int CasterShader::add_caster(NamedNode* node) +{ + make_dirty(); + _casters.push_back(node); + return (_casters.size()); +} + +//////////////////////////////////////////////////////////////////// +// Function: CasterShader::remove_caster +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +int CasterShader::remove_caster(NamedNode* node) +{ + make_dirty(); + int found = 0; + NamedNodeVector::iterator i; + for (i = _casters.begin(); i != _casters.end(); ++i) { + if ((*i) == node) { + _casters.erase(i); + return true; + } + } + return false; +} diff --git a/panda/src/shader/casterShader.cxx b/panda/src/shader/casterShader.cxx new file mode 100644 index 0000000000..c03302dc3d --- /dev/null +++ b/panda/src/shader/casterShader.cxx @@ -0,0 +1,14 @@ +// Filename: casterShader.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "casterShader.h" + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle CasterShader::_type_handle; diff --git a/panda/src/shader/casterShader.h b/panda/src/shader/casterShader.h new file mode 100644 index 0000000000..d21cad7992 --- /dev/null +++ b/panda/src/shader/casterShader.h @@ -0,0 +1,67 @@ +// Filename: casterShader.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef CASTERSHADER_H +#define CASTERSHADER_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "shader.h" + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : CasterShader +// Description : Frustum Shader that computes effect based on a list +// of "casting" objects +//////////////////////////////////////////////////////////////////// +class EXPCL_SHADER CasterShader : public FrustumShader +{ + protected: + + CasterShader(void) : FrustumShader() { } + + public: + + virtual ~CasterShader(void) { } + + INLINE int get_num_casters(void) const { return _casters.size(); } + INLINE int add_caster(NamedNode* node); + INLINE int remove_caster(NamedNode* node); + + typedef vector NamedNodeVector; + + protected: + + NamedNodeVector _casters; + + public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + FrustumShader::init_type(); + register_type(_type_handle, "CasterShader", + FrustumShader::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + + private: + + static TypeHandle _type_handle; +}; + +#include "casterShader.I" + +#endif diff --git a/panda/src/shader/config_shader.cxx b/panda/src/shader/config_shader.cxx new file mode 100644 index 0000000000..32f2a4e6da --- /dev/null +++ b/panda/src/shader/config_shader.cxx @@ -0,0 +1,55 @@ +// Filename: config_shader.cxx +// Created by: drose (19Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_shader.h" +#include "casterShader.h" +#include "outlineShader.h" +#include "planarReflector.h" +#include "projtexShader.h" +#include "projtexShadower.h" +#include "shader.h" +#include "shaderTransition.h" +#include "spheretexHighlighter.h" +#include "spheretexReflector.h" +#include "spheretexShader.h" +#include "spotlightShader.h" + +#include + +Configure(config_shader); +NotifyCategoryDef(shader, ""); + +ConfigureFn(config_shader) { + CasterShader::init_type(); + OutlineShader::init_type(); + PlanarReflector::init_type(); + ProjtexShader::init_type(); + ProjtexShadower::init_type(); + Shader::init_type(); + FrustumShader::init_type(); + ShaderTransition::init_type(); + SpheretexHighlighter::init_type(); + SpheretexReflector::init_type(); + SpheretexShader::init_type(); + SpotlightShader::init_type(); + + //Setup shader rendering orders + ShaderTransition::set_shader_order(ProjtexShadower::get_class_type(), 2); + + ShaderTransition::set_shader_order(OutlineShader::get_class_type(), 4); + ShaderTransition::set_shader_order(ProjtexShader::get_class_type(), 4); + ShaderTransition::set_shader_order(SpheretexShader::get_class_type(), 4); + ShaderTransition::set_shader_order(SpotlightShader::get_class_type(), 4); + + ShaderTransition::set_shader_order(SpheretexHighlighter::get_class_type(), 6); + + ShaderTransition::set_shader_order(SpheretexReflector::get_class_type(), 8); + + ShaderTransition::set_shader_order(PlanarReflector::get_class_type(), 10); + + //Tell shaderTransition the shaders that require blending + ShaderTransition::set_shader_always_blend(SpotlightShader::get_class_type()); + ShaderTransition::set_shader_always_blend(SpheretexHighlighter::get_class_type()); +} diff --git a/panda/src/shader/config_shader.h b/panda/src/shader/config_shader.h new file mode 100644 index 0000000000..43aa99ec6d --- /dev/null +++ b/panda/src/shader/config_shader.h @@ -0,0 +1,14 @@ +// Filename: config_shader.h +// Created by: drose (19Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_SHADER_H +#define CONFIG_SHADER_H + +#include +#include + +NotifyCategoryDecl(shader, EXPCL_SHADER, EXPTP_SHADER); + +#endif diff --git a/panda/src/shader/outlineShader.cxx b/panda/src/shader/outlineShader.cxx new file mode 100644 index 0000000000..4abaa416b6 --- /dev/null +++ b/panda/src/shader/outlineShader.cxx @@ -0,0 +1,101 @@ +// Filename: outlineShader.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "outlineShader.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle OutlineShader::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: OutlineShader::constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +OutlineShader::OutlineShader(void) : Shader() { + set_color(Colorf(0, 0, 0, 1)); +} + +//////////////////////////////////////////////////////////////////// +// Function: OutlineShader::constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +OutlineShader::OutlineShader(const Colorf &color) : Shader() { + set_color(color); +} + +//////////////////////////////////////////////////////////////////// +// Function: OutlineShader::config +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void OutlineShader::config(void) { + Configurable::config(); +} + +//////////////////////////////////////////////////////////////////// +// Function: OutlineShader::apply +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void OutlineShader:: +apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, GraphicsStateGuardian *gsg) { + Shader::apply(node, init_state, net_trans, gsg); + DirectRenderTraverser drt(gsg, RenderRelation::get_class_type()); + + // If the node is un-textured, we need to render it once first (since + // the shader transition won't have drawn it unless it is textured + if (!is_textured(node, init_state)) { + gsg->render_subgraph(&drt, node, init_state, net_trans); + } + + // Copy the transition wrapper so we can modify it freely. + AllTransitionsWrapper trans(net_trans); + + // Turn lighting off + trans.set_transition(new LightTransition(LightTransition::all_off())); + + // Enable line drawing + RenderModeTransition *rm = + new RenderModeTransition(RenderModeProperty::M_wireframe, 2.0); + trans.set_transition(rm); + + // Enable line smooth + // LinesmoothTransition *lsm = new LinesmoothTransition; + // trans.set_transition(lsm); + + // Draw shared edges + DepthTestTransition *dta = + new DepthTestTransition(DepthTestProperty::M_less_equal); + trans.set_transition(dta); + + // Draw back facing edges only + CullFaceTransition *cf = + new CullFaceTransition(CullFaceProperty::M_cull_counter_clockwise); + trans.set_transition(cf); + + // Set the outline color + ColorTransition *c = new ColorTransition(_color); + trans.set_transition(c); + + gsg->render_subgraph(&drt, node, init_state, trans); +} diff --git a/panda/src/shader/outlineShader.h b/panda/src/shader/outlineShader.h new file mode 100644 index 0000000000..00b083e2cf --- /dev/null +++ b/panda/src/shader/outlineShader.h @@ -0,0 +1,58 @@ +// Filename: outlineShader.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef OUTILNESHADER_H +#define OUTLINESHADER_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "shader.h" + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : OutlineShader +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_SHADER OutlineShader : public Shader { +public: + OutlineShader(void); + OutlineShader(const Colorf &color); + ~OutlineShader() { } + + virtual void config(void); + virtual void apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, + GraphicsStateGuardian *gsg); + + INLINE void set_color(const Colorf &color) { _color = color; } + +protected: + Colorf _color; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Shader::init_type(); + register_type(_type_handle, "OutlineShader", + Shader::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/shader/planarReflector.cxx b/panda/src/shader/planarReflector.cxx new file mode 100644 index 0000000000..772ee23e05 --- /dev/null +++ b/panda/src/shader/planarReflector.cxx @@ -0,0 +1,353 @@ +// Filename: planarReflector.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "planarReflector.h" +#include "config_shader.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle PlanarReflector::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: PlanarReflector::Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +PlanarReflector::PlanarReflector(void) : CasterShader() +{ + Colorf c(0.8f, 0.8f, 0.8f, 1.0f); + init(NULL, c); +} + +//////////////////////////////////////////////////////////////////// +// Function: PlanarReflector::Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +PlanarReflector:: +PlanarReflector(PlaneNode* plane_node) : CasterShader() +{ + Colorf c(0.8f, 0.8f, 0.8f, 1.0f); + init(plane_node, c); +} + +//////////////////////////////////////////////////////////////////// +// Function: PlanarReflector::Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +PlanarReflector:: +PlanarReflector(const Colorf& c) : CasterShader() +{ + init(NULL, c); +} + +//////////////////////////////////////////////////////////////////// +// Function: PlanarReflector::Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +PlanarReflector:: +PlanarReflector(PlaneNode* plane_node, const Colorf& c) : CasterShader() +{ + init(plane_node, c); +} + +//////////////////////////////////////////////////////////////////// +// Function: PlanarReflector::init +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void PlanarReflector::init(PlaneNode *plane_node, const Colorf& c) +{ + if (plane_node == NULL) + plane_node = new PlaneNode; + + _save_color_buffer = true; + _save_depth_buffer = true; + _clip_to_plane = true; + + _color_buffer = NULL; + _depth_buffer = NULL; + + _plane_node = plane_node; + _color = c; +} + +//////////////////////////////////////////////////////////////////// +// Function: PlanarReflector::pre_apply +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void PlanarReflector:: +pre_apply(Node *, const AllAttributesWrapper &, + const AllTransitionsWrapper &, GraphicsStateGuardian *gsg) +{ + int xo, yo, w, h; + gsg->get_current_display_region()->get_region_pixels(xo, yo, w, h); + _color_buffer = new PixelBuffer(PixelBuffer::rgb_buffer(w, h)); + _depth_buffer = new PixelBuffer(PixelBuffer::depth_buffer(w, h)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PlanarReflector::apply +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void PlanarReflector:: +apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, GraphicsStateGuardian *gsg) { + DirectRenderTraverser drt(gsg, RenderRelation::get_class_type()); + + Shader::apply(node, init_state, net_trans, gsg); + + init_state.write(shader_cat->debug(false), 4); + + if (get_num_casters() == 0) { + shader_cat.error() + << "PlanarReflector::apply() - no casters in caster list" << endl; + return; + } + + //Due to possible priority problems, make an Off TextureTransition + //with a high priority to ensure that textures are indeed turned of + PT(TextureTransition) tex_off = new TextureTransition(TextureTransition::off()); + tex_off->set_priority(100); + + // If the node is un-textured, we need to render it once first (since + // the shader transition won't have drawn it unless it is textured) + + if (!_multipass_on) { + gsg->render_subgraph(&drt, node, init_state, net_trans); + } + + // Save the stencil buffer clear value for below + bool clear_stencil = gsg->get_stencil_clear_value(); + gsg->set_stencil_clear_value(false); + + { + // Copy the transition wrapper so we can modify it freely. + AllTransitionsWrapper trans(net_trans); + + // Save the current fully-rendered scene that is in the back buffer + int buffer_mask = RenderBuffer::T_stencil; + if (_save_color_buffer) { + gsg->copy_pixel_buffer(_color_buffer, + gsg->get_current_display_region(), + gsg->get_render_buffer(RenderBuffer::T_back)); + buffer_mask |= RenderBuffer::T_back; + + ColorMaskTransition *cm = new ColorMaskTransition(0); + trans.set_transition(cm); + } else { + ColorMaskTransition *cm = + new ColorMaskTransition(ColorMaskProperty::M_a); + trans.set_transition(cm); + } + + if (_save_depth_buffer) + { + gsg->copy_pixel_buffer(_depth_buffer, + gsg->get_current_display_region(), + gsg->get_render_buffer(RenderBuffer::T_depth)); + // gsg->get_render_buffer(RenderBuffer::T_back)); + } + + // The scene has already been rendered so we need to stencil in an area + // on the reflecting plane that is covered by the reflected objects. + + // Turn lighting off + trans.set_transition(new LightTransition(LightTransition::all_off())); + + // Set the depth test to M_equal (? Or should this be M_none?) + DepthTestTransition *dta = + new DepthTestTransition(DepthTestProperty::M_equal); + trans.set_transition(dta); + + // Turn off writes to the depth buffer + DepthWriteTransition *dwa = new DepthWriteTransition; + dwa->set_off(); + trans.set_transition(dwa); + + // Enable the stencil buffer + StencilTransition *sa = + new StencilTransition(StencilProperty::M_not_equal, + StencilProperty::A_replace); + trans.set_transition(sa); + + // Disable texturing + trans.set_transition(tex_off); + + // Disable blending + trans.set_transition(new ColorBlendTransition(ColorBlendProperty::M_none)); + + // Clear the stencil buffer (and color buffer if we're saving it) + gsg->clear(gsg->get_render_buffer(buffer_mask)); + + // Draw the reflecting object + gsg->render_subgraph(&drt, node, init_state, trans); + } + + { + // Reflecting area on the plane has a stencil value of 1. We can now + // draw the reflected objects into this area. + gsg->clear(gsg->get_render_buffer(RenderBuffer::T_depth)); + + // Copy the transition wrapper so we can modify it freely. + AllTransitionsWrapper trans(net_trans); + + // Adjust the stencil buffer properties + StencilTransition *sa = + new StencilTransition(StencilProperty::M_equal, + StencilProperty::A_keep); + trans.set_transition(sa); + + + // Draw back facing polys only + CullFaceTransition *cf = + new CullFaceTransition(CullFaceProperty::M_cull_counter_clockwise); + trans.set_transition(cf); + + if (_clip_to_plane) { + // Clip to the indicated plane. + ClipPlaneTransition *cp = new ClipPlaneTransition; + cp->set_on(_plane_node); + trans.set_transition(cp); + } + + // Reflect objects about the given plane + PT_Node caster_group = new Node; + NamedNodeVector::iterator c; + for (c = _casters.begin(); c != _casters.end(); ++c) { + Node *caster = (*c); + NamedNode *output = DCAST(NamedNode, caster); + + LMatrix4f mat; + get_rel_mat(caster, _plane_node, mat); + + RenderRelation *arc = new RenderRelation(caster_group, caster); + arc->set_transition(new TransformTransition(mat)); + } + + // Parent this caster group to the node itself, since it's already + // converted to the coordinate space of the node. + PT(RenderRelation) caster_arc = + new RenderRelation(_plane_node, caster_group); + + LMatrix4f plane_mat = _plane_node->get_plane().get_reflection_mat(); + caster_arc->set_transition(new TransformTransition(plane_mat)); + + // Draw the reflected objects + gsg->render_subgraph(&drt, _plane_node, init_state, trans); + + // Remove the caster group from the scene graph. This will also + // delete the temporary group node (since the reference count is + // zero), but not the caster objects themselves. + remove_arc(caster_arc); + } + + { + // Draw the reflecting object once more to modulate the reflected objects + // by the reflectivity color + AllTransitionsWrapper trans(net_trans); + + trans.set_transition(new StencilTransition(StencilProperty::M_none)); + + ColorBlendTransition *cb = + new ColorBlendTransition(ColorBlendProperty::M_multiply); + trans.set_transition(cb); + + ColorTransition *c = new ColorTransition; + c->set_on(_color); + trans.set_transition(c); + + trans.set_transition(new LightTransition(LightTransition::all_off())); + trans.set_transition(tex_off); + + gsg->render_subgraph(&drt, node, init_state, trans); + } + + // Blend the previously rendered image with the reflected image + + if (_save_color_buffer) { + NodeAttributes na; + + ColorBlendAttribute *cb = + new ColorBlendAttribute; + cb->set_mode(ColorBlendProperty::M_add); + na.set_attribute(ColorBlendTransition::get_class_type(), cb); + + gsg->draw_pixel_buffer(_color_buffer, gsg->get_current_display_region(), + gsg->get_render_buffer(RenderBuffer::T_back), na); + } else { + // One more pass to redraw the reflecting object (additive blending) + // Final color is reflecting obj color + (ref. obj. color * reflectivity) + AllTransitionsWrapper trans(net_trans); + + StencilTransition *sa = + new StencilTransition(StencilProperty::M_equal, + StencilProperty::A_keep); + trans.set_transition(sa); + + DepthTestTransition *dta = + new DepthTestTransition(DepthTestProperty::M_equal); + trans.set_transition(dta); + + DepthWriteTransition *dwa = new DepthWriteTransition; + dwa->set_off(); + trans.set_transition(dwa); + + ColorBlendTransition *cb = + new ColorBlendTransition(ColorBlendProperty::M_add); + trans.set_transition(cb); + + gsg->render_subgraph(&drt, node, init_state, trans); + } + + if (_save_depth_buffer) + { + gsg->draw_pixel_buffer(_depth_buffer, gsg->get_current_display_region(), + gsg->get_render_buffer(RenderBuffer::T_depth)); + // gsg->get_render_buffer(RenderBuffer::T_back)); + } + + + // Restore the stencil buffer clear value + gsg->set_stencil_clear_value(clear_stencil); +} + + + + diff --git a/panda/src/shader/planarReflector.h b/panda/src/shader/planarReflector.h new file mode 100644 index 0000000000..e8b251c88c --- /dev/null +++ b/panda/src/shader/planarReflector.h @@ -0,0 +1,82 @@ +// Filename: planarReflector.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef PLANARREFLECTOR_H +#define PLANARREFLECTOR_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "casterShader.h" +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : PlanarReflector +// Description : Reflects about a specified plane +//////////////////////////////////////////////////////////////////// +class EXPCL_SHADER PlanarReflector : public CasterShader +{ + public: + + PlanarReflector(void); + PlanarReflector(PlaneNode* plane_node); + PlanarReflector(const Colorf& c); + PlanarReflector(PlaneNode* plane_node, const Colorf& c); + + virtual void pre_apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, + GraphicsStateGuardian *gsg); + virtual void apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, + GraphicsStateGuardian *gsg); + + INLINE void set_save_color_buffer(bool val) { _save_color_buffer = val; } + INLINE void set_save_depth_buffer(bool val) { _save_depth_buffer = val; } + INLINE void set_clip_to_plane(bool val) { _clip_to_plane = val; } + INLINE void set_plane_node(PlaneNode* p) { _plane_node = p; } + INLINE void set_color(const Colorf& c) { _color = c; } + + protected: + + void init(PlaneNode* plane_node, const Colorf& c); + + protected: + + bool _save_color_buffer; + bool _save_depth_buffer; + bool _clip_to_plane; + PT(PixelBuffer) _color_buffer; + PT(PixelBuffer) _depth_buffer; + PT(PlaneNode) _plane_node; + Colorf _color; + + public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CasterShader::init_type(); + register_type(_type_handle, "PlanarReflector", + CasterShader::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + + private: + + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/shader/projtexShader.cxx b/panda/src/shader/projtexShader.cxx new file mode 100644 index 0000000000..ca17c12532 --- /dev/null +++ b/panda/src/shader/projtexShader.cxx @@ -0,0 +1,145 @@ +// Filename: projtexShader.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "projtexShader.h" +#include "config_shader.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle ProjtexShader::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ProjtexShader::constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +ProjtexShader::ProjtexShader(Texture* texture, + ColorBlendProperty::Mode mode) + : FrustumShader(), _blend(mode) +{ + set_texture(texture); +} + +//////////////////////////////////////////////////////////////////// +// Function: ProjtexShader::config +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void ProjtexShader::config(void) +{ + Configurable::config(); + + nassertv(_texture != (Texture *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: ProjtexShader::apply +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void ProjtexShader:: +apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, GraphicsStateGuardian *gsg) { + + Shader::apply(node, init_state, net_trans, gsg); + + // Make sure the shader has been configured properly + if (get_num_frusta() == 0) { + shader_cat.error() + << "ProjtexShader::apply() - frusta list is empty" << endl; + return; + } else if (get_num_frusta() > 1) { + shader_cat.warning() + << "ProjtexShader::apply() - frusta list has more than one " + << "- ignoring all but first one for now..." << endl; + } + + // Copy the transition wrapper so we can modify it freely. + AllTransitionsWrapper trans(net_trans); + + // Create the texture projector transition + // NOTE: If the node being projected upon has its own textures, + // we need to render it first with its own texturing before + // this projected texture pass and then blend the results + LMatrix4f model_mat; + const Projection* projection = _frusta[0]->get_projection(); + get_rel_mat(node, _frusta[0], model_mat); + + // Create the texture generation transition + TexGenTransition *tg = new TexGenTransition; + tg->set_texture_projector(); + trans.set_transition(tg); + // Create the texture transition + nassertv(_texture != (Texture *)NULL); + TextureTransition *t = new TextureTransition; + t->set_on(_texture); + t->set_priority(_priority); + trans.set_transition(t); + + if (_viz != (Shader::Visualize*)0L) + _viz->DisplayTexture(_texture, this); + // Create the texture matrix transition + // Define texture matrix so that object space coords used as + // tex coords will be projected onto the frustum's viewing plane + // An additional scale and translate is required to go from x,y + // NDC coords in [-1,1] to tex coords [0,1] + LMatrix4f tex_mat, proj_mat; + const PerspectiveProjection *pp = DCAST(PerspectiveProjection, projection); + proj_mat = pp->get_projection_mat(); + tex_mat = model_mat * proj_mat * + LMatrix4f::scale_mat(LVector3f(0.5,0.5,1)) * + LMatrix4f::translate_mat(LVector3f(0.5,0.5,0)); + + TexMatrixTransition *tm = new TexMatrixTransition; + tm->set_matrix(tex_mat); + trans.set_transition(tm); + + // Turn lighting off + trans.set_transition(new LightTransition(LightTransition::all_off())); + // Do some extra work if we're doing the 2-pass version (e.g. the + // object we are shading is textured) + if (_multipass_on) { + //Probably should move the brains for determing blending out to + //shader transition + + // Set a color blend mode that assumes this is a second pass over + // textured geometry + ColorBlendTransition *cb = new ColorBlendTransition(_blend); + trans.set_transition(cb); + + TextureApplyTransition *ta = + new TextureApplyTransition(TextureApplyProperty::M_decal); + trans.set_transition(ta); + + // Set the depth test to M_equal (? Or should this be M_none?) + DepthTestTransition *dta = + new DepthTestTransition(DepthTestProperty::M_equal); + trans.set_transition(dta); + } + + DirectRenderTraverser drt(gsg, RenderRelation::get_class_type()); + gsg->render_subgraph(&drt, node, init_state, trans); +} + + + diff --git a/panda/src/shader/projtexShader.h b/panda/src/shader/projtexShader.h new file mode 100644 index 0000000000..1c2e3b9fea --- /dev/null +++ b/panda/src/shader/projtexShader.h @@ -0,0 +1,71 @@ +// Filename: projtexShader.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef PROJTEXSHADER_H +#define PROJTEXSHADER_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "shader.h" +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : ProjtexShader +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_SHADER ProjtexShader : public FrustumShader +{ +public: + + ProjtexShader(Texture* texture = NULL, + ColorBlendProperty::Mode mode = ColorBlendProperty::M_multiply); + ~ProjtexShader() { } + + virtual void config(void); + virtual void apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, + GraphicsStateGuardian *gsg); + + INLINE void set_texture(Texture* texture) { + _texture = texture; + make_dirty(); + } + + INLINE Texture* get_texture(void) { return _texture; } + +protected: + + PT(Texture) _texture; + ColorBlendProperty::Mode _blend; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + FrustumShader::init_type(); + register_type(_type_handle, "ProjtexShader", + FrustumShader::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/shader/projtexShadower.cxx b/panda/src/shader/projtexShadower.cxx new file mode 100644 index 0000000000..dad085f46e --- /dev/null +++ b/panda/src/shader/projtexShadower.cxx @@ -0,0 +1,249 @@ +// Filename: projtexShadower.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "projtexShadower.h" +#include "config_shader.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle ProjtexShadower::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ProjtexShadower::Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +ProjtexShadower::ProjtexShadower(int size) : CasterShader() +{ + set_size(size); + + Texture* texture = new Texture; + texture->set_minfilter(Texture::FT_linear); + texture->set_magfilter(Texture::FT_linear); + texture->set_wrapu(Texture::WM_clamp); + texture->set_wrapv(Texture::WM_clamp); + texture->_pbuffer->set_xsize(_size); + texture->_pbuffer->set_ysize(_size); + + _projtex_shader = new ProjtexShader(texture, ColorBlendProperty::M_multiply); +} + +//////////////////////////////////////////////////////////////////// +// Function: ProjtexShadower::set_priority +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ProjtexShadower:: +set_priority(int priority) +{ + Shader::set_priority(priority); + _projtex_shader->set_priority(priority); +} + +//////////////////////////////////////////////////////////////////// +// Function: ProjtexShadower::set_multipass +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ProjtexShadower:: +set_multipass(bool on) +{ + Shader::set_multipass(on); + _projtex_shader->set_multipass(on); +} + +//////////////////////////////////////////////////////////////////// +// Function: ProjtexShadower::pre_apply +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void ProjtexShadower:: +pre_apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, GraphicsStateGuardian *gsg) +{ + DirectRenderTraverser drt(gsg, RenderRelation::get_class_type()); + + int num_lights = 1; + + if (get_num_casters() == 0) { + shader_cat.error() + << "ProjtexShadower::config() - no casters in caster list" << endl; + return; + } + if (get_num_frusta() == 0) { + shader_cat.error() + << "ProjtexShadower::config() - no lights in frusta list" << endl; + return; + } else if (get_num_frusta() > 1) { + shader_cat.warning() + << "ProjtexShadower::config() - frusta list has more than one " + << "frustum - ignore all but the first one for now..." << endl; + num_lights = 1; + } + if (_frusta[0]->get_type() != Spotlight::get_class_type()) { + shader_cat.error() + << "ProjtexShadower::config() - only works for Spotlights" << endl; + return; + } + Spotlight* light = (Spotlight *)_frusta[0]; + + // Save the current display region from the gsg + Colorf clear_color = gsg->get_color_clear_value(); + + // Make sure the projtex shader has the same frustum + _projtex_shader->remove_frustum(light); + _projtex_shader->add_frustum(light); + + // Compute an approximation for the shadow color + Colorf diffuse = light->get_color(); + Colorf shadow_color; + for (int i = 0; i < 4; i++) + shadow_color[i] = 1.0 / ((diffuse[i] + 1) * num_lights); + + nassertv(node != (Node *)NULL && gsg != (GraphicsStateGuardian *)NULL); + + // First, draw the receiving node quickly (with no rendering attributes) + // from the point of view of the light + + // Make a display region of the proper size and clear it to prepare for + // rendering the shadow map + PT(DisplayRegion) disp_region = + gsg->get_window()->make_scratch_display_region(_size, _size); + DisplayRegionStack old_dr = gsg->push_display_region(disp_region); + + // Save scratch region before clearing it to be non-destructive + FrameBufferStack old_fb = gsg->push_frame_buffer + (gsg->get_render_buffer(RenderBuffer::T_back | RenderBuffer::T_depth), + disp_region); + + gsg->set_color_clear_value(Colorf(1., 1., 1., 1.)); + gsg->clear(gsg->get_render_buffer(RenderBuffer::T_back | + RenderBuffer::T_depth), disp_region); + + // Copy the transition wrapper so we can modify it freely. + AllTransitionsWrapper trans(net_trans); + + // Disable lighting, texturing, blending, dithering + trans.set_transition(new LightTransition(LightTransition::all_off())); + //The 100 is to set the priority of this transition high enough + //(hopefully) to turn off any existing texture in the geometry + TextureTransition *t = new TextureTransition(TextureTransition::off()); + t->set_priority(100); + trans.set_transition(t); + trans.set_transition(new ColorBlendTransition(ColorBlendProperty::M_none)); + + + //We need a state that we can modify to remove the camera transform + //that is passed to us + AllAttributesWrapper state(init_state); + state.clear_attribute(TransformTransition::get_class_type()); + + // Render the node with the new transitions from the viewpoint of the light + + gsg->render_subgraph(&drt, node, light, state, trans); + + // Now draw each of the casting objects in the shadow color using the + // depth buffer from the receiving object (this will project the shadows + // onto the surface properly), and use this to generate the shadow texture + // map + + DepthTestTransition *dta = + new DepthTestTransition(DepthTestProperty::M_less); + trans.set_transition(dta); + + DepthWriteTransition *dwa = new DepthWriteTransition; + trans.set_transition(dwa); + + ColorTransition *ca = new ColorTransition(shadow_color); + trans.set_transition(ca); + + // Create a temporary node that is the parent of all of the caster + // objects, so we can render them from the point of view of the + // light. + + PT_Node caster_group = new Node; + NamedNodeVector::iterator ci; + for (ci = _casters.begin(); ci != _casters.end(); ++ci) { + Node *caster = (*ci); + + LMatrix4f mat; + get_rel_mat(caster, node, mat); + + RenderRelation *arc = new RenderRelation(caster_group, caster); + arc->set_transition(new TransformTransition(mat)); + } + + // Parent this caster group to the node itself, since it's already + // converted to the coordinate space of the node + PT(RenderRelation) caster_arc = new RenderRelation(node, caster_group); + + // Now render the group. + gsg->render_subgraph(&drt, caster_group, light, state, trans); + + // Remove the caster group from the scene graph. This will also + // delete the temporary group node (since the reference count is + // zero), but not the caster objects themselves. + remove_arc(caster_arc); + + // Copy the results of the render from the framebuffer into the projtex + // shader's texture + // _projtex_shader->get_texture()->prepare(gsg); + _projtex_shader->get_texture()->copy(gsg, disp_region, gsg->get_render_buffer(RenderBuffer::T_back)); + + // Restore the original display region and clear value + + gsg->set_color_clear_value(clear_color); + gsg->pop_frame_buffer(old_fb); + gsg->pop_display_region(old_dr); +} + +//////////////////////////////////////////////////////////////////// +// Function: ProjtexShadower::apply +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void ProjtexShadower:: +apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, GraphicsStateGuardian *gsg) { + + Shader::apply(node, init_state, net_trans, gsg); + + // Now apply the projected shadow map + _projtex_shader->apply(node, init_state, net_trans, gsg); +} + + + + + + + + + + + + + diff --git a/panda/src/shader/projtexShadower.h b/panda/src/shader/projtexShadower.h new file mode 100644 index 0000000000..8b201522d7 --- /dev/null +++ b/panda/src/shader/projtexShadower.h @@ -0,0 +1,77 @@ +// Filename: projtexShadower.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef PROJTEXSHADOWER_H +#define PROJTEXSHADOWER_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "casterShader.h" +#include "projtexShader.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : ProjtexShadower +// Description : Creates the shadow of a casting object(s) and projects +// it onto a receiving object(s) from a given light +// frustum +//////////////////////////////////////////////////////////////////// +class EXPCL_SHADER ProjtexShadower : public CasterShader +{ +public: + + ProjtexShadower(int size = 256); + + virtual void pre_apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, + GraphicsStateGuardian *gsg); + virtual void apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, + GraphicsStateGuardian *gsg); + + // MPG - should we make this check for powers of 2? + INLINE void set_size(int size) { _size = size; } + INLINE int get_size(void) { return _size; } + + //Override base class shader's set_priority to allow + //us to set the priority of the contained projtex_shader at + //the same time + virtual void set_priority(int priority); + virtual void set_multipass(bool on); + +protected: + + PT(ProjtexShader) _projtex_shader; + int _size; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CasterShader::init_type(); + register_type(_type_handle, "ProjtexShadower", + CasterShader::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/shader/shader.I b/panda/src/shader/shader.I new file mode 100644 index 0000000000..73e512a2c5 --- /dev/null +++ b/panda/src/shader/shader.I @@ -0,0 +1,55 @@ +// Filename: shader.I +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Function: Shader::get_viz +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +Shader::Visualize* Shader::get_viz(void) +{ + return _viz; +} + +//////////////////////////////////////////////////////////////////// +// Function: Shader::set_viz +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void Shader::set_viz(Shader::Visualize* v) +{ + _viz = v; +} + +//////////////////////////////////////////////////////////////////// +// Function: FrustumShader::add_frustum +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +int FrustumShader::add_frustum(ProjectionNode* frust) +{ + make_dirty(); + _frusta.push_back(frust); + return (_frusta.size()); +} + +//////////////////////////////////////////////////////////////////// +// Function: FrustumShader::remove_frustum +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +int FrustumShader::remove_frustum(ProjectionNode* frust) +{ + make_dirty(); + int found = 0; + ProjectionVector::iterator i; + for (i = _frusta.begin(); i != _frusta.end(); ++i) { + if ((*i) == frust) { + _frusta.erase(i); + return true; + } + } + return false; +} diff --git a/panda/src/shader/shader.cxx b/panda/src/shader/shader.cxx new file mode 100644 index 0000000000..d845143349 --- /dev/null +++ b/panda/src/shader/shader.cxx @@ -0,0 +1,101 @@ +// Filename: shader.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "shader.h" + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle Shader::_type_handle; +TypeHandle FrustumShader::_type_handle; + +Shader::Visualize* Shader::_viz = (Shader::Visualize*)0L; + +Shader::Visualize::Visualize(void) +{ +} + +Shader::Visualize::~Visualize(void) +{ +} + +void Shader::Visualize::DisplayTexture(PT(Texture)&, Shader*) +{ +} + +void Shader::Visualize::DisplayPixelBuffer(PT(PixelBuffer)&, Shader*) +{ +} + +void Shader::Visualize::Flush(void) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: Shader::apply +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Shader:: +apply(Node *, const AllAttributesWrapper &, + const AllTransitionsWrapper &, + GraphicsStateGuardian *) +{ + if (is_dirty()) config(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Shader::pre_apply +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Shader:: +pre_apply(Node *, const AllAttributesWrapper &, + const AllTransitionsWrapper &, + GraphicsStateGuardian *) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: Shader::set_priority +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Shader:: +set_priority(int priority) +{ + _priority = priority; +} + +//////////////////////////////////////////////////////////////////// +// Function: Shader::get_priority +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int Shader:: +get_priority(void) const +{ + return _priority; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Shader::set_multipass +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Shader:: +set_multipass(bool on) +{ + _multipass_on = on; +} + + + + + diff --git a/panda/src/shader/shader.h b/panda/src/shader/shader.h new file mode 100644 index 0000000000..a578e1dbee --- /dev/null +++ b/panda/src/shader/shader.h @@ -0,0 +1,141 @@ +// Filename: shader.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef SHADER_H +#define SHADER_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : Shader +// Description : Renders an effect on a list of receiving objects +//////////////////////////////////////////////////////////////////// +class EXPCL_SHADER Shader : public ReferenceCount, public Configurable +{ +protected: + + Shader(void) : Configurable(), _priority(0) { } + + int _priority; + bool _multipass_on; + +public: + + virtual ~Shader(void) { } + + virtual void pre_apply(Node *, const AllAttributesWrapper &, + const AllTransitionsWrapper &, + GraphicsStateGuardian *); + + + virtual void apply(Node *, const AllAttributesWrapper &, + const AllTransitionsWrapper &, + GraphicsStateGuardian *); + + virtual void set_priority(int priority); + virtual int get_priority(void) const; + virtual void set_multipass(bool on); + +public: + + // This is used to visualize partial results, for debugging (and demos) + class EXPCL_SHADER Visualize { + public: + Visualize(void); + virtual ~Visualize(void); + virtual void DisplayTexture(PT(Texture)&, Shader*) = 0; + virtual void DisplayPixelBuffer(PT(PixelBuffer)&, Shader*) = 0; + virtual void Flush(void) = 0; + }; + + static INLINE Visualize* get_viz(void); + static INLINE void set_viz(Visualize*); + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ReferenceCount::init_type(); + Configurable::init_type(); + register_type(_type_handle, "Shader", + ReferenceCount::get_class_type(), + Configurable::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + + static Visualize* _viz; + +private: + + static TypeHandle _type_handle; +}; + +//////////////////////////////////////////////////////////////////// +// Class : FrustumShader +// Description : Shader that computes effect based on a frustum +// (most often a light or a camera) for a list of +// receiving objects +//////////////////////////////////////////////////////////////////// +class EXPCL_SHADER FrustumShader : public Shader +{ +protected: + + FrustumShader(void) : Shader() { } + +public: + + virtual ~FrustumShader(void) { } + + INLINE int get_num_frusta(void) const { return _frusta.size(); } + INLINE int add_frustum(ProjectionNode* frust); + INLINE int remove_frustum(ProjectionNode* frust); + + typedef vector ProjectionVector; + +protected: + + ProjectionVector _frusta; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Shader::init_type(); + register_type(_type_handle, "FrustumShader", + Shader::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + +private: + + static TypeHandle _type_handle; +}; + +#include "shader.I" + +#endif diff --git a/panda/src/shader/shaderTransition.I b/panda/src/shader/shaderTransition.I new file mode 100644 index 0000000000..c0d4f31af8 --- /dev/null +++ b/panda/src/shader/shaderTransition.I @@ -0,0 +1,67 @@ +// Filename: shaderTransition.I +// Created by: drose (24Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////// +// Function: ShaderTransition::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ShaderTransition:: +ShaderTransition() { +} + +//////////////////////////////////////////////////////////////////// +// Function: set_shader +// Description: An external function, handy for setting shaders +// directly onto arcs. This will add the indicated +// shader to an existing ShaderTransition, or create a +// new ShaderTransition if necessary. +//////////////////////////////////////////////////////////////////// +INLINE bool +set_shader(NodeRelation *arc, Shader *shader) { + ShaderTransition *st; + if (get_transition_into(st, arc)) { + return st->set_shader(shader); + } + st = new ShaderTransition; + st->set_shader(shader); + arc->set_transition(st); + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: clear_shader +// Description: An external function, handy for setting shaders +// directly onto arcs. This will remove the indicated +// shader from the existing ShaderTransition, if there +// is one. +//////////////////////////////////////////////////////////////////// +INLINE bool +clear_shader(NodeRelation *arc, Shader *shader) { + ShaderTransition *st; + if (get_transition_into(st, arc)) { + return st->clear_shader(shader); + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: has_shader +// Description: An external function, handy for setting shaders +// directly onto arcs. This returns true if there +// exists a ShaderTransition and it contains the +// indicated shader, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool +has_shader(const NodeRelation *arc, Shader *shader) { + const ShaderTransition *st; + if (get_transition_into(st, arc)) { + return st->has_shader(shader); + } + return false; +} diff --git a/panda/src/shader/shaderTransition.cxx b/panda/src/shader/shaderTransition.cxx new file mode 100644 index 0000000000..0369bc2cb3 --- /dev/null +++ b/panda/src/shader/shaderTransition.cxx @@ -0,0 +1,311 @@ +// Filename: shaderTransition.cxx +// Created by: mike (06Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "shaderTransition.h" + +#include +#include +#include +#include +#include +#include + +#include "config_shader.h" + +TypeHandle ShaderTransition::_type_handle; +ShaderTransition::ShaderOrder* ShaderTransition::_shader_order = (ShaderTransition::ShaderOrder *)NULL; +ShaderTransition::ShaderBlend* ShaderTransition::_shader_always_blend = (ShaderTransition::ShaderBlend *)NULL; + +//////////////////////////////////////////////////////////////////// +// Function: ShaderTransition::make_copy +// Access: Public, Virtual +// Description: Returns a newly allocated ShaderTransition just like +// this one. +//////////////////////////////////////////////////////////////////// +NodeTransition *ShaderTransition:: +make_copy() const { + return new ShaderTransition(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderTransition::set_shader_order +// Access: Public, Static +// Description: Stores the order "priority" of a shader. (Note, there +// is nothing to prevent you from adding a none shader +// handle type, but it won't really do you any good). +// This order value is used to make sure that multiple +// shaders are ordered in a defined, hopefully +// intelligent order. +//////////////////////////////////////////////////////////////////// +void ShaderTransition:: +set_shader_order(TypeHandle shader, int order) { + if (_shader_order == (ShaderOrder *)NULL) + { + _shader_order = new ShaderOrder; + } + (*_shader_order)[shader] = order; +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderTransition::set_shader_blend +// Access: Public, Static +// Description: Some shaders (i.e. spotlight and highlight) need to +// blend with the scene no matter what. +//////////////////////////////////////////////////////////////////// +void ShaderTransition:: +set_shader_always_blend(TypeHandle shader) { + if (_shader_always_blend == (ShaderBlend *)NULL) + { + _shader_always_blend = new ShaderBlend; + } + _shader_always_blend->insert(shader); +} + + +//////////////////////////////////////////////////////////////////// +// Function: ShaderTransition::clear +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void ShaderTransition:: +clear() { + _shaders.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderTransition::is_empty +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool ShaderTransition:: +is_empty() const { + return _shaders.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderTransition::insert +// Access: Public +// Description: Inserts the shader into the list, based +// on the set order of the list. If there is no order set for +// the shader, it is put at the end of the list. +//////////////////////////////////////////////////////////////////// +void ShaderTransition:: +insert(Shader *shader) { + PT(Shader) sp(shader); + Shaders::iterator si = _shaders.begin(); + ShaderOrder::iterator soi; + int order = -1; + + if ((_shader_order == NULL) && (_overrides.find(sp) == _overrides.end())) + { + si = _shaders.end(); + } + + if (_overrides.find(sp) != _overrides.end()) + { + order = _overrides[sp]; + } + else if (_shader_order->find(sp->get_type()) != _shader_order->end()) + { + order = (*_shader_order)[sp->get_type()]; + } + + if (order == -1) + { + si = _shaders.end(); + } + + while(si != _shaders.end()) + { + if (_overrides.find((*si)) != _overrides.end()) + { + if (order < _overrides[(*si)]) + { + break; + } + } + else if (order < (*_shader_order)[(*si)->get_type()]) + { + break; + } + si++; + } + + _shaders.insert(si, shader); +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderTransition::set_shader +// Access: Public +// Description: Adds the shader to the transition, if it is not +// already there. Returns true if the shader was added, +// false if it was there already. If the shader *was* +// already on the list, it is moved to the end of the +// list. +//////////////////////////////////////////////////////////////////// +bool ShaderTransition:: +set_shader(Shader *shader, int override) { + // We need to search for a PT(Shader), not a Shader* . + PT(Shader) sp(shader); + + if (override != -1) + { + _overrides[sp] = override; + } + + Shaders::iterator si = find(_shaders.begin(), _shaders.end(), sp); + if (si != _shaders.end()) { + // The shader was there already; move it to the end. + return false; + } + insert(shader); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderTransition::clear_shader +// Access: Public +// Description: Removes the first occurrence of the indicated shader +// from the list, and returns true if anything was +// removed, false if there were no occurrences. +//////////////////////////////////////////////////////////////////// +bool ShaderTransition:: +clear_shader(Shader *shader) { + // We need to search for a PT(Shader), not a Shader* . + PT(Shader) sp(shader); + Shaders::iterator si = find(_shaders.begin(), _shaders.end(), sp); + if (si != _shaders.end()) { + _shaders.erase(si); + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderTransition::has_shader +// Access: Public +// Description: Returns true if the indicated shader appears on the +// list, false otherwise. +//////////////////////////////////////////////////////////////////// +bool ShaderTransition:: +has_shader(Shader *shader) const { + // We need to search for a PT(Shader), not a Shader* . + PT(Shader) sp(shader); + Shaders::const_iterator si = find(_shaders.begin(), _shaders.end(), sp); + return (si != _shaders.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderTransition::begin +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ShaderTransition::const_iterator ShaderTransition:: +begin() const { + return _shaders.begin(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderTransition::end +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ShaderTransition::const_iterator ShaderTransition:: +end() const { + return _shaders.end(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderTransition::must_blend +// Access: Public +// Description: Checks to see if one of the shaders on the shaderTransition +// is a kind that requires blending +//////////////////////////////////////////////////////////////////// +bool ShaderTransition:: +must_blend() +{ + const_iterator si; + for(si = begin(); si != end(); si++) + { + if (_shader_always_blend->find((*si)->get_type()) != _shader_always_blend->end()) + { + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderTransition::sub_render +// Access: Public, Virtual +// Description: This is called by the RenderTraverser to tell the +// shader to do its thing. +//////////////////////////////////////////////////////////////////// +bool ShaderTransition:: +sub_render(NodeRelation *arc, const AllAttributesWrapper &attrib, + AllTransitionsWrapper &trans, GraphicsStateGuardianBase *gsgbase) { + Node *node = arc->get_child(); + GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, gsgbase); + bool multipass_on = false; + + // No shaders; never mind. + if (is_empty()) { + return true; + } + + AllAttributesWrapper node_attrib; + node_attrib.apply_from(attrib, trans); + + multipass_on = must_blend() | is_textured(node, node_attrib) | + is_shaded(node) | (_shaders.size() > 1); + + const_iterator si; + // In order to avoid strange nasty looking artifacts due to(?) the + // depth buffer not being restored properly when we employ scratch + // display regions. Call pre-apply before the scene has been + // rendered. Any shader that uses a scratch region will do it's + // thing and then when the scene is actually rendered, it will blow + // those artifacts away. Those shaders will store their work to be + // actually applied later when we call apply. + for (si = begin(); si != end(); ++si) + { + // We'll go ahead and tell each shader in turn if multipass is + // needed or not now. + (*si)->set_multipass(multipass_on); + (*si)->pre_apply(node, node_attrib, AllTransitionsWrapper(), gsg); + } + + if ( multipass_on ) + { + // Multipass is needed at this point. Render the node with its normal + // attributes (including texturing). + DirectRenderTraverser drt(gsg, RenderRelation::get_class_type()); + gsg->render_subgraph(&drt, node, node_attrib, AllTransitionsWrapper()); + } + + // Now apply each shader in turn. + for (si = begin(); si != end(); ++si) + { + (*si)->apply(node, node_attrib, AllTransitionsWrapper(), gsg); + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderTransition::has_sub_render +// Access: Public, Virtual +// Description: Should be redefined to return true if the function +// sub_render(), above, expects to be called during +// traversal. +//////////////////////////////////////////////////////////////////// +bool ShaderTransition:: +has_sub_render() const { + return true; +} + + + diff --git a/panda/src/shader/shaderTransition.h b/panda/src/shader/shaderTransition.h new file mode 100644 index 0000000000..a333052bcc --- /dev/null +++ b/panda/src/shader/shaderTransition.h @@ -0,0 +1,109 @@ +// Filename: shaderTransition.h +// Created by: mike (19Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SHADERTRANSITION_H +#define SHADERTRANSITION_H + +#include + +#include "shader.h" + +#include +#include +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : ShaderTransition +// Description : A ShaderTransition holds the set of shaders that +// might be in effect to render a particular part of the +// subgraph. These shaders can range from lightweight +// rendering effects to full-blown multipass renderings; +// use with caution. +//////////////////////////////////////////////////////////////////// +class EXPCL_SHADER ShaderTransition : public ImmediateTransition { +private: + // typedef vector Shaders; + typedef list Shaders; + typedef map ShaderOrder; + typedef map ShaderOverride; + typedef set ShaderBlend; + +public: + INLINE ShaderTransition(); + +public: + // Functions to access and adjust the list of shaders. + typedef Shaders::const_iterator iterator; + typedef Shaders::const_iterator const_iterator; + + void clear(); + bool is_empty() const; + void insert(Shader *shader); + + //If you wish to override the ordering of your shader. Say for some + //reason you don't want this Shadow to appear in the reflections, or you want + //Spheremaped objects to reflect the reflections in planar objects instead of + //the other way around. Then set override appropriately. + bool set_shader(Shader *shader, int override = -1); + bool clear_shader(Shader *shader); + bool has_shader(Shader *shader) const; + + const_iterator begin() const; + const_iterator end() const; + +public: + static void set_shader_order(TypeHandle shader, int order); + static void set_shader_always_blend(TypeHandle shader); + +private: + bool must_blend(); + +public: + virtual NodeTransition *make_copy() const; + + virtual bool sub_render(NodeRelation *arc, + const AllAttributesWrapper &attrib, + AllTransitionsWrapper &trans, + GraphicsStateGuardianBase *gsgbase); + virtual bool has_sub_render() const; + +private: + Shaders _shaders; + static ShaderOrder* _shader_order; + static ShaderBlend* _shader_always_blend; + ShaderOverride _overrides; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ImmediateTransition::init_type(); + register_type(_type_handle, "ShaderTransition", + ImmediateTransition::get_class_type()); + } + +private: + static TypeHandle _type_handle; + friend class ShaderAttribute; +}; + +INLINE bool set_shader(NodeRelation *arc, Shader *shader); +INLINE bool clear_shader(NodeRelation *arc, Shader *shader); +INLINE bool has_shader(const NodeRelation *arc, Shader *shader); + +#include "shaderTransition.I" + +#endif + + diff --git a/panda/src/shader/spheretexHighlighter.cxx b/panda/src/shader/spheretexHighlighter.cxx new file mode 100644 index 0000000000..c2b098e75d --- /dev/null +++ b/panda/src/shader/spheretexHighlighter.cxx @@ -0,0 +1,176 @@ +// Filename: spheretexHighlighter.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "spheretexHighlighter.h" +#include "config_shader.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle SpheretexHighlighter::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: SpheretexHighlighter::Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +SpheretexHighlighter::SpheretexHighlighter(int size) : FrustumShader() +{ + set_size(size); + + Texture* texture = new Texture; + texture->set_minfilter(Texture::FT_linear); + texture->set_magfilter(Texture::FT_linear); + texture->set_wrapu(Texture::WM_clamp); + texture->set_wrapv(Texture::WM_clamp); + texture->_pbuffer = new PixelBuffer(PixelBuffer::rgb_buffer(_size, _size)); + _spheretex_shader.set_texture(texture); + _spheretex_shader.set_blend_mode(ColorBlendProperty::M_add); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpheretexHighlighter::set_multipass +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void SpheretexHighlighter:: +set_multipass(bool) +{ + //A highlight shader always needs to blend + _multipass_on = true; + _spheretex_shader.set_multipass(true); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpheretexHighlighter::set_priority +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void SpheretexHighlighter:: +set_priority(int priority) +{ + Shader::set_priority(priority); + _spheretex_shader.set_priority(priority); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpheretexHighlighter::pre_apply +// Access: +// Description: +/////////////////////////////////////////////////////////////////// +void SpheretexHighlighter:: +pre_apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &, GraphicsStateGuardian *gsg) +{ + if (get_num_frusta() == 0) { + shader_cat.error() + << "SpheretexHighlighter::config() - no lights in frusta list" + << endl; + return; + } else if (get_num_frusta() > 1) { + shader_cat.warning() + << "SpheretexHighlighter::config() - frusta list has more than one " + << "frustum - ignore all but the first one for now..." << endl; + } + if (_frusta[0]->get_type() != Spotlight::get_class_type()) { + shader_cat.error() + << "SpheretexHighlighter::config() - only works for Spotlights" + << " so far - we'll add point lights later" << endl; + return; + } + // MPG - we could make this work for point lights as well + Spotlight* light = (Spotlight *)_frusta[0]; + + // Figure out how shiny the highlighted object is + float shininess = 0.0; + + const NodeAttribute *mat_attrib = + init_state.get_attribute(MaterialTransition::get_class_type()); + if (mat_attrib != (NodeAttribute *)NULL) { + const Material *material = + DCAST(MaterialAttribute, mat_attrib)->get_material(); + shininess = material->get_shininess(); + } + + if (shininess == 0.0) { + shininess = 100.0; + } + + // Figure out where the highlight is relative to the object's center and + // the light + const ProjectionNode* projnode = gsg->get_current_projection_node(); + LVector3f model_pos = get_rel_pos(node, projnode); + LVector3f norm_model_pos = normalize(model_pos); + LPoint3f light_pos = get_rel_pos(light, projnode); + LVector3f light_vec = light_pos - model_pos; + light_vec = normalize(light_vec); + + // Compute highlight for all pixels on the image that are on the sphere + LVector3f view_vec(0, -1, 0); // Looking down +y axis + LVector3f norm, S; + float ZZ, XX_ZZ, intensity, grazing; + uchar color[3]; + float scale = 1.0 / ((float)(_size - 1)); + + Texture* texture = _spheretex_shader.get_texture(); + + for (int y = 0; y < _size; ++y) { + norm[2] = 2. * ((float)y * scale) - 1.; // 0.._size-1 -> -1..1 + ZZ = norm[2] * norm[2]; // z^2 + + for (int x = 0; x < _size; ++x) { + norm[0] = 2. * ((float)x * scale) -1.; // 0.._size-1 -> -1..1 + XX_ZZ = (norm[0] * norm[0]) + ZZ; // x^2 + z^2 + + color[0] = color[1] = color[2] = 0; + + if (XX_ZZ <= 1.) { // Are we on the unit sphere? + norm[1] = -(float)sqrt(1. - XX_ZZ); // This yields a unit vector + grazing = dot(norm, light_vec); + if (grazing > 0.) { // Does any light actually reflect off this point? + S = norm; + S *= 2. * grazing; + S = S - light_vec; // the reflection vector + intensity = -dot(S, norm_model_pos); // approximate R . V + // If, relative to the viewer, the normal doesn't point towards the + // light (even slightly), no reflection will be seen + if (intensity > 0.) { + intensity = pow(intensity, shininess); + color[0] = color[1] = color[2] = (uchar)(intensity * 255.); + } + } + } + texture->_pbuffer->set_uchar_rgb_texel(color, x, y, _size); + texture->unprepare(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SpheretexHighlighter::apply +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void SpheretexHighlighter:: +apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, GraphicsStateGuardian *gsg) { + + Shader::apply(node, init_state, net_trans, gsg); + + _spheretex_shader.apply(node, init_state, net_trans, gsg); +} diff --git a/panda/src/shader/spheretexHighlighter.h b/panda/src/shader/spheretexHighlighter.h new file mode 100644 index 0000000000..aacad5c5ca --- /dev/null +++ b/panda/src/shader/spheretexHighlighter.h @@ -0,0 +1,75 @@ +// Filename: spheretexHighlighter.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef SPHERETEXHIGHLIGHTER_H +#define SPHERETEXHIGHLIGHTER_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "shader.h" +#include "spheretexShader.h" + +#include "pt_Node.h" + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : SpheretexHighlighter +// Description : Creates a highlight texture from a given light and +// projects this onto the receiving object +//////////////////////////////////////////////////////////////////// +class EXPCL_SHADER SpheretexHighlighter : public FrustumShader +{ +public: + + SpheretexHighlighter(int size = 128); + + virtual void pre_apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, + GraphicsStateGuardian *gsg); + virtual void apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, + GraphicsStateGuardian *gsg); + + // MPG - should we make this check for powers of 2? + INLINE void set_size(int size) { _size = size; } + INLINE int get_size(void) { return _size; } + //Override base class shader's set_priority to allow + //us to set the priority of the contained projtex_shader at + //the same time + virtual void set_priority(int priority); + virtual void set_multipass(bool on); + +protected: + + SpheretexShader _spheretex_shader; + int _size; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + FrustumShader::init_type(); + register_type(_type_handle, "SpheretexHighlighter", + FrustumShader::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/shader/spheretexReflector.cxx b/panda/src/shader/spheretexReflector.cxx new file mode 100644 index 0000000000..e56a127b6a --- /dev/null +++ b/panda/src/shader/spheretexReflector.cxx @@ -0,0 +1,225 @@ +// Filename: spheretexReflector.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "spheretexReflector.h" +#include "config_shader.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle SpheretexReflector::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: SpheretexReflector::Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +SpheretexReflector::SpheretexReflector(int size) : CasterShader() +{ + set_size(size); + _is_reflector = true; + + _fnear = 0.1f; + _ffar = 1000.0f; + + Texture* texture = new Texture; + texture->set_minfilter(Texture::FT_linear); + texture->set_magfilter(Texture::FT_linear); + texture->set_wrapu(Texture::WM_clamp); + texture->set_wrapv(Texture::WM_clamp); + texture->_pbuffer->set_xsize(_size); + texture->_pbuffer->set_ysize(_size); + + _spheretex_shader.set_texture(texture); + _spheretex_shader.set_blend_mode(ColorBlendProperty::M_add); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpheretexReflector::destructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +SpheretexReflector::~SpheretexReflector(void) { +} + +//////////////////////////////////////////////////////////////////// +// Function: SpheretexReflector::set_priority +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void SpheretexReflector:: +set_priority(int priority) +{ + Shader::set_priority(priority); + _spheretex_shader.set_priority(priority); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpheretexReflector::set_multipass +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void SpheretexReflector:: +set_multipass(bool on) +{ + Shader::set_multipass(on); + _spheretex_shader.set_multipass(on); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpheretexHighlighter::pre_apply +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void SpheretexReflector:: +pre_apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, GraphicsStateGuardian *gsg) +{ + if (get_num_casters() == 0) { + shader_cat.error() + << "SpheretexReflector::config() - no casters in caster list" << endl; + return; + } + + //We need a state that we can modify to remove the camera transform + //that is passed to us + AllAttributesWrapper state(init_state); + state.clear_attribute(TransformTransition::get_class_type()); + + // Get the current camera from the gsg + const ProjectionNode* camera = gsg->get_current_projection_node(); + + // Save the clear color. + Colorf clear_color = gsg->get_color_clear_value(); + + // Make a display region of the proper size and clear it to prepare for + // rendering the shadow map + PT(DisplayRegion) scratch_region = + gsg->get_window()->make_scratch_display_region(_size, _size); + + DisplayRegionStack old_dr = gsg->push_display_region(scratch_region); + + // Save scratch region before clearing it to be non-destructive + FrameBufferStack old_fb = gsg->push_frame_buffer + (gsg->get_render_buffer(RenderBuffer::T_back | RenderBuffer::T_depth), + scratch_region); + + gsg->set_color_clear_value(Colorf(0., 0., 0., 1.)); + gsg->clear(gsg->get_render_buffer(RenderBuffer::T_back | + RenderBuffer::T_depth), scratch_region); + + // Copy the transition wrapper so we can modify it freely. + AllTransitionsWrapper trans(net_trans); + + // Create a temporary node that is the parent of all of the caster + // objects, so we can render them from the point of view of the + // reflector. + + PT_Node caster_group = new Node; + NamedNodeVector::iterator ci; + for (ci = _casters.begin(); ci != _casters.end(); ++ci) { + Node *caster = (*ci); + + // Get the transform of the caster from the point of view of the + // reflector. + LMatrix4f mat; + get_rel_mat(caster, node, mat); + + RenderRelation *arc = new RenderRelation(caster_group, caster); + arc->set_transition(new TransformTransition(mat)); + } + + // Build a ProjectionNode that represents a wide-angle view from the + // reflecting object towards the camera. + Frustumf frust; + frust.make_perspective_hfov(150.0f, 1.0f, _fnear, _ffar); + PerspectiveProjection projection(frust); + ProjectionNode* projnode = new ProjectionNode; + projnode->set_projection(projection); + + // How shall we rotate the ProjectionNode? + LMatrix4f r_mat; + + // Get camera pos in the coordinate space of the reflecting object + LVector3f camera_pos = get_rel_pos(camera, node); + LVector3f camera_up = get_rel_up(camera, node); + if (_is_reflector == true) + { + // Take the picture looking toward the camera + look_at(r_mat, camera_pos, camera_up, gsg->get_coordinate_system()); + + // Flip along the X axis to make it behave like a mirror + r_mat = LMatrix4f::scale_mat(LVector3f(-1.0f, 1.0f, 1.0f)) * r_mat; + + // And the vertex order for front facing polygons to compensate + // for the above flip. + CullFaceTransition *cf = + new CullFaceTransition(CullFaceProperty::M_cull_counter_clockwise); + trans.set_transition(cf); + } + else + { + // For a refractor, take the picture looking away from the camera. + look_at(r_mat, -camera_pos, camera_up, gsg->get_coordinate_system()); + } + + RenderRelation *projarc = new RenderRelation(caster_group, projnode); + projarc->set_transition(new TransformTransition(r_mat)); + + // Now render the casters. + DirectRenderTraverser drt(gsg, RenderRelation::get_class_type()); + gsg->render_subgraph(&drt, caster_group, projnode, state, trans); + + // The caster group, and all of the temporary group nodes below, + // will automatically be destructed when the caster_group PointerTo + // object goes out of scope. We don't have to remove it from the + // scene graph because we never actually inserted it. + + // Copy the results of the render from the framebuffer into the spheretex + // shader's texture + _spheretex_shader.get_texture()->copy(gsg, scratch_region, gsg->get_render_buffer(RenderBuffer::T_back)); + + // Restore the original display region and clear value + + //Make sure the restore the depth buffer. There's only one you know. + + gsg->set_color_clear_value(clear_color); + gsg->pop_frame_buffer(old_fb); + gsg->pop_display_region(old_dr); +} +//////////////////////////////////////////////////////////////////// +// Function: SpheretexHighlighter::apply +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void SpheretexReflector:: +apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, GraphicsStateGuardian *gsg) + { + Shader::apply(node, init_state, net_trans, gsg); + + // Apply the spheremap reflection + //gsg->prepare_display_region(); + _spheretex_shader.apply(node, init_state, net_trans, gsg); +} + diff --git a/panda/src/shader/spheretexReflector.h b/panda/src/shader/spheretexReflector.h new file mode 100644 index 0000000000..c743b3d4f1 --- /dev/null +++ b/panda/src/shader/spheretexReflector.h @@ -0,0 +1,87 @@ +// Filename: spheretexReflector.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef SPHERETEXREFLECTOR_H +#define SPHERETEXREFLECTOR_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "shader.h" +#include "spheretexShader.h" +#include "casterShader.h" + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : SpheretexReflector +// Description : Creates a reflection texture map for a given object +//////////////////////////////////////////////////////////////////// +class EXPCL_SHADER SpheretexReflector : public CasterShader +{ +public: + + SpheretexReflector(int size = 256); + ~SpheretexReflector(void); + + virtual void pre_apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, + GraphicsStateGuardian *gsg); + virtual void apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, + GraphicsStateGuardian *gsg); + + // MPG - should we make this check for powers of 2? + INLINE void set_size(int size) { _size = size; } + INLINE int get_size(void) const { return _size; } + + INLINE bool is_reflector(void) const { return _is_reflector; } + INLINE void make_reflector(void) { _is_reflector = true; } + INLINE void make_refractor(void) { _is_reflector = false; } + + INLINE void set_near(float fnear) { _fnear = fnear; } + INLINE float get_near(void) const { return _fnear; } + INLINE void set_far(float ffar) { _ffar = ffar; } + INLINE float get_far(void) const { return _ffar; } + + //Override base class shader's set_priority to allow + //us to set the priority of the contained projtex_shader at + //the same time + virtual void set_priority(int priority); + virtual void set_multipass(bool on); + +protected: + + SpheretexShader _spheretex_shader; + int _size; + bool _is_reflector; + float _fnear; + float _ffar; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CasterShader::init_type(); + register_type(_type_handle, "SpheretexReflector", + CasterShader::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/shader/spheretexShader.cxx b/panda/src/shader/spheretexShader.cxx new file mode 100644 index 0000000000..6fae4efade --- /dev/null +++ b/panda/src/shader/spheretexShader.cxx @@ -0,0 +1,122 @@ +// Filename: spheretexShader.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "spheretexShader.h" +#include "config_shader.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle SpheretexShader::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: SpheretexShader::constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +SpheretexShader::SpheretexShader(Texture* texture) : Shader() +{ + set_texture(texture); + _blend_mode = ColorBlendProperty::M_multiply_add; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpheretexShader::config +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void SpheretexShader::config(void) +{ + Configurable::config(); + + nassertv(_texture != (Texture *)NULL); + _texture->set_minfilter(Texture::FT_linear); + _texture->set_magfilter(Texture::FT_linear); + _texture->set_wrapu(Texture::WM_clamp); + _texture->set_wrapv(Texture::WM_clamp); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpheretexShader::apply +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void SpheretexShader:: +apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, GraphicsStateGuardian *gsg) { + + Shader::apply(node, init_state, net_trans, gsg); + + // Copy the transition wrapper so we can modify it freely. + AllTransitionsWrapper trans(net_trans); + + // Create the texture generation transition + TexGenTransition *tg = new TexGenTransition; + tg->set_sphere_map(); + trans.set_transition(tg); + + // Create the texture transition + nassertv(_texture != (Texture *)NULL); + TextureTransition *t = new TextureTransition; + t->set_on(_texture); + t->set_priority(_priority); + trans.set_transition(t); + + if (_viz != (Shader::Visualize*)0L) + _viz->DisplayTexture(_texture, this); + + // Clear the texture matrix transition + trans.set_transition(new TexMatrixTransition(LMatrix4f::ident_mat())); + + // Turn lighting off - we still want to issue normals, though + trans.set_transition(new LightTransition(LightTransition::all_off())); + gsg->enable_normals(true); + + // Do some extra work if we're doing the 2-pass version (e.g. the + // object we are shading is textured) + if (_multipass_on) { + // Set a color blend mode that assumes this is a second pass over + // textured geometry + ColorBlendTransition *cb = + new ColorBlendTransition(_blend_mode); + trans.set_transition(cb); + + TextureApplyTransition *ta = + new TextureApplyTransition(TextureApplyProperty::M_decal); + trans.set_transition(ta); + + // Set the depth test to M_equal (? Or should this be M_none?) + DepthTestTransition *dta = + new DepthTestTransition(DepthTestProperty::M_equal); + trans.set_transition(dta); + } + + // Render the node with the new transitions. + DirectRenderTraverser drt(gsg, RenderRelation::get_class_type()); + gsg->render_subgraph(&drt, node, init_state, trans); + + gsg->enable_normals(false); +} + + + diff --git a/panda/src/shader/spheretexShader.h b/panda/src/shader/spheretexShader.h new file mode 100644 index 0000000000..f05c2a251c --- /dev/null +++ b/panda/src/shader/spheretexShader.h @@ -0,0 +1,72 @@ +// Filename: spheretexShader.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef SPHERETEXSHADER_H +#define SPHERETEXSHADER_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "shader.h" +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : SpheretexShader +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_SHADER SpheretexShader : public Shader +{ + public: + + SpheretexShader(Texture* texture = NULL); + ~SpheretexShader() { } + + virtual void config(void); + virtual void apply(Node *node, const AllAttributesWrapper &init_state, + const AllTransitionsWrapper &net_trans, + GraphicsStateGuardian *gsg); + + INLINE void set_texture(Texture* texture) { + _texture = texture; + make_dirty(); + } + INLINE Texture* get_texture(void) { return _texture; } + INLINE void set_blend_mode(ColorBlendProperty::Mode mode) { + _blend_mode = mode; + } + + protected: + + PT(Texture) _texture; + ColorBlendProperty::Mode _blend_mode; + + public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Shader::init_type(); + register_type(_type_handle, "SpheretexShader", + Shader::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + + private: + + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/shader/spotlightShader.cxx b/panda/src/shader/spotlightShader.cxx new file mode 100644 index 0000000000..b10a63495e --- /dev/null +++ b/panda/src/shader/spotlightShader.cxx @@ -0,0 +1,67 @@ +// Filename: spotlightShader.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "spotlightShader.h" +#include "config_shader.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle SpotlightShader::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: SpotlightShader::constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +SpotlightShader::SpotlightShader(int size, float radius) : ProjtexShader(NULL, ColorBlendProperty::M_multiply_add) +{ + Texture* texture = new Texture; + texture->set_minfilter(Texture::FT_linear); + texture->set_magfilter(Texture::FT_linear); + texture->set_wrapu(Texture::WM_clamp); + texture->set_wrapv(Texture::WM_clamp); + texture->_pbuffer = new PixelBuffer(PixelBuffer::rgb_buffer(size, size)); + set_texture(texture); + _radius = radius; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpotlightShader::config +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void SpotlightShader::config(void) +{ + Configurable::config(); + + if (get_num_frusta() == 0) { + shader_cat.error() + << "SpotlightShader::config() - no lights in frusta list" << endl; + return; + } else if (get_num_frusta() > 1) { + shader_cat.warning() + << "SpotlightShader::config() - frusta list has more than one " + << "frustum - ignore all but the first one for now..." << endl; + } + if (_frusta[0]->get_type() != Spotlight::get_class_type()) { + shader_cat.error() + << "SpotlightShader::config() - frusta is not a spotlight" << endl; + return; + } + Spotlight* spotlight = (Spotlight *)_frusta[0]; + + spotlight->make_image(_texture, _radius); +} + + + + + diff --git a/panda/src/shader/spotlightShader.h b/panda/src/shader/spotlightShader.h new file mode 100644 index 0000000000..3f64063975 --- /dev/null +++ b/panda/src/shader/spotlightShader.h @@ -0,0 +1,55 @@ +// Filename: spotlightShader.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef SPOTLIGHTSHADER_H +#define SPOTLIGHTSHADER_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include "projtexShader.h" + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : SpotlightShader +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_SHADER SpotlightShader : public ProjtexShader +{ +public: + SpotlightShader(int size = 256, float radius = 0.7); + + virtual void config(void); + // SpotlightShader uses ProjtexShader::apply() + +protected: + float _radius; + +public: + + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ProjtexShader::init_type(); + register_type(_type_handle, "SpotlightShader", + ProjtexShader::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/switchnode/LODNode.I b/panda/src/switchnode/LODNode.I new file mode 100644 index 0000000000..48f00bf51a --- /dev/null +++ b/panda/src/switchnode/LODNode.I @@ -0,0 +1,73 @@ +// Filename: LODNode.I +// Created by: mike (27Sep99) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: LODNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LODNode:: +LODNode(const string &name) : NamedNode(name) { +} + +//////////////////////////////////////////////////////////////////// +// Function: LODNode::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LODNode:: +LODNode(const LODNode ©) : + NamedNode(copy), + _lod(copy._lod) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: LODNode::add_switch +// Access: Public +// Description: Adds a switch range to the LODNode. This implies +// that an corresponding child node is also parent to +// the node. +// +// The sense of in vs. out distances is as if the object +// were coming towards you from far away: it switches +// "in" at the far distance, and switches "out" at the +// close distance. Thus, "in" should be larger than +// "out". +//////////////////////////////////////////////////////////////////// +INLINE void LODNode:: +add_switch(float in, float out) { + _lod._switch_vector.push_back(LODSwitch(in, out)); +} + +//////////////////////////////////////////////////////////////////// +// Function: LODNode::set_switch +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool LODNode:: +set_switch(int index, float in, float out) { + nassertr(index >= 0 && index < _lod._switch_vector.size(), false); + _lod._switch_vector[index].set_range(in, out); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: LODNode::clear_switches +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void LODNode:: +clear_switches(void) { + _lod._switch_vector.erase(_lod._switch_vector.begin(), + _lod._switch_vector.end()); +} diff --git a/panda/src/switchnode/LODNode.cxx b/panda/src/switchnode/LODNode.cxx new file mode 100644 index 0000000000..ef58811f47 --- /dev/null +++ b/panda/src/switchnode/LODNode.cxx @@ -0,0 +1,182 @@ +// Filename: LODNode.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "LODNode.h" +#include "projectionNode.h" +#include "config_switchnode.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle LODNode::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LODNode::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Node that is a shallow copy +// of this one. It will be a different Node pointer, +// but its internal data may or may not be shared with +// that of the original Node. No children will be +// copied. +//////////////////////////////////////////////////////////////////// +Node *LODNode:: +make_copy() const { + return new LODNode(*this); +} + + +//////////////////////////////////////////////////////////////////// +// Function: LODNode::xform +// Access: Public, Virtual +// Description: Transforms the contents of this node by the indicated +// matrix, if it means anything to do so. +//////////////////////////////////////////////////////////////////// +void LODNode:: +xform(const LMatrix4f &mat) { + _lod.xform(mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: sub_render +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +bool LODNode:: +sub_render(const AllAttributesWrapper &attrib, AllTransitionsWrapper &trans, + GraphicsStateGuardianBase *gsgbase) { + + GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, gsgbase); + + // Get the current camera position from the gsg + const ProjectionNode* camera = gsg->get_current_projection_node(); + LPoint3f camera_pos(0, 0, 0); + + // Get the LOD center in camera space + LPoint3f LOD_pos; + + NodeTransitionWrapper ntw(TransformTransition::get_class_type()); + wrt(this, camera, ntw, RenderRelation::get_class_type()); + const TransformTransition *tt; + if (get_transition_into(tt, ntw)) { + LOD_pos = _lod._center * tt->get_matrix(); + } else { + LOD_pos = _lod._center; + } + + // Determine which child to traverse + int index = _lod.compute_child(camera_pos, LOD_pos); + int num_children = get_num_children(RenderRelation::get_class_type()); + if (index >= 0 && index < num_children) { + NodeRelation *arc = get_child(RenderRelation::get_class_type(), index); + + if (switchnode_cat.is_debug()) { + switchnode_cat.debug() + << "Selecting child " << index << " of " << *this << ": " + << *arc->get_child() << "\n"; + } + + // We have to be sure to pick up any state transitions on the arc + // itself. + AllTransitionsWrapper arc_trans; + arc_trans.extract_from(arc); + + AllTransitionsWrapper new_trans(trans); + new_trans.compose_in_place(arc_trans); + + // Now render everything from this node and below. + gsg->render_subgraph(gsg->get_render_traverser(), + arc->get_child(), attrib, new_trans); + + } else { + if (switchnode_cat.is_debug()) { + switchnode_cat.debug() + << "Cannot select child " << index << " of " << *this << "; only " + << num_children << " children.\n"; + } + } + + // Short circuit the rest of the render pass below this node + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: LODNode::has_sub_render +// Access: Public, Virtual +// Description: Should be redefined to return true if the function +// sub_render(), above, expects to be called during +// traversal. +//////////////////////////////////////////////////////////////////// +bool LODNode:: +has_sub_render() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: LODNode::register_with_read_factory +// Access: Public, Static +// Description: Tells the BamReader how to create objects of type +// LODNode. +//////////////////////////////////////////////////////////////////// +void LODNode:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_LODNode); +} + +//////////////////////////////////////////////////////////////////// +// Function: LODNode::write_object +// Description: Writes the contents of this object to the datagram +// for shipping out to a Bam file. +//////////////////////////////////////////////////////////////////// +void LODNode:: +write_datagram(BamWriter *manager, Datagram &me) { + NamedNode::write_datagram(manager, me); + _lod.write_datagram(me); +} + +//////////////////////////////////////////////////////////////////// +// Function: LODNode::make_LODNode +// Access: Protected +// Description: This function is called by the BamReader's factory +// when a new object of type LODNode is encountered in +// the Bam file. It should create the LODNode and +// extract its information from the file. +//////////////////////////////////////////////////////////////////// +TypedWriteable *LODNode:: +make_LODNode(const FactoryParams ¶ms) { + LODNode *me = new LODNode; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: LODNode::fillin +// Access: Protected +// Description: This internal function is called by make_LODNode to +// read in all of the relevant data from the BamFile for +// the new LODNode. +//////////////////////////////////////////////////////////////////// +void LODNode:: +fillin(DatagramIterator &scan, BamReader *manager) { + NamedNode::fillin(scan, manager); + _lod.read_datagram(scan); +} diff --git a/panda/src/switchnode/LODNode.h b/panda/src/switchnode/LODNode.h new file mode 100644 index 0000000000..47f8bd4b73 --- /dev/null +++ b/panda/src/switchnode/LODNode.h @@ -0,0 +1,79 @@ +// Filename: LODNode.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef LODNODE_H +#define LODNODE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Class : LODNode +// Description : A node that implements level of detail +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA LODNode : public NamedNode { +public: + INLINE LODNode(const string &name = ""); + INLINE LODNode(const LODNode ©); + + virtual Node *make_copy() const; + virtual void xform(const LMatrix4f &mat); + + // The sense of in vs. out distances is as if the object were coming + // towards you from far away: it switches "in" at the far distance, + // and switches "out" at the close distance. Thus, "in" should be + // larger than "out". + + INLINE void add_switch(float in, float out); + INLINE bool set_switch(int index, float in, float out); + INLINE void clear_switches(void); + + virtual bool sub_render(const AllAttributesWrapper &attrib, + AllTransitionsWrapper &trans, + GraphicsStateGuardianBase *gsgbase); + virtual bool has_sub_render() const; + +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &me); + + static TypedWriteable *make_LODNode(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator &scan, BamReader *manager); + +public: + LOD _lod; + +public: + static TypeHandle get_class_type( void ) { + return _type_handle; + } + static void init_type( void ) { + NamedNode::init_type(); + register_type( _type_handle, "LODNode", + NamedNode::get_class_type() ); + } + virtual TypeHandle get_type( void ) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "LODNode.I" + +#endif diff --git a/panda/src/switchnode/Sources.pp b/panda/src/switchnode/Sources.pp new file mode 100644 index 0000000000..362907bdbf --- /dev/null +++ b/panda/src/switchnode/Sources.pp @@ -0,0 +1,26 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET switchnode + #define LOCAL_LIBS \ + display sgraph graph sgraphutil sgattrib gobj putil gsgbase linmath \ + mathutil + + #define SOURCES \ + LODNode.I LODNode.cxx LODNode.h config_switchnode.cxx \ + config_switchnode.h sequenceNode.cxx sequenceNode.h + + #define INSTALL_HEADERS \ + LODNode.I LODNode.h sequenceNode.h + +#end lib_target + +#begin test_bin_target + #define TARGET test_sequences + #define LOCAL_LIBS switchnode + + #define SOURCES \ + test_sequences.cxx + +#end test_bin_target + diff --git a/panda/src/switchnode/config_switchnode.cxx b/panda/src/switchnode/config_switchnode.cxx new file mode 100644 index 0000000000..ae58dcbd21 --- /dev/null +++ b/panda/src/switchnode/config_switchnode.cxx @@ -0,0 +1,23 @@ +// Filename: config_switchnode.cxx +// Created by: drose (19Jan00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_switchnode.h" +#include "LODNode.h" +#include "sequenceNode.h" + +#include + +Configure(config_switchnode); +NotifyCategoryDef(switchnode, ""); + +ConfigureFn(config_switchnode) { + LODNode::init_type(); + SequenceNode::init_type(); + + //Registration of writeable object's creation + //functions with BamReader's factory + LODNode::register_with_read_factory(); + SequenceNode::register_with_read_factory(); +} diff --git a/panda/src/switchnode/config_switchnode.h b/panda/src/switchnode/config_switchnode.h new file mode 100644 index 0000000000..c522b97ba1 --- /dev/null +++ b/panda/src/switchnode/config_switchnode.h @@ -0,0 +1,14 @@ +// Filename: config_switchnode.h +// Created by: drose (19Jan00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_SWITCHNODE_H +#define CONFIG_SWITCHNODE_H + +#include +#include + +NotifyCategoryDecl(switchnode, EXPCL_PANDA, EXPTP_PANDA); + +#endif /* CONFIG_SWITCHNODE_H */ diff --git a/panda/src/switchnode/sequenceNode.cxx b/panda/src/switchnode/sequenceNode.cxx new file mode 100644 index 0000000000..844fa228ca --- /dev/null +++ b/panda/src/switchnode/sequenceNode.cxx @@ -0,0 +1,173 @@ +// Filename: sequenceNode.h +// Created by: jason (18Jul00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include +#include + +#include "sequenceNode.h" +#include "config_switchnode.h" + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle SequenceNode::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: SequenceNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +SequenceNode:: +SequenceNode(const string &initial_name) : + NamedNode(initial_name), TimedCycle() +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: SequenceNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +SequenceNode:: +SequenceNode(float switch_time, const string &initial_name) : + NamedNode(initial_name), TimedCycle(switch_time, 0) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: SequenceNode::set_switch_time +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void SequenceNode:: +set_switch_time(float switch_time) +{ + set_cycle_time(switch_time); +} + +//////////////////////////////////////////////////////////////////// +// Function: SequenceNode::sub_render +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool SequenceNode:: +sub_render(const AllAttributesWrapper &attrib, AllTransitionsWrapper &trans, + GraphicsStateGuardianBase *gsgbase) +{ + GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, gsgbase); + + // Determine which child to traverse + int num_children = get_num_children(RenderRelation::get_class_type()); + set_element_count(num_children); + int index = next_element(); + if (index >= 0 && index < num_children) { + NodeRelation *arc = get_child(RenderRelation::get_class_type(), index); + + if (switchnode_cat.is_debug()) { + switchnode_cat.debug() + << "Selecting child " << index << " of " << *this << ": " + << *arc->get_child() << "\n"; + } + + // We have to be sure to pick up any state transitions on the arc + // itself. + AllTransitionsWrapper arc_trans; + arc_trans.extract_from(arc); + + AllTransitionsWrapper new_trans(trans); + new_trans.compose_in_place(arc_trans); + + // Now render everything from this node and below. + gsg->render_subgraph(gsg->get_render_traverser(), + arc->get_child(), attrib, new_trans); + + } else { + if (switchnode_cat.is_debug()) { + switchnode_cat.debug() + << "Cannot select child " << index << " of " << *this << "; only " + << num_children << " children.\n"; + } + } + + // Short circuit the rest of the render pass below this node + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: SequenceNode::has_sub_render +// Access: Public, Virtual +// Description: Should be redefined to return true if the function +// sub_render(), above, expects to be called during +// traversal. +//////////////////////////////////////////////////////////////////// +bool SequenceNode:: +has_sub_render() const +{ + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: SequenceNode::write_object +// Description: Writes the contents of this object to the datagram +// for shipping out to a Bam file. +//////////////////////////////////////////////////////////////////// +void SequenceNode:: +write_datagram(BamWriter *manager, Datagram &me) { + NamedNode::write_datagram(manager, me); + TimedCycle::write_datagram(me); +} + +//////////////////////////////////////////////////////////////////// +// Function: SequenceNode::fillin +// Access: Protected +// Description: This internal function is called by make_SequenceNode to +// read in all of the relevant data from the BamFile for +// the new SequenceNode. +//////////////////////////////////////////////////////////////////// +void SequenceNode:: +fillin(DatagramIterator &scan, BamReader *manager) { + NamedNode::fillin(scan, manager); + TimedCycle::fillin(scan); +} + +//////////////////////////////////////////////////////////////////// +// Function: SequenceNode::make_SequenceNode +// Access: Protected +// Description: This function is called by the BamReader's factory +// when a new object of type SequenceNode is encountered in +// the Bam file. It should create the SequenceNode and +// extract its information from the file. +//////////////////////////////////////////////////////////////////// +TypedWriteable *SequenceNode:: +make_SequenceNode(const FactoryParams ¶ms) { + SequenceNode *me = new SequenceNode; + BamReader *manager; + Datagram packet; + + parse_params(params, manager, packet); + DatagramIterator scan(packet); + + me->fillin(scan, manager); + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: SequenceNode::register_with_read_factory +// Access: Public, Static +// Description: Tells the BamReader how to create objects of type +// SequenceNode. +//////////////////////////////////////////////////////////////////// +void SequenceNode:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_SequenceNode); +} diff --git a/panda/src/switchnode/sequenceNode.h b/panda/src/switchnode/sequenceNode.h new file mode 100644 index 0000000000..88b90307fe --- /dev/null +++ b/panda/src/switchnode/sequenceNode.h @@ -0,0 +1,64 @@ +// Filename: sequenceNode.h +// Created by: jason (18Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SEQUENCENODE_H +#define SEQUENCENODE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : SequenceNode +// Description : A node that implements the ability to switch out +// what node is rendered based on time +//////////////////////////////////////////////////////////////////// + +class EXPCL_PANDA SequenceNode : public NamedNode, public TimedCycle +{ +public: + SequenceNode(const string &initial_name = ""); + SequenceNode(float switch_time, const string &initial_name = ""); + + void set_switch_time(float switch_time); + + virtual bool sub_render(const AllAttributesWrapper &attrib, + AllTransitionsWrapper &trans, + GraphicsStateGuardianBase *gsgbase); + virtual bool has_sub_render() const; + +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &me); + + static TypedWriteable *make_SequenceNode(const FactoryParams ¶ms); + +protected: + void fillin(DatagramIterator &scan, BamReader *manager); + +public: + static TypeHandle get_class_type( void ) { + return _type_handle; + } + static void init_type( void ) { + NamedNode::init_type(); + register_type( _type_handle, "SequenceNode", + NamedNode::get_class_type() ); + } + virtual TypeHandle get_type( void ) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/switchnode/test_sequences.cxx b/panda/src/switchnode/test_sequences.cxx new file mode 100644 index 0000000000..851a4cfc1f --- /dev/null +++ b/panda/src/switchnode/test_sequences.cxx @@ -0,0 +1,122 @@ +//Nodes +#include +#include + +//Relations (arcs) +#include +#include + +//Misc +#include +#include +#include +#include + +#include + +#include "sequenceNode.h" + +Configure(sequences); +ConfigureFn(sequences) { +} + +//Framework extern variables and functions +extern PT_NamedNode render; +extern NodeAttributes initial_state; +extern RenderRelation* first_arc; +extern PT_NamedNode lights; +extern PT_NamedNode root; +extern PT(GeomNode) geomnode; +extern PT_NamedNode cameras; +extern PT(MouseAndKeyboard) mak; + +extern void set_alt_trackball(Node *trackball); + +extern int framework_main(int argc, char *argv[]); +extern void (*extra_display_func)(); +extern void (*define_keys)(EventHandler&); + +extern PT(GraphicsWindow) main_win; + +//////////////////////////////////////////////////////////////////// +// Function: keys +// Access: Public +// Description: Set event handlers for the various keys needed, and +// do any initialization necessary +//////////////////////////////////////////////////////////////////// +void keys(EventHandler &eh) +{ + PTA_Vertexf c1, c2, c3, c4, c5, c6; + GeomSphere *s1, *s2, *s3, *s4, *s5, *s6; + GeomNode *n1, *n2, *n3, *n4, *n5, *n6; + + s1 = new GeomSphere(); + s2 = new GeomSphere(); + s3 = new GeomSphere(); + s4 = new GeomSphere(); + s5 = new GeomSphere(); + s6 = new GeomSphere(); + + n1 = new GeomNode("Sphere 1"); + n2 = new GeomNode("Sphere 2"); + n3 = new GeomNode("Sphere 3"); + n4 = new GeomNode("Sphere 4"); + n5 = new GeomNode("Sphere 5"); + n6 = new GeomNode("Sphere 6"); + + c1.push_back(Vertexf(0,0,0)); + c1.push_back(Vertexf(0,0,4)); + + c2.push_back(Vertexf(0,0,0)); + c2.push_back(Vertexf(0,0,6)); + + c3.push_back(Vertexf(0,0,0)); + c3.push_back(Vertexf(0,0,8)); + + c4.push_back(Vertexf(0,0,0)); + c4.push_back(Vertexf(0,0,10)); + + c5.push_back(Vertexf(0,0,0)); + c5.push_back(Vertexf(0,0,8)); + + c6.push_back(Vertexf(0,0,0)); + c6.push_back(Vertexf(0,0,6)); + + s1->set_coords(c1, G_PER_VERTEX); + s1->set_num_prims(1); + s2->set_coords(c2, G_PER_VERTEX); + s2->set_num_prims(1); + s3->set_coords(c3, G_PER_VERTEX); + s3->set_num_prims(1); + s4->set_coords(c4, G_PER_VERTEX); + s4->set_num_prims(1); + s5->set_coords(c5, G_PER_VERTEX); + s5->set_num_prims(1); + s6->set_coords(c6, G_PER_VERTEX); + s6->set_num_prims(1); + + n1->add_geom(s1); + n2->add_geom(s2); + n3->add_geom(s3); + n4->add_geom(s4); + n5->add_geom(s5); + n6->add_geom(s6); + + PT_NamedNode sn = DCAST(NamedNode, new SequenceNode(1)); + + new RenderRelation(render, sn); + + new RenderRelation(sn, n1); + new RenderRelation(sn, n2); + new RenderRelation(sn, n3); + new RenderRelation(sn, n4); + new RenderRelation(sn, n5); + new RenderRelation(sn, n6); + + remove_arc(first_arc); +} + +int main(int argc, char *argv[]) { + define_keys = &keys; + return framework_main(argc, argv); +} diff --git a/panda/src/testbed/Configrc b/panda/src/testbed/Configrc new file mode 100644 index 0000000000..1a36e6dd2f --- /dev/null +++ b/panda/src/testbed/Configrc @@ -0,0 +1,2 @@ +load-display glgsg +load-display glxdisplay diff --git a/panda/src/testbed/Sources.pp b/panda/src/testbed/Sources.pp new file mode 100644 index 0000000000..bf82d049f0 --- /dev/null +++ b/panda/src/testbed/Sources.pp @@ -0,0 +1,126 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase pystub +#define LOCAL_LIBS \ + framework putil collide loader sgmanip chan text chancfg cull \ + pnmimage pnmimagetypes event effects shader + + +#begin bin_target + #define TARGET demo + + #define SOURCES \ + demo.cxx + +#end bin_target + +#begin test_bin_target + #define TARGET chat + + #define SOURCES \ + chat_test.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET downloader + + #define SOURCES \ + downloader_test.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET herc + + #define SOURCES \ + herc.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET loader + + #define SOURCES \ + loader_test.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET lod + + #define SOURCES \ + lod_test.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET min_herc + + #define SOURCES \ + min_herc.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET min_shader + + #define SOURCES \ + min_shader.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET panda + + #define SOURCES \ + panda.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET shader + + #define SOURCES \ + shader_test.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET test_eggWrite + + #define SOURCES \ + test_eggWrite.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET test_particles + + #define SOURCES \ + test_particles.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET test_recparticles + + #define SOURCES \ + test_recparticles.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET text + + #define SOURCES \ + text_test.cxx + +#end test_bin_target + +#begin test_bin_target + #define TARGET vrpn_demo + + #define SOURCES \ + vrpn_demo.cxx + +#end test_bin_target + diff --git a/panda/src/testbed/chat_test.cxx b/panda/src/testbed/chat_test.cxx new file mode 100644 index 0000000000..6206c3e32d --- /dev/null +++ b/panda/src/testbed/chat_test.cxx @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +PT(ChatInput) chat_input; +PT(TextNode) input_text_node; +PT(TextNode) output_text_node; + +bool in_chat_mode = false; + +extern int framework_main(int argc, char *argv[]); +extern void (*define_keys)(EventHandler&); + +void event_enter(CPT_Event) { + if (!in_chat_mode) { + nout << "Enter chat mode" << endl; + chat_input->reset(); + new DataRelation(mak, chat_input); + in_chat_mode = true; + } +} + +void event_chat_exit(CPT_Event) { + if (in_chat_mode) { + nout << "Exit chat mode" << endl; + remove_child(mak, chat_input, DataRelation::get_class_type()); + output_text_node->set_text(chat_input->get_string()); + in_chat_mode = false; + } +} + +void event_chat_overflow(CPT_Event) { + nout << "Too many characters." << endl; +} + +void chat_keys(EventHandler& eh) { + eh.add_hook("enter", event_enter); + eh.add_hook("chat_exit", event_chat_exit); + eh.add_hook("chat_overflow", event_chat_overflow); + + PT_NamedNode font = loader.load_sync("ttf-comic"); + + // Create the input text node + input_text_node = new TextNode("input_text_node"); + input_text_node->set_billboard(false); + input_text_node->set_font(font.p()); + input_text_node->set_text("Press Enter to begin chat mode."); + RenderRelation *text_arc = new RenderRelation(cameras, input_text_node); + LMatrix4f mat = LMatrix4f::scale_mat(0.25); + mat.set_row(3, LVector3f(-3, 8, -2.4)); + text_arc->set_transition(new TransformTransition(mat)); + LightTransition *no_light = new LightTransition(LightTransition::all_off()); + text_arc->set_transition(no_light); + + chat_input = new ChatInput(input_text_node, "chat input"); + chat_input->set_max_chars(20); + + // Create the output text node + output_text_node = new TextNode("output_text_node"); + output_text_node->set_billboard(true); + output_text_node->set_font(font.p()); + output_text_node->set_text_color(0, 0, 0, 1); + output_text_node->set_card_as_margin(0.2, 0.2, 0.2, 0.2); + output_text_node->set_card_color(1, 1, 1, 1); + output_text_node->set_align(TM_ALIGN_CENTER); + output_text_node->set_wordwrap(10.0); + output_text_node->set_text("Output text"); + + RenderRelation *out_arc = new RenderRelation(root, output_text_node); + out_arc->set_transition(no_light); +} + +int main(int argc, char *argv[]) { + define_keys = &chat_keys; + return framework_main(argc, argv); +} diff --git a/panda/src/testbed/demo.cxx b/panda/src/testbed/demo.cxx new file mode 100644 index 0000000000..766ac2ab91 --- /dev/null +++ b/panda/src/testbed/demo.cxx @@ -0,0 +1,588 @@ +#include "framework.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//From framework +extern PT(GeomNode) geomnode; +extern RenderRelation* first_arc; + +// These variables are used to implement the set_highlight() and +// related functions to allow the user to walk through the scene graph +// with the arrow keys. + +// highlight_render_node is a special node that has a funny render +// mode set on it to draw things in red wireframe, but it has no +// geometry of its own. The node that we're currently highlighting +// (current_node) is instanced under highlight_render_node via +// current_arc, so that the highlight applies to that node. +RenderRelation *highlight_arc = NULL; +PT_NamedNode highlight_render_node; +RenderRelation *current_arc = NULL; +Node *current_node; + +// bounds_arc just keeps a pointer to the current arc whose bounding +// volumes has been highlighted. +NodeRelation *bounds_arc = NULL; + +PT_NamedNode render2d_top; +RenderRelation *render2d_arc = NULL; +PT_NamedNode render2d; + +PT(TextNode) label2d; + +//Global Node used by LensFlare and NodePath color/alpha demos +PT_NamedNode sky = (NamedNode *)NULL; +RenderRelation *sky_arc = (RenderRelation *)NULL; +RenderRelation *flare_arc = (RenderRelation *)NULL; + +DownRelationPointers *current_siblings; +DownRelationPointers::iterator current_sib; + +void +setup_panda2d() { + static bool already_setup = false; + if (already_setup) { + nout << "Already have 2-d layer set up.\n"; + return; + } + already_setup = true; + nout << "Setting up 2-d layer.\n"; + + render2d_top = new NamedNode("render2d_top"); + render2d = new NamedNode("render2d"); + render2d_arc = new RenderRelation(render2d_top, render2d); + + // Set up some overrides to turn off certain properties which we + // probably won't need for 2-d objects. + render2d_arc->set_transition(new DepthTestTransition(DepthTestProperty::M_none), 1); + render2d_arc->set_transition(new DepthWriteTransition(DepthWriteTransition::off()), 1); + render2d_arc->set_transition(new LightTransition(LightTransition::all_off()), 1); + render2d_arc->set_transition(new MaterialTransition(MaterialTransition::off()), 1); + render2d_arc->set_transition(new CullFaceTransition(CullFaceProperty::M_cull_none), 1); + + // Create a 2-d camera. + PT(Camera) cam2d = new Camera("cam2d"); + new RenderRelation(render2d, cam2d); + cam2d->set_scene(render2d_top); + + Frustumf frustum2d; + frustum2d.make_ortho_2D(); + cam2d->set_projection(OrthoProjection(frustum2d)); + + // Now create a new layer. + GraphicsChannel *chan = main_win->get_channel(0); + nassertv(chan != (GraphicsChannel *)NULL); + + GraphicsLayer *layer = chan->make_layer(); + nassertv(layer != (GraphicsLayer *)NULL); + + DisplayRegion *dr = layer->make_display_region(); + nassertv(dr != (DisplayRegion *)NULL); + dr->set_camera(cam2d); + + + // Put some interesting things in the 2-d scene graph. + PT_Node font = loader.load_sync("ttf-comic"); + + if (font != (NamedNode *)NULL) { + label2d = new TextNode("label2d"); + RenderRelation *text_arc = new RenderRelation(render2d, label2d); + + LMatrix4f mat = + LMatrix4f::scale_mat(0.1) * + LMatrix4f::translate_mat(0.0, 0.0, 0.8); + + label2d->set_transform(mat); + label2d->set_font(font.p()); + label2d->set_card_color(0.5, 0.5, 0.5, 0.5); + label2d->set_card_as_margin(0.5, 0.5, 0.2, 0.2); + label2d->set_frame_color(1.0, 1.0, 1.0, 1.0); + label2d->set_frame_as_margin(0.5, 0.5, 0.2, 0.2); + label2d->set_align(TM_ALIGN_CENTER); + label2d->set_text_color(0.2, 0.5, 1.0, 1.0); + label2d->set_text("Now presenting Panda 2-D!"); + + MouseWatcherRegion *region = + new MouseWatcherRegion("label2d", label2d->get_card_transformed()); + region->set_suppress_below(true); + mouse_watcher->add_region(region); + + } else { + nout << "Couldn't load font ttf-comic\n"; + } + + PT_Node p2egg = loader.load_sync("panda2d"); + if (p2egg != (NamedNode *)NULL) { + RenderRelation *arc = new RenderRelation(render2d, p2egg); + } else { + nout << "Couldn't find panda2d.egg\n"; + } + + // Load up a mouse cursor for fun. + PT_Node lilsmiley = loader.load_sync("lilsmiley"); + if (lilsmiley != (NamedNode *)NULL) { + NamedNode *n2 = new NamedNode("cursor"); + RenderRelation *s1_arc = new RenderRelation(render2d, n2); + RenderRelation *s2_arc = new RenderRelation(n2, lilsmiley); + + // Make up a scale to make it 32x32 pixels (it starts out 1x1 + // units). + LMatrix4f scale_mat = LMatrix4f::scale_mat(32.0 / main_win->get_width(), 1.0, 32.0 / main_win->get_height()); + s2_arc->set_transition(new TransformTransition(scale_mat)); + mouse_watcher->set_geometry(s1_arc); + } else { + nout << "Couldn't find lilsmiley.egg\n"; + } + +} + +static void +event_in_label2d(CPT_Event) { + // The mouse moved over the label. + nassertv(!label2d.is_null()); + + label2d->set_card_color(0.8, 0.2, 1.0, 1.0); +} + +static void +event_out_label2d(CPT_Event) { + // The mouse moved out from the label. + nassertv(!label2d.is_null()); + + label2d->set_card_color(0.5, 0.5, 0.5, 0.5); +} + + +static void +set_highlight(Node *node) { + if (bounds_arc != NULL) { + bounds_arc->clear_transition(DrawBoundsTransition::get_class_type()); + bounds_arc = NULL; + } + if (current_arc != NULL) { + remove_arc(current_arc); + current_arc = NULL; + } + + LMatrix4f mat; + + // Transform the highlight to the coordinate space of the node. + get_rel_mat(node, render, mat); + highlight_arc->set_transition(new TransformTransition(mat)); + + nout << "Highlighting " << *node << "\n"; + + const UpRelationPointers &urp = + node->_parents[RenderRelation::get_class_type()]; + nout << "Bounding volume of node is " << node->get_bound() << "\n"; + if (!urp.empty()) { + NodeRelation *arc = (*urp.begin()); + const BoundingVolume &vol = arc->get_bound(); + nout << "Bounding volume of arc is " << vol << "\n"; + + arc->set_transition(new DrawBoundsTransition); + bounds_arc = arc; + } + + current_arc = new RenderRelation(highlight_render_node, node); + //current_arc = NULL; + current_node = node; +} + +static void +event_up(CPT_Event) { + if (current_node != NULL) { + if (current_node != root) { + // First, break the connection to the current node so we won't + // be confused by it. + if (current_arc != NULL) { + remove_arc(current_arc); + current_arc = NULL; + } + + // Now walk up from the current node to its first parent. + UpRelations::iterator uri = + current_node->_parents.find(RenderRelation::get_class_type()); + if (uri == current_node->_parents.end() || (*uri).second.empty()) { + // There is no parent. Weird. + return; + } + + UpRelationPointers &parent_siblings = (*uri).second; + UpRelationPointers::iterator pi = parent_siblings.begin(); + assert(pi != parent_siblings.end()); + + Node *node = (*pi)->get_parent(); + set_highlight(node); + current_siblings = NULL; + } + } +} + +static void +event_down(CPT_Event) { + if (current_node != NULL) { + // Walk down from the current node to its first child. + DownRelations::iterator dri = + current_node->_children.find(RenderRelation::get_class_type()); + if (dri == current_node->_children.end() || (*dri).second.empty()) { + // There are no children. + return; + } + + current_siblings = &(*dri).second; + current_sib = current_siblings->begin(); + assert(current_sib != current_siblings->end()); + + Node *node = (*current_sib)->get_child(); + set_highlight(node); + } +} + +static void +event_left(CPT_Event) { + // Go to the previous child in the sibling list, if there is one. + if (current_node != NULL && current_siblings != NULL) { + if (current_sib != current_siblings->begin()) { + --current_sib; + Node *node = (*current_sib)->get_child(); + set_highlight(node); + } + } +} + +static void +event_right(CPT_Event) { + // Go to the next child in the sibling list, if there is one. + if (current_node != NULL && current_siblings != NULL) { + DownRelationPointers::iterator next_sib = current_sib; + ++next_sib; + if (next_sib != current_siblings->end()) { + current_sib = next_sib; + Node *node = (*current_sib)->get_child(); + set_highlight(node); + } + } +} + +static void +event_fkey(CPT_Event event) { + if (current_node != NULL) { + // Apply a color to the selected node. + NodeRelation *arc = + current_node->get_parent(RenderRelation::get_class_type(), 0); + nassertv(arc != (NodeRelation *)NULL); + + if (event->get_name() == "f9") { + // F9: restore the natural color. + arc->clear_transition(ColorTransition::get_class_type()); + cerr << "Clearing color on " << *arc << "\n"; + + } else { + Colorf color; + if (event->get_name() == "f1") { + color.set(0.0, 0.0, 1.0, 1.0); + } else if (event->get_name() == "f2") { + color.set(0.0, 1.0, 0.0, 1.0); + } else if (event->get_name() == "f3") { + color.set(0.0, 1.0, 1.0, 1.0); + } else if (event->get_name() == "f4") { + color.set(1.0, 0.0, 0.0, 1.0); + } else if (event->get_name() == "f5") { + color.set(1.0, 0.0, 1.0, 1.0); + } else if (event->get_name() == "f6") { + color.set(1.0, 1.0, 0.0, 1.0); + } else if (event->get_name() == "f7") { + color.set(1.0, 1.0, 1.0, 1.0); + } else if (event->get_name() == "f8") { + color.set(0.0, 0.0, 0.0, 1.0); + } + arc->set_transition(new ColorTransition(color)); + cerr << "Setting color on " << *arc << " to " << color << "\n"; + } + } +} + +static void +enable_highlight() { + if (highlight_render_node == NULL) { + nout << "Creating highlight render node\n"; + highlight_render_node = new NamedNode("highlight"); + highlight_arc = new RenderRelation(render, highlight_render_node); + + // Set up the funny rendering attributes on the highlighted + // geometry. + RenderModeTransition *rmt = + new RenderModeTransition(RenderModeProperty::M_wireframe); + ColorTransition *ct = + new ColorTransition(1.0, 0.0, 0.0, 1.0); + CullFaceTransition *cft = + new CullFaceTransition(CullFaceProperty::M_cull_none); + TextureTransition *tt = + new TextureTransition; + LightTransition *lt = + new LightTransition; + + rmt->set_priority(100); + ct->set_priority(100); + cft->set_priority(100); + tt->set_priority(100); + lt->set_priority(100); + + highlight_arc->set_transition(rmt); + highlight_arc->set_transition(ct); + highlight_arc->set_transition(cft); + highlight_arc->set_transition(tt); + highlight_arc->set_transition(lt); + } + + // Add a temporary arc from the highlight render node to the node we + // are highlighting. + nout << "Enabling highlight\n"; + assert(current_node == NULL); + set_highlight(root); + current_siblings = NULL; +} + +static void +disable_highlight() { + nout << "Disabling highlight\n"; + assert(current_node != NULL); + if (bounds_arc != NULL) { + bounds_arc->clear_transition(DrawBoundsTransition::get_class_type()); + bounds_arc = NULL; + } + if (current_arc != NULL) { + remove_arc(current_arc); + current_arc = NULL; + } + current_node = NULL; +} + +static void +event_h(CPT_Event) { + if (current_node == NULL) { + enable_highlight(); + } else { + disable_highlight(); + } +} + +static void +event_2(CPT_Event) { + static bool is_setup = false; + if (!is_setup) { + setup_panda2d(); + is_setup = true; + } +} + +static void attach_sky() { + // Load the sun and sky + sky = DCAST(NamedNode, loader.load_sync("sky")); + if (sky != (NamedNode *)NULL) { + if (sky_arc == (RenderRelation *)NULL) { + sky_arc = new RenderRelation(render, sky); + } + } +} + +static void +event_k(CPT_Event) { + static bool is_color_mat = false; + + if (!is_color_mat) { + LMatrix4f color_mat = LMatrix4f::scale_mat(0,0,0) * LMatrix4f::translate_mat(1,0,0); + + first_arc->set_transition(new ColorMatrixTransition(color_mat)); + } + else { + LMatrix4f color_mat = LMatrix4f::ident_mat(); + + first_arc->set_transition(new ColorMatrixTransition(color_mat)); + } + is_color_mat = !is_color_mat; +} + +static void +event_a(CPT_Event) { + static bool is_alpha = false; + + if (!is_alpha) { + first_arc->set_transition(new AlphaTransformTransition(-0.5, 1)); + first_arc->set_transition(new TransparencyTransition(TransparencyProperty::M_alpha)); + } + else { + first_arc->clear_transition(AlphaTransformTransition::get_class_type()); + first_arc->clear_transition(TransparencyTransition::get_class_type()); + } + is_alpha = !is_alpha; +} + + +static void +event_v(CPT_Event) { + static bool is_color_scale = false; + + attach_sky(); + + NodePath search(sky); + NodePath sky_search(search, "**/sun"); + + if (!is_color_scale) { + sky_search.set_color_scale(1, 0.5, 0.5, 0.5); + sky_search.set_transparency(true); + } + else { + sky_search.clear_color_scale(); + sky_search.clear_transparency(); + } + is_color_scale = !is_color_scale; +} + +static void +event_L(CPT_Event) { + static bool is_flare = false; + + attach_sky(); + + if (!is_flare) { + //Texture *shine = TexturePool::load_texture("MyShine.bw"); + Texture *shine = TexturePool::load_texture("bigsmileycrop.rgba"); + + Texture *f4 = TexturePool::load_texture("Flare4.bw"); + Texture *f5 = TexturePool::load_texture("Flare5.bw"); + Texture *f6 = TexturePool::load_texture("Flare6.bw"); + + LensFlareNode *flare = new LensFlareNode(); + + PTA_float scales, offsets, angles; + PTA_Colorf colors; + + scales.push_back(0.5); offsets.push_back(0.2); angles.push_back(0); colors.push_back(Colorf(0.3, 0.6, 0.3, 1)); + scales.push_back(0.5); offsets.push_back(0.4); angles.push_back(0); colors.push_back(Colorf(0.6, 0.6, 0.6, 1)); + scales.push_back(0.75); offsets.push_back(0.7); angles.push_back(0.2); colors.push_back(Colorf(0.3, 0.6, 0.3, 1)); + scales.push_back(1.5); offsets.push_back(1.2); angles.push_back(0); colors.push_back(Colorf(0.6, 0.6, 0.6, 1)); + scales.push_back(0.75); offsets.push_back(1.5); angles.push_back(0); colors.push_back(Colorf(0.6, 0.6, 0.6, 1)); + + flare->add_flare(f6, scales, offsets, angles, colors); + + scales.clear(); offsets.clear(); angles.clear(); colors.clear(); + + scales.push_back(0.3); offsets.push_back(0.55); angles.push_back(0); colors.push_back(Colorf(0.0, 0.0, 0.6, 1)); + scales.push_back(0.3); offsets.push_back(0.8); angles.push_back(0); colors.push_back(Colorf(0.0, 0.0, 0.6, 1)); + scales.push_back(0.5); offsets.push_back(1.1); angles.push_back(0); colors.push_back(Colorf(0.0, 0.0, 0.6, 1)); + scales.push_back(0.15); offsets.push_back(1.35); angles.push_back(0.2); colors.push_back(Colorf(0.3, 0.6, 0.3, 1)); + + flare->add_flare(f5, scales, offsets, angles, colors); + + scales.clear(); offsets.clear(); angles.clear(); colors.clear(); + + scales.push_back(1); offsets.push_back(0.0); angles.push_back(-0.3); colors.push_back(Colorf(1.0, 0.0, 0.0, 1)); + scales.push_back(1.05); offsets.push_back(0.0); angles.push_back(-0.3); colors.push_back(Colorf(0.0, 1.0, 0.0, 1)); + scales.push_back(1.1); offsets.push_back(0.0); angles.push_back(-0.3); colors.push_back(Colorf(0.0, 0.0, 1.0, 1)); + + flare->add_flare(f4, scales, offsets, angles, colors); + + flare->add_blind(shine); + flare->set_blind_falloff(5); + flare->set_flare_falloff(45); + + NodePath search(sky); + NodePath sky_search(search, "**/sun"); + PT_Node light = sky_search.get_bottom_node(); + + flare->set_light_source(light); + flare_arc = new RenderRelation(light, flare, 10); + ColorBlendTransition *cb = new ColorBlendTransition(ColorBlendProperty::M_add); + flare_arc->set_transition(cb); + + TextureApplyTransition *ta = + new TextureApplyTransition(TextureApplyProperty::M_decal); + flare_arc->set_transition(ta); + + DepthTestTransition *dta = + new DepthTestTransition(DepthTestProperty::M_none); + flare_arc->set_transition(dta); + } + else { + remove_arc(flare_arc); + } + + is_flare = !is_flare; +} + +void demo_keys(EventHandler&) { + new RenderRelation( lights, dlight ); + have_dlight = true; + + event_handler.add_hook("mw-in-label2d", event_in_label2d); + event_handler.add_hook("mw-out-label2d", event_out_label2d); + + event_handler.add_hook("h", event_h); + event_handler.add_hook("up", event_up); + event_handler.add_hook("down", event_down); + event_handler.add_hook("left", event_left); + event_handler.add_hook("right", event_right); + event_handler.add_hook("f1", event_fkey); + event_handler.add_hook("f2", event_fkey); + event_handler.add_hook("f3", event_fkey); + event_handler.add_hook("f4", event_fkey); + event_handler.add_hook("f5", event_fkey); + event_handler.add_hook("f6", event_fkey); + event_handler.add_hook("f7", event_fkey); + event_handler.add_hook("f8", event_fkey); + event_handler.add_hook("f9", event_fkey); + + event_handler.add_hook("2", event_2); + + event_handler.add_hook("L", event_L); + event_handler.add_hook("k", event_k); + event_handler.add_hook("a", event_a); + event_handler.add_hook("v", event_v); +} + +int main(int argc, char *argv[]) { + define_keys = &demo_keys; + return framework_main(argc, argv); +} diff --git a/panda/src/testbed/downloader_test.cxx b/panda/src/testbed/downloader_test.cxx new file mode 100644 index 0000000000..0cd3c3b846 --- /dev/null +++ b/panda/src/testbed/downloader_test.cxx @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include +#include + +Downloader downloader; +Decompressor decompressor; +Extractor extractor; +Filename src_file; +Filename dest_file; + +void event_y(CPT_Event) { + downloader.request_download(src_file, dest_file, ""); +} + +void event_u(CPT_Event) { + downloader.request_download("/oldfoobar.mfz", "/fit/people/mike/download/oldfoobar.mfz", ""); +} + +void event_i(CPT_Event) { + downloader.request_download("/foobar.mfz", "/fit/people/mike/download/partfoobar.mfz", "", 0, 99, 151); +} + +void event_o(CPT_Event) { + downloader.request_download("/foobar.mfz", "/fit/people/mike/download/partfoobar.mfz", "", 100, 151, 151); +} + +void event_p(CPT_Event) { + decompressor.request_decompress("/fit/people/mike/download/dload.mf.pz", ""); +} + +void event_la(CPT_Event) { + extractor.request_extract("/fit/people/mike/download/dload.mf", ""); +} + +void loader_keys(EventHandler& eh) { + eh.add_hook("y", event_y); + eh.add_hook("u", event_u); + eh.add_hook("i", event_i); + eh.add_hook("o", event_o); + eh.add_hook("p", event_p); + eh.add_hook("[", event_la); +} + +int main(int argc, char *argv[]) { + + if (argc < 4) { + cerr << "Usage: download " << endl; + return 0; + } + + string server_name = argv[1]; + src_file = argv[2]; + dest_file = argv[3]; + argc -= 3; + argv += 3; + + define_keys = &loader_keys; + downloader.create_thread(); + downloader.connect_to_server(server_name); + decompressor.create_thread(); + extractor.create_thread(); + return framework_main(argc, argv); +} diff --git a/panda/src/testbed/herc.cxx b/panda/src/testbed/herc.cxx new file mode 100644 index 0000000000..97a415ab18 --- /dev/null +++ b/panda/src/testbed/herc.cxx @@ -0,0 +1,512 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +Configure(shader_test); + +ConfigureFn(shader_test) {} + +PT(ProjectionNode) tex_proj; +PT(Trackball) tex_proj_trackball; +PT(ProjtexShader) proj_shader; +PT(ProjtexShadower) proj_shadow; +PT(SpheretexShader) spheretex; +PT(SpheretexHighlighter) highlight; +PT(SpheretexReflector) sreflect; +PT(PlanarReflector) preflect; +PT(OutlineShader) outline_shader; +PT(GeomNode) proj_geom_node; +PT_NamedNode spot_shaft; + +PT_NamedNode herc; +PT_NamedNode ball; +PT_NamedNode hide_ball; + +ShaderTransition shader_trans; + +PT(Spotlight) tex_proj_spot; +PT(Trackball) tex_spot_trackball; +PT(SpotlightShader) spot_shader; + +PT(PlanarSlider) ball_slider; +bool follow_ball; + +RenderRelation* room_arc; +RenderRelation* spot_arc; +RenderRelation* ball_arc; +RenderRelation* hide_ball_arc; +RenderRelation* herc_arc; +RenderRelation* herc_rot_arc; +RenderRelation* camera_model_arc; + +extern PT_NamedNode render; +extern NodeAttributes initial_state; +extern PT_NamedNode lights; +extern PT_NamedNode root; +extern PT(GeomNode) geomnode; +extern PT_NamedNode cameras; +extern PT(MouseAndKeyboard) mak; + +extern void set_alt_trackball(Node *trackball); + +extern PT(GraphicsWindow) main_win; + +extern int framework_main(int argc, char *argv[]); +extern void (*extra_display_func)(); +extern void (*define_keys)(EventHandler&); +extern void (*extra_overrides_func)(ChanCfgOverrides&, std::string&); +extern void (*additional_idle)(); + +// nothing extra to do for either of these +void herc_display_func(void) { + Shader::Visualize* v = Shader::get_viz(); + if (v != (Shader::Visualize*)0L) + v->Flush(); +} + +void herc_overrides_func(ChanCfgOverrides &override, std::string&) { + override.setField(ChanCfgOverrides::Mask, + ((unsigned int)(W_DOUBLE|W_DEPTH|W_MULTISAMPLE|W_STENCIL))); + override.setField(ChanCfgOverrides::Title, "Lighting Demo"); +} + + +void herc_idle() { + static const double walk_speed = 4.0; // feet per second + + if (follow_ball) { + LPoint3f bp = get_rel_pos(ball, herc); + LVector2f bv2(bp[0], bp[1]); + float dist = length(bv2); + if (dist > 0.0001) { + LMatrix4f mat; + look_at(mat, LPoint3f(-bp[0], -bp[1], 0.0)); + herc_rot_arc->set_transition(new TransformTransition(mat)); + + float stride = walk_speed * ClockObject::get_global_clock()->get_dt(); + if (dist > stride) { + LVector2f step = bv2 / dist * stride; + + const TransformTransition *tt; + if (!get_transition_into(tt, herc_arc)) { + herc_arc->set_transition + (new TransformTransition + (LMatrix4f::translate_mat(step[0], step[1], 0.0))); + } else { + LMatrix4f mat = tt->get_matrix(); + mat(3, 0) += step[0]; + mat(3, 1) += step[1]; + herc_arc->set_transition(new TransformTransition(mat)); + } + } + } + } +} + +void event_p(CPT_Event) { + // The "p" key was pressed. Toggle projected texture. + static bool projtex_mode = false; + + projtex_mode = !projtex_mode; + if (!projtex_mode) { + // Set the normal mode on the render arc. + clear_shader(room_arc, proj_shader); + clear_shader(herc_arc, proj_shader); + clear_shader(ball_arc, proj_shader); + + set_alt_trackball(NULL); + remove_child(tex_proj, proj_geom_node, RenderRelation::get_class_type()); + + } else { + set_shader(room_arc, proj_shader); + set_shader(herc_arc, proj_shader); + set_shader(ball_arc, proj_shader); + + set_alt_trackball(tex_proj_trackball); + // Display the texture projector frustum + RenderRelation *prr = new RenderRelation(tex_proj, proj_geom_node); + LightTransition *plt = new LightTransition(LightTransition::all_off()); + prr->set_transition(plt); + } +} + +void event_s(CPT_Event) { + // The "s" key was pressed. Toggle projected texture spotlight. + static bool projtexspot_mode = false; + LightTransition light_trans; + + projtexspot_mode = !projtexspot_mode; + if (!projtexspot_mode) { + // Set the normal mode on the render arc. + clear_shader(room_arc, spot_shader); + clear_shader(herc_arc, spot_shader); + clear_shader(ball_arc, spot_shader); + + set_alt_trackball(NULL); + remove_child(tex_proj_spot, spot_shaft, RenderRelation::get_class_type()); + + } else { + set_shader(room_arc, spot_shader); + set_shader(herc_arc, spot_shader); + set_shader(ball_arc, spot_shader); + + set_alt_trackball(tex_spot_trackball); + new RenderRelation(tex_proj_spot, spot_shaft, 10); + } +} + +void event_d(CPT_Event) { + // The "d" key was pressed. Toggle projected texture shadows. + static bool projtex_shadow_mode = false; + + projtex_shadow_mode = !projtex_shadow_mode; + if (!projtex_shadow_mode) { + // Set the normal mode on the render arc. + clear_shader(room_arc, proj_shadow); + set_alt_trackball(NULL); + + } else { + set_shader(room_arc, proj_shadow); + set_alt_trackball(tex_spot_trackball); + } +} + +void event_h(CPT_Event) { + // The "h" key was pressed. Toggle highlight. + static bool highlight_mode = false; + + highlight_mode = !highlight_mode; + if (!highlight_mode) { + // Set the normal mode on the render arc. + clear_shader(ball_arc, highlight); + + } else { + set_shader(ball_arc, highlight); + } +} + +void event_e(CPT_Event) { + // The "e" key was pressed. Toggle sphere texture. + static bool spheretex_mode = false; + + spheretex_mode = !spheretex_mode; + if (!spheretex_mode) { + // Set the normal mode on the render arc. + clear_shader(ball_arc, spheretex); + + } else { + // Set an override on the initial state. + set_shader(ball_arc, spheretex); + } +} + +void event_m(CPT_Event) { + // The "m" key was pressed. Toggle sphere reflection. + static bool sphere_reflect_mode = false; + + sphere_reflect_mode = !sphere_reflect_mode; + if (!sphere_reflect_mode) { + // Set the normal mode on the render arc. + clear_shader(ball_arc, sreflect); + + } else { + // Set an override on the initial state. + set_shader(ball_arc, sreflect); + } +} + +void event_r(CPT_Event) { + // The "r" key was pressed. Toggle planar reflection. + static bool plane_reflect_mode = false; + + plane_reflect_mode = !plane_reflect_mode; + if (!plane_reflect_mode) { + // Set the normal mode on the render arc. + clear_shader(room_arc, preflect); + + } else { + // Set an override on the initial state. + set_shader(room_arc, preflect); + } +} + +void event_z(CPT_Event) { + // The "z" key was pressed. Allow the user to move the ball around. + set_alt_trackball(ball_slider); +} + +void event_Z(CPT_Event) { + // The "Z" key was pressed. Toggle follow-ball mode. + follow_ball = !follow_ball; + + if (follow_ball) { + // Hide the ball while we're following it. + remove_arc(hide_ball_arc); + } else { + // Reveal the ball when we're done following it. + hide_ball_arc = new RenderRelation(render, hide_ball); + } +} + +void event_o(CPT_Event) { + // The "o" key was pressed. Toggle outlining. + static bool outline_mode = false; + + outline_mode = !outline_mode; + if (!outline_mode) { + // Set the normal mode on the render arc. + clear_shader(herc_arc, outline_shader); + + } else { + set_shader(herc_arc, outline_shader); + } +} + +void herc_keys(EventHandler &eh) { + + eh.add_hook("p", event_p); // Projected Texture Shader + eh.add_hook("s", event_s); // Projected Texture Spotlight + eh.add_hook("d", event_d); // Projected Texture Shadower + eh.add_hook("e", event_e); // Sphere Texture Shader + eh.add_hook("h", event_h); // Sphere Texture Highlighter + eh.add_hook("m", event_m); // Sphere Texture Reflector + eh.add_hook("r", event_r); // Planar Reflector + eh.add_hook("z", event_z); // Move ball + eh.add_hook("Z", event_Z); // Follow ball + eh.add_hook("o", event_o); // Outlining + +//========================================================================== +// Models +//========================================================================== + // Load herc + PT_NamedNode herc_model = loader.load_sync("herc-6000.egg"); + PT_NamedNode herc_anim = loader.load_sync("HB_1_HE1.egg"); + assert(herc_model != (NamedNode *)NULL && + herc_anim != (NamedNode *)NULL); + PT_NamedNode herc_parent = new NamedNode("herc_parent"); + new RenderRelation(herc_parent, herc_model); + new RenderRelation(herc_parent, herc_anim); + AnimControlCollection anim_controls; + auto_bind(herc_parent, anim_controls, ~0); + // And start looping any animations we successfully bound. + anim_controls.loop_all(true); + PT_NamedNode herc_rot = new NamedNode("herc_rot"); + RenderRelation *herc_arc1 = new RenderRelation(herc_rot, herc_parent); + herc_arc1->set_transition + (new TransformTransition + (LMatrix4f::scale_mat(LVector3f(0.25, 0.25, 0.25)))); + herc = new NamedNode("herc"); + herc_rot_arc = new RenderRelation(herc, herc_rot); + herc_arc = new RenderRelation(render, herc); + + // Load ball + ball = loader.load_sync("marble_ball.egg"); + assert(ball != (NamedNode *)NULL); + PT_NamedNode scaled_ball = new NamedNode("scaled_ball"); + RenderRelation *ball_arc1 = new RenderRelation(scaled_ball, ball); + ball_arc1->set_transition + (new TransformTransition(LMatrix4f::scale_mat(0.2))); + hide_ball = new NamedNode("hide_ball"); + ball_arc = new RenderRelation(hide_ball, scaled_ball); + ball_arc->set_transition + (new TransformTransition(LMatrix4f::translate_mat(4., 2., 1.))); + hide_ball_arc = new RenderRelation(render, hide_ball); + + // Control the ball using a PlanarSlider tform. + ball_slider = new PlanarSlider("ball_slider"); + ball_slider->set_transform(LMatrix4f::translate_mat(0.0, 0.0, 1.0) * + LMatrix4f::scale_mat(7.0, -7.0, 1.0)); + ball_slider->set_mouse_pos(LPoint2f(4.0 / 7.0, 2.0 / -7.0)); + Transform2SG *slider2ball = new Transform2SG("slider2ball"); + slider2ball->set_arc(ball_arc); + new RenderRelation(ball_slider, slider2ball); + follow_ball = false; + + // Load the room file + PT_NamedNode room = loader.load_sync("lfloor.egg"); + assert(room != (NamedNode *)NULL); + room_arc = new RenderRelation(render, room); + + // Load up a camera model to visualize our eyepoint. + PT_NamedNode camera_model = loader.load_sync("camera.egg"); + assert(camera_model != (NamedNode *)NULL); + camera_model_arc = new RenderRelation(cameras, camera_model); + + // Remove any model that was loaded by default or on command line + if (root != (NamedNode *)NULL) + remove_child(render, root, RenderRelation::get_class_type()); + if (geomnode != (GeomNode *)NULL) + remove_child(render, geomnode, RenderRelation::get_class_type()); + + +//========================================================================== +// Projected Texture Shader +//========================================================================== + // Load a texture to project + Texture* tex = new Texture; + tex->read("smiley.rgba"); + tex->set_name("smiley.rgba"); + + // Put the texture projector into the scene graph + tex_proj = new ProjectionNode("texture_projector"); + RenderRelation* proj_arc = new RenderRelation(render, tex_proj); + + // Create a trackball to spin this around. + tex_proj_trackball = new Trackball("tex_proj_trackball"); + tex_proj_trackball->set_invert(false); + tex_proj_trackball->set_rel_to(cameras); + Transform2SG *tball2cam = new Transform2SG("tball2cam"); + tball2cam->set_arc(proj_arc); + new RenderRelation(tex_proj_trackball, tball2cam); + + // Raise it and aim it at the origin + LMatrix4f proj_mat; + LPoint3f proj_pos = LPoint3f::rfu(2., 3., 8.); + LVector3f fwd_vec = proj_pos - LPoint3f::origin(); + look_at(proj_mat, -fwd_vec); + proj_mat.set_row(3, proj_pos); + tex_proj_trackball->set_mat(proj_mat); + proj_arc->set_transition(new TransformTransition(proj_mat)); + + // Create a shader for the texture projector + proj_shader = new ProjtexShader(tex); + proj_shader->add_frustum(tex_proj); + + // Create a wireframe representation of the texture projector frustum + GeomLine* proj_geom = + (GeomLine *)tex_proj->get_projection()->make_geometry(); + proj_geom_node = new GeomNode("proj_geometry"); + proj_geom_node->add_geom(proj_geom); + + +//========================================================================== +// Projected Texture Spotlight Shader +//========================================================================== + tex_proj_spot = new Spotlight("tex_proj_spotlight"); + spot_arc = new RenderRelation(render, tex_proj_spot, 10); + + // Create a trackball to spin this around. + tex_spot_trackball = new Trackball("tex_spot_trackball"); + tex_spot_trackball->set_invert(false); + tex_spot_trackball->set_rel_to(cameras); + tball2cam = new Transform2SG("tball2cam"); + tball2cam->set_arc(spot_arc); + new RenderRelation(tex_spot_trackball, tball2cam); + + // Raise it and aim it at the origin + LMatrix4f spot_mat; + LPoint3f spot_pos = LPoint3f::rfu(-4., -3., 8.); + LVector3f spot_vec = spot_pos - LPoint3f::origin(); + look_at(spot_mat, -spot_vec); + spot_mat.set_row(3, spot_pos); + tex_spot_trackball->set_mat(spot_mat); + spot_arc->set_transition(new TransformTransition(spot_mat)); + + // Create a shader for the spotlight + spot_shader = new SpotlightShader; + spot_shader->add_frustum(tex_proj_spot); + + // Create a light shaft for the spotlight + spot_shaft = tex_proj_spot->make_geometry(0.05, 8.0, 36); + + +//========================================================================== +// Projected Texture Shadower +//========================================================================== + proj_shadow = new ProjtexShadower; + proj_shadow->add_frustum(tex_proj_spot); + proj_shadow->add_caster(herc); + proj_shadow->add_caster(ball); + proj_shadow->add_caster(camera_model); + + +//========================================================================== +// Sphere Texture Shader +//========================================================================== + spheretex = new SpheretexShader(tex); + + +//========================================================================== +// Sphere Texture Highlighter +//========================================================================== + highlight = new SpheretexHighlighter; + highlight->add_frustum(tex_proj_spot); + + +//========================================================================== +// Sphere Texture Reflector +//========================================================================== + sreflect = new SpheretexReflector; + sreflect->add_frustum(tex_proj_spot); + sreflect->add_caster(room); + sreflect->add_caster(herc); + sreflect->add_caster(camera_model); + + +//========================================================================== +// Planar Reflector +//========================================================================== + // Create a plane that corresponds to the floor of the room + Planef p(LVector3f::up(), LPoint3f::origin()); + PlaneNode* plane_node = new PlaneNode; + plane_node->set_plane(p); + new RenderRelation(room, plane_node); + preflect = new PlanarReflector(plane_node); + preflect->add_caster(ball); + preflect->add_caster(herc); + preflect->add_caster(camera_model); + + +//========================================================================== +// Outline Shader +//========================================================================== + outline_shader = new OutlineShader; + + additional_idle = &herc_idle; +} + +int main(int argc, char *argv[]) { + extra_display_func = &herc_display_func; + define_keys = &herc_keys; + extra_overrides_func = &herc_overrides_func; + return framework_main(argc, argv); +} diff --git a/panda/src/testbed/indirect.cxx b/panda/src/testbed/indirect.cxx new file mode 100644 index 0000000000..f0ca0994a1 --- /dev/null +++ b/panda/src/testbed/indirect.cxx @@ -0,0 +1,160 @@ +// Filename: indirect.cxx +// Created by: cary (25Mar99) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include + +Configure(indirect); + +extern GraphicsWindow* win; +extern std::string chan_config; + +extern int framework_main(int argc, char *argv[]); +extern void (*extra_display_func)(); +extern void (*define_keys)(EventHandler&); +extern void (*first_init)(); + +Texture* t = (Texture*)0L; +PixelBuffer* pb = (PixelBuffer *)0L; +DisplayRegion* dr1 = (DisplayRegion*)0L; +DisplayRegion* dr2 = (DisplayRegion*)0L; + +bool use_canned_texture = indirect.GetBool("canned-texture", false); +bool use_texture = indirect.GetBool("use-texture", true); +bool full_region = indirect.GetBool("full-region", true); +bool side_by_side = indirect.GetBool("side-by-side", true); +bool right_to_left = indirect.GetBool("right-to-left", true); + +int logs[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 0 }; + +int binary_log_cap(int x) { + int i = 0; + for (; (x > logs[i])&&(logs[i] != 0); ++i); + if (logs[i] == 0) + return 4096; + return logs[i]; +} + +void indirect_display_func( void ) { + GraphicsStateGuardian* g = win->get_gsg(); + const RenderBuffer& rf = g->get_render_buffer(RenderBuffer::T_front); + const RenderBuffer& rb = g->get_render_buffer(RenderBuffer::T_back); + + if (use_texture) { + if (t == (Texture*)0L) { + t = new Texture; + if (use_canned_texture) + t->read( "smiley.rgba" ); + else if (full_region) { + int a, b, w, h; + dr1->get_region_pixels(a, b, w, h); + t->_pbuffer->set_xsize(binary_log_cap(w)); + t->_pbuffer->set_ysize(binary_log_cap(h)); + } else { + t->_pbuffer->set_xsize( 512 ); + t->_pbuffer->set_ysize( 512 ); + } + } + if (side_by_side) { + if (right_to_left) + g->prepare_display_region(dr2); + else + g->prepare_display_region(dr1); + } + if (!use_canned_texture) { + if (full_region) { + if (side_by_side) { + if (right_to_left) + t->copy_from( rb, dr2 ); + else + t->copy_from( rb, dr1 ); + } else + t->copy_from( rb, dr1 ); + } else + t->copy_from( rb ); + } + if (side_by_side) { + if (right_to_left) + g->prepare_display_region(dr1); + else + g->prepare_display_region(dr2); + } + if (full_region) { + if (side_by_side) { + if (right_to_left) + t->draw_to( rf, dr1 ); + else + t->draw_to( rf, dr2 ); + } else + t->draw_to( rf, dr1 ); + } else + t->draw_to( rf ); + } else { + if (pb == (PixelBuffer*)0L) { + pb = new PixelBuffer; + if (use_canned_texture) + pb->read( "smiley.rgba" ); + else if (full_region) { + int a, b, w, h; + dr1->get_region_pixels(a, b, w, h); + w = binary_log_cap(w); + h = binary_log_cap(h); + pb->set_xsize(w); + pb->set_ysize(h); + pb->_image = PTA_uchar(w * h * 3); + } else { + pb->set_xsize(512); + pb->set_ysize(512); + pb->_image = PTA_uchar(512 * 512 * 3); + } + } + if (side_by_side) { + if (right_to_left) + g->prepare_display_region(dr2); + else + g->prepare_display_region(dr1); + } + if (!use_canned_texture) + pb->copy_from( rf ); + if (side_by_side) { + if (right_to_left) + g->prepare_display_region(dr1); + else + g->prepare_display_region(dr2); + } + pb->draw_to( rf ); + } +} + +void indirect_init(void) { + if (side_by_side) + chan_config = "two-frame"; +} + +void indirect_keys(EventHandler&) { + DRList::iterator i = win->getDRBegin(); + dr1 = *i; + if (side_by_side) { + ++i; + dr2 = *i; + if (right_to_left) + dr1->set_active(false); + else + dr2->set_active(false); + } +} + +int main(int argc, char *argv[]) { + define_keys = &indirect_keys; + extra_display_func = &indirect_display_func; + first_init = &indirect_init; + return framework_main(argc, argv); +} diff --git a/panda/src/testbed/loader_test.cxx b/panda/src/testbed/loader_test.cxx new file mode 100644 index 0000000000..80792fa5f5 --- /dev/null +++ b/panda/src/testbed/loader_test.cxx @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include +#include + +uint model_id; +const int MAX_LOOPS = 100; + +void event_p(CPT_Event) { + //model_id = loader.request_load("jafar-statue.egg"); + //model_id = loader.request_load("camera.egg"); + //model_id = loader.request_load("box.egg"); + //model_id = loader.request_load("yup-axis.egg"); + //model_id = loader.request_load("hand.egg"); + + //model_id = loader.request_load("frowney.egg", ""); + //model_id = loader.request_load("trolley.bam", ""); + //model_id = loader.request_load("smiley.egg", ""); + //model_id = loader.request_load("jack.bam", ""); + model_id = loader.request_load("herc-6000.egg", ""); +} + +void event_c(CPT_Event) { + if (loader.check_load(model_id)) + cerr << "load is complete" << endl; + else + cerr << "loading not finished yet" << endl; +} + +void event_s(CPT_Event) { + PT_Node model = loader.fetch_load(model_id); + if (model != (NamedNode*)0L) + new RenderRelation(render, model); + else + cerr << "null model!" << endl; +} + +void loader_keys(EventHandler& eh) { + eh.add_hook("p", event_p); + eh.add_hook("c", event_c); + eh.add_hook("s", event_s); +} + +int main(int argc, char *argv[]) { + define_keys = &loader_keys; +// loader.fork_asynchronous_thread(); + return framework_main(argc, argv); +} diff --git a/panda/src/testbed/lod_test.cxx b/panda/src/testbed/lod_test.cxx new file mode 100644 index 0000000000..c2aa165c51 --- /dev/null +++ b/panda/src/testbed/lod_test.cxx @@ -0,0 +1,36 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +extern PT_NamedNode render; +extern PT_NamedNode egg_root; +extern EventHandler event_handler; + +extern int framework_main(int argc, char *argv[]); +extern void (*define_keys)(EventHandler&); + +void lod_keys(EventHandler& eh) { + + PT(LODNode) lodnode = new LODNode("lodnode"); + new RenderRelation(egg_root, lodnode); + + PT_NamedNode lodnode0 = loader.load_sync("smiley.egg"); + new RenderRelation(lodnode, lodnode0); + lodnode->add_switch(10, 0); + + PT_NamedNode lodnode1 = loader.load_sync("frowney.egg"); + new RenderRelation(lodnode, lodnode1); + lodnode->add_switch(1000, 10); +} + +int main(int argc, char *argv[]) { + define_keys = &lod_keys; + return framework_main(argc, argv); +} diff --git a/panda/src/testbed/min_herc.cxx b/panda/src/testbed/min_herc.cxx new file mode 100644 index 0000000000..a61c744495 --- /dev/null +++ b/panda/src/testbed/min_herc.cxx @@ -0,0 +1,431 @@ +// Filename: min_herc.cxx +// Created by: jason (28Jun00) +// +//////////////////////////////////////////////////////////////////// + +//Shaders +#include +#include +#include +#include + +//Channel stuff +#include + +//Textures +#include +#include + +//Transitions +#include +#include +#include +#include +#include + +//Light stuff +#include +#include + +//Nodes +#include +#include + +//Relations (arcs) +#include +#include +#include + +//Misc +#include +#include +#include +#include +#include + +//Math/Matrix/Vector/Transformation stuff +#include +#include +#include +#include + +//Control/IO +#include +#include + +//Animation +#include +#include +#include + +Configure(min_herc); +ConfigureFn(min_herc) { +} + +PT(ProjectionNode) tex_proj; +PT(Trackball) tex_proj_trackball; +PT(ProjtexShader) proj_shader; +PT(ProjtexShadower) proj_shadow; +PT(PlanarReflector) preflect; +PT(GeomNode) proj_geom_node; +PT_NamedNode spot_shaft; + +PT_NamedNode herc; +PT_NamedNode ball; +PT_NamedNode hide_ball; + +ShaderTransition shader_trans; + +PT(Spotlight) tex_proj_spot; +PT(Trackball) tex_spot_trackball; +PT(SpotlightShader) spot_shader; + +PT(PlanarSlider) ball_slider; +bool follow_ball; + +RenderRelation* room_arc; +RenderRelation* spot_arc; +RenderRelation* ball_arc; +RenderRelation* hide_ball_arc; +RenderRelation* herc_arc; +RenderRelation* herc_rot_arc; +RenderRelation* camera_model_arc; + +extern PT_NamedNode render; +extern NodeAttributes initial_state; +extern PT_NamedNode lights; +extern PT_NamedNode egg_root; +extern PT(GeomNode) geomnode; +extern PT_NamedNode cameras; +extern PT(MouseAndKeyboard) mak; + +extern void set_alt_trackball(Node *trackball); + +extern PT(GraphicsWindow) main_win; + +extern int framework_main(int argc, char *argv[]); +extern void (*extra_display_func)(); +extern void (*define_keys)(EventHandler&); +extern void (*extra_overrides_func)(ChanCfgOverrides&, std::string&); +extern void (*additional_idle)(); + +void herc_overrides_func(ChanCfgOverrides &override, std::string&) { + override.setField(ChanCfgOverrides::Mask, + ((unsigned int)(W_DOUBLE|W_DEPTH|W_MULTISAMPLE|W_STENCIL))); + override.setField(ChanCfgOverrides::Title, "Lighting Demo"); +} + + +void herc_idle() { + static const double walk_speed = 4.0; // feet per second + + if (follow_ball) { + LPoint3f bp = get_rel_pos(ball, herc); + LVector2f bv2(bp[0], bp[1]); + float dist = length(bv2); + if (dist > 0.0001) { + LMatrix4f mat; + look_at(mat, LPoint3f(-bp[0], -bp[1], 0.0)); + herc_rot_arc->set_transition(new TransformTransition(mat)); + + float stride = walk_speed * ClockObject::get_global_clock()->get_dt(); + if (dist > stride) { + LVector2f step = bv2 / dist * stride; + + const TransformTransition *tt; + if (!get_transition_into(tt, herc_arc)) { + herc_arc->set_transition + (new TransformTransition + (LMatrix4f::translate_mat(step[0], step[1], 0.0))); + } else { + LMatrix4f mat = tt->get_matrix(); + mat(3, 0) += step[0]; + mat(3, 1) += step[1]; + herc_arc->set_transition(new TransformTransition(mat)); + } + } + } + } +} + +void event_p(CPT_Event) { + // The "p" key was pressed. Toggle projected texture. + static bool projtex_mode = false; + + projtex_mode = !projtex_mode; + if (!projtex_mode) { + // Set the normal mode on the render arc. + clear_shader(room_arc, proj_shader); + clear_shader(herc_arc, proj_shader); + clear_shader(ball_arc, proj_shader); + + set_alt_trackball(NULL); + remove_child(tex_proj, proj_geom_node, RenderRelation::get_class_type()); + + } else { + set_shader(room_arc, proj_shader); + set_shader(herc_arc, proj_shader); + set_shader(ball_arc, proj_shader); + + set_alt_trackball(tex_proj_trackball); + // Display the texture projector frustum + RenderRelation *prr = new RenderRelation(tex_proj, proj_geom_node); + LightTransition *plt = new LightTransition(LightTransition::all_off()); + prr->set_transition(plt); + } +} + +void event_s(CPT_Event) { + // The "s" key was pressed. Toggle projected texture spotlight. + static bool projtexspot_mode = false; + LightTransition light_trans; + + projtexspot_mode = !projtexspot_mode; + if (!projtexspot_mode) { + // Set the normal mode on the render arc. + clear_shader(room_arc, spot_shader); + clear_shader(herc_arc, spot_shader); + clear_shader(ball_arc, spot_shader); + + set_alt_trackball(NULL); + remove_child(tex_proj_spot, spot_shaft, RenderRelation::get_class_type()); + + } else { + set_shader(room_arc, spot_shader); + set_shader(herc_arc, spot_shader); + set_shader(ball_arc, spot_shader); + + set_alt_trackball(tex_spot_trackball); + new RenderRelation(tex_proj_spot, spot_shaft, 10); + } +} + +void event_d(CPT_Event) { + // The "d" key was pressed. Toggle projected texture shadows. + static bool projtex_shadow_mode = false; + + projtex_shadow_mode = !projtex_shadow_mode; + if (!projtex_shadow_mode) { + // Set the normal mode on the render arc. + clear_shader(room_arc, proj_shadow); + set_alt_trackball(NULL); + + } else { + set_shader(room_arc, proj_shadow); + set_alt_trackball(tex_spot_trackball); + } +} + +void event_r(CPT_Event) { + // The "r" key was pressed. Toggle planar reflection. + static bool plane_reflect_mode = false; + + plane_reflect_mode = !plane_reflect_mode; + if (!plane_reflect_mode) { + // Set the normal mode on the render arc. + clear_shader(room_arc, preflect); + + } else { + // Set an override on the initial state. + set_shader(room_arc, preflect); + } +} + +void event_z(CPT_Event) { + // The "z" key was pressed. Allow the user to move the ball around. + set_alt_trackball(ball_slider); +} + +void event_Z(CPT_Event) { + // The "Z" key was pressed. Toggle follow-ball mode. + follow_ball = !follow_ball; + + if (follow_ball) { + // Hide the ball while we're following it. + remove_arc(hide_ball_arc); + } else { + // Reveal the ball when we're done following it. + hide_ball_arc = new RenderRelation(render, hide_ball); + } +} + +void herc_keys(EventHandler &eh) { + Loader loader; + + eh.add_hook("p", event_p); // Projected Texture Shader + eh.add_hook("s", event_s); // Projected Texture Spotlight + eh.add_hook("d", event_d); // Projected Texture Shadower + eh.add_hook("r", event_r); // Planar Reflector + eh.add_hook("z", event_z); // Move ball + eh.add_hook("Z", event_Z); // Follow ball + +//========================================================================== +// Models +//========================================================================== + // Load herc + PT_NamedNode herc_model = DCAST(NamedNode, loader.load_sync("herc-6000.egg")); + PT_NamedNode herc_anim = DCAST(NamedNode, loader.load_sync("HB_1_HE1.egg")); + assert(herc_model != (NamedNode *)NULL && + herc_anim != (NamedNode *)NULL); + PT_NamedNode herc_parent = new NamedNode("herc_parent"); + new RenderRelation(herc_parent, herc_model); + new RenderRelation(herc_parent, herc_anim); + + AnimControlCollection anim_controls; + auto_bind(herc_parent, anim_controls, ~0); + + // And start looping any animations we successfully bound. + anim_controls.loop_all(true); + + PT_NamedNode herc_rot = new NamedNode("herc_rot"); + RenderRelation *herc_arc1 = new RenderRelation(herc_rot, herc_parent); + herc_arc1->set_transition + (new TransformTransition + (LMatrix4f::scale_mat(LVector3f(0.25, 0.25, 0.25)))); + herc = new NamedNode("herc"); + herc_rot_arc = new RenderRelation(herc, herc_rot); + herc_arc = new RenderRelation(render, herc); + + // Load ball + ball = DCAST(NamedNode, loader.load_sync("marble_ball.egg")); + assert(ball != (NamedNode *)NULL); + PT_NamedNode scaled_ball = new NamedNode("scaled_ball"); + RenderRelation *ball_arc1 = new RenderRelation(scaled_ball, ball); + ball_arc1->set_transition + (new TransformTransition(LMatrix4f::scale_mat(0.2))); + hide_ball = new NamedNode("hide_ball"); + ball_arc = new RenderRelation(hide_ball, scaled_ball); + ball_arc->set_transition + (new TransformTransition(LMatrix4f::translate_mat(4., 2., 1.))); + hide_ball_arc = new RenderRelation(render, hide_ball); + + // Control the ball using a PlanarSlider tform. + ball_slider = new PlanarSlider("ball_slider"); + ball_slider->set_transform(LMatrix4f::translate_mat(0.0, 0.0, 1.0) * + LMatrix4f::scale_mat(7.0, -7.0, 1.0)); + ball_slider->set_mouse_pos(LPoint2f(4.0 / 7.0, 2.0 / -7.0)); + Transform2SG *slider2ball = new Transform2SG("slider2ball"); + slider2ball->set_arc(ball_arc); + new RenderRelation(ball_slider, slider2ball); + follow_ball = false; + + // Load the room file + PT_NamedNode room = DCAST(NamedNode, loader.load_sync("lfloor.egg")); + assert(room != (NamedNode *)NULL); + room_arc = new RenderRelation(render, room); + + // Load up a camera model to visualize our eyepoint. + PT_NamedNode camera_model = DCAST(NamedNode, loader.load_sync("camera.egg")); + assert(camera_model != (NamedNode *)NULL); + camera_model_arc = new RenderRelation(cameras, camera_model); + + // Remove any model that was loaded by default or on command line + if (root != (NamedNode *)NULL) + remove_child(render, root, RenderRelation::get_class_type()); + if (geomnode != (GeomNode *)NULL) + remove_child(render, geomnode, RenderRelation::get_class_type()); + + +//========================================================================== +// Projected Texture Shader +//========================================================================== + // Load a texture to project + Texture* tex = TexturePool::load_texture("smiley.rgba"); + tex->set_minfilter(Texture::FT_linear); + tex->set_magfilter(Texture::FT_linear); + tex->set_wrapu(Texture::WM_clamp); + tex->set_wrapv(Texture::WM_clamp); + + // Put the texture projector into the scene graph + tex_proj = new ProjectionNode("texture_projector"); + RenderRelation* proj_arc = new RenderRelation(render, tex_proj); + + // Create a trackball to spin this around. + tex_proj_trackball = new Trackball("tex_proj_trackball"); + tex_proj_trackball->set_invert(false); + tex_proj_trackball->set_rel_to(cameras); + Transform2SG *tball2cam = new Transform2SG("tball2cam"); + tball2cam->set_arc(proj_arc); + new RenderRelation(tex_proj_trackball, tball2cam); + + // Raise it and aim it at the origin + LMatrix4f proj_mat; + LPoint3f proj_pos = LPoint3f::rfu(2., 3., 8.); + LVector3f fwd_vec = proj_pos - LPoint3f::origin(); + look_at(proj_mat, -fwd_vec); + proj_mat.set_row(3, proj_pos); + tex_proj_trackball->set_mat(proj_mat); + proj_arc->set_transition(new TransformTransition(proj_mat)); + + // Create a shader for the texture projector + proj_shader = new ProjtexShader(tex); + proj_shader->add_frustum(tex_proj); + + // Create a wireframe representation of the texture projector frustum + GeomLine* proj_geom = + (GeomLine *)tex_proj->get_projection()->make_geometry(); + proj_geom_node = new GeomNode("proj_geometry"); + proj_geom_node->add_geom(proj_geom); + + +//========================================================================== +// Projected Texture Spotlight Shader +//========================================================================== + tex_proj_spot = new Spotlight("tex_proj_spotlight"); + spot_arc = new RenderRelation(render, tex_proj_spot, 10); + + // Create a trackball to spin this around. + tex_spot_trackball = new Trackball("tex_spot_trackball"); + tex_spot_trackball->set_invert(false); + tex_spot_trackball->set_rel_to(cameras); + tball2cam = new Transform2SG("tball2cam"); + tball2cam->set_arc(spot_arc); + new RenderRelation(tex_spot_trackball, tball2cam); + + // Raise it and aim it at the origin + LMatrix4f spot_mat; + LPoint3f spot_pos = LPoint3f::rfu(-4., -3., 8.); + LVector3f spot_vec = spot_pos - LPoint3f::origin(); + look_at(spot_mat, -spot_vec); + spot_mat.set_row(3, spot_pos); + tex_spot_trackball->set_mat(spot_mat); + spot_arc->set_transition(new TransformTransition(spot_mat)); + + // Create a shader for the spotlight + spot_shader = new SpotlightShader; + spot_shader->add_frustum(tex_proj_spot); + + // Create a light shaft for the spotlight + spot_shaft = tex_proj_spot->make_geometry(0.05, 8.0, 36); + + +//========================================================================== +// Projected Texture Shadower +//========================================================================== + proj_shadow = new ProjtexShadower; + proj_shadow->add_frustum(tex_proj_spot); + proj_shadow->add_caster(herc); + proj_shadow->add_caster(ball); + proj_shadow->add_caster(camera_model); + +//========================================================================== +// Planar Reflector +//========================================================================== + // Create a plane that corresponds to the floor of the room + Planef p(LVector3f::up(), LPoint3f::origin()); + PlaneNode* plane_node = new PlaneNode; + plane_node->set_plane(p); + new RenderRelation(room, plane_node); + preflect = new PlanarReflector(plane_node); + preflect->add_caster(ball); + preflect->add_caster(herc); + preflect->add_caster(camera_model); + + additional_idle = &herc_idle; +} + +int main(int argc, char *argv[]) { + define_keys = &herc_keys; + extra_overrides_func = &herc_overrides_func; + return framework_main(argc, argv); +} diff --git a/panda/src/testbed/min_shader.cxx b/panda/src/testbed/min_shader.cxx new file mode 100644 index 0000000000..282ff47f09 --- /dev/null +++ b/panda/src/testbed/min_shader.cxx @@ -0,0 +1,809 @@ +// Filename: min_shader.cxx +// Created by: jason (28Jun00) +// +//////////////////////////////////////////////////////////////////// + +//Shaders +#include +#include +#include +#include +#include +#include +#include +#include + +//Channel stuff +#include + +//Textures +#include +#include + +//Transitions +#include +#include +#include +#include +#include + +//Light stuff +#include +#include + +//Nodes +#include +#include + +//Relations (arcs) +#include +#include +#include + +//Misc +#include +#include +#include +#include + +//Math/Matrix/Vector/Transformation stuff +#include +#include +#include +#include + +Configure(min_shader); +ConfigureFn(min_shader) { +} + +//--------Projective texture stuff-------- +PT(ProjectionNode) tex_proj; +PT(Trackball) tex_proj_trackball; +PT(ProjtexShader) proj_shader; +//--------Spotlight stuff----------------- +PT(Spotlight) tex_proj_spot; +PT(Trackball) tex_spot_trackball; +PT(SpotlightShader) spot_shader; +//--------Projective Shadow stuff-------- +PT(ProjtexShadower) proj_shadow; +//--------Planar reflecter stuff--------- +PT(PlanarReflector) preflect; +//--------Sphere texture stuff--------- +PT(SpheretexShader) spheretex; +//--------Sphere texture highlight stuff--------- +PT(SpheretexHighlighter) highlight; +//--------Sphere texture reflector stuff--------- +PT(SpheretexReflector) sreflect; +//--------Outline shader stuff--------- +PT(OutlineShader) outline_shader; + +ShaderTransition shader_trans; + +RenderRelation* room_arc; +RenderRelation* spot_arc; +RenderRelation* jack_arc; +RenderRelation* camera_model_arc; +RenderRelation* smiley_arc; + +PT(LightTransition) light_transition; + +//Framework extern variables and functions +extern PT_NamedNode render; +extern NodeAttributes initial_state; +extern RenderRelation* first_arc; +extern PT_NamedNode lights; +extern PT_NamedNode root; +extern PT(GeomNode) geomnode; +extern PT_NamedNode cameras; +extern PT(MouseAndKeyboard) mak; + +extern void set_alt_trackball(Node *trackball); + +extern int framework_main(int argc, char *argv[]); +extern void (*extra_display_func)(); +extern void (*define_keys)(EventHandler&); + +extern PT(GraphicsWindow) main_win; + +//GLOBALS +LPoint3f center_pos = LPoint3f::origin(); +Transform2SG *tball2cam; +Texture* tex; + +//VIZ STUFF +class BaseViz : public Shader::Visualize { +public: + BaseViz(void); + virtual ~BaseViz(void); + virtual void DisplayTexture(PT(Texture)&, Shader*); + virtual void DisplayPixelBuffer(PT(PixelBuffer)&, Shader*); +protected: + typedef std::list texlist; + typedef texlist::iterator texiter; + typedef std::list pblist; + typedef pblist::iterator pbiter; + + texlist _texs; + pblist _pbs; +}; + +BaseViz::BaseViz(void) {} + +BaseViz::~BaseViz(void) {} + +void BaseViz::DisplayTexture(PT(Texture)& tex, Shader*) { + if (tex->has_ram_image()) + _texs.push_back(tex); + else { + GraphicsStateGuardian* g = main_win->get_gsg(); + PT(PixelBuffer) pb(new PixelBuffer); + g->texture_to_pixel_buffer(tex->prepare(g), pb); + _pbs.push_back(pb); + } +} + +void BaseViz::DisplayPixelBuffer(PT(PixelBuffer)& pb, Shader*) { + _pbs.push_back(pb); +} + +class Oldviz : public BaseViz { +public: + Oldviz(PT(GraphicsWindow)&); + virtual ~Oldviz(void); + virtual void Flush(void); +protected: + PT(GraphicsWindow) _w; +}; + +Oldviz::Oldviz(PT(GraphicsWindow)& win) : _w(win) {} + +Oldviz::~Oldviz(void) {} + +void Oldviz::Flush(void) { + GraphicsStateGuardian* g = _w->get_gsg(); + const RenderBuffer& r = g->get_render_buffer(RenderBuffer::T_front); + PT(DisplayRegion) d(_w->make_scratch_display_region(256, 256)); + + for (texiter i=_texs.begin(); i!=_texs.end(); ++i) + (*i)->draw(g, d, r); + for (pbiter j=_pbs.begin(); j!=_pbs.end(); ++j) + (*j)->draw(g, d, r); + _texs.erase(_texs.begin(), _texs.end()); + _pbs.erase(_pbs.begin(), _pbs.end()); +} + +class Viztex : public BaseViz { +public: + Viztex(PT(GraphicsPipe)&); + virtual ~Viztex(void); + virtual void Flush(void); +protected: + typedef std::list winlist; + typedef winlist::iterator winiter; + + winlist _wins; + PT(GraphicsPipe) _pipe; +}; + +Viztex::Viztex(PT(GraphicsPipe)& p) : _pipe(p) {} + +Viztex::~Viztex(void) {} + +void Viztex::Flush(void) { + winiter _witer = _wins.begin(); + + for (texiter i=_texs.begin(); i!=_texs.end(); ++i) { + GraphicsWindow* w; + + if (_witer == _wins.end()) { + ChanCfgOverrides override; + + override.setField(ChanCfgOverrides::Mask, + ((unsigned int)(W_SINGLE))); + override.setField(ChanCfgOverrides::Title, "Multipass partial"); + override.setField(ChanCfgOverrides::SizeX, 256); + override.setField(ChanCfgOverrides::SizeY, 256); + override.setField(ChanCfgOverrides::Cameras, false); + w = ChanConfig(_pipe, "single", (Node *)NULL, render, override); + _wins.push_back(w); + } + else + { + w = *_witer; + ++_witer; + } + GraphicsStateGuardian* g = w->get_gsg(); + const RenderBuffer& r = g->get_render_buffer(RenderBuffer::T_front); + PT(DisplayRegion) d(w->make_scratch_display_region(w->get_width(), + w->get_height())); + // g->prepare_display_region(d); + (*i)->draw(g, d, r); + } + for (pbiter j=_pbs.begin(); j!=_pbs.end(); ++j) { + GraphicsWindow* w; + + if (_witer == _wins.end()) { + ChanCfgOverrides override; + + override.setField(ChanCfgOverrides::Mask, + ((unsigned int)(W_SINGLE))); + override.setField(ChanCfgOverrides::Title, "Multipass partial"); + override.setField(ChanCfgOverrides::SizeX, 256); + override.setField(ChanCfgOverrides::SizeY, 256); + override.setField(ChanCfgOverrides::Cameras, false); + w = ChanConfig(_pipe, "single", cameras, render, override); + _wins.push_back(w); + } else { + w = *_witer; + ++_witer; + } + GraphicsStateGuardian* g = w->get_gsg(); + const RenderBuffer& r = g->get_render_buffer(RenderBuffer::T_front); + PT(DisplayRegion) d(w->make_scratch_display_region(w->get_width(), + w->get_height())); + // g->prepare_display_region(d); + (*j)->draw(g, d, r); + } + _texs.erase(_texs.begin(), _texs.end()); + _pbs.erase(_pbs.begin(), _pbs.end()); +} + +class Tiledviz : public Viztex { +public: + Tiledviz(PT(GraphicsPipe)&); + virtual ~Tiledviz(void); + virtual void Flush(void); +}; + +Tiledviz::Tiledviz(PT(GraphicsPipe)& p) : Viztex(p) {} + +Tiledviz::~Tiledviz(void) {} + +void Tiledviz::Flush(void) { + int count = 0; + int layer_count = 0; + winiter _witer = _wins.begin(); + + for (texiter texi=_texs.begin(); texi!=_texs.end(); ++texi) { + GraphicsWindow* w; + DisplayRegion* d; + + if (_witer == _wins.end()) { + ChanCfgOverrides override; + + override.setField(ChanCfgOverrides::Mask, + ((unsigned int)(W_SINGLE))); + override.setField(ChanCfgOverrides::Title, "Multipass partial"); + override.setField(ChanCfgOverrides::SizeX, main_win->get_width()); + override.setField(ChanCfgOverrides::SizeY, main_win->get_height()); + override.setField(ChanCfgOverrides::Cameras, false); + w = ChanConfig(_pipe, "multipass-tile", cameras, render, override); + _wins.push_back(w); + _witer = --(_wins.end()); + } else + w = *_witer; + + int dridx; + GraphicsStateGuardian* g = w->get_gsg(); + GraphicsChannel *chan = w->get_channel(0); + GraphicsLayer *layer = chan->get_layer(layer_count); + const RenderBuffer& r = g->get_render_buffer(RenderBuffer::T_front); + + d = layer->get_dr(count); + (*texi)->draw(g, d, r); + + ++count; + if (count == layer->get_num_drs()) { + count = 0; + ++layer_count; + } + + if (layer_count == chan->get_num_layers()) + { + layer_count = 0; + ++_witer; + } + } + + for (pbiter pbi=_pbs.begin(); pbi!=_pbs.end(); ++pbi) { + GraphicsWindow* w; + DisplayRegion* d; + + if (_witer == _wins.end()) { + ChanCfgOverrides override; + + override.setField(ChanCfgOverrides::Mask, + ((unsigned int)(W_SINGLE))); + override.setField(ChanCfgOverrides::Title, "Multipass partial"); + override.setField(ChanCfgOverrides::SizeX, main_win->get_width()); + override.setField(ChanCfgOverrides::SizeY, main_win->get_height()); + override.setField(ChanCfgOverrides::Cameras, false); + w = ChanConfig(_pipe, "multipass-tile", cameras, render, override); + _wins.push_back(w); + _witer = --(_wins.end()); + } else + w = *_witer; + + int dridx; + GraphicsStateGuardian* g = w->get_gsg(); + GraphicsChannel *chan = w->get_channel(0); + GraphicsLayer *layer = chan->get_layer(layer_count); + const RenderBuffer& r = g->get_render_buffer(RenderBuffer::T_front); + + d = layer->get_dr(count); + (*pbi)->draw(g, d, r); + + ++count; + if (count == layer->get_num_drs()) { + count = 0; + ++layer_count; + } + + if (layer_count == chan->get_num_layers()) + { + layer_count = 0; + ++_witer; + } + + } + _texs.erase(_texs.begin(), _texs.end()); + _pbs.erase(_pbs.begin(), _pbs.end()); +} + +void shader_display_func(void) { + Shader::Visualize* v = Shader::get_viz(); + if (v != (Shader::Visualize*)0L) + v->Flush(); +} + +//END VIZ STUFF + +//////////////////////////////////////////////////////////////////// +// Function: event_p +// Access: Public +// Description: Toggle the projective texture being on and off +//////////////////////////////////////////////////////////////////// +void event_p(CPT_Event) { + static bool projtex_mode = false; + + projtex_mode = !projtex_mode; + if (!projtex_mode) { + // Set the normal mode on the render arc. + clear_shader(room_arc, proj_shader); + //clear_shader(first_arc, proj_shader); + //clear_shader(jack_arc, proj_shader); + + set_alt_trackball(NULL); + + } else { + // Set an override on the initial state. + set_shader(room_arc, proj_shader); + //set_shader(first_arc, proj_shader); + //set_shader(jack_arc, proj_shader); + + set_alt_trackball(tex_proj_trackball); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: event_s +// Access: Public +// Description: Toggle the spotlight being on and off +//////////////////////////////////////////////////////////////////// +void event_s(CPT_Event) { + static bool projtexspot_mode = false; + + projtexspot_mode = !projtexspot_mode; + if (!projtexspot_mode) { + // Set the normal mode on the render arc. + clear_shader(room_arc, spot_shader); + + set_alt_trackball(NULL); + } else { + // Set an override on the initial state. + set_shader(room_arc, spot_shader); + + set_alt_trackball(tex_spot_trackball); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: event_d +// Access: Public +// Description: Toggle projected shadows +//////////////////////////////////////////////////////////////////// +void event_d(CPT_Event) +{ + static bool projtex_shadow_mode = false; + + projtex_shadow_mode = !projtex_shadow_mode; + if (!projtex_shadow_mode) { + // Set the normal mode on the render arc. + clear_shader(room_arc, proj_shadow); + set_alt_trackball(NULL); + + } + else + { + set_shader(room_arc, proj_shadow); + set_alt_trackball(tex_spot_trackball); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: event_r +// Access: Public +// Description: Toggle planar reflection +//////////////////////////////////////////////////////////////////// +void event_r(CPT_Event) +{ + static bool plane_reflect_mode = false; + + plane_reflect_mode = !plane_reflect_mode; + if (!plane_reflect_mode) { + // Set the normal mode on the render arc. + clear_shader(room_arc, preflect); + } + else + { + // Set an override on the initial state. + set_shader(room_arc, preflect); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: event_e +// Access: Public +// Description: Toggle sphere texture +//////////////////////////////////////////////////////////////////// +void event_e(CPT_Event) { + static bool spheretex_mode = false; + + spheretex_mode = !spheretex_mode; + if (!spheretex_mode) { + // Set the normal mode on the render arc. + //clear_shader(first_arc, spheretex); + clear_shader(jack_arc, spheretex); + + } else { + // Set an override on the initial state. + //set_shader(first_arc, spheretex); + set_shader(jack_arc, spheretex); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: event_h +// Access: Public +// Description: Toggle sphere texture highlight +//////////////////////////////////////////////////////////////////// +void event_h(CPT_Event) { + static bool highlight_mode = false; + + highlight_mode = !highlight_mode; + if (!highlight_mode) { + // Set the normal mode on the render arc. + clear_shader(jack_arc, highlight); + + } else { + set_shader(jack_arc, highlight); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: event_h +// Access: Public +// Description: Toggle sphere texture reflector +//////////////////////////////////////////////////////////////////// +void event_m(CPT_Event) { + static bool sphere_reflect_mode = false; + + sphere_reflect_mode = !sphere_reflect_mode; + if (!sphere_reflect_mode) { + // Set the normal mode on the render arc. + clear_shader(jack_arc, sreflect); + + } else { + // Set an override on the initial state. + set_shader(jack_arc, sreflect); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: event_o +// Access: Public +// Description: Toggle outline shader +//////////////////////////////////////////////////////////////////// +void event_o(CPT_Event) { + static bool outline_mode = false; + + outline_mode = !outline_mode; + if (!outline_mode) { + // Set the normal mode on the render arc. + clear_shader(jack_arc, outline_shader); + + } else { + set_shader(jack_arc, outline_shader); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: setup_projtex +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void setup_projtex(void) +{ + // Create a projected texture shader + + // Put the texture projector into the scene graph + tex_proj = new ProjectionNode("texture_projector"); + RenderRelation* proj_arc = new RenderRelation(render, tex_proj); + + // Create a trackball to spin this around. + tex_proj_trackball = new Trackball("tex_proj_trackball"); + tex_proj_trackball->set_invert(false); + tex_proj_trackball->set_rel_to(cameras); + tball2cam = new Transform2SG("tball2cam"); + tball2cam->set_arc(proj_arc); + new DataRelation(tex_proj_trackball, tball2cam); + + // Raise it and aim it at the origin + LMatrix4f proj_mat; + LPoint3f proj_pos = LPoint3f::rfu(2., 3., 8.); + LVector3f fwd_vec = proj_pos - center_pos; + look_at(proj_mat, -fwd_vec); + proj_mat.set_row(3, proj_pos); + tex_proj_trackball->set_mat(proj_mat); + proj_arc->set_transition(new TransformTransition(proj_mat)); + + // Create a shader for the texture projector + proj_shader = new ProjtexShader(tex); + proj_shader->set_priority(150); + proj_shader->add_frustum(tex_proj); + +#define DISPLAY_TEXPROJFRUST +#ifdef DISPLAY_TEXPROJFRUST + // Display a wireframe representation of the texture projector frustum + GeomLine* proj_geom = + (GeomLine *)tex_proj->get_projection()->make_geometry(); + GeomNode* proj_geom_node = new GeomNode("proj_geometry"); + proj_geom_node->add_geom(proj_geom); + RenderRelation *prr = new RenderRelation(tex_proj, proj_geom_node); + LightTransition *plt = new LightTransition(LightTransition::all_off()); + prr->set_transition(plt); +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: setup_spotlight +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void setup_spotlight(void) +{ + // Create a projected texture spotlight shader + tex_proj_spot = new Spotlight("tex_proj_spotlight"); + //Push out the far clipping plane of the spotlight frustum + Frustumf f; + f.make_perspective(45.0f, 45.0f, f._fnear, 13); + PerspectiveProjection pp(f); + tex_proj_spot->set_projection(pp); + + spot_arc = new RenderRelation(render, tex_proj_spot, 10); + + // Create a trackball to spin this around. + tex_spot_trackball = new Trackball("tex_spot_trackball"); + tex_spot_trackball->set_invert(false); + tex_spot_trackball->set_rel_to(cameras); + tball2cam = new Transform2SG("tball2cam"); + tball2cam->set_arc(spot_arc); + new DataRelation(tex_spot_trackball, tball2cam); + + // Raise it and aim it at the origin + LMatrix4f spot_mat; + LPoint3f spot_pos = LPoint3f::rfu(-4., -3., 8.); + LVector3f spot_vec = spot_pos - center_pos; + look_at(spot_mat, -spot_vec); + spot_mat.set_row(3, spot_pos); + tex_spot_trackball->set_mat(spot_mat); + spot_arc->set_transition(new TransformTransition(spot_mat)); + + // Create a shader for the spotlight + spot_shader = new SpotlightShader; + spot_shader->set_priority(150); + spot_shader->add_frustum(tex_proj_spot); + +#define DISPLAY_TEXPROJSPOTFRUST +#ifdef DISPLAY_TEXPROJSPOTFRUST + // Display a wireframe representation of the spotlight frustum + Colorf color_red(1., 0., 0., 1.); + GeomLine* frust_geom = + (GeomLine *)tex_proj_spot->get_projection()->make_geometry(color_red); + GeomNode* frust_geom_node = new GeomNode("frustum_geometry"); + frust_geom_node->add_geom(frust_geom); + RenderRelation *rr = new RenderRelation(tex_proj_spot, frust_geom_node); + LightTransition *lt = new LightTransition(LightTransition::all_off()); + rr->set_transition(lt); +#endif + +#define DISPLAY_SHAFT +#ifdef DISPLAY_SHAFT + // Draw a light shaft for the spotlight + NamedNode* shaft = tex_proj_spot->make_geometry(0.05, 8.0, 36); + RenderRelation *sr = new RenderRelation(tex_proj_spot, shaft, 10); +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: setup_planar +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void setup_planar(void) +{ + // Create a planar reflection + Planef p(LVector3f(0., 0., 1.), LVector3f(0., 0., -10.)); + PlaneNode* plane_node = new PlaneNode; + plane_node->set_plane(p); + preflect = new PlanarReflector(plane_node); +} + +//////////////////////////////////////////////////////////////////// +// Function: setup_projshadow +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void setup_projshadow(void) +{ + // Create a projected texture shadower + proj_shadow = new ProjtexShadower; + proj_shadow->add_frustum(tex_proj_spot); + proj_shadow->set_priority(150); + if (root != (NamedNode *)NULL) { + proj_shadow->add_caster(root); + preflect->add_caster(root); + } else if (geomnode != (GeomNode *)NULL) { + proj_shadow->add_caster(geomnode); + preflect->add_caster(geomnode); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: setup_vizes +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void setup_vizes(void) +{ + // and now for some multipass partial visualization + Shader::Visualize* v = Shader::get_viz(); + std::string viztype = min_shader.GetString("multipass-viz", "none"); + + if (viztype == "old-style") + { + PT(GraphicsWindow) w(main_win); + v = new Oldviz(w); + } + else if (viztype == "new-single") + { + PT(GraphicsPipe) p(((GraphicsPipe*)(main_win->get_pipe()))); + v = new Viztex(p); + } + else if (viztype == "new-tile") + { + PT(GraphicsPipe) p(((GraphicsPipe*)(main_win->get_pipe()))); + v = new Tiledviz(p); + } + + Shader::set_viz(v); +} + +//////////////////////////////////////////////////////////////////// +// Function: min_shader_keys +// Access: Public +// Description: Set event handlers for the various keys needed, and +// do any initialization necessary +//////////////////////////////////////////////////////////////////// +void min_shader_keys(EventHandler &eh) { + Loader loader; + + eh.add_hook("p", event_p); + eh.add_hook("s", event_s); + eh.add_hook("d", event_d); + eh.add_hook("r", event_r); + eh.add_hook("e", event_e); + eh.add_hook("h", event_h); + eh.add_hook("m", event_m); + eh.add_hook("o", event_o); + + // Load a texture to project + tex = TexturePool::load_texture("smiley.rgba"); + tex->set_minfilter(Texture::FT_linear); + tex->set_magfilter(Texture::FT_linear); + tex->set_wrapu(Texture::WM_clamp); + tex->set_wrapv(Texture::WM_clamp); + +//--------------------PROJECTED TEXTURE SHADER------------------- + setup_projtex(); +//--------------------SPOTLIGHT SHADER------------------- + setup_spotlight(); +//--------------------PLANAR REFLECTOR SHADER------------------- + setup_planar(); +//--------------------SHADOW SHADER------------------- + setup_projshadow(); +//--------------------SPHERE TEXTURE SHADER------------------- + spheretex = new SpheretexShader(tex); + spheretex->set_priority(150); +//--------------------SPHERE HIGHLIGHTER TEXTURE SHADER------------------- + highlight = new SpheretexHighlighter(32); + highlight->add_frustum(tex_proj_spot); + highlight->set_priority(150); +//--------------------SPHERE REFLECTOR TEXTURE SHADER------------------- + sreflect = new SpheretexReflector; + sreflect->add_frustum(tex_proj_spot); + sreflect->set_priority(150); + sreflect->add_caster(root); +//--------------------OUTLINE SHADER------------------- + outline_shader = new OutlineShader; + outline_shader->set_priority(150); + + // Load the room file + PT_NamedNode room = DCAST(NamedNode, loader.load_sync("big-room.egg")); + if (room != (NamedNode *)NULL) { + room_arc = new RenderRelation(render, room, 20); + + sreflect->add_caster(room); + } + + // Load jack + PT_NamedNode jack = DCAST(NamedNode, loader.load_sync("jack.egg")); + if (jack != (NamedNode *)NULL) { + jack_arc = new RenderRelation(render, jack); + LMatrix4f jack_mat = LMatrix4f::ident_mat(); + LPoint3f jack_pos = LPoint3f::rfu(-2., -2., -6.); + jack_mat.set_row(3, jack_pos); + jack_arc->set_transition(new TransformTransition(jack_mat)); + + proj_shadow->add_caster(jack); + preflect->add_caster(jack); + } + + // Load jack + PT_NamedNode smiley = DCAST(NamedNode, loader.load_sync("smiley.egg")); + if (jack != (NamedNode *)NULL) { + smiley_arc = new RenderRelation(render, smiley); + + proj_shadow->add_caster(smiley); + preflect->add_caster(smiley); + } + + // Load up a camera model to visualize our eyepoint. + PT_NamedNode camera_model = DCAST(NamedNode, loader.load_sync("camera.egg")); + if (camera_model != (NamedNode *)NULL) { + camera_model_arc = new RenderRelation(cameras, camera_model); + + sreflect->add_caster(camera_model); + proj_shadow->add_caster(camera_model); + preflect->add_caster(camera_model); + } + + // Set up a transition for the spotlight + light_transition = new LightTransition; + if (first_arc != (NodeRelation *)NULL) { + first_arc->set_transition(light_transition); + } + if (jack_arc != (NodeRelation *)NULL) { + jack_arc->set_transition(light_transition); + } + + setup_vizes(); +} + +int main(int argc, char *argv[]) { + extra_display_func = &shader_display_func; + define_keys = &min_shader_keys; + return framework_main(argc, argv); +} + diff --git a/panda/src/testbed/motion.cxx b/panda/src/testbed/motion.cxx new file mode 100644 index 0000000000..17ccc0d555 --- /dev/null +++ b/panda/src/testbed/motion.cxx @@ -0,0 +1,113 @@ +// Filename: motion.cxx +// Created by: frang (23Mar99) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include + +#include +#include + +extern GraphicsWindow* win; + +extern int framework_main(int argc, char *argv[]); +extern void (*extra_display_func)(); +extern void (*define_keys)(EventHandler&); + +#include +typedef list TexList; +TexList screenshots; +typedef list PBList; +PBList sshots; +int num_frames = 4; +int frame_num = 0; + +void motion_display_func( void ) { + GraphicsStateGuardian* g = win->get_gsg(); + const RenderBuffer& r = g->get_render_buffer(RenderBuffer::T_front); + Texture* t; + PixelBuffer* p; + + if (frame_num < num_frames) { + t = new Texture; + t->_pbuffer->set_xsize(win->get_width()); + t->_pbuffer->set_ysize(win->get_height()); + p = new PixelBuffer; + p->set_xsize(win->get_width()); + p->set_ysize(win->get_height()); + p->_image = PTA_uchar(win->get_width() * win->get_height() * 4); + } else { + TexList::iterator i = screenshots.begin(); + PBList::iterator l = sshots.begin(); + for (int j=0, k=frame_num%num_frames; jcopy_texture_from(t, r); + g->copy_pixel_buffer_from(p, r); + + const RenderBuffer& rb = g->get_render_buffer(RenderBuffer::T_back); + + if (frame_num < num_frames) { + // screenshots.push_back(t); + sshots.push_back(p); + } + + // now we accumulate. Off the top of my head I have 3 plans for this: + // 1) apply the screen shots to one or several whole-screen polygon(s) with + // appropriate alpha + // 2) accumulate this into an accumulation buffer every frame + // 3) blend the new frame into the accumulation buffer, and subtractively + // blend the old one out. + + // version 2. Even this could be better if we didn't start by blowing away + // the frame we just rendered. + GLfloat d = 1. / GLfloat(screenshots.size() + 1.); + TexList::iterator i = screenshots.begin(); + PBList::iterator j = sshots.begin(); + // for (TexList::iterator i=screenshots.begin(); i!=screenshots.end(); ++i) { + for (; j!=sshots.end(); ++j) { + // t = *i; + // g->draw_texture_to(t, rb); + p = *j; + g->draw_pixel_buffer_to(p, rb); + // if (i == screenshots.begin()) + if (j == sshots.begin()) + glAccum(GL_LOAD, d); + else + glAccum(GL_ACCUM, d); + } + glAccum(GL_RETURN, 1.0); + g->end_frame(); + + ++frame_num; +} + +void event_plus(CPT_Event) { + frame_num %= num_frames++; + nout << "now blending " << num_frames << " frames." << endl; +} + +void event_minus(CPT_Event) { + frame_num %= --num_frames; + nout << "now blending " << num_frames << " frames." << endl; + // screenshots.erase(--(screenshots.end())); + sshots.erase(--(sshots.end())); +} + +void motion_keys(EventHandler& eh) { + eh.add_hook("plus", event_plus); + eh.add_hook("minus", event_minus); +} + +int main(int argc, char *argv[]) { + extra_display_func = &motion_display_func; + define_keys = &motion_keys; + return framework_main(argc, argv); +} diff --git a/panda/src/testbed/panda.cxx b/panda/src/testbed/panda.cxx new file mode 100644 index 0000000000..ea8a5d7bf2 --- /dev/null +++ b/panda/src/testbed/panda.cxx @@ -0,0 +1,444 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//From framework +extern PT(GeomNode) geomnode; +extern RenderRelation* first_arc; +extern Loader loader; + +PT_NamedNode panda; +PT_NamedNode ball; +PT_NamedNode room; +PT_NamedNode hide_ball; +PT_NamedNode camera_model; + +PT(RenderRelation) panda_arc; +PT(RenderRelation) panda_rot_arc; +PT(RenderRelation) room_arc; +PT(RenderRelation) ball_arc; +PT(RenderRelation) hide_ball_arc; + +bool follow_ball; +PT(PlanarSlider) ball_slider; + +PT(ProjectionNode) tex_proj; +PT(ProjtexShader) proj_shader; +PT(GeomNode) proj_geom_node; +PT(Trackball) tex_proj_trackball; +PT(SpotlightShader) spot_shader; +PT_NamedNode spot_shaft; +PT(Spotlight) tex_proj_spot; +PT(Trackball) tex_spot_trackball; +PT(ProjtexShadower) proj_shadow; +PT(SpheretexHighlighter) highlight; +PT(SpheretexShader) spheretex; +PT(SpheretexReflector) sreflect; +PT(PlanarReflector) preflect; +PT(OutlineShader) outline_shader; + +void panda_overrides_func(ChanCfgOverrides& override, std::string&) { + override.setField(ChanCfgOverrides::Mask, + ((unsigned int)(W_DOUBLE|W_DEPTH|W_MULTISAMPLE|W_STENCIL))); + override.setField(ChanCfgOverrides::Title, "Panda Demo"); +} + +void panda_idle(void) { + static const double walk_speed = 4.0; // feet per second + if (follow_ball) { + LPoint3f bp = get_rel_pos(ball, panda); + LVector2f bv2(bp[0], bp[1]); + float dist = length(bv2); + if (dist > 0.0001) { + LMatrix4f mat; + look_at(mat, LPoint3f(-bp[0], -bp[1], 0.)); + panda_rot_arc->set_transition(new TransformTransition(mat)); + + float stride = walk_speed * ClockObject::get_global_clock()->get_dt(); + if (dist > stride) { + LVector2f step = bv2 / dist * stride; + const TransformTransition *tt; + if (!get_transition_into(tt, panda_arc)) { + panda_arc->set_transition + (new TransformTransition + (LMatrix4f::translate_mat(step[0], step[1], 0.))); + } else { + LMatrix4f mat = tt->get_matrix(); + mat(3, 0) += step[0]; + mat(3, 1) += step[1]; + panda_arc->set_transition(new TransformTransition(mat)); + } + } + } + } +} + +void event_p(CPT_Event) { + static bool projtex_mode = false; + + projtex_mode = !projtex_mode; + if (!projtex_mode) { + // disable the projtex shader + clear_shader(room_arc, proj_shader); + clear_shader(panda_arc, proj_shader); + clear_shader(ball_arc, proj_shader); + + set_alt_trackball((Node*)0L); + remove_child(tex_proj, proj_geom_node, RenderRelation::get_class_type()); + } else { + // enable the projtex shader + set_shader(room_arc, proj_shader); + set_shader(panda_arc, proj_shader); + set_shader(ball_arc, proj_shader); + + set_alt_trackball(tex_proj_trackball); + // Display the texture projector frustum + RenderRelation *prr = new RenderRelation(tex_proj, proj_geom_node); + LightTransition *plt = new LightTransition(LightTransition::all_off()); + prr->set_transition(plt); + } +} + +void event_s(CPT_Event) { + static bool projtexspot_mode = false; + + projtexspot_mode = !projtexspot_mode; + if (!projtexspot_mode) { + // disable the projtex spotlight shader + clear_shader(room_arc, spot_shader); + clear_shader(panda_arc, spot_shader); + clear_shader(ball_arc, spot_shader); + + set_alt_trackball((Node*)0L); + remove_child(tex_proj_spot, spot_shaft, RenderRelation::get_class_type()); + } else { + // enable the projtex spotlight shader + set_shader(room_arc, spot_shader); + set_shader(panda_arc, spot_shader); + set_shader(ball_arc, spot_shader); + + set_alt_trackball(tex_spot_trackball); + new RenderRelation(tex_proj_spot, spot_shaft, 10); + } +} + +void event_d(CPT_Event) { + static bool projtex_shadow_mode = false; + + projtex_shadow_mode = !projtex_shadow_mode; + if (!projtex_shadow_mode) { + // disable projtex shadows + clear_shader(room_arc, proj_shadow); + set_alt_trackball((Node*)0L); + } else { + // enable projtex shadows + set_shader(room_arc, proj_shadow); + set_alt_trackball(tex_spot_trackball); + } +} + +void event_h(CPT_Event) { + static bool highlight_mode = false; + + highlight_mode = !highlight_mode; + if (!highlight_mode) { + // disable highlight shader + clear_shader(ball_arc, highlight); + } else { + // enable highlight shader + set_shader(ball_arc, highlight); + } +} + +void event_e(CPT_Event) { + static bool spheretex_mode = false; + + spheretex_mode = !spheretex_mode; + if (!spheretex_mode) { + // enable spheretex shader + clear_shader(ball_arc, spheretex); + } else { + // disable spheretex shader + set_shader(ball_arc, spheretex); + } +} + +void event_m(CPT_Event) { + static bool sphere_reflect_mode = false; + + sphere_reflect_mode = !sphere_reflect_mode; + if (!sphere_reflect_mode) { + // disable sphere reflections + clear_shader(ball_arc, sreflect); + } else { + // enable sphere reflections + set_shader(ball_arc, sreflect); + } +} + +void event_r(CPT_Event) { + static bool plane_reflect_mode = false; + + plane_reflect_mode = !plane_reflect_mode; + if (!plane_reflect_mode) { + // disable planar reflections + clear_shader(room_arc, preflect); + } else { + // enable planar reflections + set_shader(room_arc, preflect); + } +} + +void event_z(CPT_Event) { + set_alt_trackball(ball_slider); +} + +void event_Z(CPT_Event) { + follow_ball = !follow_ball; + if (follow_ball) { + // hide the ball while following it. + remove_arc(hide_ball_arc); + } else { + // reveal the ball when we're done following it + hide_ball_arc = new RenderRelation(render, hide_ball); + } +} + +void event_o(CPT_Event) { + static bool outline_mode = false; + + outline_mode = !outline_mode; + if (!outline_mode) { + // disable outline shader + clear_shader(panda_arc, outline_shader); + } else { + // enable outline shader + set_shader(panda_arc, outline_shader); + } +} + +void load_our_models(void) { + // load the room + PT_Node rroom = loader.load_sync("lfloor.egg"); + assert(rroom != (Node*)0L); + room = new NamedNode("The_room"); + new RenderRelation(room, rroom); + room_arc = new RenderRelation(root, room); + + // load the ball + PT_Node rball = loader.load_sync("marble_ball.egg"); + assert(rball != (Node*)0L); + ball = new NamedNode("scaled_ball"); + PT(RenderRelation) ball_arc1 = new RenderRelation(ball, rball); + ball_arc1->set_transition + (new TransformTransition(LMatrix4f::scale_mat(0.2))); + hide_ball = new NamedNode("hide_ball"); + ball_arc = new RenderRelation(hide_ball, ball); + ball_arc->set_transition + (new TransformTransition(LMatrix4f::translate_mat(4., 2., 1.))); + hide_ball_arc = new RenderRelation(root, hide_ball); + + // load a camera model to visualize our eyepoint + PT_Node rcamera_model = loader.load_sync("camera.egg"); + assert(rcamera_model != (Node*)0L); + camera_model = new NamedNode("camera_model"); + new RenderRelation(camera_model, rcamera_model); + new RenderRelation(cameras, camera_model); + + // load the panda + PT_Node pmodel = loader.load_sync("panda-3k.egg"); + assert(pmodel != (Node*)0L); + PT_Node panim = loader.load_sync("panda-walk.egg"); + assert(panim != (Node*)0L); + PT_NamedNode pparent = new NamedNode("panda_parent"); + new RenderRelation(pparent, pmodel); + new RenderRelation(pparent, panim); + PT_NamedNode prot = new NamedNode("panda_rot"); + PT(RenderRelation) p_arc1 = new RenderRelation(prot, pparent); + p_arc1->set_transition + (new TransformTransition(LMatrix4f::scale_mat(0.35))); + panda = new NamedNode("Panda"); + panda_rot_arc = new RenderRelation(panda, prot); + panda_arc = new RenderRelation(root, panda); + AnimControlCollection anim_controls; + auto_bind(pparent, anim_controls, ~0); + anim_controls.loop_all(true); + + // control the ball using a PlanarSlider tform + ball_slider = new PlanarSlider("ball_slider"); + ball_slider->set_transform(LMatrix4f::translate_mat(0., 0., 1.) * + LMatrix4f::scale_mat(7., -7., 1.)); + ball_slider->set_mouse_pos(LPoint2f(4. / 7., 2. / -7.)); + Transform2SG* slider2ball = new Transform2SG("slider2ball"); + slider2ball->set_arc(ball_arc); + new RenderRelation(ball_slider, slider2ball); + follow_ball = false; +} + +void setup_shaders(void) { + // Projected texture shader + Texture* tex = TexturePool::load_texture("smiley.rgba"); + assert(tex != (Texture*)0L); + tex_proj = new ProjectionNode("texture_projector"); + RenderRelation* proj_arc = new RenderRelation(root, tex_proj); + tex_proj_trackball = new Trackball("tex_proj_trackball"); + tex_proj_trackball->set_invert(false); + tex_proj_trackball->set_rel_to(cameras); + Transform2SG* tball2cam = new Transform2SG("tball2cam"); + tball2cam->set_arc(proj_arc); + new RenderRelation(tex_proj_trackball, tball2cam); + LMatrix4f proj_mat; + LPoint3f proj_pos = LPoint3f::rfu(2., 3., 8.); + LVector3f fwd_vec = proj_pos - LPoint3f::origin(); + look_at(proj_mat, -fwd_vec); + proj_mat.set_row(3, proj_pos); + tex_proj_trackball->set_mat(proj_mat); + proj_arc->set_transition(new TransformTransition(proj_mat)); + proj_shader = new ProjtexShader(tex); + proj_shader->add_frustum(tex_proj); + GeomLine* proj_geom = + (GeomLine *)tex_proj->get_projection()->make_geometry(); + proj_geom_node = new GeomNode("proj_geometry"); + proj_geom_node->add_geom(proj_geom); + proj_shader->set_priority(150); + + cerr << "done with projected texture setup" << endl; + + // projected texture spotlight shader + tex_proj_spot = new Spotlight("tex_proj_spotlight"); + Frustumf f; + f.make_perspective(45.0f, 45.0f, f._fnear, 13); + PerspectiveProjection pp(f); + tex_proj_spot->set_projection(pp); + RenderRelation* spot_arc = new RenderRelation(root, tex_proj_spot, 10); + tex_spot_trackball = new Trackball("tex_spot_trackball"); + tex_spot_trackball->set_invert(false); + tex_spot_trackball->set_rel_to(cameras); + tball2cam = new Transform2SG("tball2cam"); + tball2cam->set_arc(spot_arc); + new RenderRelation(tex_spot_trackball, tball2cam); + LMatrix4f spot_mat; + LPoint3f spot_pos = LPoint3f::rfu(-4., -3., 8.); + LVector3f spot_vec = spot_pos - LPoint3f::origin(); + look_at(spot_mat, -spot_vec); + spot_mat.set_row(3, spot_pos); + tex_spot_trackball->set_mat(spot_mat); + spot_arc->set_transition(new TransformTransition(spot_mat)); + spot_shader = new SpotlightShader; + spot_shader->add_frustum(tex_proj_spot); + spot_shaft = tex_proj_spot->make_geometry(0.05, 8., 36); + spot_shader->set_priority(150); + + cerr << "done with projected texture spotlight setup" << endl; + + // projected texture shadower + proj_shadow = new ProjtexShadower; + proj_shadow->add_frustum(tex_proj_spot); + proj_shadow->add_caster(panda); + proj_shadow->add_caster(ball); + proj_shadow->add_caster(camera_model); + proj_shadow->set_priority(150); + + cerr << "done with projected texture shadower setup" << endl; + + // sphere texture shader + spheretex = new SpheretexShader(tex); + spheretex->set_priority(150); + + cerr << "done with sphere texture shader" << endl; + + // sphere texture highlighter + highlight = new SpheretexHighlighter; + highlight->add_frustum(tex_proj_spot); + highlight->set_priority(150); + + cerr << "done with sphere texture highlighter setup" << endl; + + // sphere texture reflector + sreflect = new SpheretexReflector; + sreflect->add_frustum(tex_proj_spot); + sreflect->add_caster(room); + sreflect->add_caster(panda); + sreflect->add_caster(camera_model); + sreflect->set_priority(150); + + cerr << "done with sphere texture reflector setup" << endl; + + // planar reflector + Planef p(LVector3f::up(), LPoint3f::origin()); + PlaneNode* plane_node = new PlaneNode; + plane_node->set_plane(p); + new RenderRelation(room, plane_node); + preflect = new PlanarReflector(plane_node); + preflect->add_caster(ball); + preflect->add_caster(panda); + preflect->add_caster(camera_model); + preflect->set_priority(150); + + cerr << "done with planar reflector setup" << endl; + + // outline shader + outline_shader = new OutlineShader; + outline_shader->set_priority(150); + + cerr << "done with outline shader setup" << endl; +} + +void panda_keys(EventHandler& eh) { + new RenderRelation( lights, dlight ); + have_dlight = true; + + eh.add_hook("p", event_p); // projected texture shader + eh.add_hook("s", event_s); // projected texture spotlight shader + eh.add_hook("d", event_d); // projected texture shadow shader + eh.add_hook("h", event_h); // fake phong highlight + eh.add_hook("e", event_e); // sphere texture shader + eh.add_hook("m", event_m); // sphere reflection shader + eh.add_hook("r", event_r); // planar reflection shader + eh.add_hook("o", event_o); // outline shader + + eh.add_hook("z", event_z); // user controls the ball + eh.add_hook("Z", event_Z); // follow the ball around + + load_our_models(); + setup_shaders(); +} + +int main(int argc, char *argv[]) { + define_keys = &panda_keys; + additional_idle = &panda_idle; + extra_overrides_func = &panda_overrides_func; + return framework_main(argc, argv); +} diff --git a/panda/src/testbed/rock-floor.rgb b/panda/src/testbed/rock-floor.rgb new file mode 100644 index 0000000000000000000000000000000000000000..823b5fd38185163686004f8115e1dadf9647fa6f GIT binary patch literal 99161 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�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 K%%tiA^Zx+oC1tw+ literal 0 HcmV?d00001 diff --git a/panda/src/testbed/shader_test.cxx b/panda/src/testbed/shader_test.cxx new file mode 100644 index 0000000000..0adf9b98a5 --- /dev/null +++ b/panda/src/testbed/shader_test.cxx @@ -0,0 +1,703 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef SHADER_VERBOSE +#include +#endif + +Configure(shader_test); +ConfigureFn(shader_test) { +} + +PT(ProjectionNode) tex_proj; +PT(Trackball) tex_proj_trackball; +PT(ProjtexShader) proj_shader; +PT(ProjtexShadower) proj_shadow; +PT(SpheretexShader) spheretex; +PT(SpheretexHighlighter) highlight; +PT(SpheretexReflector) sreflect; +PT(PlanarReflector) preflect; +PT(OutlineShader) outline_shader; + +ShaderTransition shader_trans; + +PT(Spotlight) tex_proj_spot; +PT(Trackball) tex_spot_trackball; +PT(SpotlightShader) spot_shader; + +RenderRelation* room_arc; +RenderRelation* spot_arc; +RenderRelation* jack_arc; +RenderRelation* camera_model_arc; + +PT(LightTransition) light_transition; + +extern PT_NamedNode render; +extern NodeAttributes initial_state; +extern RenderRelation* first_arc; +extern PT_NamedNode lights; +extern PT_NamedNode root; +extern PT(GeomNode) geomnode; +extern PT_NamedNode cameras; +extern PT(MouseAndKeyboard) mak; + +extern void set_alt_trackball(Node *trackball); + +extern int framework_main(int argc, char *argv[]); +extern void (*extra_display_func)(); +extern void (*define_keys)(EventHandler&); + +extern PT(GraphicsWindow) main_win; + +class SomeViz : public Shader::Visualize { +public: + SomeViz(void); + virtual ~SomeViz(void); + virtual void DisplayTexture(PT(Texture)&, Shader*); + virtual void DisplayPixelBuffer(PT(PixelBuffer)&, Shader*); +protected: + typedef std::list texlist; + typedef texlist::iterator texiter; + typedef std::list pblist; + typedef pblist::iterator pbiter; + + texlist _texs; + pblist _pbs; +}; + +SomeViz::SomeViz(void) {} + +SomeViz::~SomeViz(void) {} + +void SomeViz::DisplayTexture(PT(Texture)& tex, Shader*) { + if (tex->has_ram_image()) + _texs.push_back(tex); + else { + GraphicsStateGuardian* g = main_win->get_gsg(); + PT(PixelBuffer) pb(new PixelBuffer); + g->texture_to_pixel_buffer(tex->prepare(g), pb); + _pbs.push_back(pb); + } +} + +void SomeViz::DisplayPixelBuffer(PT(PixelBuffer)& pb, Shader*) { + _pbs.push_back(pb); +} + +class Oldviz : public SomeViz { +public: + Oldviz(PT(GraphicsWindow)&); + virtual ~Oldviz(void); + virtual void Flush(void); +protected: + PT(GraphicsWindow) _w; +}; + +Oldviz::Oldviz(PT(GraphicsWindow)& win) : _w(win) {} + +Oldviz::~Oldviz(void) {} + +void Oldviz::Flush(void) { + GraphicsStateGuardian* g = _w->get_gsg(); + const RenderBuffer& r = g->get_render_buffer(RenderBuffer::T_front); + PT(DisplayRegion) d(_w->make_scratch_display_region(256, 256)); + + for (texiter i=_texs.begin(); i!=_texs.end(); ++i) + (*i)->draw(g, d, r); + for (pbiter j=_pbs.begin(); j!=_pbs.end(); ++j) + (*j)->draw(g, d, r); + _texs.erase(_texs.begin(), _texs.end()); + _pbs.erase(_pbs.begin(), _pbs.end()); +} + +class Viztex : public SomeViz { +public: + Viztex(PT(GraphicsPipe)&); + virtual ~Viztex(void); + virtual void Flush(void); +protected: + typedef std::list winlist; + typedef winlist::iterator winiter; + + winlist _wins; + PT(GraphicsPipe) _pipe; +}; + +Viztex::Viztex(PT(GraphicsPipe)& p) : _pipe(p) {} + +Viztex::~Viztex(void) {} + +void Viztex::Flush(void) { + winiter _witer = _wins.begin(); + + for (texiter i=_texs.begin(); i!=_texs.end(); ++i) { + GraphicsWindow* w; + + if (_witer == _wins.end()) { + ChanCfgOverrides override; + + override.setField(ChanCfgOverrides::Mask, + ((unsigned int)(W_SINGLE))); + override.setField(ChanCfgOverrides::Title, "Multipass partial"); + override.setField(ChanCfgOverrides::SizeX, 256); + override.setField(ChanCfgOverrides::SizeY, 256); + override.setField(ChanCfgOverrides::Cameras, false); + //JMC: Addeded cameras and render for parameters of ChanConfig + w = ChanConfig(_pipe, "single", (Node *)NULL, render, override); + //JMC: Removed set_active calls to channels + _wins.push_back(w); + } else { + w = *_witer; + ++_witer; + } + GraphicsStateGuardian* g = w->get_gsg(); + const RenderBuffer& r = g->get_render_buffer(RenderBuffer::T_front); + PT(DisplayRegion) d(w->make_scratch_display_region(w->get_width(), + w->get_height())); + // g->prepare_display_region(d); + (*i)->draw(g, d, r); + } + for (pbiter j=_pbs.begin(); j!=_pbs.end(); ++j) { + GraphicsWindow* w; + + if (_witer == _wins.end()) { + ChanCfgOverrides override; + + override.setField(ChanCfgOverrides::Mask, + ((unsigned int)(W_SINGLE))); + override.setField(ChanCfgOverrides::Title, "Multipass partial"); + override.setField(ChanCfgOverrides::SizeX, 256); + override.setField(ChanCfgOverrides::SizeY, 256); + override.setField(ChanCfgOverrides::Cameras, false); + //JMC: Addeded cameras and render for parameters of ChanConfig + w = ChanConfig(_pipe, "single", cameras, render, override); + //JMC: Removed set_active calls to channels + _wins.push_back(w); + } else { + w = *_witer; + ++_witer; + } + GraphicsStateGuardian* g = w->get_gsg(); + const RenderBuffer& r = g->get_render_buffer(RenderBuffer::T_front); + PT(DisplayRegion) d(w->make_scratch_display_region(w->get_width(), + w->get_height())); + // g->prepare_display_region(d); + (*j)->draw(g, d, r); + } + _texs.erase(_texs.begin(), _texs.end()); + _pbs.erase(_pbs.begin(), _pbs.end()); +} + +class Tiledviz : public Viztex { +public: + Tiledviz(PT(GraphicsPipe)&); + virtual ~Tiledviz(void); + virtual void Flush(void); +}; + +Tiledviz::Tiledviz(PT(GraphicsPipe)& p) : Viztex(p) {} + +Tiledviz::~Tiledviz(void) {} + +void Tiledviz::Flush(void) { + int count = 0; + //JMC: Added layer count + int layer_count = 0; + winiter _witer = _wins.begin(); + +#ifdef SHADER_VERBOSE + int _level = 2; + if (!_texs.empty()) + { + nout << "Tiledvid::Flush:" << endl; + } +#endif + +#ifdef SHADER_VERBOSE + if (!_texs.empty()) + { + indent(nout, _level) << "TexList Loop" << endl; + } + _level += 2; +#endif + for (texiter texi=_texs.begin(); texi!=_texs.end(); ++texi) { + GraphicsWindow* w; + DisplayRegion* d; + + if (_witer == _wins.end()) { + ChanCfgOverrides override; + + override.setField(ChanCfgOverrides::Mask, + ((unsigned int)(W_SINGLE))); + override.setField(ChanCfgOverrides::Title, "Multipass partial"); + override.setField(ChanCfgOverrides::SizeX, 512); + override.setField(ChanCfgOverrides::SizeY, 512); + override.setField(ChanCfgOverrides::Cameras, false); + //JMC: Addeded cameras and render for parameters of ChanConfig + w = ChanConfig(_pipe, "multipass-tile", cameras, render, override); + //JMC: Removed set_active calls to channels + _wins.push_back(w); + _witer = --(_wins.end()); + } else + w = *_witer; + + int dridx; + GraphicsStateGuardian* g = w->get_gsg(); + GraphicsChannel *chan = w->get_channel(0); + //JMC: Added call to get_layer + GraphicsLayer *layer = chan->get_layer(layer_count); + const RenderBuffer& r = g->get_render_buffer(RenderBuffer::T_front); + //JMC: Removed display region iterators for GraphicsChannel. They + //no longer exist. + //JMC: Added call to get_dr +#ifdef SHADER_VERBOSE + indent(nout, _level) << "Getting display region " << count + << " from graphics layer" << endl; +#endif + d = layer->get_dr(count); + (*texi)->draw(g, d, r); + ++count; + if (count == layer->get_num_drs()) { + count = 0; + ++layer_count; + } + + if (layer_count == chan->get_num_layers()) + { + layer_count = 0; + ++_witer; + } + } +#ifdef SHADER_VERBOSE + _level -= 2; +#endif + for (pbiter pbi=_pbs.begin(); pbi!=_pbs.end(); ++pbi) { + GraphicsWindow* w; + DisplayRegion* d; + + if (_witer == _wins.end()) { + ChanCfgOverrides override; + + override.setField(ChanCfgOverrides::Mask, + ((unsigned int)(W_SINGLE))); + override.setField(ChanCfgOverrides::Title, "Multipass partial"); + override.setField(ChanCfgOverrides::SizeX, 512); + override.setField(ChanCfgOverrides::SizeY, 512); + override.setField(ChanCfgOverrides::Cameras, false); + //JMC: Addeded cameras and render for parameters of ChanConfig + w = ChanConfig(_pipe, "multipass-tile", cameras, render, override); + //JMC: Removed set_active calls to channels + _wins.push_back(w); + _witer = --(_wins.end()); + } else + w = *_witer; + + int dridx; + GraphicsStateGuardian* g = w->get_gsg(); + GraphicsChannel *chan = w->get_channel(0); + //JMC: Added call to get_layer + GraphicsLayer *layer = chan->get_layer(0); + const RenderBuffer& r = g->get_render_buffer(RenderBuffer::T_front); + //JMC: Removed display region iterators for GraphicsChannel. They + //no longer exist. + //JMC: Added call to get_dr + d = layer->get_dr(count); + // g->prepare_display_region(d); + (*pbi)->draw(g, d, r); + ++count; + if (count == 16) { + count = 0; + ++_witer; + } + } + _texs.erase(_texs.begin(), _texs.end()); + _pbs.erase(_pbs.begin(), _pbs.end()); +} + +// nothing extra to do for either of these +void shader_display_func(void) { + Shader::Visualize* v = Shader::get_viz(); + if (v != (Shader::Visualize*)0L) + v->Flush(); +} + +void event_p(CPT_Event) { + // The "p" key was pressed. Toggle projected texture. + static bool projtex_mode = false; + + projtex_mode = !projtex_mode; + if (!projtex_mode) { + // Set the normal mode on the render arc. + clear_shader(room_arc, proj_shader); + clear_shader(first_arc, proj_shader); + clear_shader(jack_arc, proj_shader); + + set_alt_trackball(NULL); + + } else { + // Set an override on the initial state. + set_shader(room_arc, proj_shader); + set_shader(first_arc, proj_shader); + set_shader(jack_arc, proj_shader); + + set_alt_trackball(tex_proj_trackball); + } +} + +void event_s(CPT_Event) { + // The "s" key was pressed. Toggle projected texture spotlight. + static bool projtexspot_mode = false; + + projtexspot_mode = !projtexspot_mode; + if (!projtexspot_mode) { + // Set the normal mode on the render arc. + clear_shader(room_arc, spot_shader); + set_alt_trackball(NULL); + + } else { + // Set an override on the initial state. + set_shader(room_arc, spot_shader); + set_alt_trackball(tex_spot_trackball); + } +} + +void event_d(CPT_Event) { + // The "d" key was pressed. Toggle projected texture shadows. + static bool projtex_shadow_mode = false; + + projtex_shadow_mode = !projtex_shadow_mode; + if (!projtex_shadow_mode) { + // Set the normal mode on the render arc. + clear_shader(room_arc, proj_shadow); + set_alt_trackball(NULL); + + } else { + set_shader(room_arc, proj_shadow); + set_alt_trackball(tex_spot_trackball); + } +} + +void event_h(CPT_Event) { + // The "h" key was pressed. Toggle highlight. + static bool highlight_mode = false; + + highlight_mode = !highlight_mode; + if (!highlight_mode) { + // Set the normal mode on the render arc. + clear_shader(first_arc, highlight); + + } else { + set_shader(first_arc, highlight); + } +} + +void event_e(CPT_Event) { + // The "e" key was pressed. Toggle sphere texture. + static bool spheretex_mode = false; + + spheretex_mode = !spheretex_mode; + if (!spheretex_mode) { + // Set the normal mode on the render arc. + clear_shader(first_arc, spheretex); + + } else { + // Set an override on the initial state. + set_shader(first_arc, spheretex); + } +} + +void event_m(CPT_Event) { + // The "m" key was pressed. Toggle sphere reflection. + static bool sphere_reflect_mode = false; + + sphere_reflect_mode = !sphere_reflect_mode; + if (!sphere_reflect_mode) { + // Set the normal mode on the render arc. + clear_shader(first_arc, sreflect); + + } else { + // Set an override on the initial state. + set_shader(first_arc, sreflect); + } +} + +void event_r(CPT_Event) { + // The "r" key was pressed. Toggle planar reflection. + static bool plane_reflect_mode = false; + + plane_reflect_mode = !plane_reflect_mode; + if (!plane_reflect_mode) { + // Set the normal mode on the render arc. + clear_shader(first_arc, preflect); + + } else { + // Set an override on the initial state. + set_shader(first_arc, preflect); + } +} + +void event_n(CPT_Event) { + // The "n" key was pressed. Toggle the spot light. + static bool spotlight_on = false; + spotlight_on = !spotlight_on; + if (!spotlight_on) { + light_transition->set_off(tex_proj_spot.p()); + set_alt_trackball(NULL); + } else { + light_transition->set_on(tex_proj_spot.p()); + set_alt_trackball(tex_spot_trackball); + } +} + +void event_o(CPT_Event) { + // The "o" key was pressed. Toggle outlining. + static bool outline_mode = false; + + outline_mode = !outline_mode; + if (!outline_mode) { + // Set the normal mode on the render arc. + clear_shader(first_arc, outline_shader); + + } else { + set_shader(first_arc, outline_shader); + } +} + +void shader_keys(EventHandler &eh) { + eh.add_hook("p", event_p); + eh.add_hook("s", event_s); + eh.add_hook("d", event_d); + eh.add_hook("h", event_h); + eh.add_hook("e", event_e); + eh.add_hook("m", event_m); + eh.add_hook("r", event_r); + eh.add_hook("n", event_n); + eh.add_hook("o", event_o); + + LPoint3f center_pos = LPoint3f::origin(); + + // Create a projected texture shader + // Load a texture to project + Texture* tex = new Texture; + tex->read("smiley.rgba"); + tex->set_name("smiley.rgba"); + + // Put the texture projector into the scene graph + tex_proj = new ProjectionNode("texture_projector"); + RenderRelation* proj_arc = new RenderRelation(render, tex_proj); + + // Create a trackball to spin this around. + tex_proj_trackball = new Trackball("tex_proj_trackball"); + tex_proj_trackball->set_invert(false); + tex_proj_trackball->set_rel_to(cameras); + Transform2SG *tball2cam = new Transform2SG("tball2cam"); + tball2cam->set_arc(proj_arc); + new DataRelation(tex_proj_trackball, tball2cam); + + // Raise it and aim it at the origin + LMatrix4f proj_mat; + LPoint3f proj_pos = LPoint3f::rfu(2., 3., 8.); + LVector3f fwd_vec = proj_pos - center_pos; + look_at(proj_mat, -fwd_vec); + proj_mat.set_row(3, proj_pos); + tex_proj_trackball->set_mat(proj_mat); + proj_arc->set_transition(new TransformTransition(proj_mat)); + + // Create a shader for the texture projector + proj_shader = new ProjtexShader(tex); + proj_shader->add_frustum(tex_proj); + +#define DISPLAY_TEXPROJFRUST +#ifdef DISPLAY_TEXPROJFRUST + // Display a wireframe representation of the texture projector frustum + GeomLine* proj_geom = + (GeomLine *)tex_proj->get_projection()->make_geometry(); + GeomNode* proj_geom_node = new GeomNode("proj_geometry"); + //JMC: Removed _drawable_vector.push_back call and added add_geom + //call + proj_geom_node->add_geom(proj_geom); + RenderRelation *prr = new RenderRelation(tex_proj, proj_geom_node); + LightTransition *plt = new LightTransition(LightTransition::all_off()); + prr->set_transition(plt); +#endif + + // Create a projected texture spotlight shader + tex_proj_spot = new Spotlight("tex_proj_spotlight"); + spot_arc = new RenderRelation(render, tex_proj_spot, 10); + + // Create a trackball to spin this around. + tex_spot_trackball = new Trackball("tex_spot_trackball"); + tex_spot_trackball->set_invert(false); + tex_spot_trackball->set_rel_to(cameras); + tball2cam = new Transform2SG("tball2cam"); + tball2cam->set_arc(spot_arc); + new DataRelation(tex_spot_trackball, tball2cam); + + // Raise it and aim it at the origin + LMatrix4f spot_mat; + LPoint3f spot_pos = LPoint3f::rfu(-4., -3., 8.); + LVector3f spot_vec = spot_pos - center_pos; + look_at(spot_mat, -spot_vec); + spot_mat.set_row(3, spot_pos); + tex_spot_trackball->set_mat(spot_mat); + spot_arc->set_transition(new TransformTransition(spot_mat)); + + // Create a shader for the spotlight + spot_shader = new SpotlightShader; + spot_shader->add_frustum(tex_proj_spot); + +#define DISPLAY_TEXPROJSPOTFRUST +#ifdef DISPLAY_TEXPROJSPOTFRUST + // Display a wireframe representation of the spotlight frustum + Colorf color_red(1., 0., 0., 1.); + GeomLine* frust_geom = + (GeomLine *)tex_proj_spot->get_projection()->make_geometry(color_red); + GeomNode* frust_geom_node = new GeomNode("frustum_geometry"); + //JMC: Removed _drawable_vector.push_back call and added add_geom + //call + frust_geom_node->add_geom(frust_geom); + RenderRelation *rr = new RenderRelation(tex_proj_spot, frust_geom_node); + LightTransition *lt = new LightTransition(LightTransition::all_off()); + rr->set_transition(lt); +#endif + +#ifdef DISPLAY_SHAFT + // Draw a light shaft for the spotlight + NamedNode* shaft = tex_proj_spot->make_geometry(0.05, 8.0, 36); + new RenderRelation(tex_proj_spot, shaft, 10); +#endif + + // Create a planar reflection + Planef p(LVector3f(0., 0., 1.), LVector3f(0., 0., -10.)); + PlaneNode* plane_node = new PlaneNode; + plane_node->set_plane(p); + preflect = new PlanarReflector(plane_node); + + // Create a projected texture shadower + proj_shadow = new ProjtexShadower; + proj_shadow->add_frustum(tex_proj_spot); + if (root != (NamedNode *)NULL) { + proj_shadow->add_caster(root); + preflect->add_caster(root); + } else if (geomnode != (GeomNode *)NULL) { + proj_shadow->add_caster(geomnode); + preflect->add_caster(geomnode); + } + + // Create a spheretex shader + spheretex = new SpheretexShader(tex); + + // Create a highlight + highlight = new SpheretexHighlighter; + highlight->add_frustum(tex_proj_spot); + + // Create a spheremap reflection + sreflect = new SpheretexReflector; + sreflect->add_frustum(tex_proj_spot); + + // Create an outline shader + outline_shader = new OutlineShader; + + // Load the room file + PT_NamedNode room = loader.load_sync("big-room.egg"); + if (room != (NamedNode *)NULL) { + room_arc = new RenderRelation(render, room, 20); + + sreflect->add_caster(room); + new RenderRelation(room, plane_node); + } + + // Load jack + PT_NamedNode jack = loader.load_sync("jack.egg"); + if (jack != (NamedNode *)NULL) { + jack_arc = new RenderRelation(render, jack); + LMatrix4f jack_mat = LMatrix4f::ident_mat(); + LPoint3f jack_pos = LPoint3f::rfu(-2., -2., 2.); + jack_mat.set_row(3, jack_pos); + jack_arc->set_transition(new TransformTransition(jack_mat)); + + sreflect->add_caster(jack); + proj_shadow->add_caster(jack); + preflect->add_caster(jack); + } + + // Load up a camera model to visualize our eyepoint. + PT_NamedNode camera_model = loader.load_sync("camera.egg"); + if (camera_model != (NamedNode *)NULL) { + camera_model_arc = new RenderRelation(cameras, camera_model); + + sreflect->add_caster(camera_model); + proj_shadow->add_caster(camera_model); + preflect->add_caster(camera_model); + } + + // Set up a transition for the spotlight + light_transition = new LightTransition; + if (first_arc != (NodeRelation *)NULL) { + first_arc->set_transition(light_transition); + } + if (jack_arc != (NodeRelation *)NULL) { + jack_arc->set_transition(light_transition); + } + + // and now for some multipass partial visualization + Shader::Visualize* v = Shader::get_viz(); + std::string viztype = shader_test.GetString("multipass-viz", "none"); + if (viztype == "old-style") { + PT(GraphicsWindow) w(main_win); + v = new Oldviz(w); + } else if (viztype == "new-single") { + PT(GraphicsPipe) p(((GraphicsPipe*)(main_win->get_pipe()))); + v = new Viztex(p); + } else if (viztype == "new-tile") { + PT(GraphicsPipe) p(((GraphicsPipe*)(main_win->get_pipe()))); + v = new Tiledviz(p); + } + Shader::set_viz(v); +} + +int main(int argc, char *argv[]) { + extra_display_func = &shader_display_func; + define_keys = &shader_keys; + return framework_main(argc, argv); +} + + + + + diff --git a/panda/src/testbed/test_eggWrite.cxx b/panda/src/testbed/test_eggWrite.cxx new file mode 100644 index 0000000000..29e2f2b5af --- /dev/null +++ b/panda/src/testbed/test_eggWrite.cxx @@ -0,0 +1,98 @@ +// Filename: test_eggWrite.cxx +// Created by: jason (16Jun00) +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +template +class PrintNodes : public TraverserVisitor { +public: + PrintNodes() { + _indent_level = 0; + } + bool reached_node(Node *node, AttributeWrapper &state, NullLevelState &) + { + indent(nout, _indent_level) << "Reached " << *node << ", state is " << state << "\n"; + // if (node->is_of_type(GeomNode::get_class_type())) +// { +// GeomNode *geomNode = (GeomNode *)node; +// int num_geoms = geomNode->get_num_geoms(); +// for (int i = 0; i < num_geoms; i++) +// { +// dDrawable *draw = geomNode->get_geom(i); +// Geom *geom = DCAST(Geom, draw); +// indent(nout, _indent_level+1) << *geom << ":\n"; +// geom->output_verbose(nout); +// } +// } + return true; + } + bool forward_arc(NodeRelation *, TransitionWrapper &, AttributeWrapper &, AttributeWrapper &, NullLevelState &) + { + _indent_level += 2; + return true; + } + void backward_arc(NodeRelation *, TransitionWrapper &, AttributeWrapper &, AttributeWrapper &, const NullLevelState &) + { + _indent_level -= 2; + } + int _indent_level; +}; + + + +int main(int argc, char* argv[]) { + if (argc < 2) + { + nout << "Need an egg file" << endl; + exit(0); + } + Filename file(argv[1]); + if (file.get_extension().compare("egg") != 0) + { + nout << "Need an egg file" << endl; + exit(0); + } + datagram_file stream(file.get_basename_wo_extension()+string(".bam")); + BamWriter manager(&stream); + + PT_NamedNode smiley = loader.load_sync(file); + + nout << "\n"; + PrintNodes pn; + df_traverse(smiley, pn, + NodeAttributeWrapper(TransformTransition::get_class_type()), + NullLevelState(), + RenderRelation::get_class_type()); + nout << "\n"; + + stream.open(file::FILE_WRITE, _bam_header); + manager.init(); + manager.write_object(smiley); + stream.close(); + + return 0; +} + diff --git a/panda/src/testbed/test_particles.cxx b/panda/src/testbed/test_particles.cxx new file mode 100644 index 0000000000..f6323bfc2f --- /dev/null +++ b/panda/src/testbed/test_particles.cxx @@ -0,0 +1,138 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// physics. particle systems. + +PhysicsManager physics_manager; +ParticleSystemManager ps_manager; + +PT(ParticleSystem) smoke_system = new ParticleSystem(768); +PT(ZSpinParticleFactory) zpf = new ZSpinParticleFactory; +PT(SpriteParticleRenderer) spr = new SpriteParticleRenderer; +PT(DiscEmitter) de = new DiscEmitter; + +PT(Texture) smoke; + +PT(LinearVectorForce) wind_force = new LinearVectorForce(0.5, 0, 0); +PT(LinearNoiseForce) noise_force = new LinearNoiseForce(0.02f); + +PT(PhysicalNode) pn = new PhysicalNode; +PT(ForceNode) fn = new ForceNode; + +static void +event_csn_update(CPT_Event) { + float dt = ClockObject::get_global_clock()->get_dt(); + + physics_manager.do_physics(dt); + ps_manager.do_particles(dt); +} + +static void +event_add_particles(CPT_Event) { + // renderer setup + spr->set_texture(smoke); + spr->set_animation_flags(true, true, true); + spr->set_x_ratios(0.02f, 0.4f); + spr->set_y_ratios(0.02f, 0.4f); + spr->set_alpha_decay(PR_ALPHA_OUT); + + // factory setup + zpf->set_lifespan_base(5.0f); + zpf->set_mass_base(1.0f); + zpf->set_mass_delta(0.25f); + zpf->set_initial_theta(0.0f); + zpf->set_final_theta(0.0f); + zpf->set_theta_delta(90.0f); + + // emitter setup + de->set_radius(0.2f); + de->set_outer_aoe(70.0f); + de->set_inner_aoe(90.0f); + de->set_inner_magnitude(3.0f); + de->set_outer_magnitude(2.5f); + + // system setup + smoke_system->set_birth_rate(0.15f); + smoke_system->set_litter_size(2); + smoke_system->set_emitter(de); + smoke_system->set_renderer(spr); + smoke_system->set_factory(zpf); + + smoke_system->set_render_parent(render); + + pn->add_physical(smoke_system); + new RenderRelation(render, pn); + + fn->add_force(wind_force); + fn->add_force(noise_force); + new RenderRelation(render, fn); + + physics_manager.attach_linear_integrator(new LinearEulerIntegrator); + physics_manager.attach_physical(smoke_system); + physics_manager.add_linear_force(wind_force); + + ps_manager.attach_particlesystem(smoke_system); + + smoke_system->add_linear_force(noise_force); + + nout << "Added particles." << endl; + event_handler.add_hook("NewFrame", event_csn_update); +} + +void demo_keys(EventHandler&) { + new RenderRelation( lights, dlight ); + have_dlight = true; + + event_handler.add_hook("p", event_add_particles); +} + +int main(int argc, char *argv[]) { + define_keys = &demo_keys; + + smoke = TexturePool::load_texture("smoke.rgba"); + + return framework_main(argc, argv); +} diff --git a/panda/src/testbed/test_recparticles.cxx b/panda/src/testbed/test_recparticles.cxx new file mode 100644 index 0000000000..689b7dc34a --- /dev/null +++ b/panda/src/testbed/test_recparticles.cxx @@ -0,0 +1,145 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// physics. particle systems. + +PhysicsManager physics_manager; +ParticleSystemManager ps_manager; + +static void +event_csn_update(CPT_Event) { + float dt = ClockObject::get_global_clock()->get_dt(); + + physics_manager.do_physics(dt); + ps_manager.do_particles(dt); +} + +PT(ParticleSystem) explosion_system = new ParticleSystem(128); +PT(PointParticleFactory) explosion_factory = new PointParticleFactory; +PT(SpriteParticleRenderer) explosion_renderer = new SpriteParticleRenderer; +PT(SphereSurfaceEmitter) explosion_emitter = new SphereSurfaceEmitter; + +PT(ParticleSystem) fireworks_system = new ParticleSystem(32); +PT(PointParticleFactory) fireworks_factory = new PointParticleFactory; +PT(PointParticleRenderer) fireworks_renderer = new PointParticleRenderer; +PT(LineEmitter) fireworks_emitter = new LineEmitter; +PT(Texture) fireworks_texture; + +static void +event_add_particles(CPT_Event) { + // set up the base system + fireworks_factory->set_lifespan_base(1.5f); + fireworks_factory->set_lifespan_delta(0.5f); + fireworks_emitter->set_endpoints(LPoint3f(-2.0f, 0.0f, 0.0f), + LPoint3f(2.0f, 0.0f, 0.0f)); + fireworks_emitter->set_launch_vec(LVector3f(0.0f, 0.0f, 1.0f)); + fireworks_renderer->set_point_size(4.0f); + fireworks_renderer->set_color1(Colorf(1, 1, 1, 1)); + fireworks_renderer->set_color2(Colorf(1, 0, 0, 1)); + fireworks_renderer->set_blend_type(PP_BLEND_LIFE); + fireworks_renderer->set_blend_method(PP_BLEND_LINEAR); + fireworks_renderer->set_alpha_decay(PR_ALPHA_OUT); + fireworks_system->set_birth_rate(0.25f); + fireworks_system->set_litter_size(1); + fireworks_system->set_birth_node(render); + fireworks_system->set_emitter(fireworks_emitter); + fireworks_system->set_renderer(fireworks_renderer); + fireworks_system->set_factory(fireworks_factory); + fireworks_system->set_render_parent(render); + fireworks_system->set_spawn_on_death_flag(true); + fireworks_system->set_spawn_render_node(render); + + // set up the explosion system + explosion_factory->set_lifespan_base(1.0f); + explosion_factory->set_lifespan_delta(0.0f); + explosion_emitter->set_radius(0.1f); + explosion_emitter->set_amplitude(2.0f); + explosion_emitter->set_offset_force(LVector3f(2.0f, 0.0f, 2.0f)); + explosion_renderer->set_texture(fireworks_texture); + explosion_renderer->set_alpha_decay(PR_ALPHA_OUT); + explosion_renderer->set_color(Colorf(1, 0, 0, 1)); + explosion_system->set_birth_rate(5.0f); + explosion_system->set_litter_size(64); + explosion_system->set_template_system_flag(true); + explosion_system->set_system_grows_older_flag(true); + explosion_system->set_system_lifespan(1.0f); + explosion_system->set_emitter(explosion_emitter); + explosion_system->set_renderer(explosion_renderer); + explosion_system->set_factory(explosion_factory); + + fireworks_system->add_spawn_template(explosion_system); + + // fireworks_system->add_force(new VectorForce(0, 0, -9.8f)); + + /* + particle_system->add_spawn_template(explosion_system); + particle_system->add_spawn_template(explosion_system2); + particle_system->add_spawn_template(explosion_system3); + + particle_system->set_spawn_on_death_flag(true); + particle_system->set_birth_node(render); + particle_system->set_render_parent(render); + particle_system->set_spawn_render_node(render); + */ + + ps_manager.attach_particlesystem(fireworks_system); + physics_manager.attach_physical(fireworks_system); + + nout << "Added particles." << endl; + event_handler.add_hook("NewFrame", event_csn_update); +} + +void demo_keys(EventHandler&) { + event_handler.add_hook("p", event_add_particles); +} + +int main(int argc, char *argv[]) { + fireworks_texture = TexturePool::load_texture("firework.rgba"); + + define_keys = &demo_keys; + return framework_main(argc, argv); +} + + + + + + diff --git a/panda/src/testbed/text_test.cxx b/panda/src/testbed/text_test.cxx new file mode 100644 index 0000000000..085101b4a0 --- /dev/null +++ b/panda/src/testbed/text_test.cxx @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include + +extern PT_NamedNode render; +extern PT_NamedNode egg_root; +extern EventHandler event_handler; + +extern int framework_main(int argc, char *argv[]); +extern void (*define_keys)(EventHandler&); + +PT(TextNode) text_node; +char *textStr; + +void event_p(CPT_Event) { + text_node->set_text("I'm a woo woo woo!"); + + nout << "text is " << text_node->get_width() << " by " + << text_node->get_height() << "\n"; +} + +void event_s(CPT_Event) { + text_node->set_wordwrap(5.0); + + nout << "text is " << text_node->get_width() << " by " + << text_node->get_height() << "\n"; +} + +void text_keys(EventHandler& eh) { + eh.add_hook("p", event_p); + eh.add_hook("s", event_s); + + text_node = new TextNode("text_node"); + PT_NamedNode font = loader.load_sync("cmr12"); + text_node->set_font(font.p()); + text_node->set_wordwrap(20.0); + text_node->set_card_as_margin(0.25, 0.25, 0.25, 0.25); + PT(Texture) tex = new Texture; + tex->set_name("genericButton.rgb"); + tex->set_minfilter(Texture::FT_linear); + tex->set_magfilter(Texture::FT_linear); + tex->read("/beta/toons/textures/smGreyButtonUp.rgb"); + text_node->set_card_texture( tex ); + text_node->set_card_border(0.1, 0.1); + text_node->set_text( textStr ); + text_node->set_text_color( 0.0, 0.0, 0.0, 1.0 ); + if (text_node->has_card_texture()) + nout << "I've got a texture!" << "\n"; + else + nout << "I don't have a texture..." << "\n"; + nout << "text is " << text_node->get_width() << " by " + << text_node->get_height() << "\n"; + + new RenderRelation(egg_root, text_node); +} + +int main(int argc, char *argv[]) { + define_keys = &text_keys; + if (argc > 1) + textStr = argv[1]; + else + textStr = argv[0]; + return framework_main(argc, argv); +} diff --git a/panda/src/testbed/vrpn_demo.cxx b/panda/src/testbed/vrpn_demo.cxx new file mode 100644 index 0000000000..90d09bfde2 --- /dev/null +++ b/panda/src/testbed/vrpn_demo.cxx @@ -0,0 +1,48 @@ +#include "framework.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//////////////////////////////////////////////// +//Globals +//////////////////////////////////////////////// +VrpnClient *vrpn_client; +Transform2SG *tracker2cam; +TrackerTransform *evil_transform; +TrackerNode *evil_tracker; + +//From framework +extern PT_NamedNode data_root; +extern RenderRelation* first_arc; +extern PT(Trackball) trackball; +extern PT(MouseWatcher) mouse_watcher; + +void demo_keys(EventHandler&) { + vrpn_client = new VrpnClient(string("evildyne")); + evil_tracker = new TrackerNode(vrpn_client, string("Isense"), 2); + evil_transform = new TrackerTransform("evil_transform"); + + new DataRelation(data_root, evil_tracker); + new DataRelation(evil_tracker, evil_transform); + + tracker2cam = new Transform2SG("tracker2cam"); + tracker2cam->set_arc(first_arc); + + new DataRelation(evil_transform, tracker2cam); +} + +int main(int argc, char *argv[]) { + define_keys = &demo_keys; + return framework_main(argc, argv); +} diff --git a/panda/src/text/Sources.pp b/panda/src/text/Sources.pp new file mode 100644 index 0000000000..afee7157cb --- /dev/null +++ b/panda/src/text/Sources.pp @@ -0,0 +1,18 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET text + #define LOCAL_LIBS \ + putil gobj sgattrib graph sgraph linmath sgraphutil pnmimage gsgbase \ + mathutil + + #define SOURCES \ + config_text.cxx config_text.h textNode.I textNode.cxx textNode.h + + #define INSTALL_HEADERS \ + textNode.I textNode.h + + #define IGATESCAN all + +#end lib_target + diff --git a/panda/src/text/config_text.cxx b/panda/src/text/config_text.cxx new file mode 100644 index 0000000000..1af2eb2050 --- /dev/null +++ b/panda/src/text/config_text.cxx @@ -0,0 +1,18 @@ +// Filename: config_text.cxx +// Created by: drose (02Mar00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_text.h" +#include "textNode.h" + +#include + +Configure(config_text); + +NotifyCategory &text_cat = *Notify::ptr()->get_category(":text"); + +ConfigureFn(config_text) { + TextNode::init_type(); +} + diff --git a/panda/src/text/config_text.h b/panda/src/text/config_text.h new file mode 100644 index 0000000000..4e6c4440bd --- /dev/null +++ b/panda/src/text/config_text.h @@ -0,0 +1,16 @@ +// Filename: config_text.h +// Created by: drose (02Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_TEXT_H +#define CONFIG_TEXT_H + +#include +#include + +// Configure variables for text package. + +extern NotifyCategory &text_cat; + +#endif diff --git a/panda/src/text/textNode.I b/panda/src/text/textNode.I new file mode 100644 index 0000000000..8dd69de28f --- /dev/null +++ b/panda/src/text/textNode.I @@ -0,0 +1,960 @@ +// Filename: textNode.I +// Created by: mike (04Feb99) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_font +// Access: Public, Scheme +// Description: Sets the font that will be used when making text. +// This is a model generated via egg-mkfont. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_font(Node *font_def) { + nassertv(font_def != (Node *)NULL); + _font = font_def; + _defs.clear(); + _font_height = 1.0; + + find_characters(font_def); + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_font +// Access: Public, Scheme +// Description: Returns the font currently in use. +//////////////////////////////////////////////////////////////////// +INLINE Node *TextNode:: +get_font() const { + return _font; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_line_height +// Access: Public +// Description: Returns the number of units high each line of text +// is. This is based on the font. +//////////////////////////////////////////////////////////////////// +INLINE float TextNode:: +get_line_height() const { + return _font_height; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_slant +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_slant(float slant) { + _slant = slant; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_slant +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float TextNode:: +get_slant() const { + return _slant; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_align +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_align(int align_type) { + _align = align_type; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_align +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int TextNode:: +get_align() const { + return _align; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_wordwrap +// Access: Public, Scheme +// Description: Sets the TextNode up to automatically wordwrap text +// that exceeds the indicated width. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_wordwrap(float wordwrap) { + _flags |= F_has_wordwrap; + _wordwrap_width = wordwrap; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::clear_wordwrap +// Access: Public, Scheme +// Description: Removes the wordwrap setting from the TextNode. Text +// will be as wide as it is. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +clear_wordwrap() { + _flags &= ~F_has_wordwrap; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::has_wordwrap +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool TextNode:: +has_wordwrap() const { + return (_flags & F_has_wordwrap) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_wordwrap +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float TextNode:: +get_wordwrap() const { + return _wordwrap_width; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_text_color +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_text_color(float r, float g, float b, float a) { + set_text_color(Colorf(r, g, b, a)); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_text_color +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_text_color(const Colorf &text_color) { + _text_color = text_color; + _flags |= F_has_text_color; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::clear_text_color +// Access: Public, Scheme +// Description: Removes the text color specification; the text will +// be colored whatever it was in the source font file. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +clear_text_color() { + _flags &= ~F_has_text_color; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::has_text_color +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool TextNode:: +has_text_color() const { + return (_flags & F_has_text_color) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_text_color +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Colorf TextNode:: +get_text_color() const { + return _text_color; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_frame_color +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_frame_color(float r, float g, float b, float a) { + set_frame_color(Colorf(r, g, b, a)); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_frame_color +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_frame_color(const Colorf &frame_color) { + _frame_color = frame_color; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_frame_color +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Colorf TextNode:: +get_frame_color() const { + return _frame_color; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_card_border +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_card_border(float size, float uv_portion) { + _flags |= F_has_card_border; + _card_border_size = size; + _card_border_uv_portion = uv_portion; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::clear_card_border +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +clear_card_border() { + _flags &= ~F_has_card_border; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_card_border_size +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float TextNode:: +get_card_border_size() const { + return _card_border_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_card_border_uv_portion +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float TextNode:: +get_card_border_uv_portion() const { + return _card_border_uv_portion; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::has_card_border +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool TextNode:: +has_card_border() const { + return (_flags & F_has_card_border) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_card_color +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_card_color(float r, float g, float b, float a) { + set_card_color(Colorf(r, g, b, a)); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_card_color +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_card_color(const Colorf &card_color) { + _card_color = card_color; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_card_color +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Colorf TextNode:: +get_card_color() const { + return _card_color; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_card_texture +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_card_texture(Texture *card_texture) { + _flags |= F_has_card_texture; + _card_texture = card_texture; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::clear_card_texture +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +clear_card_texture() { + _flags &= ~F_has_card_texture; + _card_texture = NULL; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::has_card_texture +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool TextNode:: +has_card_texture() const { + return (_flags & F_has_card_texture) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_card_texture +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Texture *TextNode:: +get_card_texture() const { + return _card_texture; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_shadow_color +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_shadow_color(float r, float g, float b, float a) { + set_shadow_color(Colorf(r, g, b, a)); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_shadow_color +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_shadow_color(const Colorf &shadow_color) { + _shadow_color = shadow_color; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_shadow_color +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Colorf TextNode:: +get_shadow_color() const { + return _shadow_color; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_frame_as_margin +// Access: Public, Scheme +// Description: Specifies that a border will be drawn around the text +// when it is next created. The parameters are the +// amount of additional padding to insert between the +// frame and the text in each dimension, and all should +// generally be positive. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_frame_as_margin(float left, float right, float bottom, float top) { + _flags |= (F_has_frame | F_frame_as_margin); + _frame_ul.set(left, top); + _frame_lr.set(right, bottom); + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_frame_actual +// Access: Public, Scheme +// Description: Similar to set_frame_as_margin, except the frame is +// specified in actual coordinate units (relative to +// the text's origin), irrespective of the size of the +// text. The left and bottom coordinates should +// generally be negative, while the right and top +// coordinates should generally be positive. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_frame_actual(float left, float right, float bottom, float top) { + _flags |= F_has_frame; + _flags &= ~F_frame_as_margin; + _frame_ul.set(left, top); + _frame_lr.set(right, bottom); + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::clear_frame +// Access: Public, Scheme +// Description: Specifies that a border will not be drawn around the +// text. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +clear_frame() { + _flags &= ~F_has_frame; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::has_frame +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool TextNode:: +has_frame() const { + return (_flags & F_has_frame) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::is_frame_as_margin +// Access: Public, Scheme +// Description: If this is true, the frame was set via a call to +// set_frame_as_margin(), and the dimension of the frame +// as returned by get_frame_as_set() represent a margin +// all around the text. If false, then the frame was +// set via a call to set_frame_actual(), and the +// dimensions of the frame as returned by +// get_frame_as_set() are relative to the text's origin. +//////////////////////////////////////////////////////////////////// +INLINE bool TextNode:: +is_frame_as_margin() const { + nassertr(has_frame(), false); + return (_flags & F_frame_as_margin) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_frame_as_set +// Access: Public, Scheme +// Description: Returns the dimensions of the frame as set by +// set_frame_as_margin() or set_frame_actual(). Use +// is_frame_actual() to determine how to interpret the +// values returned by this function. It is an error to +// call this if has_frame() is false. +//////////////////////////////////////////////////////////////////// +INLINE LVecBase4f TextNode:: +get_frame_as_set() const { + nassertr(has_frame(), LVecBase4f(0.0, 0.0, 0.0, 0.0)); + return LVecBase4f(_frame_ul[0], _frame_lr[0], _frame_lr[1], _frame_ul[1]); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_frame_actual +// Access: Public, Scheme +// Description: Returns the actual dimensions of the frame around the +// text. If the frame was set via set_frame_as_margin(), +// the result returned by this function reflects the +// size of the current text; if the frame was set via +// set_frame_actual(), this returns the values +// actually set. +//////////////////////////////////////////////////////////////////// +INLINE LVecBase4f TextNode:: +get_frame_actual() const { + nassertr(has_frame(), LVecBase4f(0.0, 0.0, 0.0, 0.0)); + if (is_frame_as_margin()) { + return LVecBase4f(get_left() - _frame_ul[0], + get_right() + _frame_lr[0], + get_bottom() - _frame_lr[1], + get_top() + _frame_ul[1]); + } else { + return get_frame_as_set(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_frame_line_width +// Access: Public, Scheme +// Description: Specifies the thickness of the lines that will be +// used to draw the frame. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_frame_line_width(float frame_width) { + _frame_width = frame_width; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_frame_line_width +// Access: Public, Scheme +// Description: Returns the thickness of the lines that will be +// used to draw the frame. +//////////////////////////////////////////////////////////////////// +INLINE float TextNode:: +get_frame_line_width() const { + return _frame_width; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_frame_corners +// Access: Public, Scheme +// Description: Enables or disables the drawing of corners for the +// frame. These are extra points drawn at each of the +// four corners, to soften the ugly edges generated when +// the line width is greater than one. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_frame_corners(bool corners) { + if (corners) { + _flags |= F_frame_corners; + } else { + _flags &= ~F_frame_corners; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_frame_corners +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool TextNode:: +get_frame_corners() const { + return (_flags & F_frame_corners) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_card_as_margin +// Access: Public, Scheme +// Description: Specifies that a (possibly opaque or semitransparent) +// card will be held behind the text when it is next +// created. Like set_frame_as_margin, the parameters are +// the amount of additional padding to insert around the +// text in each dimension, and all should generally be +// positive. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_card_as_margin(float left, float right, float bottom, float top) { + _flags |= (F_has_card | F_card_as_margin); + _card_ul.set(left, top); + _card_lr.set(right, bottom); + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_card_actual +// Access: Public, Scheme +// Description: Similar to set_card_as_margin, except the card is +// specified in actual coordinate units (relative to +// the text's origin), irrespective of the size of the +// text. The left and bottom coordinates should +// generally be negative, while the right and top +// coordinates should generally be positive. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_card_actual(float left, float right, float bottom, float top) { + _flags |= F_has_card; + _flags &= ~F_card_as_margin; + _card_ul.set(left, top); + _card_lr.set(right, bottom); + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::clear_card +// Access: Public, Scheme +// Description: Specifies that a card will not be drawn behind the +// text. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +clear_card() { + _flags &= ~F_has_card; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::has_card +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool TextNode:: +has_card() const { + return (_flags & F_has_card) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::is_card_as_margin +// Access: Public, Scheme +// Description: If this is true, the card was set via a call to +// set_card_as_margin(), and the dimension of the card +// as returned by get_card_as_set() represent a margin +// all around the text. If false, then the card was +// set via a call to set_card_actual(), and the +// dimensions of the card as returned by +// get_card_as_set() are relative to the text's origin. +//////////////////////////////////////////////////////////////////// +INLINE bool TextNode:: +is_card_as_margin() const { + nassertr(has_card(), false); + return (_flags & F_card_as_margin) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_card_as_set +// Access: Public, Scheme +// Description: Returns the dimensions of the card as set by +// set_card_as_margin() or set_card_actual(). Use +// is_card_actual() to determine how to interpret the +// values returned by this function. It is an error to +// call this if has_card() is false. +//////////////////////////////////////////////////////////////////// +INLINE LVecBase4f TextNode:: +get_card_as_set() const { + nassertr(has_card(), LVecBase4f(0.0, 0.0, 0.0, 0.0)); + return LVecBase4f(_card_ul[0], _card_lr[0], _card_lr[1], _card_ul[1]); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_card_actual +// Access: Public, Scheme +// Description: Returns the actual dimensions of the card around the +// text. If the card was set via set_card_as_margin(), +// the result returned by this function reflects the +// size of the current text; if the card was set via +// set_card_actual(), this returns the values +// actually set. +//////////////////////////////////////////////////////////////////// +INLINE LVecBase4f TextNode:: +get_card_actual() const { + nassertr(has_card(), LVecBase4f(0.0, 0.0, 0.0, 0.0)); + if (is_card_as_margin()) { + return LVecBase4f(get_left() - _card_ul[0], + get_right() + _card_lr[0], + get_bottom() - _card_lr[1], + get_top() + _card_ul[1]); + } else { + return get_card_as_set(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_card_transformed +// Access: Public, Scheme +// Description: Returns the actual card dimensions, transformed by +// the matrix set by set_transform(). This returns the +// card dimensions in actual coordinates as seen by the +// rest of the world. Also see get_upper_left_3d() and +// get_lower_right_3d(). +//////////////////////////////////////////////////////////////////// +INLINE LVecBase4f TextNode:: +get_card_transformed() const { + LVecBase4f card = get_card_actual(); + LPoint3f ul = LPoint3f(card[0], 0.0, card[3]) * _transform; + LPoint3f lr = LPoint3f(card[1], 0.0, card[2]) * _transform; + + return LVecBase4f(ul[0], lr[0], lr[2], ul[2]); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_shadow +// Access: Public, Scheme +// Description: Specifies that the text should be drawn with a +// shadow, by creating a second copy of the text and +// offsetting it slightly behind the first. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_shadow(float xoffset, float yoffset) { + _flags |= F_has_shadow; + _shadow_offset.set(xoffset, yoffset); + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::clear_shadow +// Access: Public, Scheme +// Description: Specifies that a shadow will not be drawn behind the +// text. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +clear_shadow() { + _flags &= ~F_has_shadow; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::has_shadow +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool TextNode:: +has_shadow() const { + return (_flags & F_has_shadow) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_shadow +// Access: Public, Scheme +// Description: Returns the offset of the shadow as set by +// set_shadow(). It is an error to call this if +// has_shadow() is false. +//////////////////////////////////////////////////////////////////// +INLINE LVecBase2f TextNode:: +get_shadow() const { + nassertr(has_shadow(), LVecBase2f(0.0, 0.0)); + return _shadow_offset; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_draw_order +// Access: Public, Scheme +// Description: Sets the drawing order of text created by the +// TextMaker. This is actually the draw order of the +// card and frame. The shadow is drawn at +// _draw_order+1, and the text at _draw_order+2. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_draw_order(int draw_order) { + _draw_order = draw_order; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_draw_order +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int TextNode:: +get_draw_order() const { + return _draw_order; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_billboard +// Access: Public, Scheme +// Description: Sets the flag indicating whether the text should be +// generated as a billboard object or not. If this is +// true, the text will automatically billboard. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_billboard(bool billboard) { + if (billboard) { + _flags |= F_billboard; + } else { + _flags &= ~F_billboard; + } + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_billboard +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool TextNode:: +get_billboard() const { + return (_flags & F_billboard) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_transform +// Access: Public, Scheme +// Description: Sets an additional transform that is applied to the +// entire text paragraph. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_transform(const LMatrix4f &transform) { + _transform = transform; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_transform +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE LMatrix4f TextNode:: +get_transform() const { + return _transform; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_coordinate_system +// Access: Public, Scheme +// Description: Specifies the coordinate system in which the text +// will be generated. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_coordinate_system(CoordinateSystem coordinate_system) { + _coordinate_system = coordinate_system; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_coordinate_system +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CoordinateSystem TextNode:: +get_coordinate_system() const { + return _coordinate_system; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::set_text +// Access: Public, Scheme +// Description: Changes the text that is displayed under the +// TextNode. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +set_text(const string &text) { + _text = text; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::clear_text +// Access: Public, Scheme +// Description: Removes the text from the TextNode. +//////////////////////////////////////////////////////////////////// +INLINE void TextNode:: +clear_text() { + _text = ""; + rebuild(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::has_text +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool TextNode:: +has_text() const { + return !_text.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_text +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +INLINE string TextNode:: +get_text() const { + return _text; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_left +// Access: Public, Scheme +// Description: Returns the leftmost extent of the text in local 2-d +// coordinates, unmodified by the set_transform() +// matrix. +//////////////////////////////////////////////////////////////////// +INLINE float TextNode:: +get_left() const { + return _ul2d[0]; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_right +// Access: Public, Scheme +// Description: Returns the rightmost extent of the text in local 2-d +// coordinates, unmodified by the set_transform() +// matrix. +//////////////////////////////////////////////////////////////////// +INLINE float TextNode:: +get_right() const { + return _lr2d[0]; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_bottom +// Access: Public, Scheme +// Description: Returns the bottommost extent of the text in local +// 2-d coordinates, unmodified by the set_transform() +// matrix. +//////////////////////////////////////////////////////////////////// +INLINE float TextNode:: +get_bottom() const { + return _lr2d[1]; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_top +// Access: Public, Scheme +// Description: Returns the topmost extent of the text in local 2-d +// coordinates, unmodified by the set_transform() +// matrix. +//////////////////////////////////////////////////////////////////// +INLINE float TextNode:: +get_top() const { + return _ul2d[1]; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_height +// Access: Public, Scheme +// Description: Returns the net height of the text in local 2-d +// coordinates. +//////////////////////////////////////////////////////////////////// +INLINE float TextNode:: +get_height() const { + return get_top() - get_bottom(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_width +// Access: Public, Scheme +// Description: Returns the net width of the text in local 2-d +// coordinates. +//////////////////////////////////////////////////////////////////// +INLINE float TextNode:: +get_width() const { + return get_right() - get_left(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_upper_left_3d +// Access: Public, Scheme +// Description: Returns the upper-left extent of the text object, +// after it has been transformed into 3-d space by +// applying the set_transform() matrix. +//////////////////////////////////////////////////////////////////// +INLINE LPoint3f TextNode:: +get_upper_left_3d() const { + return _ul3d; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_upper_left_3d +// Access: Public, Scheme +// Description: Returns the lower-right extent of the text object, +// after it has been transformed into 3-d space by +// applying the set_transform() matrix. +//////////////////////////////////////////////////////////////////// +INLINE LPoint3f TextNode:: +get_lower_right_3d() const { + return _lr3d; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::get_num_rows +// Access: Public, Scheme +// Description: Returns the number of rows of text that were +// generated. This counts word-wrapped rows as well as +// rows generated due to embedded newlines. +//////////////////////////////////////////////////////////////////// +INLINE int TextNode:: +get_num_rows() const { + return _num_rows; +} diff --git a/panda/src/text/textNode.cxx b/panda/src/text/textNode.cxx new file mode 100644 index 0000000000..730e12dc4b --- /dev/null +++ b/panda/src/text/textNode.cxx @@ -0,0 +1,934 @@ +// Filename: textNode.cxx +// Created by: drose (12May99) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "textNode.h" +#include "config_text.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle TextNode::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: isblank +// Description: An internal function, similar to isspace(), except it +// does not consider newlines to be whitespace. +//////////////////////////////////////////////////////////////////// +INLINE bool +isblank(char ch) { + return (ch == ' ' || ch == '\t'); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::CharDef::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +TextNode::CharDef:: +CharDef(Geom *geom, float width, const AllTransitionsWrapper &trans) : + _geom(geom), _width(width), _trans(trans) { } + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +TextNode:: +TextNode(const string &name) : NamedNode(name) { + _font_height = 1.0; + _slant = 0.0; + + _flags = 0; + _align = TM_ALIGN_LEFT; + _wordwrap_width = 1.0; + + _text_color.set(1.0, 1.0, 1.0, 1.0); + _frame_color.set(1.0, 1.0, 1.0, 1.0); + _card_color.set(1.0, 1.0, 1.0, 1.0); + _shadow_color.set(1.0, 1.0, 1.0, 1.0); + + _frame_width = 1.0; + + _frame_ul.set(0.0, 0.0); + _frame_lr.set(0.0, 0.0); + _card_ul.set(0.0, 0.0); + _card_lr.set(0.0, 0.0); + _shadow_offset.set(0.0, 0.0); + + _draw_order = 1; + + _transform = LMatrix4f::ident_mat(); + _coordinate_system = CS_default; + + _ul2d.set(0.0, 0.0); + _lr2d.set(0.0, 0.0); + _ul3d.set(0.0, 0.0, 0.0); + _lr3d.set(0.0, 0.0, 0.0); + _num_rows = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +TextNode:: +~TextNode() { +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::calc_width +// Access: Public +// Description: Returns the width of a single character of the font, +// or 0.0 if the character is not known. +//////////////////////////////////////////////////////////////////// +float TextNode:: +calc_width(char ch) const { + if (ch == ' ') { + // A space is a special case. + return 0.25; + } + + CharDefs::const_iterator cdi = _defs.find(ch); + if (cdi == _defs.end()) { + // Unknown character. + return 0.0; + } + + return (*cdi).second._width; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::calc_width +// Access: Public +// Description: Returns the width of a line of text of arbitrary +// characters. The line should not include the newline +// character. +//////////////////////////////////////////////////////////////////// +float TextNode:: +calc_width(const string &line) const { + float width = 0.0; + + string::const_iterator si; + for (si = line.begin(); si != line.end(); ++si) { + width += calc_width(*si); + } + + return width; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::wordwrap_to +// Access: Public +// Description: Inserts newlines into the given text at the +// appropriate places in order to make each line be the +// longest possible line that is not longer than +// wordwrap_width (and does not break any words, if +// possible). Returns the new string. +//////////////////////////////////////////////////////////////////// +string TextNode:: +wordwrap_to(const string &text, float wordwrap_width) const { + string output_text; + + size_t p = 0; + + // Preserve any initial whitespace and newlines. + while (p < text.length() && isspace(text[p])) { + output_text += text[p]; + p++; + } + bool first_line = true; + + while (p < text.length()) { + nassertr(!isspace(text[p]), ""); + + // Scan the next n characters, until the end of the string or an + // embedded newline character, or we exceed wordwrap_width. + + size_t q = p; + bool any_spaces = false; + + float width = 0.0; + while (q < text.length() && text[q] != '\n' && width <= wordwrap_width) { + if (isspace(text[q])) { + any_spaces = true; + } + + width += calc_width(text[q]); + q++; + } + + if (q < text.length() && any_spaces) { + // If we stopped because we exceeded the wordwrap width, then + // back up to the end of the last complete word. + + while (q > p && !isspace(text[q])) { + q--; + } + } + + // Skip additional whitespace between the lines. + size_t next_start = q; + while (next_start < text.length() && isblank(text[next_start])) { + next_start++; + } + + // Trim off any more blanks on the end. + while (q > p && isspace(text[q - 1])) { + q--; + } + + if (next_start == p) { + // No characters got in at all. This could only happen if the + // wordwrap width is narrower than a single character. + q++; + next_start++; + while (next_start < text.length() && isblank(text[next_start])) { + next_start++; + } + } + + if (!first_line) { + output_text += '\n'; + } + first_line = false; + output_text += text.substr(p, q - p); + + // Now prepare to wrap the next line. + + if (next_start < text.length() && text[next_start] == '\n') { + // Skip a single embedded newline. + next_start++; + } + p = next_start; + + // Preserve any initial whitespace and newlines. + while (p < text.length() && isspace(text[p])) { + output_text += text[p]; + p++; + } + } + + return output_text; +} + + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::print +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +void TextNode:: +print() const { + write(nout); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::write +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +void TextNode:: +write(ostream &out) const { + out << "TextNode\n" + << _defs.size() << " characters available in font.\n" + << "line height is " << _font_height << " units.\n"; + if (has_text_color()) { + out << "text color is " << _text_color << "\n"; + } else { + out << "text color is unchanged from source\n"; + } + out << "alignment is "; + switch (_align) { + case TM_ALIGN_LEFT: + out << "TM_ALIGN_LEFT\n"; + break; + + case TM_ALIGN_RIGHT: + out << "TM_ALIGN_RIGHT\n"; + break; + + case TM_ALIGN_CENTER: + out << "TM_ALIGN_CENTER\n"; + break; + } + + if (has_wordwrap()) { + out << "Word-wrapping at " << _wordwrap_width << " units.\n"; + } + + if (has_frame()) { + out << "frame of color " << _frame_color << " at " + << get_frame_as_set() << " line width " << _frame_width << "\n"; + if (get_frame_corners()) { + out << "frame corners are enabled\n"; + } + if (is_frame_as_margin()) { + out << "frame coordinates are specified as margin; actual frame is:\n" + << get_frame_actual() << "\n"; + } else { + out << "frame coordinates are actual\n"; + } + } + if (has_card()) { + out << "card of color " << _card_color << " at " + << get_card_as_set() << "\n"; + if (is_card_as_margin()) { + out << "card coordinates are specified as margin; actual card is:\n" + << get_card_actual() << "\n"; + } else { + out << "card coordinates are actual\n"; + } + } + if (has_shadow()) { + out << "shadow of color " << _shadow_color << " at " + << _shadow_offset << "\n"; + } + out << "draw order is " << _draw_order << ", " + << _draw_order + 1 << ", " << _draw_order + 2 << "\n"; + + LVecBase3f scale, hpr, trans; + if (decompose_matrix(_transform, scale, hpr, trans, _coordinate_system)) { + out << "transform is:\n" + << " scale: " << scale << "\n" + << " hpr: " << hpr << "\n" + << " trans: " << hpr << "\n"; + } else { + out << "transform is:\n" << _transform; + } + out << "in coordinate system " << _coordinate_system << "\n"; + + out << "\ntext is " << _text << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::rebuild +// Access: Public, Scheme +// Description: Removes any geometry previously defined in the geode, +// and fills it with new geometry that represents the +// current text string and all its accoutrements. +// +// Normally, this function is called automatically +// whenever any of the parameters changes. It should +// not need to be called explicitly unless something +// goes wrong. +//////////////////////////////////////////////////////////////////// +void TextNode:: +rebuild() { + if (text_cat.is_debug()) { + text_cat.debug() + << "Rebuilding " << *this << " with '" << _text << "'\n"; + } + + // The strategy here will be to assemble together a bunch of + // letters, instanced from the letter hierarchy of font_def, into + // our own little hierarchy. + + // There will be one root over the whole text block, that + // contains the transform passed in. Under this root there will be + // another node for each row, that moves the row into the right place + // horizontally and vertically, and for each row, there is another + // node for each character. + + // First delete the arc below self (the TextNode). This will eliminate + // the entire sub-tree result of a prior call to make_text (if one + // exists). + + if (_root_arc != (RenderRelation *)NULL) { + remove_arc(_root_arc); + _root_arc.clear(); + } + + _root.clear(); + _text_root.clear(); + _frame_root.clear(); + _card_root.clear(); + + _ul2d.set(0.0, 0.0); + _lr2d.set(0.0, 0.0); + _ul3d.set(0.0, 0.0, 0.0); + _lr3d.set(0.0, 0.0, 0.0); + _num_rows = 0; + + if (_text.empty() || _defs.empty()) { + return; + } + + // Now build a new sub-tree for all the text components. + _root = new NamedNode(_text); + _root_arc = new RenderRelation(this, _root); + + // Compute the overall text transform matrix. We build the text in + // a Z-up coordinate system and then convert it to whatever the user + // asked for. + LMatrix4f mat = + LMatrix4f::convert_mat(CS_zup_right, _coordinate_system) * + _transform; + + _root_arc->set_transition(new TransformTransition(mat)); + + if (get_billboard()) { + _root_arc->set_transition(new BillboardTransition(BillboardTransition::axis(_coordinate_system))); + } + + string text = _text; + if (has_wordwrap()) { + text = wordwrap_to(text, _wordwrap_width); + } + + // Assemble the text. + LVector2f ul, lr; + int num_rows = 0; + _text_root = assemble_text(text.c_str(), ul, lr, num_rows); + RenderRelation *text_arc = + new RenderRelation(_root, _text_root, _draw_order + 2); + + if (has_text_color()) { + text_arc->set_transition(new ColorTransition(_text_color)); + if (_text_color[3] != 1.0) { + text_arc->set_transition + (new TransparencyTransition(TransparencyProperty::M_alpha)); + } + } + + // Save the bounding-box information about the text in a form + // friendly to the user. + _num_rows = num_rows; + _ul2d = ul; + _lr2d = lr; + _ul3d.set(ul[0], 0.0, ul[1]); + _lr3d.set(lr[0], 0.0, lr[1]); + + _ul3d = _ul3d * _transform; + _lr3d = _lr3d * _transform; + + + // Now deal with all the decorations. + + if (has_shadow()) { + // Make a shadow by instancing the text behind itself. + LMatrix4f offset = + LMatrix4f::translate_mat(_shadow_offset[0], 0.01, -_shadow_offset[1]); + RenderRelation *shadow_arc = + new RenderRelation(_root, _text_root, _draw_order + 1); + shadow_arc->set_transition(new TransformTransition(offset)); + shadow_arc->set_transition(new ColorTransition(_shadow_color)); + + if (_shadow_color[3] != 1.0) { + shadow_arc->set_transition + (new TransparencyTransition(TransparencyProperty::M_alpha)); + } + } + + if (has_frame()) { + _frame_root = make_frame(); + RenderRelation *frame_arc = + new RenderRelation(_root, _frame_root, _draw_order + 1); + frame_arc->set_transition(new ColorTransition(_frame_color)); + if (_frame_color[3] != 1.0) { + frame_arc->set_transition + (new TransparencyTransition(TransparencyProperty::M_alpha)); + } + } + + if (has_card()) { + if (has_card_border()) + _card_root = make_card_with_border(); + else + _card_root = make_card(); + RenderRelation *card_arc = + new RenderRelation(_root, _card_root, _draw_order); + card_arc->set_transition(new ColorTransition(_card_color)); + if (_card_color[3] != 1.0) { + card_arc->set_transition + (new TransparencyTransition(TransparencyProperty::M_alpha)); + } + if (has_card_texture()) { + card_arc->set_transition(new TextureTransition(_card_texture)); + } + } + + // Now flatten our hierarchy to get rid of the transforms we put in, + // applying them to the vertices. + + SceneGraphReducer gr(RenderRelation::get_class_type()); + gr.apply_transitions(_root); + gr.flatten(_root, true); +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::find_character_gsets +// Access: Protected +// Description: Given that 'root' is a Node containing at least a +// polygon and a point which define the character's +// appearance and kern position, respectively, +// recursively walk the hierarchy and root and locate +// those two Geoms. +//////////////////////////////////////////////////////////////////// +bool TextNode:: +find_character_gsets(Node *root, Geom *&ch, GeomPoint *&dot, + AllTransitionsWrapper &trans) { + if (root->is_of_type(GeomNode::get_class_type())) { + GeomNode *geode = (GeomNode *)root; + + bool found = false; + for (int i = 0; i < geode->get_num_geoms(); i++) { + dDrawable *geom = geode->get_geom(i); + if (geom->is_of_type(GeomPoint::get_class_type())) { + dot = DCAST(GeomPoint, geom); + + } else if (geom->is_of_type(Geom::get_class_type())) { + ch = DCAST(Geom, geom); + found = true; + } + } + return found; + + } else { + DownRelations::const_iterator dri; + dri = root->_children.find(RenderRelation::get_class_type()); + if (dri != root->_children.end()) { + const DownRelationPointers &drp = (*dri).second; + DownRelationPointers::const_iterator drpi; + for (drpi = drp.begin(); drpi != drp.end(); ++drpi) { + if (find_character_gsets((*drpi)->get_child(), ch, dot, trans)) { + trans.extract_from(*drpi); + } + } + } + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::find_characters +// Access: Protected +// Description: Walk the hierarchy beginning at the indicated root +// and locate any nodes whose names are just integers. +// These are taken to be characters, and their +// definitions and kern informations are retrieved. +//////////////////////////////////////////////////////////////////// +void TextNode:: +find_characters(Node *root) { + string name; + if (root->is_of_type(NamedNode::get_class_type())) { + name = DCAST(NamedNode, root)->get_name(); + } + + bool all_digits = !name.empty(); + const char *p = name.c_str(); + while (all_digits && *p != '\0') { + // VC++ complains if we treat an int as a bool, so we have to do + // this != 0 comparsion on the int isdigit() function to shut it + // up. + all_digits = (isdigit(*p) != 0); + p++; + } + + if (all_digits) { + int character = atoi(name.c_str()); + Geom *ch = NULL; + GeomPoint *dot = NULL; + AllTransitionsWrapper trans; + find_character_gsets(root, ch, dot, trans); + if (dot != NULL) { + // Get the first vertex from the "dot" geoset. This will be the + // origin of the next character. + PTA_Vertexf alist; + PTA_ushort ilist; + GeomBindType bind; + float width; + dot->get_coords(alist, bind, ilist); + if (ilist.empty()) { + width = alist[0][0]; + } else { + width = alist[ilist[0]][0]; + } + + _defs[character] = CharDef(ch, width, trans); + } + + } else if (name == "ds") { + // The group "ds" is a special node that indicate's the font's + // design size, or line height. + + Geom *ch = NULL; + GeomPoint *dot = NULL; + AllTransitionsWrapper trans; + find_character_gsets(root, ch, dot, trans); + if (dot != NULL) { + // Get the first vertex from the "dot" geoset. This will be the + // design size indicator. + PTA_Vertexf alist; + PTA_ushort ilist; + GeomBindType bind; + dot->get_coords(alist, bind, ilist); + if (ilist.empty()) { + _font_height = alist[0][2]; + } else { + _font_height = alist[ilist[0]][2]; + } + } + + } else { + DownRelations::const_iterator dri; + dri = root->_children.find(RenderRelation::get_class_type()); + if (dri != root->_children.end()) { + const DownRelationPointers &drp = (*dri).second; + DownRelationPointers::const_iterator drpi; + for (drpi = drp.begin(); drpi != drp.end(); ++drpi) { + Node *node = (*drpi)->get_child(); + find_characters(node); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::assemble_row +// Access: Protected +// Description: Assembles the letters in the source string, up until +// the first newline or the end of the string into a +// single row (which is parented to 'dest'), and returns +// the length of the row. The source pointer is moved +// to the terminating character. +//////////////////////////////////////////////////////////////////// +float TextNode:: +assemble_row(const char *&source, Node *dest) { + float xpos = 0.0; + while (*source != '\0' && *source != '\n') { + int character = (unsigned char)*source; + + if (character == ' ') { + // A space is a special case. + xpos += 0.25; + + } else { + // A printable character. + + CharDefs::const_iterator cdi = _defs.find(character); + if (cdi == _defs.end()) { + nout << "No definition for character " << character << endl; + + } else { + Geom *char_geom = (*cdi).second._geom; + float char_width = (*cdi).second._width; + const AllTransitionsWrapper &trans = (*cdi).second._trans; + + LMatrix4f mat = LMatrix4f::ident_mat(); + mat.set_row(3, LVector3f(xpos, 0, 0)); + if (char_geom != NULL) { + string ch(1, (char)character); + GeomNode *geode = new GeomNode(ch); + geode->add_geom(char_geom); + RenderRelation* rel = new RenderRelation(dest, geode); + rel->set_transition(new TransformTransition(mat)); + trans.store_to(rel); + } + + xpos += char_width; + } + } + source++; + } + + return xpos; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::assemble_text +// Access: Protected +// Description: Constructs a hierarchy of nodes that contain the +// geometry representing the indicated source text, and +// returns it. Also sets the ul, lr corners. +//////////////////////////////////////////////////////////////////// +Node *TextNode:: +assemble_text(const char *source, LVector2f &ul, LVector2f &lr, + int &num_rows) { + ul.set(0.0, 0.8 * _font_height); + lr.set(0.0, 0.0); + + // Make a group node to hold our formatted text geometry. + Node *root_node = new NamedNode("text"); + + float posy = 0.0; + int row_index = 0; + while (*source != '\0') { + char numstr[20]; + sprintf(numstr, "row%d", row_index); + nassertr(strlen(numstr) < 20, root_node); + + Node *row = new NamedNode(numstr); + float row_width = assemble_row(source, row); + if (*source != '\0') { + // Skip past the newline. + source++; + } + + LMatrix4f mat = LMatrix4f::ident_mat(); + if (_align == TM_ALIGN_LEFT) { + mat.set_row(3, LVector3f(0, 0, posy)); + lr[0] = max(lr[0], row_width); + + } else if (_align == TM_ALIGN_RIGHT) { + mat.set_row(3, LVector3f(-row_width, 0, posy)); + ul[0] = min(ul[0], -row_width); + + } else { + mat.set_row(3, LVector3f(-row_width / 2.0, 0, posy)); + lr[0] = max(lr[0], row_width / 2); + ul[0] = min(ul[0], -row_width / 2); + } + + // Also apply whatever slant the user has asked for to the entire + // row. This is an X shear. + if (_slant != 0.0) { + LMatrix4f shear(1, 0, 0, 0, + 0, 1, 0, 0, + _slant, 0, 1, 0, + 0, 0, 0, 1); + mat = shear * mat; + } + + RenderRelation *arc = new RenderRelation(root_node, row); + arc->set_transition(new TransformTransition(mat)); + + posy -= _font_height; + num_rows++; + } + + lr[1] = posy + 0.8 * _font_height; + + return root_node; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::make_frame +// Access: Protected +// Description: Creates a frame around the text. +//////////////////////////////////////////////////////////////////// +Node *TextNode:: +make_frame() { + GeomNode *frame_geode = new GeomNode("frame"); + + LVector4f dimensions = get_frame_actual(); + float left = dimensions[0]; + float right = dimensions[1]; + float bottom = dimensions[2]; + float top = dimensions[3]; + + GeomLinestrip *geoset = new GeomLinestrip; + PTA_int lengths(0); + PTA_Vertexf verts; + lengths.push_back(5); + verts.push_back(Vertexf(left, 0.0, top)); + verts.push_back(Vertexf(left, 0.0, bottom)); + verts.push_back(Vertexf(right, 0.0, bottom)); + verts.push_back(Vertexf(right, 0.0, top)); + verts.push_back(Vertexf(left, 0.0, top)); + + geoset->set_num_prims(1); + geoset->set_lengths(lengths); + + geoset->set_coords(verts, G_PER_VERTEX); + geoset->set_width(_frame_width); + frame_geode->add_geom(geoset); + + if (get_frame_corners()) { + GeomPoint *geoset = new GeomPoint; + + geoset->set_num_prims(4); + geoset->set_coords(verts, G_PER_VERTEX); + geoset->set_size(_frame_width); + frame_geode->add_geom(geoset); + } + + return frame_geode; +} + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::make_card +// Access: Protected +// Description: Creates a card behind the text. +//////////////////////////////////////////////////////////////////// +Node *TextNode:: +make_card() { + GeomNode *card_geode = new GeomNode("card"); + + LVector4f dimensions = get_card_actual(); + float left = dimensions[0]; + float right = dimensions[1]; + float bottom = dimensions[2]; + float top = dimensions[3]; + + GeomTristrip *geoset = new GeomTristrip; + PTA_int lengths(0); + lengths.push_back(4); + + PTA_Vertexf verts; + verts.push_back(Vertexf::rfu(left, 0.02, top)); + verts.push_back(Vertexf::rfu(left, 0.02, bottom)); + verts.push_back(Vertexf::rfu(right, 0.02, top)); + verts.push_back(Vertexf::rfu(right, 0.02, bottom)); + + geoset->set_num_prims(1); + geoset->set_lengths(lengths); + + geoset->set_coords(verts, G_PER_VERTEX); + + if (has_card_texture()) { + PTA_TexCoordf uvs; + uvs.push_back(TexCoordf(0.0, 1.0)); + uvs.push_back(TexCoordf(0.0, 0.0)); + uvs.push_back(TexCoordf(1.0, 1.0)); + uvs.push_back(TexCoordf(1.0, 0.0)); + + geoset->set_texcoords(uvs, G_PER_VERTEX); + } + + card_geode->add_geom(geoset); + + return card_geode; +} + + +//////////////////////////////////////////////////////////////////// +// Function: TextNode::make_card_with_border +// Access: Protected +// Description: Creates a card behind the text with a specified border +// for button edge or what have you. +//////////////////////////////////////////////////////////////////// +Node *TextNode:: +make_card_with_border() { + GeomNode *card_geode = new GeomNode("card"); + + LVector4f dimensions = get_card_actual(); + float left = dimensions[0]; + float right = dimensions[1]; + float bottom = dimensions[2]; + float top = dimensions[3]; + + // we now create three tri-strips instead of one + // with vertices arranged as follows: + // + // 1 3 5 7 - one + // 2 4 6 8 / \ two + // 9 11 13 15 \ / + // 10 12 14 16 - three + // + GeomTristrip *geoset = new GeomTristrip; + PTA_int lengths; + lengths.push_back(8); + lengths.push_back(8); + lengths.push_back(8); + + PTA_Vertexf verts; + // verts 1,2,3,4 + verts.push_back(Vertexf::rfu(left, 0.02, top)); + verts.push_back(Vertexf::rfu(left, 0.02, top - _card_border_size)); + verts.push_back(Vertexf::rfu(left + _card_border_size, 0.02, top)); + verts.push_back(Vertexf::rfu(left + _card_border_size, 0.02, + top - _card_border_size)); + // verts 5,6,7,8 + verts.push_back(Vertexf::rfu(right - _card_border_size, 0.02, top)); + verts.push_back(Vertexf::rfu(right - _card_border_size, 0.02, + top - _card_border_size)); + verts.push_back(Vertexf::rfu(right, 0.02, top)); + verts.push_back(Vertexf::rfu(right, 0.02, top - _card_border_size)); + // verts 9,10,11,12 + verts.push_back(Vertexf::rfu(left, 0.02, bottom + _card_border_size)); + verts.push_back(Vertexf::rfu(left, 0.02, bottom)); + verts.push_back(Vertexf::rfu(left + _card_border_size, 0.02, + bottom + _card_border_size)); + verts.push_back(Vertexf::rfu(left + _card_border_size, 0.02, bottom)); + // verts 13,14,15,16 + verts.push_back(Vertexf::rfu(right - _card_border_size, 0.02, + bottom + _card_border_size)); + verts.push_back(Vertexf::rfu(right - _card_border_size, 0.02, bottom)); + verts.push_back(Vertexf::rfu(right, 0.02, bottom + _card_border_size)); + verts.push_back(Vertexf::rfu(right, 0.02, bottom)); + + PTA_ushort indices; + // tristrip #1 + indices.push_back(0); + indices.push_back(1); + indices.push_back(2); + indices.push_back(3); + indices.push_back(4); + indices.push_back(5); + indices.push_back(6); + indices.push_back(7); + // tristrip #2 + indices.push_back(1); + indices.push_back(8); + indices.push_back(3); + indices.push_back(10); + indices.push_back(5); + indices.push_back(12); + indices.push_back(7); + indices.push_back(14); + // tristrip #3 + indices.push_back(8); + indices.push_back(9); + indices.push_back(10); + indices.push_back(11); + indices.push_back(12); + indices.push_back(13); + indices.push_back(14); + indices.push_back(15); + + geoset->set_num_prims(3); + geoset->set_lengths(lengths); + + geoset->set_coords(verts, G_PER_VERTEX, indices); + + if (has_card_texture()) { + PTA_TexCoordf uvs; + uvs.push_back(TexCoordf(0.0, 1.0)); //1 + uvs.push_back(TexCoordf(0.0, 1.0 - _card_border_uv_portion)); //2 + uvs.push_back(TexCoordf(0.0 + _card_border_uv_portion, 1.0)); //3 + uvs.push_back(TexCoordf(0.0 + _card_border_uv_portion, + 1.0 - _card_border_uv_portion)); //4 + uvs.push_back(TexCoordf( 1.0 -_card_border_uv_portion, 1.0)); //5 + uvs.push_back(TexCoordf( 1.0 -_card_border_uv_portion, + 1.0 - _card_border_uv_portion)); //6 + uvs.push_back(TexCoordf(1.0, 1.0)); //7 + uvs.push_back(TexCoordf(1.0, 1.0 - _card_border_uv_portion)); //8 + + uvs.push_back(TexCoordf(0.0, _card_border_uv_portion)); //9 + uvs.push_back(TexCoordf(0.0, 0.0)); //10 + uvs.push_back(TexCoordf(_card_border_uv_portion, _card_border_uv_portion)); //11 + uvs.push_back(TexCoordf(_card_border_uv_portion, 0.0)); //12 + + uvs.push_back(TexCoordf(1.0 - _card_border_uv_portion, _card_border_uv_portion));//13 + uvs.push_back(TexCoordf(1.0 - _card_border_uv_portion, 0.0));//14 + uvs.push_back(TexCoordf(1.0, _card_border_uv_portion));//15 + uvs.push_back(TexCoordf(1.0, 0.0));//16 + + // we can use same ref's as before (same order) + geoset->set_texcoords(uvs, G_PER_VERTEX, indices); + + } + + card_geode->add_geom(geoset); + + return card_geode; +} diff --git a/panda/src/text/textNode.h b/panda/src/text/textNode.h new file mode 100644 index 0000000000..da9e7e78d4 --- /dev/null +++ b/panda/src/text/textNode.h @@ -0,0 +1,255 @@ +// Filename: textNode.h +// Created by: drose (12May99) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef TEXTNODE_H +#define TEXTNODE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +#define TM_ALIGN_LEFT 1 +#define TM_ALIGN_RIGHT 2 +#define TM_ALIGN_CENTER 3 + +//////////////////////////////////////////////////////////////////// +// Class : TextNode +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TextNode : public NamedNode { +public: + TextNode(const string &name = ""); + ~TextNode(); + + INLINE void set_font(Node *font_def); + INLINE Node *get_font() const; + + INLINE float get_line_height() const; + + INLINE void set_slant(float slant); + INLINE float get_slant() const; + + INLINE void set_align(int align_type); + INLINE int get_align() const; + + INLINE void set_wordwrap(float width); + INLINE void clear_wordwrap(); + INLINE bool has_wordwrap() const; + INLINE float get_wordwrap() const; + + INLINE void set_text_color(float r, float g, float b, float a); + INLINE void set_text_color(const Colorf &text_color); + INLINE void clear_text_color(); + INLINE bool has_text_color() const; + INLINE Colorf get_text_color() const; + + INLINE void set_frame_color(float r, float g, float b, float a); + INLINE void set_frame_color(const Colorf &frame_color); + INLINE Colorf get_frame_color() const; + + INLINE void set_card_border(float size, float uv_portion); + INLINE void clear_card_border(); + INLINE float get_card_border_size() const; + INLINE float get_card_border_uv_portion() const; + INLINE bool has_card_border() const; + + INLINE void set_card_color(float r, float g, float b, float a); + INLINE void set_card_color(const Colorf &card_color); + INLINE Colorf get_card_color() const; + + INLINE void set_card_texture(Texture *card_texture); + INLINE void clear_card_texture(); + INLINE bool has_card_texture() const; + INLINE Texture *get_card_texture() const; + + INLINE void set_shadow_color(float r, float g, float b, float a); + INLINE void set_shadow_color(const Colorf &shadow_color); + INLINE Colorf get_shadow_color() const; + + INLINE void set_frame_as_margin(float left, float right, + float bottom, float top); + INLINE void set_frame_actual(float left, float right, + float bottom, float top); + INLINE void clear_frame(); + INLINE bool has_frame() const; + INLINE bool is_frame_as_margin() const; + INLINE LVecBase4f get_frame_as_set() const; + INLINE LVecBase4f get_frame_actual() const; + + INLINE void set_frame_line_width(float line_width); + INLINE float get_frame_line_width() const; + INLINE void set_frame_corners(bool corners); + INLINE bool get_frame_corners() const; + + INLINE void set_card_as_margin(float left, float right, + float bottom, float top); + INLINE void set_card_actual(float left, float right, + float bottom, float top); + INLINE void clear_card(); + INLINE bool has_card() const; + INLINE bool is_card_as_margin() const; + INLINE LVecBase4f get_card_as_set() const; + INLINE LVecBase4f get_card_actual() const; + INLINE LVecBase4f get_card_transformed() const; + + INLINE void set_shadow(float xoffset, float yoffset); + INLINE void clear_shadow(); + INLINE bool has_shadow() const; + INLINE LVecBase2f get_shadow() const; + + INLINE void set_draw_order(int draw_order); + INLINE int get_draw_order() const; + + INLINE void set_billboard(bool billboard); + INLINE bool get_billboard() const; + + INLINE void set_transform(const LMatrix4f &transform); + INLINE LMatrix4f get_transform() const; + + INLINE void set_coordinate_system(CoordinateSystem cs); + INLINE CoordinateSystem get_coordinate_system() const; + + INLINE void set_text(const string &str); + INLINE void clear_text(); + INLINE bool has_text() const; + INLINE string get_text() const; + + float calc_width(char ch) const; + float calc_width(const string &line) const; + string wordwrap_to(const string &text, float wordwrap_width) const; + + void print() const; + void write(ostream &out) const; + + void rebuild(); + + // The following functions return information about the text that + // was last built (and is currently visible). + INLINE float get_left() const; + INLINE float get_right() const; + INLINE float get_bottom() const; + INLINE float get_top() const; + INLINE float get_height() const; + INLINE float get_width() const; + + INLINE LPoint3f get_upper_left_3d() const; + INLINE LPoint3f get_lower_right_3d() const; + + INLINE int get_num_rows() const; + + +private: + bool find_character_gsets(Node *root, Geom *&ch, GeomPoint *&dot, + AllTransitionsWrapper &trans); + void find_characters(Node *root); + float assemble_row(const char *&source, Node *dest); + Node *assemble_text(const char *source, LVector2f &ul, LVector2f &lr, + int &num_rows); + Node *make_frame(); + Node *make_card(); + Node *make_card_with_border(); + + class EXPCL_PANDA CharDef { + public: + CharDef() { } + CharDef(Geom *geom, float width, const AllTransitionsWrapper &trans); + Geom *_geom; + float _width; + AllTransitionsWrapper _trans; + }; + + typedef map CharDefs; + CharDefs _defs; + float _font_height; + float _slant; + + PT(RenderRelation) _root_arc; + + PT_Node _root; + PT_Node _text_root; + PT_Node _frame_root; + PT_Node _card_root; + PT_Node _font; + + PT(Texture) _card_texture; + Colorf _text_color; + Colorf _shadow_color; + Colorf _frame_color; + Colorf _card_color; + + enum Flags { + F_has_text_color = 0x0001, + F_has_wordwrap = 0x0002, + F_has_frame = 0x0004, + F_frame_as_margin = 0x0008, + F_has_card = 0x0010, + F_card_as_margin = 0x0020, + F_has_card_texture = 0x0040, + F_has_shadow = 0x0080, + F_frame_corners = 0x0100, + F_card_transp = 0x0200, + F_has_card_border = 0x0400, + F_billboard = 0x0800, + }; + + int _flags; + int _align; + float _wordwrap_width; + float _frame_width; + float _card_border_size; + float _card_border_uv_portion; + + LVector2f _frame_ul, _frame_lr; + LVector2f _card_ul, _card_lr; + LVector2f _shadow_offset; + + int _draw_order; + + LMatrix4f _transform; + CoordinateSystem _coordinate_system; + + string _text; + + LPoint2f _ul2d, _lr2d; + LPoint3f _ul3d, _lr3d; + int _num_rows; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + NamedNode::init_type(); + register_type(_type_handle, "TextNode", + NamedNode::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "textNode.I" + +#endif diff --git a/panda/src/tform/Sources.pp b/panda/src/tform/Sources.pp new file mode 100644 index 0000000000..7c315d0e55 --- /dev/null +++ b/panda/src/tform/Sources.pp @@ -0,0 +1,25 @@ +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET tform + #define LOCAL_LIBS \ + dgraph graph linmath sgattrib display event putil gobj gsgbase \ + mathutil sgraph device sgraphutil + + #define SOURCES \ + buttonThrower.cxx buttonThrower.h config_tform.cxx config_tform.h \ + driveInterface.cxx driveInterface.h mouseWatcher.I mouseWatcher.cxx \ + mouseWatcher.h mouseWatcherRegion.I mouseWatcherRegion.cxx \ + mouseWatcherRegion.h planarSlider.cxx planarSlider.h trackball.cxx \ + trackball.h trackerTransform.cxx trackerTransform.h \ + transform2sg.cxx transform2sg.h + + #define INSTALL_HEADERS \ + buttonThrower.h driveInterface.h mouseWatcher.I mouseWatcher.h \ + mouseWatcherRegion.I mouseWatcherRegion.h planarSlider.h \ + trackball.h trackerTransform.h transform2sg.h + + #define IGATESCAN all + +#end lib_target + diff --git a/panda/src/tform/buttonThrower.cxx b/panda/src/tform/buttonThrower.cxx new file mode 100644 index 0000000000..d28b994614 --- /dev/null +++ b/panda/src/tform/buttonThrower.cxx @@ -0,0 +1,151 @@ +// Filename: buttonThrower.cxx +// Created by: drose (09Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "buttonThrower.h" + +#include +#include +#include +#include +#include + +TypeHandle ButtonThrower::_type_handle; +TypeHandle ButtonThrower::_button_events_type; + + +//////////////////////////////////////////////////////////////////// +// Function: ButtonThrower::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ButtonThrower:: +ButtonThrower(const string &name) : DataNode(name) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: ButtonThrower::set_prefix +// Access: Public +// Description: Sets the prefix which is prepended to all event names +// thrown by this object. +//////////////////////////////////////////////////////////////////// +void ButtonThrower:: +set_prefix(const string &prefix) { + _prefix = prefix; +} + + +//////////////////////////////////////////////////////////////////// +// Function: ButtonThrower::has_prefix +// Access: Public +// Description: Returns true if the ButtonThrower has a prefix set, +// false otherwise. +//////////////////////////////////////////////////////////////////// +bool ButtonThrower:: +has_prefix() const { + return !_prefix.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonThrower::get_prefix +// Access: Public +// Description: Returns the prefix that has been set on this +// ButtonThrower. See set_prefix(). +//////////////////////////////////////////////////////////////////// +string ButtonThrower:: +get_prefix() const { + return _prefix; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonThrower::get_modifier_buttons +// Access: Public +// Description: Returns the set of ModifierButtons that the +// ButtonThrower will consider important enough to +// prepend the event name with. Normally, this set will +// be empty, and the ButtonThrower will therefore ignore +// all ModifierButtons attached to the key events, but +// if one or more buttons have been added to this set, +// and those modifier buttons are set on the button +// event, then the event name will be prepended with the +// names of the modifier buttons. +//////////////////////////////////////////////////////////////////// +const ModifierButtons &ButtonThrower:: +get_modifier_buttons() const { + return _mods; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonThrower::set_modifier_buttons +// Access: Public +// Description: Changes the set of ModifierButtons that the +// ButtonThrower will consider important enough to +// prepend the event name with. Normally, this set will +// be empty, and the ButtonThrower will therefore ignore +// all ModifierButtons attached to the key events, but +// if one or more buttons have been added to this set, +// and those modifier buttons are set on the button +// event, then the event name will be prepended with the +// names of the modifier buttons. +//////////////////////////////////////////////////////////////////// +void ButtonThrower:: +set_modifier_buttons(const ModifierButtons &mods) { + _mods = mods; +} + + +//////////////////////////////////////////////////////////////////// +// Function: ButtonThrower::transmit_data +// Access: Public +// Description: Throw all events for button events found in the data +// element. +//////////////////////////////////////////////////////////////////// +void ButtonThrower:: +transmit_data(NodeAttributes &data) { + const ButtonEventDataAttribute *b; + if (get_attribute_into(b, data, _button_events_type)) { + ButtonEventDataAttribute::const_iterator bi; + for (bi = b->begin(); bi != b->end(); ++bi) { + const ButtonEvent &be = (*bi); + string event_name = _prefix + be._button.get_name(); + if (!be._down) { + event_name += "-up"; + + } else { + // We only prepend modifier names on the button-down events. + string prepend; + + for (int i = 0; i < _mods.get_num_buttons(); i++) { + ButtonHandle modifier = _mods.get_button(i); + if (be._mods.is_down(modifier)) { + prepend += modifier.get_name() + "-"; + } + } + event_name = prepend + event_name; + } + + throw_event(event_name); + } + } + + // Clear the data going down the pipe. + data.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonThrower::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +void ButtonThrower:: +init_type() { + DataNode::init_type(); + register_type(_type_handle, "ButtonThrower", + DataNode::get_class_type()); + + ButtonEventDataTransition::init_type(); + register_data_transition(_button_events_type, "ButtonEvents", + ButtonEventDataTransition::get_class_type()); +} diff --git a/panda/src/tform/buttonThrower.h b/panda/src/tform/buttonThrower.h new file mode 100644 index 0000000000..1396af1b6f --- /dev/null +++ b/panda/src/tform/buttonThrower.h @@ -0,0 +1,67 @@ +// Filename: buttonThrower.h +// Created by: drose (09Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef BUTTONTHROWER_H +#define BUTTONTHROWER_H + +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : ButtonThrower +// Description : Throws Panda Events for button down/up events +// generated within the data graph. +// +// This is a DataNode which is intended to be parented +// to the data graph below a device which is generating +// a sequence of button events as stored in an +// ButtonEventDataAttributes data member. It simply +// takes each button it finds and throws a corresponding +// event based on the button name via the throw_event() +// call. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA ButtonThrower : public DataNode { +public: + ButtonThrower(const string &name = ""); + + void set_prefix(const string &prefix); + bool has_prefix() const; + string get_prefix() const; + + const ModifierButtons &get_modifier_buttons() const; + void set_modifier_buttons(const ModifierButtons &mods); + +protected: + string _prefix; + ModifierButtons _mods; + +//////////////////////////////////////////////////////////////////// +// From parent class DataNode +//////////////////////////////////////////////////////////////////// +public: + + virtual void + transmit_data(NodeAttributes &data); + + // inputs + static TypeHandle _button_events_type; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/tform/config_tform.cxx b/panda/src/tform/config_tform.cxx new file mode 100644 index 0000000000..8b1922d84a --- /dev/null +++ b/panda/src/tform/config_tform.cxx @@ -0,0 +1,40 @@ +// Filename: config_tform.cxx +// Created by: drose (23Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_tform.h" +#include "driveInterface.h" +#include "buttonThrower.h" +#include "mouseWatcher.h" +#include "mouseWatcherRegion.h" +#include "planarSlider.h" +#include "trackball.h" +#include "transform2sg.h" +#include "trackerTransform.h" + +#include + +Configure(config_tform); +NotifyCategoryDef(tform, ""); + +const double drive_forward_speed = config_tform.GetDouble("drive-forward-speed", 20.0); +const double drive_reverse_speed = config_tform.GetDouble("drive-reverse-speed", 10.0); +const double drive_rotate_speed = config_tform.GetDouble("drive-rotate-speed", 80.0); +const double drive_vertical_dead_zone = config_tform.GetDouble("drive-vertical-dead-zone", 0.1); +const double drive_horizontal_dead_zone = config_tform.GetDouble("drive-horizontal-dead-zone", 0.1); +const double drive_vertical_ramp_up_time = config_tform.GetDouble("drive-vertical-ramp-up-time", 0.0); +const double drive_vertical_ramp_down_time = config_tform.GetDouble("drive-vertical-ramp-down-time", 0.0); +const double drive_horizontal_ramp_up_time = config_tform.GetDouble("drive-horizontal-ramp-up-time", 0.0); +const double drive_horizontal_ramp_down_time = config_tform.GetDouble("drive-horizontal-ramp-down-time", 0.0); + +ConfigureFn(config_tform) { + DriveInterface::init_type(); + ButtonThrower::init_type(); + MouseWatcher::init_type(); + MouseWatcherRegion::init_type(); + PlanarSlider::init_type(); + Trackball::init_type(); + Transform2SG::init_type(); + TrackerTransform::init_type(); +} diff --git a/panda/src/tform/config_tform.h b/panda/src/tform/config_tform.h new file mode 100644 index 0000000000..ecd281d2f4 --- /dev/null +++ b/panda/src/tform/config_tform.h @@ -0,0 +1,25 @@ +// Filename: config_tform.h +// Created by: drose (23Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_TFORM_H +#define CONFIG_TFORM_H + +#include +#include + +NotifyCategoryDecl(tform, EXPCL_PANDA, EXPTP_PANDA); + +// Configure variables for tform package. +extern const double EXPCL_PANDA drive_forward_speed; +extern const double EXPCL_PANDA drive_reverse_speed; +extern const double EXPCL_PANDA drive_rotate_speed; +extern const double EXPCL_PANDA drive_vertical_dead_zone; +extern const double EXPCL_PANDA drive_horizontal_dead_zone; +extern const double EXPCL_PANDA drive_vertical_ramp_up_time; +extern const double EXPCL_PANDA drive_vertical_ramp_down_time; +extern const double EXPCL_PANDA drive_horizontal_ramp_up_time; +extern const double EXPCL_PANDA drive_horizontal_ramp_down_time; + +#endif diff --git a/panda/src/tform/driveInterface.cxx b/panda/src/tform/driveInterface.cxx new file mode 100644 index 0000000000..05b6bf3142 --- /dev/null +++ b/panda/src/tform/driveInterface.cxx @@ -0,0 +1,821 @@ +// Filename: driveInterface.cxx +// Created by: drose (17Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "driveInterface.h" +#include "config_tform.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TypeHandle DriveInterface::_type_handle; + +TypeHandle DriveInterface::_mods_type; +TypeHandle DriveInterface::_xyz_type; +TypeHandle DriveInterface::_button_events_type; +TypeHandle DriveInterface::_transform_type; + + +DriveInterface::KeyHeld:: +KeyHeld() { + _down = false; + _changed_time = 0.0; + _effect = 0.0; + _effect_at_change = 0.0; +} + +float DriveInterface::KeyHeld:: +get_effect(float ramp_up_time, float ramp_down_time) { + double elapsed = ClockObject::get_global_clock()->get_time() - _changed_time; + + if (_down) { + // We are currently holding down the key. That means we base our + // effect on the ramp_up_time. + if (ramp_up_time == 0.0) { + _effect = 1.0; + + } else { + float change = elapsed / ramp_up_time; + _effect = min(_effect_at_change + change, 1.0f); + } + + } else { + // We are *not* currently holding down the key. That means we + // base our effect on the ramp_down_time. + if (ramp_down_time == 0.0) { + _effect = 0.0; + + } else { + float change = elapsed / ramp_down_time; + _effect = max(_effect_at_change - change, 0.0f); + } + } + + return _effect; +} + +void DriveInterface::KeyHeld:: +set_key(bool down) { + _down = down; + _changed_time = ClockObject::get_global_clock()->get_time(); + _effect_at_change = _effect; +} + +void DriveInterface::KeyHeld:: +clear() { + _down = false; + _changed_time = 0.0; + _effect = 0.0; + _effect_at_change = 0.0; +} + +bool DriveInterface::KeyHeld:: +operator < (const DriveInterface::KeyHeld &other) const { + if (_down != other._down) { + // If one has the key held down and the other doesn't, the down + // key wins. + return _down; + } + + // Otherwise, the most-recently changed key wins. + return _changed_time > other._changed_time; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::Constructor +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +DriveInterface:: +DriveInterface(const string &name) : DataNode(name) { + _forward_speed = drive_forward_speed; + _reverse_speed = drive_reverse_speed; + _rotate_speed = drive_rotate_speed; + _vertical_dead_zone = drive_vertical_dead_zone; + _horizontal_dead_zone = drive_horizontal_dead_zone; + + _vertical_ramp_up_time = drive_vertical_ramp_up_time; + _vertical_ramp_down_time = drive_vertical_ramp_down_time; + _horizontal_ramp_up_time = drive_horizontal_ramp_up_time; + _horizontal_ramp_down_time = drive_horizontal_ramp_down_time; + + _speed = 0.0; + _rot_speed = 0.0; + + _xyz.set(0.0, 0.0, 0.0); + _hpr.set(0.0, 0.0, 0.0); + _mat = LMatrix4f::ident_mat(); + + _cs = default_coordinate_system; + + _transform = new MatrixDataAttribute; + _transform->set_value(_mat); + _attrib.set_attribute(_transform_type, _transform); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::Destructor +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +DriveInterface:: +~DriveInterface() { +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::set_forward_speed +// Access: Public, Scheme +// Description: Sets the speed of full forward motion, when the mouse +// is at the very top of the window. This is in units +// (e.g. feet) per second. +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +set_forward_speed(float speed) { + _forward_speed = speed; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::get_forward_speed +// Access: Public, Scheme +// Description: Returns the speed of full forward motion, when the +// mouse is at the very top of the window. This is in +// units (e.g. feet) per second. +//////////////////////////////////////////////////////////////////// +float DriveInterface:: +get_forward_speed() const { + return _forward_speed; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::set_reverse_speed +// Access: Public, Scheme +// Description: Sets the speed of full reverse motion, when the mouse +// is at the very bottom of the window. This is in +// units (e.g. feet) per second. +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +set_reverse_speed(float speed) { + _reverse_speed = speed; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::get_reverse_speed +// Access: Public, Scheme +// Description: Returns the speed of full reverse motion, when the +// mouse is at the very bottom of the window. This is +// in units (e.g. feet) per second. +//////////////////////////////////////////////////////////////////// +float DriveInterface:: +get_reverse_speed() const { + return _reverse_speed; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::set_rotate_speed +// Access: Public, Scheme +// Description: Sets the maximum rate at which the user can rotate +// left or right, when the mouse is at the very edge of +// the window. This is in degrees per second. +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +set_rotate_speed(float speed) { + _rotate_speed = speed; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::get_rotate_speed +// Access: Public, Scheme +// Description: Returns the maximum rate at which the user can rotate +// left or right, when the mouse is at the very edge of +// the window. This is in degrees per second. +//////////////////////////////////////////////////////////////////// +float DriveInterface:: +get_rotate_speed() const { + return _rotate_speed; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::set_vertical_dead_zone +// Access: Public, Scheme +// Description: Sets the size of the horizontal bar in the center of +// the screen that represents the "dead zone" of +// vertical motion: the region in which the mouse does +// not report vertical motion. This is in a fraction of +// the window height, so 0.5 will set a dead zone as +// large as half the screen. +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +set_vertical_dead_zone(float speed) { + _vertical_dead_zone = speed; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::get_vertical_dead_zone +// Access: Public, Scheme +// Description: Returns the size of the horizontal bar in the center +// of the screen that represents the "dead zone" of +// vertical motion: the region in which the mouse does +// not report vertical motion. This is in a fraction of +// the window height, so 0.5 will set a dead zone as +// large as half the screen. +//////////////////////////////////////////////////////////////////// +float DriveInterface:: +get_vertical_dead_zone() const { + return _vertical_dead_zone; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::set_horizontal_dead_zone +// Access: Public, Scheme +// Description: Sets the size of the vertical bar in the center of +// the screen that represents the "dead zone" of +// horizontal motion: the region in which the mouse does +// not report horizontal motion. This is in a fraction of +// the window width, so 0.5 will set a dead zone as +// large as half the screen. +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +set_horizontal_dead_zone(float speed) { + _horizontal_dead_zone = speed; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::get_horizontal_dead_zone +// Access: Public, Scheme +// Description: Returns the size of the vertical bar in the center +// of the screen that represents the "dead zone" of +// horizontal motion: the region in which the mouse does +// not report horizontal motion. This is in a fraction of +// the window width, so 0.5 will set a dead zone as +// large as half the screen. +//////////////////////////////////////////////////////////////////// +float DriveInterface:: +get_horizontal_dead_zone() const { + return _horizontal_dead_zone; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::set_vertical_ramp_up_time +// Access: Public, Scheme +// Description: Sets the amount of time, in seconds, it takes between +// the time an up or down arrow key is pressed and the +// time it registers full forward or backward motion. +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +set_vertical_ramp_up_time(float ramp_up_time) { + _vertical_ramp_up_time = ramp_up_time; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::get_vertical_ramp_up_time +// Access: Public, Scheme +// Description: Returns the amount of time, in seconds, it takes +// between the time an up or down arrow key is pressed +// and the time it registers full forward or backward +// motion. +//////////////////////////////////////////////////////////////////// +float DriveInterface:: +get_vertical_ramp_up_time() const { + return _vertical_ramp_up_time; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::set_vertical_ramp_down_time +// Access: Public, Scheme +// Description: Sets the amount of time, in seconds, it takes between +// the time an up or down arrow key is released and the +// time it registers no motion. +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +set_vertical_ramp_down_time(float ramp_down_time) { + _vertical_ramp_down_time = ramp_down_time; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::get_vertical_ramp_down_time +// Access: Public, Scheme +// Description: Returns the amount of time, in seconds, it takes +// between the time an up or down arrow key is released +// and the time it registers no motion. +//////////////////////////////////////////////////////////////////// +float DriveInterface:: +get_vertical_ramp_down_time() const { + return _vertical_ramp_down_time; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::set_horizontal_ramp_up_time +// Access: Public, Scheme +// Description: Sets the amount of time, in seconds, it takes between +// the time a left or right arrow key is pressed and the +// time it registers full rotation. +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +set_horizontal_ramp_up_time(float ramp_up_time) { + _horizontal_ramp_up_time = ramp_up_time; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::get_horizontal_ramp_up_time +// Access: Public, Scheme +// Description: Returns the amount of time, in seconds, it takes +// between the time a left or right arrow key is pressed +// and the time it registers full rotation. +//////////////////////////////////////////////////////////////////// +float DriveInterface:: +get_horizontal_ramp_up_time() const { + return _horizontal_ramp_up_time; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::set_horizontal_ramp_down_time +// Access: Public, Scheme +// Description: Sets the amount of time, in seconds, it takes between +// the time a left or right arrow key is released and the +// time it registers no motion. +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +set_horizontal_ramp_down_time(float ramp_down_time) { + _horizontal_ramp_down_time = ramp_down_time; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::get_horizontal_ramp_down_time +// Access: Public, Scheme +// Description: Returns the amount of time, in seconds, it takes +// between the time a left or right arrow key is released +// and the time it registers no motion. +//////////////////////////////////////////////////////////////////// +float DriveInterface:: +get_horizontal_ramp_down_time() const { + return _horizontal_ramp_down_time; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::get_speed +// Access: Public, Scheme +// Description: Returns the speed of the previous update in units/sec +//////////////////////////////////////////////////////////////////// +float DriveInterface:: +get_speed() const { + return _speed; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::get_rot_speed +// Access: Public, Scheme +// Description: Returns the rot_speed of the previous update in units/sec +//////////////////////////////////////////////////////////////////// +float DriveInterface:: +get_rot_speed() const { + return _rot_speed; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::reset +// Access: Public, Scheme +// Description: Reinitializes the driver to the origin. +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +reset() { + _xyz.set(0.0, 0.0, 0.0); + _hpr.set(0.0, 0.0, 0.0); + _mat = LMatrix4f::ident_mat(); + _up_arrow.clear(); + _down_arrow.clear(); + _left_arrow.clear(); + _right_arrow.clear(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::get_pos +// Access: Public, Scheme +// Description: Returns the driver's position. +//////////////////////////////////////////////////////////////////// +const LPoint3f &DriveInterface:: +get_pos() const { + return _xyz; +} + +float DriveInterface:: +get_x() const { + return _xyz[0]; +} + +float DriveInterface:: +get_y() const { + return _xyz[1]; +} + +float DriveInterface:: +get_z() const { + return _xyz[2]; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::set_pos +// Access: Public, Scheme +// Description: Directly sets the driver's position. +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +set_pos(const LVecBase3f &vec) { + _xyz = vec; + recompute(); +} + +void DriveInterface:: +set_pos(float x, float y, float z) { + _xyz.set(x, y, z); + recompute(); +} + +void DriveInterface:: +set_x(float x) { + _xyz[0] = x; + recompute(); +} + +void DriveInterface:: +set_y(float y) { + _xyz[1] = y; + recompute(); +} + +void DriveInterface:: +set_z(float z) { + _xyz[2] = z; + recompute(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::get_hpr +// Access: Public, Scheme +// Description: Returns the driver's orientation. +//////////////////////////////////////////////////////////////////// +const LVecBase3f &DriveInterface:: +get_hpr() const { + return _hpr; +} + +float DriveInterface:: +get_h() const { + return _hpr[0]; +} + +float DriveInterface:: +get_p() const { + return _hpr[1]; +} + +float DriveInterface:: +get_r() const { + return _hpr[2]; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::set_hpr +// Access: Public, Scheme +// Description: Directly sets the driver's orientation. +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +set_hpr(const LVecBase3f &hpr) { + _hpr = hpr; + recompute(); +} + +void DriveInterface:: +set_hpr(float h, float p, float r) { + _hpr.set(h, p, r); + recompute(); +} + +void DriveInterface:: +set_h(float h) { + _hpr[0] = h; + recompute(); +} + +void DriveInterface:: +set_p(float p) { + _hpr[1] = p; + recompute(); +} + +void DriveInterface:: +set_r(float r) { + _hpr[2] = r; + recompute(); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::set_coordinate_system +// Access: Public, Scheme +// Description: Sets the coordinate system of the DriveInterface. +// Normally, this is the default coordinate system. +// This changes the plane the user drives around in; +// it's normally the horizontal plane (e.g. the X-Y +// plane in a Z-up coordinate system, or the X-Z plane +// in a Y-up coordinate system). +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +set_coordinate_system(CoordinateSystem cs) { + _cs = cs; + recompute(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::get_coordinate_system +// Access: Public, Scheme +// Description: Returns the coordinate system of the DriveInterface. +// See set_coordinate_system(). +//////////////////////////////////////////////////////////////////// +CoordinateSystem DriveInterface:: +get_coordinate_system() const { + return _cs; +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::set_mat +// Access: Public, Scheme +// Description: Stores the indicated transform in the driveInterface. +// This is a transform in global space, regardless of +// the rel_to node. +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +set_mat(const LMatrix4f &mat) { + _mat = mat; + reextract(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::get_mat +// Access: Public, Scheme +// Description: Fills mat with the net transformation applied by the +// current state. +//////////////////////////////////////////////////////////////////// +const LMatrix4f &DriveInterface:: +get_mat() const { + return _mat; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::apply +// Access: Private +// Description: Applies the operation indicated by the user's mouse +// motion to the current state. Returns the matrix +// indicating the new state. +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +apply(double x, double y, bool any_button) { + + // First reset the speeds + _speed = 0.0; + _rot_speed = 0.0; + + if (any_button) { + // If we're holding down any of the mouse buttons, do this + // computation based on the mouse position. + + // Determine, based on the mouse's position and the amount of time + // elapsed since last frame, how far forward/backward we should + // move and how much we should rotate. + + // First, how fast are we moving? This is based on the mouse's + // vertical position. + + if (y >= _vertical_dead_zone) { + // Motion is forward. Compute the throttle value: the ratio of + // the mouse pointer within the range of vertical movement. + float throttle = + (min(y, 1.0) - _vertical_dead_zone) / + (1.0 - _vertical_dead_zone); + _speed = throttle * _forward_speed; + + } else if (y <= -_vertical_dead_zone) { + // Motion is backward. + float throttle = + (max(y, -1.0) + _vertical_dead_zone) / + (-1.0 + _vertical_dead_zone); + _speed = -throttle * _reverse_speed; + } + + // Now, what's our rotational velocity? This is based on the + // mouse's horizontal position. + + if (x >= _horizontal_dead_zone) { + // Rotation is to the right. Compute the throttle value: the + // ratio of the mouse pointer within the range of horizontal + // movement. + float throttle = + (min(x, 1.0) - _horizontal_dead_zone) / + (1.0 - _horizontal_dead_zone); + _rot_speed = throttle * _rotate_speed; + + } else if (x <= -_horizontal_dead_zone) { + // Rotation is to the left. + float throttle = + (max(x, -1.0) + _horizontal_dead_zone) / + (-1.0 + _horizontal_dead_zone); + _rot_speed = -throttle * _rotate_speed; + } + + } else { + // If we're not holding down any of the mouse buttons, do this + // computation based on the arrow keys. + + // Which vertical arrow key changed state more recently? + float throttle; + + if (_up_arrow < _down_arrow) { + throttle = _up_arrow.get_effect(_vertical_ramp_up_time, + _vertical_ramp_down_time); + _speed = throttle * _forward_speed; + _down_arrow._effect = 0.0; + + } else { + throttle = _down_arrow.get_effect(_vertical_ramp_up_time, + _vertical_ramp_down_time); + _speed = -throttle * _reverse_speed; + _up_arrow._effect = 0.0; + } + + // Which horizontal arrow key changed state more recently? + if (_right_arrow < _left_arrow) { + throttle = _right_arrow.get_effect(_horizontal_ramp_up_time, + _horizontal_ramp_down_time); + _rot_speed = throttle * _rotate_speed; + _left_arrow._effect = 0.0; + + } else { + throttle = _left_arrow.get_effect(_horizontal_ramp_up_time, + _horizontal_ramp_down_time); + _rot_speed = -throttle * _rotate_speed; + _right_arrow._effect = 0.0; + } + _right_arrow._effect = throttle; + _left_arrow._effect = throttle; + } + + if (_speed == 0.0 && _rot_speed == 0.0) { + return; + } + + // Now how far did we move based on the amount of time elapsed? + float distance = ClockObject::get_global_clock()->get_dt() * _speed; + float rotation = ClockObject::get_global_clock()->get_dt() * _rot_speed; + + // Now apply the vectors. + + // rot_mat is the rotation matrix corresponding to our previous + // heading. + LMatrix3f rot_mat = + LMatrix3f::rotate_mat(_hpr[0], LVector3f::up(_cs), _cs); + + // Take a step in the direction of our previous heading. + LVector3f step = distance * (LVector3f::forward(_cs) * rot_mat); + + // To prevent upward drift due to numerical errors, force the + // vertical component of our step to zero (it should be pretty near + // zero anyway). + switch (_cs) { + case CS_zup_right: + case CS_zup_left: + step[2] = 0.0; + break; + + case CS_yup_right: + case CS_yup_left: + step[1] = 0.0; + break; + } + + _xyz += step; + _hpr[0] -= rotation; + + recompute(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::reextract +// Access: Private +// Description: Given a correctly computed _mat matrix, rederive the +// translation and rotation elements. +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +reextract() { + LVecBase3f scale; + decompose_matrix(_mat, scale, _hpr, _xyz); +} + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::recompute +// Access: Private +// Description: Rebuilds the matrix according to the stored rotation +// and translation components. +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +recompute() { + compose_matrix(_mat, LVecBase3f(1.0, 1.0, 1.0), _hpr, _xyz, _cs); +} + + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::transmit_data +// Access: Public +// Description: Convert mouse data into a driveInterface matrix +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +transmit_data(NodeAttributes &data) { + // Look for keyboard events. + const ButtonEventDataAttribute *b; + if (get_attribute_into(b, data, _button_events_type)) { + ButtonEventDataAttribute::const_iterator bi; + for (bi = b->begin(); bi != b->end(); ++bi) { + const ButtonEvent &be = (*bi); + + if (be._button == KeyboardButton::up()) { + _up_arrow.set_key(be._down); + } else if (be._button == KeyboardButton::down()) { + _down_arrow.set_key(be._down); + } else if (be._button == KeyboardButton::left()) { + _left_arrow.set_key(be._down); + } else if (be._button == KeyboardButton::right()) { + _right_arrow.set_key(be._down); + } + } + } + + // Now look for mouse activity. + bool any_button = false; + double x = 0.0; + double y = 0.0; + + const Vec3DataAttribute *xyz; + if (get_attribute_into(xyz, data, _xyz_type)) { + LVecBase3f p = xyz->get_value(); + x = p[0]; + y = p[1]; + + const ModifierButtonDataAttribute *button; + if (get_attribute_into(button, data, _mods_type)) { + ModifierButtons mods = button->get_mods(); + any_button = + mods.is_down(MouseButton::one()) || + mods.is_down(MouseButton::two()) || + mods.is_down(MouseButton::three()); + } + } + + apply(x, y, any_button); + _transform->set_value(_mat); + + // Now send our matrix down the pipe. + data = _attrib; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DriveInterface::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +void DriveInterface:: +init_type() { + DataNode::init_type(); + register_type(_type_handle, "DriveInterface", + DataNode::get_class_type()); + + ModifierButtonDataTransition::init_type(); + register_data_transition(_mods_type, "ModifierButtons", + ModifierButtonDataTransition::get_class_type()); + Vec3DataTransition::init_type(); + register_data_transition(_xyz_type, "XYZ", + Vec3DataTransition::get_class_type()); + MatrixDataTransition::init_type(); + register_data_transition(_transform_type, "Transform", + MatrixDataTransition::get_class_type()); + ButtonEventDataTransition::init_type(); + register_data_transition(_button_events_type, "ButtonEvents", + ButtonEventDataTransition::get_class_type()); +} diff --git a/panda/src/tform/driveInterface.h b/panda/src/tform/driveInterface.h new file mode 100644 index 0000000000..59ff9922a9 --- /dev/null +++ b/panda/src/tform/driveInterface.h @@ -0,0 +1,168 @@ +// Filename: driveInterface.h +// Created by: drose (17Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DRIVEINTERFACE_H +#define DRIVEINTERFACE_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +//////////////////////////////////////////////////////////////////// +// Class : DriveInterface +// Description : This is a TFormer, similar to Trackball, that moves +// around a transform matrix in response to mouse input. +// The basic motion is on a horizontal plane, as if +// driving a vehicle. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DriveInterface : public DataNode { +public: + DriveInterface(const string &name = ""); + ~DriveInterface(); + + void set_forward_speed(float speed); + float get_forward_speed() const; + void set_reverse_speed(float speed); + float get_reverse_speed() const; + void set_rotate_speed(float speed); + float get_rotate_speed() const; + void set_vertical_dead_zone(float zone); + float get_vertical_dead_zone() const; + void set_horizontal_dead_zone(float zone); + float get_horizontal_dead_zone() const; + + void set_vertical_ramp_up_time(float ramp_up_time); + float get_vertical_ramp_up_time() const; + void set_vertical_ramp_down_time(float ramp_down_time); + float get_vertical_ramp_down_time() const; + void set_horizontal_ramp_up_time(float ramp_up_time); + float get_horizontal_ramp_up_time() const; + void set_horizontal_ramp_down_time(float ramp_down_time); + float get_horizontal_ramp_down_time() const; + + float get_speed() const; + float get_rot_speed() const; + + void reset(); + + /// **** Translation **** + + const LPoint3f &get_pos() const; + float get_x() const; + float get_y() const; + float get_z() const; + void set_pos(const LVecBase3f &vec); + void set_pos(float x, float y, float z); + void set_x(float x); + void set_y(float y); + void set_z(float z); + + /// **** Rotation **** + + const LVecBase3f &get_hpr() const; + float get_h() const; + float get_p() const; + float get_r() const; + void set_hpr(const LVecBase3f &hpr); + void set_hpr(float h, float p, float r); + void set_h(float h); + void set_p(float p); + void set_r(float r); + + void set_coordinate_system(CoordinateSystem cs); + CoordinateSystem get_coordinate_system() const; + + void set_mat(const LMatrix4f &mat); + const LMatrix4f &get_mat() const; + + +private: + void apply(double x, double y, bool any_button); + + void reextract(); + void recompute(); + + float _forward_speed; // units / sec, mouse all the way up + float _reverse_speed; // units / sec, mouse all the way down + float _rotate_speed; // degrees / sec, mouse all the way over + float _vertical_dead_zone; // fraction of window size + float _horizontal_dead_zone; // fraction of window size + + // The time it takes to ramp up to full speed from a stop (or return + // to a stop from full speed) when using the keyboard. + float _vertical_ramp_up_time; + float _vertical_ramp_down_time; + float _horizontal_ramp_up_time; + float _horizontal_ramp_down_time; + + float _speed; // instantaneous units / sec + float _rot_speed; // instantaneous rotational units / sec + + LPoint3f _xyz; + LVecBase3f _hpr; + LMatrix4f _mat; + CoordinateSystem _cs; + + // Remember which arrow keys are being held down and which aren't, + // and at what point they last changed state. + class KeyHeld { + public: + KeyHeld(); + float get_effect(float ramp_up_time, float ramp_down_time); + void set_key(bool down); + void clear(); + bool operator < (const KeyHeld &other) const; + + float _effect; + bool _down; + double _changed_time; + float _effect_at_change; + }; + KeyHeld _up_arrow, _down_arrow; + KeyHeld _left_arrow, _right_arrow; + +//////////////////////////////////////////////////////////////////// +// From parent class DataNode +//////////////////////////////////////////////////////////////////// +public: + + virtual void + transmit_data(NodeAttributes &data); + + NodeAttributes _attrib; + PT(MatrixDataAttribute) _transform; + + // inputs + static TypeHandle _mods_type; + static TypeHandle _xyz_type; + static TypeHandle _button_events_type; + + // outputs + static TypeHandle _transform_type; + + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/tform/mouseWatcher.I b/panda/src/tform/mouseWatcher.I new file mode 100644 index 0000000000..05c7248618 --- /dev/null +++ b/panda/src/tform/mouseWatcher.I @@ -0,0 +1,265 @@ +// Filename: mouseWatcher.I +// Created by: drose (13Jul00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::has_mouse +// Access: Public, Scheme +// Description: Returns true if the mouse is anywhere within the +// window, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool MouseWatcher:: +has_mouse() const { + return _has_mouse; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_mouse +// Access: Public, Scheme +// Description: It is only valid to call this if has_mouse() returns +// true. If so, this returns the current position of +// the mouse within the window. +//////////////////////////////////////////////////////////////////// +INLINE const LPoint2f &MouseWatcher:: +get_mouse() const { +#ifndef NDEBUG + static LPoint2f bogus_mouse(0.0, 0.0); + nassertr(_has_mouse, bogus_mouse); +#endif + return _mouse; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_mouse_x +// Access: Public, Scheme +// Description: It is only valid to call this if has_mouse() returns +// true. If so, this returns the current X position of +// the mouse within the window. +//////////////////////////////////////////////////////////////////// +INLINE float MouseWatcher:: +get_mouse_x() const { + nassertr(_has_mouse, 0.0); + return _mouse[0]; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_mouse_y +// Access: Public, Scheme +// Description: It is only valid to call this if has_mouse() returns +// true. If so, this returns the current Y position of +// the mouse within the window. +//////////////////////////////////////////////////////////////////// +INLINE float MouseWatcher:: +get_mouse_y() const { + nassertr(_has_mouse, 0.0); + return _mouse[1]; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::is_over_region +// Access: Public, Scheme +// Description: Returns true if the mouse is over any rectangular +// region, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool MouseWatcher:: +is_over_region() const { + return get_over_region() != (MouseWatcherRegion *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::is_over_region +// Access: Public, Scheme +// Description: Returns true if the mouse is over any rectangular +// region, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool MouseWatcher:: +is_over_region(float x, float y) const { + return get_over_region(x, y) != (MouseWatcherRegion *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::is_over_region +// Access: Public, Scheme +// Description: Returns true if the mouse is over any rectangular +// region, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool MouseWatcher:: +is_over_region(const LPoint2f &pos) const { + return get_over_region(pos) != (MouseWatcherRegion *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_over_region +// Access: Public, Scheme +// Description: Returns the smallest region the mouse is currently +// over, or NULL if it is over no region. +//////////////////////////////////////////////////////////////////// +INLINE MouseWatcherRegion *MouseWatcher:: +get_over_region() const { + return _current_region; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_over_region +// Access: Public, Scheme +// Description: Returns the smallest region the indicated point is +// over, or NULL if it is over no region. +//////////////////////////////////////////////////////////////////// +INLINE MouseWatcherRegion *MouseWatcher:: +get_over_region(float x, float y) const { + return get_over_region(LPoint2f(x, y)); +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::set_button_down_pattern +// Access: Public, Scheme +// Description: Sets the pattern string that indicates how the event +// names are generated when a button is depressed. This +// is a string that may contain any of the following: +// +// %n - the name of the region the mouse is over +// %b - the name of the button pressed. +// +// The event name will be based on the in_pattern +// string specified here, with all occurrences of the +// above strings replaced with the corresponding values. +//////////////////////////////////////////////////////////////////// +INLINE void MouseWatcher:: +set_button_down_pattern(const string &pattern) { + _button_down_pattern = pattern; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_button_down_pattern +// Access: Public, Scheme +// Description: Returns the string that indicates how event names are +// generated when a button is depressed. See +// set_button_down_pattern(). +//////////////////////////////////////////////////////////////////// +INLINE const string &MouseWatcher:: +get_button_down_pattern() const { + return _button_down_pattern; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::set_button_up_pattern +// Access: Public, Scheme +// Description: Sets the pattern string that indicates how the event +// names are generated when a button is released. See +// set_button_down_pattern(). +//////////////////////////////////////////////////////////////////// +INLINE void MouseWatcher:: +set_button_up_pattern(const string &pattern) { + _button_up_pattern = pattern; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_button_up_pattern +// Access: Public, Scheme +// Description: Returns the string that indicates how event names are +// generated when a button is released. See +// set_button_down_pattern(). +//////////////////////////////////////////////////////////////////// +INLINE const string &MouseWatcher:: +get_button_up_pattern() const { + return _button_up_pattern; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::set_enter_pattern +// Access: Public, Scheme +// Description: Sets the pattern string that indicates how the event +// names are generated when the mouse wanders over a +// region. See set_button_down_pattern(). +//////////////////////////////////////////////////////////////////// +INLINE void MouseWatcher:: +set_enter_pattern(const string &pattern) { + _enter_pattern = pattern; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_enter_pattern +// Access: Public, Scheme +// Description: Returns the string that indicates how event names are +// generated when the mouse wanders over a region. See +// set_button_down_pattern(). +//////////////////////////////////////////////////////////////////// +INLINE const string &MouseWatcher:: +get_enter_pattern() const { + return _enter_pattern; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::set_leave_pattern +// Access: Public, Scheme +// Description: Sets the pattern string that indicates how the event +// names are generated when the mouse leaves a region. +// See set_button_down_pattern(). +//////////////////////////////////////////////////////////////////// +INLINE void MouseWatcher:: +set_leave_pattern(const string &pattern) { + _leave_pattern = pattern; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_leave_pattern +// Access: Public, Scheme +// Description: Returns the string that indicates how event names are +// generated when the mouse leaves a region. See +// set_button_down_pattern(). +//////////////////////////////////////////////////////////////////// +INLINE const string &MouseWatcher:: +get_leave_pattern() const { + return _leave_pattern; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::set_geometry +// Access: Public, Scheme +// Description: Sets the arc that will be transformed each frame by +// the mouse's coordinates. It will also be hidden when +// the mouse goes outside the window. This can be used +// to implement a software mouse pointer for when a +// hardware (or system) mouse pointer is unavailable. +//////////////////////////////////////////////////////////////////// +INLINE void MouseWatcher:: +set_geometry(NodeRelation *arc) { + _geometry = arc; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::has_geometry +// Access: Public, Scheme +// Description: Returns true if a software mouse pointer has been +// setup via set_geometry(), or false otherwise. See +// set_geometry(). +//////////////////////////////////////////////////////////////////// +INLINE bool MouseWatcher:: +has_geometry() const { + return !_geometry.is_null(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_geometry +// Access: Public, Scheme +// Description: Returns the arc that has been set as the software +// mouse pointer, or NULL if no arc has been set. See +// has_geometry() and set_geometry(). +//////////////////////////////////////////////////////////////////// +INLINE NodeRelation *MouseWatcher:: +get_geometry() const { + return _geometry; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::clear_geometry +// Access: Public, Scheme +// Description: Stops the use of the software cursor set up via +// set_geometry(). +//////////////////////////////////////////////////////////////////// +INLINE void MouseWatcher:: +clear_geometry() { + _geometry.clear(); +} diff --git a/panda/src/tform/mouseWatcher.cxx b/panda/src/tform/mouseWatcher.cxx new file mode 100644 index 0000000000..184ca9542b --- /dev/null +++ b/panda/src/tform/mouseWatcher.cxx @@ -0,0 +1,313 @@ +// Filename: mouseWatcher.cxx +// Created by: drose (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "mouseWatcher.h" +#include "config_tform.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TypeHandle MouseWatcher::_type_handle; + +TypeHandle MouseWatcher::_mods_type; +TypeHandle MouseWatcher::_xyz_type; +TypeHandle MouseWatcher::_button_events_type; + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::Constructor +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +MouseWatcher:: +MouseWatcher(const string &name) : DataNode(name) { + _has_mouse = false; + _current_region = (MouseWatcherRegion *)NULL; + _button_down_region = (MouseWatcherRegion *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::Destructor +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +MouseWatcher:: +~MouseWatcher() { +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::add_region +// Access: Public, Scheme +// Description: Adds the indicated region to the set of regions that +// are to be watched. Returns true if it was +// successfully added, or false if it was already on the +// list. +//////////////////////////////////////////////////////////////////// +bool MouseWatcher:: +add_region(MouseWatcherRegion *region) { + return _regions.insert(region).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::has_region +// Access: Public, Scheme +// Description: Returns true if the indicated region has already been +// added to the MouseWatcher, false otherwise. +//////////////////////////////////////////////////////////////////// +bool MouseWatcher:: +has_region(MouseWatcherRegion *region) const { + return _regions.count(region) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::remove_region +// Access: Public, Scheme +// Description: Removes the indicated region from the Watcher. +// Returns true if it was successfully removed, or false +// if it wasn't there in the first place. +//////////////////////////////////////////////////////////////////// +bool MouseWatcher:: +remove_region(MouseWatcherRegion *region) { + return _regions.erase(region) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_over_region +// Access: Public, Scheme +// Description: Returns the smallest region the indicated point is +// over, or NULL if it is over no region. +//////////////////////////////////////////////////////////////////// +MouseWatcherRegion *MouseWatcher:: +get_over_region(const LPoint2f &pos) const { + MouseWatcherRegion *over_region = (MouseWatcherRegion *)NULL; + float over_area = 0.0; + + Regions::const_iterator ri; + for (ri = _regions.begin(); ri != _regions.end(); ++ri) { + MouseWatcherRegion *region = (*ri); + const LVecBase4f &frame = region->get_frame(); + + if (region->get_active() && + pos[0] >= frame[0] && pos[0] <= frame[1] && + pos[1] >= frame[2] && pos[1] <= frame[3]) { + + // We're over this region. Is it the smallest? + if (over_region == (MouseWatcherRegion *)NULL || + region->get_area() < over_area) { + over_region = region; + over_area = region->get_area(); + } + } + } + + return over_region; +} + + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void MouseWatcher:: +output(ostream &out) const { + DataNode::output(out); + out << " (" << _regions.size() << " regions)"; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void MouseWatcher:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) + << "MouseWatcher " << get_name() << ":\n"; + Regions::const_iterator ri; + for (ri = _regions.begin(); ri != _regions.end(); ++ri) { + MouseWatcherRegion *region = (*ri); + region->write(out, indent_level + 2); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::set_current_region +// Access: Private +// Description: Changes the "current" region--the one we consider the +// mouse to be over--to the indicated one, and throws +// whatever events are appropriate because of that. +//////////////////////////////////////////////////////////////////// +void MouseWatcher:: +set_current_region(MouseWatcherRegion *region) { + if (region != _current_region) { + if (_current_region != (MouseWatcherRegion *)NULL) { + throw_event_pattern(_leave_pattern, _current_region); + } + _current_region = region; + if (_current_region != (MouseWatcherRegion *)NULL) { + throw_event_pattern(_enter_pattern, _current_region); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::throw_event_for +// Access: Private +// Description: Throws an event associated with the indicated region, +// using the given pattern. +//////////////////////////////////////////////////////////////////// +void MouseWatcher:: +throw_event_pattern(const string &pattern, const MouseWatcherRegion *region, + const string &button_name) { + if (pattern.empty()) { + return; + } + + string event; + for (size_t p = 0; p < pattern.size(); ++p) { + if (pattern[p] == '%') { + string cmd = pattern.substr(p + 1, 1); + p++; + if (cmd == "r") { + event += region->get_name(); + + } else if (cmd == "b") { + event += button_name; + + } else { + tform_cat.error() + << "Invalid symbol in event_pattern: %" << cmd << "\n"; + } + } else { + event += pattern[p]; + } + } + + if (!event.empty()) { + throw_event(event); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::transmit_data +// Access: Public +// Description: Convert mouse data into a mouseWatcher matrix +//////////////////////////////////////////////////////////////////// +void MouseWatcher:: +transmit_data(NodeAttributes &data) { + // Get the current mouse position. + const Vec3DataAttribute *xyz; + if (!get_attribute_into(xyz, data, _xyz_type)) { + if (_has_mouse) { + // Hide the mouse pointer. + if (!_geometry.is_null()) { + _geometry->set_transition(new PruneTransition); + } + } + + _has_mouse = false; + // If the mouse is outside the window, do nothing; let all the + // events continue down the pipe unmolested. + set_current_region(NULL); + return; + } + + LVecBase3f p = xyz->get_value(); + _mouse.set(p[0], p[1]); + + if (!_geometry.is_null()) { + // Transform the mouse pointer. + LMatrix4f mat = LMatrix4f::translate_mat(p[0], 0, p[1]); + _geometry->set_transition(new TransformTransition(mat)); + if (!_has_mouse) { + // Show the mouse pointer. + _geometry->clear_transition(PruneTransition::get_class_type()); + } + } + + _has_mouse = true; + + set_current_region(get_over_region(_mouse)); + + // Look for button events. + const ButtonEventDataAttribute *b; + if (get_attribute_into(b, data, _button_events_type)) { + ButtonEventDataAttribute::const_iterator bi; + for (bi = b->begin(); bi != b->end(); ++bi) { + const ButtonEvent &be = (*bi); + + if (!be._down) { + // Button up. Send the up event associated with the region we + // were over when the button went down. + + // There is some danger of losing button-up events here. If + // more than one button goes down together, we won't detect + // both the of the button-up events properly. + + if (_button_down_region != (MouseWatcherRegion *)NULL) { + throw_event_pattern(_button_up_pattern, _button_down_region, + be._button.get_name()); + } + _button_down_region = (MouseWatcherRegion *)NULL; + + } else { + // Button down. + _button_down_region = _current_region; + if (_current_region != (MouseWatcherRegion *)NULL) { + throw_event_pattern(_button_down_pattern, _button_down_region, + be._button.get_name()); + } + } + } + } + + if (_button_down_region != (MouseWatcherRegion *)NULL) { + // We're currently holding down a button. This is the effective + // region that determines whether we suppress below. + if (_button_down_region->get_suppress_below()) { + data.clear(); + } + + } else if (_current_region != (MouseWatcherRegion *)NULL) { + // We're not holding down a button, but we are within a region. + // Use this region to determine whether we suppress below. + if (_current_region->get_suppress_below()) { + data.clear(); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +void MouseWatcher:: +init_type() { + DataNode::init_type(); + register_type(_type_handle, "MouseWatcher", + DataNode::get_class_type()); + + ModifierButtonDataTransition::init_type(); + register_data_transition(_mods_type, "ModifierButtons", + ModifierButtonDataTransition::get_class_type()); + Vec3DataTransition::init_type(); + register_data_transition(_xyz_type, "XYZ", + Vec3DataTransition::get_class_type()); + ButtonEventDataTransition::init_type(); + register_data_transition(_button_events_type, "ButtonEvents", + ButtonEventDataTransition::get_class_type()); +} diff --git a/panda/src/tform/mouseWatcher.h b/panda/src/tform/mouseWatcher.h new file mode 100644 index 0000000000..67e039c8d5 --- /dev/null +++ b/panda/src/tform/mouseWatcher.h @@ -0,0 +1,133 @@ +// Filename: mouseWatcher.h +// Created by: drose (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MOUSEWATCHER_H +#define MOUSEWATCHER_H + +#include + +#include "mouseWatcherRegion.h" + +#include +#include +#include +#include +#include +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : MouseWatcher +// Description : This TFormer maintains a list of rectangular regions +// on the screen that are considered special mouse +// regions; typically these will be click buttons. When +// the mouse passes in or out of one of these regions, +// or when a button is clicked while the mouse is in one +// of these regions, an event is thrown. +// +// Mouse events may also be suppressed from the rest of +// the datagraph in these special regions. +// +// This class can also implement a software mouse +// pointer by automatically generating a transform to +// apply to a piece of geometry placed under the 2-d +// scene graph. It will move the geometry around +// according to the mouse's known position. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA MouseWatcher : public DataNode { +public: + MouseWatcher(const string &name = ""); + ~MouseWatcher(); + + bool add_region(MouseWatcherRegion *region); + bool has_region(MouseWatcherRegion *region) const; + bool remove_region(MouseWatcherRegion *region); + + INLINE bool has_mouse() const; + INLINE const LPoint2f &get_mouse() const; + INLINE float get_mouse_x() const; + INLINE float get_mouse_y() const; + + INLINE bool is_over_region() const; + INLINE bool is_over_region(float x, float y) const; + INLINE bool is_over_region(const LPoint2f &pos) const; + + INLINE MouseWatcherRegion *get_over_region() const; + INLINE MouseWatcherRegion *get_over_region(float x, float y) const; + MouseWatcherRegion *get_over_region(const LPoint2f &pos) const; + + INLINE void set_button_down_pattern(const string &pattern); + INLINE const string &get_button_down_pattern() const; + + INLINE void set_button_up_pattern(const string &pattern); + INLINE const string &get_button_up_pattern() const; + + INLINE void set_enter_pattern(const string &pattern); + INLINE const string &get_enter_pattern() const; + + INLINE void set_leave_pattern(const string &pattern); + INLINE const string &get_leave_pattern() const; + + INLINE void set_geometry(NodeRelation *arc); + INLINE bool has_geometry() const; + INLINE NodeRelation *get_geometry() const; + INLINE void clear_geometry(); + + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + +private: + void set_current_region(MouseWatcherRegion *region); + void throw_event_pattern(const string &pattern, + const MouseWatcherRegion *region, + const string &button_name = string()); + + typedef set Regions; + Regions _regions; + + bool _has_mouse; + LPoint2f _mouse; + + MouseWatcherRegion *_current_region; + MouseWatcherRegion *_button_down_region; + string _button_down_pattern; + string _button_up_pattern; + string _enter_pattern; + string _leave_pattern; + + PT(NodeRelation) _geometry; + +//////////////////////////////////////////////////////////////////// +// From parent class DataNode +//////////////////////////////////////////////////////////////////// +public: + + virtual void + transmit_data(NodeAttributes &data); + + // inputs & outputs + static TypeHandle _mods_type; + static TypeHandle _xyz_type; + static TypeHandle _button_events_type; + + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +#include "mouseWatcher.I" + +#endif diff --git a/panda/src/tform/mouseWatcherRegion.I b/panda/src/tform/mouseWatcherRegion.I new file mode 100644 index 0000000000..925850d2fc --- /dev/null +++ b/panda/src/tform/mouseWatcherRegion.I @@ -0,0 +1,122 @@ +// Filename: mouseWatcherRegion.I +// Created by: drose (13Jul00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherRegion::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MouseWatcherRegion:: +MouseWatcherRegion(const string &name, float left, float right, + float bottom, float top) : + Namable(name), + _frame(left, right, bottom, top) +{ + _active = true; + _suppress_below = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherRegion::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MouseWatcherRegion:: +MouseWatcherRegion(const string &name, const LVecBase4f &frame) : + Namable(name), + _frame(frame) +{ + _active = true; + _suppress_below = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherRegion::set_frame +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void MouseWatcherRegion:: +set_frame(float left, float right, float bottom, float top) { + set_frame(LVecBase4f(left, right, bottom, top)); +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherRegion::set_frame +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void MouseWatcherRegion:: +set_frame(const LVecBase4f &frame) { + _frame = frame; + _area = (_frame[1] - _frame[0]) * (_frame[3] - _frame[2]); +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherRegion::get_frame +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const LVecBase4f &MouseWatcherRegion:: +get_frame() const { + return _frame; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherRegion::get_area +// Access: Public +// Description: Returns the area of the rectangular region. +//////////////////////////////////////////////////////////////////// +INLINE float MouseWatcherRegion:: +get_area() const { + return _area; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherRegion::set_active +// Access: Public +// Description: Sets whether the region is active or not. If it is +// not active, the MouseWatcher will totally ignore it. +//////////////////////////////////////////////////////////////////// +INLINE void MouseWatcherRegion:: +set_active(bool active) { + _active = active; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherRegion::get_active +// Access: Public +// Description: Returns whether the region is active or not. See +// set_active(). +//////////////////////////////////////////////////////////////////// +INLINE bool MouseWatcherRegion:: +get_active() const { + return _active; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherRegion::set_suppress_below +// Access: Public +// Description: Sets the suppress_below flag. When this is true, and +// the region is active, then whenever the mouse is +// within the region the MouseWatcher will suppress any +// mouse and keyboard events further down in the data +// graph. +//////////////////////////////////////////////////////////////////// +INLINE void MouseWatcherRegion:: +set_suppress_below(bool suppress_below) { + _suppress_below = suppress_below; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherRegion::get_suppress_below +// Access: Public +// Description: Returns the suppress_below flag. See +// set_suppress_below(). +//////////////////////////////////////////////////////////////////// +INLINE bool MouseWatcherRegion:: +get_suppress_below() const { + return _suppress_below; +} diff --git a/panda/src/tform/mouseWatcherRegion.cxx b/panda/src/tform/mouseWatcherRegion.cxx new file mode 100644 index 0000000000..adbb4a624f --- /dev/null +++ b/panda/src/tform/mouseWatcherRegion.cxx @@ -0,0 +1,32 @@ +// Filename: mouseWatcherRegion.cxx +// Created by: drose (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "mouseWatcherRegion.h" + +#include + + +TypeHandle MouseWatcherRegion::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherRegion::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void MouseWatcherRegion:: +output(ostream &out) const { + out << get_name() << " lrbt = " << _frame; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherRegion::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void MouseWatcherRegion:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << get_name() << " lrbt = " << _frame << "\n"; +} + diff --git a/panda/src/tform/mouseWatcherRegion.h b/panda/src/tform/mouseWatcherRegion.h new file mode 100644 index 0000000000..e6177b9acb --- /dev/null +++ b/panda/src/tform/mouseWatcherRegion.h @@ -0,0 +1,70 @@ +// Filename: mouseWatcherRegion.h +// Created by: drose (13Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef MOUSEWATCHERREGION_H +#define MOUSEWATCHERREGION_H + +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : MouseWatcherRegion +// Description : This is the class that defines a rectangular region +// on the screen for the MouseWatcher. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA MouseWatcherRegion : public ReferenceCount, public Namable { +public: + INLINE MouseWatcherRegion(const string &name, float left, float right, + float bottom, float top); + INLINE MouseWatcherRegion(const string &name, const LVecBase4f &frame); + + INLINE void set_frame(float left, float right, float bottom, float top); + INLINE void set_frame(const LVecBase4f &frame); + INLINE const LVecBase4f &get_frame() const; + INLINE float get_area() const; + + INLINE void set_active(bool active); + INLINE bool get_active() const; + + INLINE void set_suppress_below(bool suppress_below); + INLINE bool get_suppress_below() const; + + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + +private: + LVecBase4f _frame; + float _area; + + bool _active; + bool _suppress_below; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ReferenceCount::init_type(); + Namable::init_type(); + register_type(_type_handle, "MouseWatcherRegion", + ReferenceCount::get_class_type(), + Namable::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +INLINE ostream &operator << (ostream &out, const MouseWatcherRegion ®ion) { + region.output(out); + return out; +} + +#include "mouseWatcherRegion.I" + +#endif diff --git a/panda/src/tform/planarSlider.cxx b/panda/src/tform/planarSlider.cxx new file mode 100644 index 0000000000..ef15a0161e --- /dev/null +++ b/panda/src/tform/planarSlider.cxx @@ -0,0 +1,192 @@ +// Filename: planarSlider.cxx +// Created by: drose (10Jan00) +// +//////////////////////////////////////////////////////////////////// + +#include "planarSlider.h" + +#include +#include +#include +#include +#include +#include +#include + +TypeHandle PlanarSlider::_type_handle; + +TypeHandle PlanarSlider::_mods_type; +TypeHandle PlanarSlider::_xyz_type; +TypeHandle PlanarSlider::_transform_type; + + +//////////////////////////////////////////////////////////////////// +// Function: PlanarSlider::Constructor +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +PlanarSlider:: +PlanarSlider(const string &name) : DataNode(name) { + _cs = default_coordinate_system; + + _mouse_pos.set(0.0, 0.0); + _transform_mat = LMatrix4f::ident_mat(); + + _transform = new MatrixDataAttribute; + _attrib.set_attribute(_transform_type, _transform); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: PlanarSlider::Destructor +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +PlanarSlider:: +~PlanarSlider() { +} + + + + +//////////////////////////////////////////////////////////////////// +// Function: PlanarSlider::set_coordinate_system +// Access: Public, Scheme +// Description: Sets the coordinate system of the PlanarSlider. +// Normally, this is the default coordinate system. +// This changes the definition of the ground plane. +//////////////////////////////////////////////////////////////////// +void PlanarSlider:: +set_coordinate_system(CoordinateSystem cs) { + _cs = cs; +} + +//////////////////////////////////////////////////////////////////// +// Function: PlanarSlider::get_coordinate_system +// Access: Public, Scheme +// Description: Returns the coordinate system of the PlanarSlider. +// See set_coordinate_system(). +//////////////////////////////////////////////////////////////////// +CoordinateSystem PlanarSlider:: +get_coordinate_system() const { + return _cs; +} + +//////////////////////////////////////////////////////////////////// +// Function: PlanarSlider::set_transform +// Access: Public, Scheme +// Description: Defines the plane in which the TForm operates by +// specifying a transform. By default (with an identity +// transform), the plane is the ground plane, at the +// scale -1 to 1 around the origin. This function may +// specify a rotate, translate, or scale (or any +// arbitrary transform) to reposition the plane. +//////////////////////////////////////////////////////////////////// +void PlanarSlider:: +set_transform(const LMatrix4f &trans) { + _transform_mat = trans; + _inverse_mat = invert(trans); + _inverse_mat.set_row(3, LVector3f(0.0, 0.0, 0.0)); + set_mouse_pos(_mouse_pos); +} + +//////////////////////////////////////////////////////////////////// +// Function: PlanarSlider::get_transform +// Access: Public, Scheme +// Description: Returns the matrix that transforms the plane in which +// the TForm operates. See set_transform(). +//////////////////////////////////////////////////////////////////// +LMatrix4f PlanarSlider:: +get_transform() const { + return _transform_mat; +} + +//////////////////////////////////////////////////////////////////// +// Function: PlanarSlider::set_mouse_pos +// Access: Public, Scheme +// Description: Directly sets the reported position of the mouse. In +// general, this function should not be used unless the +// data graph is disconnected for some reason; normally, +// the PlanarSlider reads the reported mouse position +// directly from the data graph. The mouse position +// ranges from -1 to 1 in both axes. +//////////////////////////////////////////////////////////////////// +void PlanarSlider:: +set_mouse_pos(const LPoint2f &mouse_pos) { + _mouse_pos.set(max(min(mouse_pos[0], 1.0f), -1.0f), + max(min(mouse_pos[1], 1.0f), -1.0f)); + + LMatrix4f mat = + _inverse_mat * + LMatrix4f::translate_mat(LVector3f::rfu(_mouse_pos[0], + _mouse_pos[1], + 0.0, + _cs)) * + _transform_mat; + + _transform->set_value(mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: PlanarSlider::get_mouse_pos +// Access: Public, Scheme +// Description: Returns the last known position of the mouse, in the +// range -1 to 1 in both axes. +//////////////////////////////////////////////////////////////////// +LPoint2f PlanarSlider:: +get_mouse_pos() const { + return _mouse_pos; +} + +//////////////////////////////////////////////////////////////////// +// Function: PlanarSlider::transmit_data +// Access: Public +// Description: Convert mouse data into a planarSlider matrix +//////////////////////////////////////////////////////////////////// +void PlanarSlider:: +transmit_data(NodeAttributes &data) { + const NodeAttribute *button = data.get_attribute(_mods_type); + const NodeAttribute *xyz = data.get_attribute(_xyz_type); + + if (xyz != (NodeAttribute *)NULL) { + bool is_down = false; + + if (button != (NodeAttribute *)NULL) { + ModifierButtons mods = + DCAST(ModifierButtonDataAttribute, button)->get_mods(); + is_down = mods.is_down(MouseButton::one()); + } + + if (is_down) { + LVecBase3f p = DCAST(Vec3DataAttribute, xyz)->get_value(); + set_mouse_pos(LPoint2f(p[0], p[1])); + } + } + + // Now send our matrix down the pipe. + data = _attrib; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PlanarSlider::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +void PlanarSlider:: +init_type() { + DataNode::init_type(); + register_type(_type_handle, "PlanarSlider", + DataNode::get_class_type()); + + ModifierButtonDataTransition::init_type(); + register_data_transition(_mods_type, "ModifierButtons", + ModifierButtonDataTransition::get_class_type()); + Vec3DataTransition::init_type(); + register_data_transition(_xyz_type, "XYZ", + Vec3DataTransition::get_class_type()); + MatrixDataTransition::init_type(); + register_data_transition(_transform_type, "Transform", + MatrixDataTransition::get_class_type()); +} diff --git a/panda/src/tform/planarSlider.h b/panda/src/tform/planarSlider.h new file mode 100644 index 0000000000..caa5fe382a --- /dev/null +++ b/panda/src/tform/planarSlider.h @@ -0,0 +1,83 @@ +// Filename: planarSlider.h +// Created by: drose (10Jan00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PLANARSLIDER_H +#define PLANARSLIDER_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +//////////////////////////////////////////////////////////////////// +// Class : PlanarSlider +// Description : A PlanarSlider TFormer can be connected to a DCS. It +// accepts mouse input from the data graph and uses it +// to slide the DCS around within a specified plane. By +// default, this is the ground plane in the range -1,1. +// This can be scaled or rotated using the +// set_transform() method. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA PlanarSlider : public DataNode { +public: + + PlanarSlider(const string &name = ""); + ~PlanarSlider(); + + void set_coordinate_system(CoordinateSystem cs); + CoordinateSystem get_coordinate_system() const; + + void set_transform(const LMatrix4f &trans); + LMatrix4f get_transform() const; + + void set_mouse_pos(const LPoint2f &mouse_pos); + LPoint2f get_mouse_pos() const; + +private: + CoordinateSystem _cs; + LMatrix4f _transform_mat, _inverse_mat; + LPoint2f _mouse_pos; + +//////////////////////////////////////////////////////////////////// +// From parent class DataNode +//////////////////////////////////////////////////////////////////// +public: + + virtual void + transmit_data(NodeAttributes &data); + + NodeAttributes _attrib; + PT(MatrixDataAttribute) _transform; + + // inputs + static TypeHandle _mods_type; + static TypeHandle _xyz_type; + + // outputs + static TypeHandle _transform_type; + + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/tform/trackball.cxx b/panda/src/tform/trackball.cxx new file mode 100644 index 0000000000..ae99184c51 --- /dev/null +++ b/panda/src/tform/trackball.cxx @@ -0,0 +1,533 @@ +// Filename: trackball.cxx +// Created by: drose (27Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "trackball.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TypeHandle Trackball::_type_handle; + +TypeHandle Trackball::_mods_type; +TypeHandle Trackball::_pixel_xyz_type; +TypeHandle Trackball::_transform_type; + +#define B1_MASK 1 +#define B2_MASK 2 +#define B3_MASK 4 + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::Constructor +// Access: Public, Scheme +// Description: Trackball uses the mouse input to spin around a DCS +// similarly to Performer's trackball mode. +//////////////////////////////////////////////////////////////////// +Trackball:: +Trackball(const string &name) : DataNode(name) { + _rotscale = 0.3; + _fwdscale = 0.3; + + _lastx = _lasty = 0.5; + + _rotation = LMatrix4f::ident_mat(); + _translation.set(0.0, 0.0, 0.0); + _mat = LMatrix4f::ident_mat(); + _orig = LMatrix4f::ident_mat(); + _invert = true; + _rel_to = NULL; + _cs = default_coordinate_system; + + _transform = new MatrixDataAttribute; + _transform->set_value(LMatrix4f::ident_mat()); + _attrib.set_attribute(_transform_type, _transform); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::Destructor +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +Trackball:: +~Trackball() { +} + + + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::reset +// Access: Public, Scheme +// Description: Reinitializes all transforms to identity. +//////////////////////////////////////////////////////////////////// +void Trackball:: +reset() { + _rotation = LMatrix4f::ident_mat(); + _translation.set(0.0, 0.0, 0.0); + _orig = LMatrix4f::ident_mat(); + _mat = LMatrix4f::ident_mat(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::get_pos +// Access: Public, Scheme +// Description: Return the offset from the center of rotation. +//////////////////////////////////////////////////////////////////// +const LPoint3f &Trackball:: +get_pos() const { + return _translation; +} + +float Trackball:: +get_x() const { + return _translation[0]; +} + +float Trackball:: +get_y() const { + return _translation[1]; +} + +float Trackball:: +get_z() const { + return _translation[2]; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::set_pos +// Access: Public, Scheme +// Description: Directly set the offset from the rotational origin. +//////////////////////////////////////////////////////////////////// +void Trackball:: +set_pos(const LVecBase3f &vec) { + _translation = vec; + recompute(); +} + +void Trackball:: +set_pos(float x, float y, float z) { + _translation.set(x, y, z); + recompute(); +} + +void Trackball:: +set_x(float x) { + _translation[0] = x; + recompute(); +} + +void Trackball:: +set_y(float y) { + _translation[1] = y; + recompute(); +} + +void Trackball:: +set_z(float z) { + _translation[2] = z; + recompute(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::get_hpr +// Access: Public, Scheme +// Description: Return the trackball's orientation. +//////////////////////////////////////////////////////////////////// +LVecBase3f Trackball:: +get_hpr() const { + LVecBase3f scale, hpr, translate; + decompose_matrix(_rotation, scale, hpr, translate); + return hpr; +} + +float Trackball:: +get_h() const { + LVecBase3f scale, hpr, translate; + decompose_matrix(_rotation, scale, hpr, translate); + return hpr[0]; +} + +float Trackball:: +get_p() const { + LVecBase3f scale, hpr, translate; + decompose_matrix(_rotation, scale, hpr, translate); + return hpr[1]; +} + +float Trackball:: +get_r() const { + LVecBase3f scale, hpr, translate; + decompose_matrix(_rotation, scale, hpr, translate); + return hpr[2]; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::set_hpr +// Access: Public, Scheme +// Description: Directly set the mover's orientation. +//////////////////////////////////////////////////////////////////// +void Trackball:: +set_hpr(const LVecBase3f &hpr) { + LVecBase3f scale, old_hpr, translate; + decompose_matrix(_rotation, scale, old_hpr, translate); + compose_matrix(_rotation, scale, hpr, translate); + recompute(); +} + +void Trackball:: +set_hpr(float h, float p, float r) { + LVecBase3f scale, hpr, translate; + decompose_matrix(_rotation, scale, hpr, translate); + hpr.set(h, p, r); + compose_matrix(_rotation, scale, hpr, translate); + recompute(); +} + +void Trackball:: +set_h(float h) { + LVecBase3f scale, hpr, translate; + decompose_matrix(_rotation, scale, hpr, translate); + hpr[0] = h; + compose_matrix(_rotation, scale, hpr, translate); + recompute(); +} + +void Trackball:: +set_p(float p) { + LVecBase3f scale, hpr, translate; + decompose_matrix(_rotation, scale, hpr, translate); + hpr[1] = p; + compose_matrix(_rotation, scale, hpr, translate); + recompute(); +} + +void Trackball:: +set_r(float r) { + LVecBase3f scale, hpr, translate; + decompose_matrix(_rotation, scale, hpr, translate); + hpr[2] = r; + compose_matrix(_rotation, scale, hpr, translate); + recompute(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::reset_origin_here +// Access: Public, Scheme +// Description: Reposition the center of rotation to coincide with +// the current translation offset. Future rotations +// will be about the current origin. +//////////////////////////////////////////////////////////////////// +void Trackball:: +reset_origin_here() { + recompute(); + _rotation = _orig; + _translation.set(0.0, 0.0, 0.0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::move_origin +// Access: Public, Scheme +// Description: Moves the center of rotation by the given amount. +//////////////////////////////////////////////////////////////////// +void Trackball:: +move_origin(float x, float y, float z) { + _rotation = LMatrix4f::translate_mat(LVecBase3f(x, y, z)) * _rotation; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::set_invert +// Access: Public, Scheme +// Description: Sets the invert flag. When this is set, the inverse +// matrix is generated, suitable for joining to a +// camera, instead of parenting the scene under it. +//////////////////////////////////////////////////////////////////// +void Trackball:: +set_invert(bool flag) { + _invert = flag; +} + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::get_invert +// Access: Public, Scheme +// Description: Returns the invert flag. When this is set, the +// inverse matrix is generated, suitable for joining to +// a camera, instead of parenting the scene under it. +//////////////////////////////////////////////////////////////////// +bool Trackball:: +get_invert() const { + return _invert; +} + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::set_rel_to +// Access: Public, Scheme +// Description: Sets the node that all trackball manipulations are to +// be assumed to be relative to. For instance, set your +// camera node here to make the trackball motion camera +// relative. The default is NULL, which means trackball +// motion is in global space. +//////////////////////////////////////////////////////////////////// +void Trackball:: +set_rel_to(const Node *rel_to) { + _rel_to = rel_to; +} + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::get_rel_to +// Access: Public, Scheme +// Description: Returns the node that all trackball manipulations are +// relative to, or NULL. +//////////////////////////////////////////////////////////////////// +const Node *Trackball:: +get_rel_to() const { + return _rel_to; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::set_coordinate_system +// Access: Public, Scheme +// Description: Sets the coordinate system of the Trackball. +// Normally, this is the default coordinate system. +// This changes the axes the Trackball manipulates so +// that the user interface remains consistent across +// different coordinate systems. +//////////////////////////////////////////////////////////////////// +void Trackball:: +set_coordinate_system(CoordinateSystem cs) { + _cs = cs; +} + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::get_coordinate_system +// Access: Public, Scheme +// Description: Returns the coordinate system of the Trackball. +// See set_coordinate_system(). +//////////////////////////////////////////////////////////////////// +CoordinateSystem Trackball:: +get_coordinate_system() const { + return _cs; +} + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::set_mat +// Access: Public, Scheme +// Description: Stores the indicated transform in the trackball. +// This is a transform in global space, regardless of +// the rel_to node. +//////////////////////////////////////////////////////////////////// +void Trackball:: +set_mat(const LMatrix4f &mat) { + _orig = mat; + if (_invert) { + _mat = invert(_orig); + } else { + _mat = _orig; + } + + reextract(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::get_mat +// Access: Public, Scheme +// Description: Returns the matrix represented by the trackball +// rotation. +//////////////////////////////////////////////////////////////////// +const LMatrix4f &Trackball:: +get_mat() const { + return _orig; +} + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::get_trans_mat +// Access: Public, Scheme +// Description: Returns the actual transform that will be applied to +// the scene graph. This is the same as get_mat(), +// unless invert is in effect. +//////////////////////////////////////////////////////////////////// +const LMatrix4f &Trackball:: +get_trans_mat() const { + return _mat; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::apply +// Access: Private +// Description: Applies the operation indicated by the user's mouse +// motion to the current state. Returns the matrix +// indicating the new state. +//////////////////////////////////////////////////////////////////// +void Trackball:: +apply(double x, double y, int button) { + if (button && _rel_to != NULL) { + // If we have a rel_to node, we must first adjust our rotation and + // translation to be in those local coordinates. + reextract(); + } + if (button == B1_MASK) { + // Button 1: translate in plane parallel to screen. + + _translation += + x * _fwdscale * LVector3f::right(_cs) + + y * _fwdscale * LVector3f::down(_cs); + + } else if (button == (B2_MASK | B3_MASK)) { + // Buttons 2 + 3: rotate about the vector perpendicular to the + // screen. + + _rotation *= + LMatrix4f::rotate_mat((x - y) * _rotscale, + LVector3f::forward(_cs), _cs); + + } else if ((button == B2_MASK) || (button == (B1_MASK | B3_MASK))) { + // Button 2, or buttons 1 + 3: rotate about the right and up + // vectors. (We alternately define this as buttons 1 + 3, to + // support two-button mice.) + + _rotation *= + LMatrix4f::rotate_mat(x * _rotscale, LVector3f::up(_cs), _cs) * + LMatrix4f::rotate_mat(y * _rotscale, LVector3f::right(_cs), _cs); + + } else if ((button == B3_MASK) || (button == (B1_MASK | B2_MASK))) { + // Button 3, or buttons 1 + 2: dolly in and out along the forward + // vector. (We alternately define this as buttons 1 + 2, to + // support two-button mice.) + _translation -= y * _fwdscale * LVector3f::forward(_cs); + } + + if (button) { + recompute(); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::reextract +// Access: Private +// Description: Given a correctly computed _orig matrix, rederive the +// translation and rotation elements. +//////////////////////////////////////////////////////////////////// +void Trackball:: +reextract() { + LMatrix4f m = _orig; + if (_rel_to != NULL) { + LMatrix4f rel_mat; + ::get_rel_mat(NULL, _rel_to, rel_mat); + m = _orig * rel_mat; + } + + _translation = m.get_row3(3); + _rotation = m; + _rotation.set_row(3, LVecBase3f(0.0, 0.0, 0.0)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::recompute +// Access: Private +// Description: Rebuilds the matrix according to the stored rotation +// and translation components. +//////////////////////////////////////////////////////////////////// +void Trackball:: +recompute() { + _orig = _rotation * LMatrix4f::translate_mat(_translation); + + if (_rel_to != NULL) { + LMatrix4f rel_mat; + ::get_rel_mat(_rel_to, NULL, rel_mat); + _orig = _orig * rel_mat; + } + + if (_invert) { + _mat = invert(_orig); + } else { + _mat = _orig; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::transmit_data +// Access: Public +// Description: Convert mouse data into a trackball matrix +//////////////////////////////////////////////////////////////////// +void Trackball:: +transmit_data(NodeAttributes &data) { + const NodeAttribute *button = data.get_attribute(_mods_type); + const NodeAttribute *pixel_xyz = data.get_attribute(_pixel_xyz_type); + + if (pixel_xyz != (NodeAttribute *)NULL) { + LVecBase3f p = DCAST(Vec3DataAttribute, pixel_xyz)->get_value(); + float this_x = p[0]; + float this_y = p[1]; + int this_button = 0; + + if (button != (NodeAttribute *)NULL) { + ModifierButtons mods = + DCAST(ModifierButtonDataAttribute, button)->get_mods(); + + if (mods.is_down(MouseButton::one())) { + this_button |= B1_MASK; + } + if (mods.is_down(MouseButton::two())) { + this_button |= B2_MASK; + } + if (mods.is_down(MouseButton::three())) { + this_button |= B3_MASK; + } + } + + float x = this_x - _lastx; + float y = this_y - _lasty; + + apply(x, y, this_button); + + _lastx = this_x; + _lasty = this_y; + } + + // Now send our matrix down the pipe. + _transform->set_value(_mat); + data = _attrib; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Trackball::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +void Trackball:: +init_type() { + DataNode::init_type(); + register_type(_type_handle, "Trackball", + DataNode::get_class_type()); + + ModifierButtonDataTransition::init_type(); + register_data_transition(_mods_type, "ModifierButtons", + ModifierButtonDataTransition::get_class_type()); + Vec3DataTransition::init_type(); + register_data_transition(_pixel_xyz_type, "PixelXYZ", + Vec3DataTransition::get_class_type()); + MatrixDataTransition::init_type(); + register_data_transition(_transform_type, "Transform", + MatrixDataTransition::get_class_type()); +} diff --git a/panda/src/tform/trackball.h b/panda/src/tform/trackball.h new file mode 100644 index 0000000000..244f93b178 --- /dev/null +++ b/panda/src/tform/trackball.h @@ -0,0 +1,136 @@ +// Filename: trackball.h +// Created by: drose (27Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TRACKBALL_H +#define TRACKBALL_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +//////////////////////////////////////////////////////////////////// +// Class : Trackball +// Description : Trackball acts like Performer in trackball mode. If +// you connect it to a DCS, either over your entire +// scene graph or over some portion of it, Trackball +// will spin the geometry under the DCS for viewing at +// all angles. The center of rotation is initially the +// origin. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Trackball : public DataNode { +public: + + Trackball(const string &name = ""); + ~Trackball(); + + void reset(); + + /// **** Translation **** + + const LPoint3f &get_pos() const; + float get_x() const; + float get_y() const; + float get_z() const; + void set_pos(const LVecBase3f &vec); + void set_pos(float x, float y, float z); + void set_x(float x); + void set_y(float y); + void set_z(float z); + + /// **** Rotation **** + + LVecBase3f get_hpr() const; + float get_h() const; + float get_p() const; + float get_r() const; + void set_hpr(const LVecBase3f &hpr); + void set_hpr(float h, float p, float r); + void set_h(float h); + void set_p(float p); + void set_r(float r); + + /// **** Origin of Rotation **** + + void reset_origin_here(); + void move_origin(float x, float y, float z); + + /// **** Misc **** + + void set_invert(bool flag); + bool get_invert() const; + + void set_rel_to(const Node *_rel_to); + const Node *get_rel_to() const; + + void set_coordinate_system(CoordinateSystem cs); + CoordinateSystem get_coordinate_system() const; + + void set_mat(const LMatrix4f &mat); + const LMatrix4f &get_mat() const; + const LMatrix4f &get_trans_mat() const; + + +private: + void apply(double x, double y, int button); + + void reextract(); + void recompute(); + + + float _lastx, _lasty; + + float _rotscale; + float _fwdscale; + + LMatrix4f _rotation; + LPoint3f _translation; + LMatrix4f _mat, _orig; + bool _invert; + const Node *_rel_to; + CoordinateSystem _cs; + + +//////////////////////////////////////////////////////////////////// +// From parent class DataNode +//////////////////////////////////////////////////////////////////// +public: + + virtual void + transmit_data(NodeAttributes &data); + + NodeAttributes _attrib; + PT(MatrixDataAttribute) _transform; + + // inputs + static TypeHandle _mods_type; + static TypeHandle _pixel_xyz_type; + + // outputs + static TypeHandle _transform_type; + + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/tform/trackerTransform.cxx b/panda/src/tform/trackerTransform.cxx new file mode 100644 index 0000000000..a1d38feee5 --- /dev/null +++ b/panda/src/tform/trackerTransform.cxx @@ -0,0 +1,82 @@ +// Filename: trackerTransform.cxx +// Created by: jason (08Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include "trackerTransform.h" +#include "config_tform.h" + +TypeHandle TrackerTransform::_type_handle; + +TypeHandle TrackerTransform::_position_type; +TypeHandle TrackerTransform::_pquat_type; + +TypeHandle TrackerTransform::_transform_type; + +//////////////////////////////////////////////////////////////////// +// Function: TrackerTransform::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +TrackerTransform:: +TrackerTransform(const string &name) : DataNode(name) { + _transform = new MatrixDataAttribute; + _transform->set_value(LMatrix4f::ident_mat()); + _transform_attrib.set_attribute(_transform_type, _transform); +} + +//////////////////////////////////////////////////////////////////// +// Function: TrackerTransform::transmit_data +// Access: Public +// Description: Constructs a transformation matrix from tracker data +// and passes it down the line +//////////////////////////////////////////////////////////////////// +void TrackerTransform:: +transmit_data(NodeAttributes &data) { + const NodeAttribute *position = data.get_attribute(_position_type); + + if (tform_cat.is_debug()) { + tform_cat.debug() << "TrackerTransform:transmit_data" << endl; + } + if (position != (NodeAttribute *)NULL) { + LVecBase3f p = DCAST(Vec3DataAttribute, position)->get_value(); + + LMatrix4f mat = LMatrix4f::translate_mat(p); + if (tform_cat.is_debug()) { + tform_cat.debug() << "Sending down " << mat << endl; + } + _transform->set_value(mat); + } + + data = _transform_attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: TrackerTransform::init_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void TrackerTransform:: +init_type() { + DataNode::init_type(); + register_type(_type_handle, "TrackerTransform", + DataNode::get_class_type()); + + Vec3DataTransition::init_type(); + Vec4DataTransition::init_type(); + MatrixDataTransition::init_type(); + + register_data_transition(_position_type, "Position", + Vec3DataTransition::get_class_type()); + register_data_transition(_pquat_type, "Position Quat", + Vec4DataTransition::get_class_type()); + + register_data_transition(_transform_type, "Transform", + MatrixDataTransition::get_class_type()); +} + + + + diff --git a/panda/src/tform/trackerTransform.h b/panda/src/tform/trackerTransform.h new file mode 100644 index 0000000000..e588afc447 --- /dev/null +++ b/panda/src/tform/trackerTransform.h @@ -0,0 +1,67 @@ +// Filename: trackerTransform.h +// Created by: jason (08Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TRACKER_TRANSFORM_H +#define TRACKER_TRANSFORM_H + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +//////////////////////////////////////////////////////////////////// +// Class : TrackerTransform +// Description : TrackerTransform reads the data send down the line +// by a TrackerNode and creates a transformation matrix +// from the data and sends that matrix down the line +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA TrackerTransform : public DataNode { +public: + TrackerTransform(const string &name = ""); + + virtual void transmit_data(NodeAttributes &data); + +private: + NodeAttributes _transform_attrib; + PT(MatrixDataAttribute) _transform; + + // inputs + static TypeHandle _position_type; + //NOTE!!! + //Currently not being factored into the matrix. Needs to be done + static TypeHandle _pquat_type; + + // outputs + static TypeHandle _transform_type; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/tform/transform2sg.cxx b/panda/src/tform/transform2sg.cxx new file mode 100644 index 0000000000..ca9be3e576 --- /dev/null +++ b/panda/src/tform/transform2sg.cxx @@ -0,0 +1,85 @@ +// Filename: transform2sg.cxx +// Created by: drose (27Jan99) +// +//////////////////////////////////////////////////////////////////// + +#include "transform2sg.h" + +#include +#include +#include +#include +#include + + +TypeHandle Transform2SG::_type_handle; + +TypeHandle Transform2SG::_transform_type; + +//////////////////////////////////////////////////////////////////// +// Function: Transform2SG::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Transform2SG:: +Transform2SG(const string &name) : DataNode(name) { + _arc = NULL; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Transform2SG::set_arc +// Access: Public +// Description: Sets the arc that this node will adjust. +//////////////////////////////////////////////////////////////////// +void Transform2SG:: +set_arc(NodeRelation *arc) { + _arc = arc; +} + +//////////////////////////////////////////////////////////////////// +// Function: Transform2SG::get_arc +// Access: Public +// Description: Returns the arc that this node will adjust, or NULL +// if the arc has not yet been set. +//////////////////////////////////////////////////////////////////// +NodeRelation *Transform2SG:: +get_arc() const { + return _arc; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Transform2SG::transmit_data +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void Transform2SG:: +transmit_data(NodeAttributes &data) { + const NodeAttribute *transform = data.get_attribute(_transform_type); + + if (transform != (NodeAttribute *)NULL && _arc != (NodeRelation *)NULL) { + const LMatrix4f &mat = DCAST(MatrixDataAttribute, transform)->get_value(); + _arc->set_transition(new TransformTransition(mat)); + } + + // Clear the data below us. + data.clear(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Transform2SG::init_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +void Transform2SG:: +init_type() { + DataNode::init_type(); + register_type(_type_handle, "Transform2SG", + DataNode::get_class_type()); + + MatrixDataTransition::init_type(); + register_data_transition(_transform_type, "Transform", + MatrixDataTransition::get_class_type()); +} diff --git a/panda/src/tform/transform2sg.h b/panda/src/tform/transform2sg.h new file mode 100644 index 0000000000..24a2b6badf --- /dev/null +++ b/panda/src/tform/transform2sg.h @@ -0,0 +1,59 @@ +// Filename: transform2sg.h +// Created by: drose (27Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TRANSFORM2SG_H +#define TRANSFORM2SG_H + +#include + +#include + +class NodeRelation; + +//////////////////////////////////////////////////////////////////// +// Class : Transform2SG +// Description : input: Transform (matrix) +// +// output: none, but applies the matrix as the transform +// attribute for a given arc of the scene graph. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA Transform2SG : public DataNode { +public: + Transform2SG(const string &name = ""); + + void set_arc(NodeRelation *arc); + NodeRelation *get_arc() const; + +private: + NodeRelation *_arc; + +//////////////////////////////////////////////////////////////////// +// From parent class DataNode +//////////////////////////////////////////////////////////////////// +public: + + virtual void + transmit_data(NodeAttributes &data); + + // inputs + static TypeHandle _transform_type; + + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type(); + +private: + static TypeHandle _type_handle; +}; + +#endif + diff --git a/panda/src/tiff/README b/panda/src/tiff/README new file mode 100644 index 0000000000..5e8e62b03a --- /dev/null +++ b/panda/src/tiff/README @@ -0,0 +1,226 @@ +$Header$ + +Configuration Comments: +---------------------- +Aside from the compression algorithm support, there are +configuration-related defines that you can override in the Makefile +or in the default configuration file tiffconf.h: + +COLORIMETRY_SUPPORT + if this is defined, support for the colorimetry + tags will be compiled in. +JPEG_SUPPORT if this is defined, support for the JPEG-related + tags will be compiled in. Note that at the present + time the JPEG compression support is not included. +YCBCR_SUPPORT if this is defined, support for the YCbCr-related + tags will be compiled in. Note that you'll want + YCBCR support for JPEG compression+decompression. +CMYK_SUPPORT if this is defined, support for the CMYK-related + tags will be compiled in. +MMAP_SUPPORT if this is set, and OS support exists for memory + mapping files, then the library will try to map + a file if it is opened for reading. If mmap does + not exist on your system, or the mmap call fails + on the file, then the normal read system calls are + used. It is not clear how useful this facility is. + +By default tiffconf.h defines COLORIMETRY_SUPPORT, JPEG_SUPPORT, +YCBCR_SUPPORT, CMYK_SUPPORT. MMAP_SUPPORT is not defined. + +General Portability Comments: +---------------------------- +I run this code on SGI machines (big-endian, MIPS CPU, 32-bit ints, +IEEE floating point). Makefiles exist for other platforms that the +code runs on -- this work has mostly been done by other people. +The code runs on Macintosh and PC-based systems, although I don't +know all the particulars. + +In general, I promise only that the code runs on SGI machines. +I will, however, gladly take back fixes to make it work on other +systems -- when the changes are reasonable unobtrusive. + +The software is written to assume an ANSI C compilation environment. +If your compiler does not support ANSI function prototypes, const, +and then you will have to make modifications to the +software. In the past I have tried to support compilers w/o const +and systems w/o , but I am NO LONGER INTERESTED in these +antiquated environments. With the general availability of gcc, I +see no reason to incorporate modifications to the software for these +purposes. + +I've tried to isolate as many of the OS-dependencies as possible in +two files: tiffcomp.h and tif_.c. The latter file contains +OS-specific routines to do I/O and I/O-related operations. The +UNIX (tif_unix.c), Macintosh (tif_apple.c), and VMS (tif_vms.c) +code has had the most use; the MS/DOS support (tif_msdos.c) assumes +some level of UNIX system call emulation (i.e. open, read, write, +fstat, malloc, free). + +Machine dependencies such as byte order are determined on the +fly and do not need to be specified. + +Two general portability-related defines are: + +BSDTYPES Define this if your system does NOT define the + usual BSD typedefs: u_char, u_short, u_int, u_longs. +HAVE_IEEEFP Define this as 0 or 1 according to the floating point + format suported by the machine. If your machine does + not support IEEE floating point then you will need to + add support to tif_machdep.c to convert between the + native format and IEEE format. + +Note that tiffcomp.h defines HAVE_IEEEFP to be 1 (BSDTYPES is not defined). + +Types and Portability: +--------------------- +The software makes extensive use of typedefs to promote portability. +Two sets of typedefs are used, one for communication with clients +of the library and one for internal data structures and parsing of the +TIFF format. There are interactions between these two to be careful +of, but for the most part you should be able to deal with portability +purely by fiddling with the following machine-dependent typedefs: + +uint16 16-bit unsigned integer tiff.h +int16 16-bit signed integer tiff.h +uint32 32-bit unsigned integer tiff.h +int32 32-bit signed integer tiff.h +dblparam_t promoted type for floats tiffcomp.h + +(to clarify dblparam_t, it is the type that float parameters are +promoted to when passed by value in a function call.) + +The following typedefs are used throughout the library and interfaces +to refer to certain objects whose size is dependent on the TIFF image +structure: + +typedef unsigned int ttag_t; directory tag +typedef uint16 tdir_t; directory index +typedef uint16 tsample_t; sample number +typedef uint32 tstrip_t; strip number +typedef uint32 ttile_t; tile number +typedef int32 tsize_t; i/o size in bytes +typedef void* tdata_t; image data ref +typedef void* thandle_t; client data handle +typedef int32 toff_t; file offset (should be off_t) +typedef unsigned char* tidata_t;internal image data + +Note that tstrip_t, ttile_t, and tsize_t are constrained to be +no more than 32-bit quantities by 32-bit fields they are stored +in in the TIFF image. Likewise tsample_t is limited by the 16-bit +field used to store the SamplesPerPixel tag. tdir_t constrains +the maximum number of IFDs that may appear in an image and may +be an arbitrary size (w/o penalty). ttag_t must be either int, +unsigned int, pointer, or double because the library uses a varargs +interface and ANSI restricts the type of the parameter before an +ellipsis to be a promoted type. toff_t is defined as int32 because +TIFF file offsets are (unsigned) 32-bit quantities. A signed +value is used because some interfaces return -1 on error (sigh). +Finally, note that tidata_t is used internally to the library to +manipulate internal data. User-specified data references are +passed as opaque handles and only cast at the lowest layers where +their type is presumed. + +General Comments: +---------------- +The library is designed to hide as much of the details of TIFF as +possible. In particular, TIFF directories are read in their entirety +into an internal format. Only the tags known by the library are +available to a user and certain tag data may be maintained that a user +doesn't care about (e.g. transfer function tables). + +To add support for a new directory tag the following mods are needed: + +1. Define the tag in tiff.h. +2. Add a field to the directory structure in tiffiop.h and define a + FIELD_* bit. +3. Add an entry in the FieldInfo array defined at the top of tiff_dirinfo.c. +4. Add entries in TIFFSetField1() and TIFFGetField1() for the new tag. +5. (optional) If the value associated with the tag is not a scalar value + (e.g. the array for TransferFunction), then add the appropriate + code to TIFFReadDirectory() and TIFFWriteDirectory(). You're best + off finding a similar tag and cribbing code. +6. Add support to TIFFPrintDirectory() in tiff_print.c to print the + tag's value. + +If you want to maintain portability, beware of making assumptions +about data types. Use the typedefs (uint16, etc. when dealing with +data on disk and t*_t when stuff is in memory) and be careful about +passing items through printf or similar vararg interfaces. + +To add support for a compression algorithm: + +1. Define the tag value in tiff.h. +2. Edit the file tiff_compress.c to add an entry to the + CompressionSchemes[] array. +3. Create a file with the compression scheme code, by convention files + are named tif_*.c (except perhaps on some systems where the tif_ prefix + pushes some filenames over 14 chars. +4. Edit the Makefiles to include the new source file. + +A compression scheme, say foo, can have up to 10 entry points: + +TIFFfoo(tif) /* initialize scheme and setup entry points in tif */ +fooPreDecode(tif) /* called once per strip, after data is read, + but before the first row in a strip is decoded */ +fooDecode*(tif, bp, cc, sample)/* decode cc bytes of data into the buffer */ + fooDecodeRow(...) /* called to decode a single scanline */ + fooDecodeStrip(...) /* called to decode an entire strip */ + fooDecodeTile(...) /* called to decode an entire tile */ +fooPreEncode(tif) /* called once per strip/tile, before the first row in + a strip is encoded */ +fooEncode*(tif, bp, cc, sample)/* encode cc bytes of user data (bp) */ + fooEncodeRow(...) /* called to decode a single scanline */ + fooEncodeStrip(...) /* called to decode an entire strip */ + fooEncodeTile(...) /* called to decode an entire tile */ +fooPostEncode(tif) /* called once per strip/tile, just before data is written */ +fooSeek(tif, row) /* seek forwards row scanlines from the beginning + of a strip (row will always be >0 and = terminate uncompress mode */ +#define UNCOMP_EXIT UNCOMP_TRUN0 + +#ifdef G3STATES +const u_char TIFFFax2DMode[20][256] = {{ +12,12,11,0,3,3,9,9,4,4,4,4,8,8,8,8,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6},{ +12,0,3,9,4,4,8,8,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,12,0,3,9,4,4,8,8,1,1,1,1,1,1,1,1 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6},{ +0,0,4,8,1,1,1,1,2,2,2,2,2,2,2,2,5,5,5,5,5,5,5,5 +,7,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,0,0,4,8,1,1,1,1 +,2,2,2,2,2,2,2,2,5,5,5,5,5,5,5,5,7,7,7,7,7,7,7,7 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,0,0,4,8,1,1,1,1,2,2,2,2,2,2,2,2 +,5,5,5,5,5,5,5,5,7,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,0,0,4,8,1,1,1,1,2,2,2,2,2,2,2,2,5,5,5,5,5,5,5,5 +,7,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6},{ +0,0,1,1,2,2,2,2,5,5,5,5,7,7,7,7,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,0,0,1,1,2,2,2,2,5,5,5,5,7,7,7,7 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,0,0,1,1,2,2,2,2 +,5,5,5,5,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,0,0,1,1,2,2,2,2,5,5,5,5,7,7,7,7,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,0,0,1,1,2,2,2,2,5,5,5,5,7,7,7,7 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,0,0,1,1,2,2,2,2 +,5,5,5,5,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,0,0,1,1,2,2,2,2,5,5,5,5,7,7,7,7,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,0,0,1,1,2,2,2,2,5,5,5,5,7,7,7,7 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6},{ +0,1,2,2,5,5,7,7,6,6,6,6,6,6,6,6,0,1,2,2,5,5,7,7 +,6,6,6,6,6,6,6,6,0,1,2,2,5,5,7,7,6,6,6,6,6,6,6,6 +,0,1,2,2,5,5,7,7,6,6,6,6,6,6,6,6,0,1,2,2,5,5,7,7 +,6,6,6,6,6,6,6,6,0,1,2,2,5,5,7,7,6,6,6,6,6,6,6,6 +,0,1,2,2,5,5,7,7,6,6,6,6,6,6,6,6,0,1,2,2,5,5,7,7 +,6,6,6,6,6,6,6,6,0,1,2,2,5,5,7,7,6,6,6,6,6,6,6,6 +,0,1,2,2,5,5,7,7,6,6,6,6,6,6,6,6,0,1,2,2,5,5,7,7 +,6,6,6,6,6,6,6,6,0,1,2,2,5,5,7,7,6,6,6,6,6,6,6,6 +,0,1,2,2,5,5,7,7,6,6,6,6,6,6,6,6,0,1,2,2,5,5,7,7 +,6,6,6,6,6,6,6,6,0,1,2,2,5,5,7,7,6,6,6,6,6,6,6,6 +,0,1,2,2,5,5,7,7,6,6,6,6,6,6,6,6},{ +0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6 +,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6 +,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6 +,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6 +,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6 +,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6 +,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6 +,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6 +,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6 +,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6 +,0,2,5,7,6,6,6,6,0,2,5,7,6,6,6,6},{ +0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6 +,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6 +,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6 +,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6 +,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6 +,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6 +,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6 +,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6 +,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6 +,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6 +,0,0,6,6,0,0,6,6,0,0,6,6,0,0,6,6},{ +0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6 +,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6 +,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6 +,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6 +,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6 +,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6 +,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6 +,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6 +,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6 +,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6 +,0,6,0,6,0,6,0,6,0,6,0,6,0,6,0,6},{ +11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10},{ +11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10},{ +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9},{ +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,10,10,10,10,10,10,10,10,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9},{ +4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8},{ +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,10,10,10,10,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8},{ +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,10,10,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +12,12,12,12,12,12,12,12,11,11,11,11,11,11,11,10,3,3,3,3,3,3,3,3 +,9,9,9,9,9,9,9,9,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7},{ +12,12,12,12,11,11,11,0,3,3,3,3,9,9,9,9,4,4,4,4,4,4,4,4 +,8,8,8,8,8,8,8,8,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7},{ +11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10} +}; +const u_char TIFFFax2DNextState[20][256] = {{ +7,7,0,8,7,7,7,7,6,6,6,6,6,6,6,6,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +0,9,0,0,7,7,7,7,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,0,9,0,0,7,7,7,7,5,5,5,5,5,5,5,5 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +10,11,0,0,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,10,11,0,0,6,6,6,6 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,10,11,0,0,6,6,6,6,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,10,11,0,0,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +12,13,7,7,6,6,6,6,6,6,6,6,6,6,6,6,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,12,13,7,7,6,6,6,6,6,6,6,6,6,6,6,6 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,12,13,7,7,6,6,6,6 +,6,6,6,6,6,6,6,6,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,12,13,7,7,6,6,6,6,6,6,6,6,6,6,6,6,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,12,13,7,7,6,6,6,6,6,6,6,6,6,6,6,6 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,12,13,7,7,6,6,6,6 +,6,6,6,6,6,6,6,6,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,12,13,7,7,6,6,6,6,6,6,6,6,6,6,6,6,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,12,13,7,7,6,6,6,6,6,6,6,6,6,6,6,6 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4},{ +14,0,7,7,7,7,7,7,5,5,5,5,5,5,5,5,14,0,7,7,7,7,7,7 +,5,5,5,5,5,5,5,5,14,0,7,7,7,7,7,7,5,5,5,5,5,5,5,5 +,14,0,7,7,7,7,7,7,5,5,5,5,5,5,5,5,14,0,7,7,7,7,7,7 +,5,5,5,5,5,5,5,5,14,0,7,7,7,7,7,7,5,5,5,5,5,5,5,5 +,14,0,7,7,7,7,7,7,5,5,5,5,5,5,5,5,14,0,7,7,7,7,7,7 +,5,5,5,5,5,5,5,5,14,0,7,7,7,7,7,7,5,5,5,5,5,5,5,5 +,14,0,7,7,7,7,7,7,5,5,5,5,5,5,5,5,14,0,7,7,7,7,7,7 +,5,5,5,5,5,5,5,5,14,0,7,7,7,7,7,7,5,5,5,5,5,5,5,5 +,14,0,7,7,7,7,7,7,5,5,5,5,5,5,5,5,14,0,7,7,7,7,7,7 +,5,5,5,5,5,5,5,5,14,0,7,7,7,7,7,7,5,5,5,5,5,5,5,5 +,14,0,7,7,7,7,7,7,5,5,5,5,5,5,5,5},{ +15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6 +,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6 +,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6 +,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6 +,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6 +,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6 +,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6 +,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6 +,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6 +,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6 +,15,0,0,0,6,6,6,6,15,0,0,0,6,6,6,6},{ +16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7 +,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7 +,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7 +,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7 +,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7 +,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7 +,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7 +,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7 +,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7 +,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7 +,16,17,7,7,16,17,7,7,16,17,7,7,16,17,7,7},{ +18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0 +,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0 +,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0 +,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0 +,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0 +,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0 +,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0 +,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0 +,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0 +,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18,0 +,18,0,18,0,18,0,18,0,18,0,18,0,18,0,18},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5 +,6,6,6,6,7,7,7,7,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +5,5,5,5,5,5,5,5,6,6,6,6,7,7,0,0,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +6,6,6,6,7,7,0,19,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} +}; +const u_char TIFFFaxUncompAction[20][256] = {{ +0,9,8,8,7,7,7,7,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +0,8,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,0,8,7,7,6,6,6,6,5,5,5,5,5,5,5,5 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +0,7,6,6,5,5,5,5,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,7,6,6,5,5,5,5 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,0,7,6,6,5,5,5,5,4,4,4,4,4,4,4,4 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,0,7,6,6,5,5,5,5,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +0,6,5,5,4,4,4,4,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,0,6,5,5,4,4,4,4,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,6,5,5,4,4,4,4 +,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,0,6,5,5,4,4,4,4,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,0,6,5,5,4,4,4,4,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,6,5,5,4,4,4,4 +,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,0,6,5,5,4,4,4,4,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,0,6,5,5,4,4,4,4,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +0,5,4,4,3,3,3,3,2,2,2,2,2,2,2,2,0,5,4,4,3,3,3,3 +,2,2,2,2,2,2,2,2,0,5,4,4,3,3,3,3,2,2,2,2,2,2,2,2 +,0,5,4,4,3,3,3,3,2,2,2,2,2,2,2,2,0,5,4,4,3,3,3,3 +,2,2,2,2,2,2,2,2,0,5,4,4,3,3,3,3,2,2,2,2,2,2,2,2 +,0,5,4,4,3,3,3,3,2,2,2,2,2,2,2,2,0,5,4,4,3,3,3,3 +,2,2,2,2,2,2,2,2,0,5,4,4,3,3,3,3,2,2,2,2,2,2,2,2 +,0,5,4,4,3,3,3,3,2,2,2,2,2,2,2,2,0,5,4,4,3,3,3,3 +,2,2,2,2,2,2,2,2,0,5,4,4,3,3,3,3,2,2,2,2,2,2,2,2 +,0,5,4,4,3,3,3,3,2,2,2,2,2,2,2,2,0,5,4,4,3,3,3,3 +,2,2,2,2,2,2,2,2,0,5,4,4,3,3,3,3,2,2,2,2,2,2,2,2 +,0,5,4,4,3,3,3,3,2,2,2,2,2,2,2,2},{ +0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2 +,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2 +,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2 +,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2 +,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2 +,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2 +,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2 +,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2 +,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2 +,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2 +,0,4,3,3,2,2,2,2,0,4,3,3,2,2,2,2},{ +0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2 +,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2 +,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2 +,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2 +,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2 +,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2 +,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2 +,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2 +,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2 +,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2 +,0,3,2,2,0,3,2,2,0,3,2,2,0,3,2,2},{ +0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2 +,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2 +,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2 +,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2 +,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2 +,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2 +,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2 +,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2 +,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2 +,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2 +,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2},{ +14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 +,14,14,14,14,14,14,14,14,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10},{ +14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9},{ +14,14,14,14,14,14,14,14,12,12,12,12,12,12,12,12,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8},{ +14,14,14,14,12,12,12,12,11,11,11,11,11,11,11,11,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7},{ +14,14,12,12,11,11,11,11,10,10,10,10,10,10,10,10,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6},{ +14,12,11,11,10,10,10,10,9,9,9,9,9,9,9,9,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5},{ +0,11,10,10,9,9,9,9,8,8,8,8,8,8,8,8,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4},{ +0,10,9,9,8,8,8,8,7,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 +,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 +,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 +,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 +,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 +,14,14,14,14,14,14,14,14,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12},{ +14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 +,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 +,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11},{ +0},{ +0} +}; +const u_char TIFFFaxUncompNextState[20][256] = {{ +8,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +9,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,9,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +10,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,10,0,7,7,6,6,6,6 +,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,10,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,10,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +11,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,11,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,11,0,7,7,6,6,6,6 +,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,11,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,11,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,11,0,7,7,6,6,6,6 +,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,11,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,11,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4},{ +12,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,12,0,7,7,6,6,6,6 +,5,5,5,5,5,5,5,5,12,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5 +,12,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,12,0,7,7,6,6,6,6 +,5,5,5,5,5,5,5,5,12,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5 +,12,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,12,0,7,7,6,6,6,6 +,5,5,5,5,5,5,5,5,12,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5 +,12,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,12,0,7,7,6,6,6,6 +,5,5,5,5,5,5,5,5,12,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5 +,12,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,12,0,7,7,6,6,6,6 +,5,5,5,5,5,5,5,5,12,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5 +,12,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5},{ +13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6 +,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6 +,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6 +,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6 +,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6 +,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6 +,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6 +,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6 +,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6 +,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6 +,13,0,7,7,6,6,6,6,13,0,7,7,6,6,6,6},{ +14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7 +,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7 +,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7 +,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7 +,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7 +,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7 +,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7 +,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7 +,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7 +,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7 +,14,0,7,7,14,0,7,7,14,0,7,7,14,0,7,7},{ +15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0 +,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0 +,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0 +,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0 +,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0 +,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0 +,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0 +,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0 +,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0 +,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15,0 +,15,0,15,0,15,0,15,0,15,0,15,0,15,0,15},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +7,7,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +0,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +16,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +17,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +0},{ +0} +}; +const u_char TIFFFax1DAction[230][256] = {{ +0,0,31,32,47,48,24,24,25,25,49,50,15,15,15,15,22,22,35,36,37,38,39,40 +,21,21,33,34,3,3,3,3,14,14,14,14,55,56,28,28,41,42,43,44,45,46,23,23 +,30,30,63,64,65,2,70,71,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13 +,29,29,61,62,0,0,20,20,26,26,51,52,53,54,27,27,57,58,59,60,68,68,68,68 +,91,91,91,91,72,73,0,75,74,0,0,0,0,0,69,69,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,67,67,67,67,67,67,67,67,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11 +,18,18,18,18,19,19,19,19,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,16,16,16,16,17,17,17,17 +,66,66,66,66,66,66,66,66,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9},{ +0,0,0,24,25,0,15,15,22,0,0,0,21,0,3,3,14,14,0,28,0,0,0,23 +,30,0,0,0,12,12,12,12,13,13,13,13,29,0,0,20,26,0,0,27,0,0,68,68 +,91,91,0,0,0,0,0,69,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5 +,67,67,67,67,10,10,10,10,11,11,11,11,18,18,19,19,6,6,6,6,6,6,6,6 +,7,7,7,7,7,7,7,7,16,16,17,17,66,66,66,66,8,8,8,8,8,8,8,8 +,9,9,9,9,9,9,9,9,0,0,0,24,25,0,15,15,22,0,0,0,21,0,3,3 +,14,14,0,28,0,0,0,23,30,0,0,0,12,12,12,12,13,13,13,13,29,0,0,20 +,26,0,0,27,0,0,68,68,91,91,0,0,0,0,0,69,4,4,4,4,4,4,4,4 +,5,5,5,5,5,5,5,5,67,67,67,67,10,10,10,10,11,11,11,11,18,18,19,19 +,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,16,16,17,17,66,66,66,66 +,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9},{ +0,0,0,15,0,0,0,3,14,0,0,0,0,0,12,12,13,13,0,0,0,0,0,68 +,91,0,0,0,4,4,4,4,5,5,5,5,67,67,10,10,11,11,18,19,6,6,6,6 +,7,7,7,7,16,17,66,66,8,8,8,8,9,9,9,9,0,0,0,15,0,0,0,3 +,14,0,0,0,0,0,12,12,13,13,0,0,0,0,0,68,91,0,0,0,4,4,4,4 +,5,5,5,5,67,67,10,10,11,11,18,19,6,6,6,6,7,7,7,7,16,17,66,66 +,8,8,8,8,9,9,9,9,0,0,0,15,0,0,0,3,14,0,0,0,0,0,12,12 +,13,13,0,0,0,0,0,68,91,0,0,0,4,4,4,4,5,5,5,5,67,67,10,10 +,11,11,18,19,6,6,6,6,7,7,7,7,16,17,66,66,8,8,8,8,9,9,9,9 +,0,0,0,15,0,0,0,3,14,0,0,0,0,0,12,12,13,13,0,0,0,0,0,68 +,91,0,0,0,4,4,4,4,5,5,5,5,67,67,10,10,11,11,18,19,6,6,6,6 +,7,7,7,7,16,17,66,66,8,8,8,8,9,9,9,9},{ +0,0,0,0,0,0,0,12,13,0,0,0,0,0,4,4,5,5,67,10,11,0,6,6 +,7,7,0,66,8,8,9,9,0,0,0,0,0,0,0,12,13,0,0,0,0,0,4,4 +,5,5,67,10,11,0,6,6,7,7,0,66,8,8,9,9,0,0,0,0,0,0,0,12 +,13,0,0,0,0,0,4,4,5,5,67,10,11,0,6,6,7,7,0,66,8,8,9,9 +,0,0,0,0,0,0,0,12,13,0,0,0,0,0,4,4,5,5,67,10,11,0,6,6 +,7,7,0,66,8,8,9,9,0,0,0,0,0,0,0,12,13,0,0,0,0,0,4,4 +,5,5,67,10,11,0,6,6,7,7,0,66,8,8,9,9,0,0,0,0,0,0,0,12 +,13,0,0,0,0,0,4,4,5,5,67,10,11,0,6,6,7,7,0,66,8,8,9,9 +,0,0,0,0,0,0,0,12,13,0,0,0,0,0,4,4,5,5,67,10,11,0,6,6 +,7,7,0,66,8,8,9,9,0,0,0,0,0,0,0,12,13,0,0,0,0,0,4,4 +,5,5,67,10,11,0,6,6,7,7,0,66,8,8,9,9},{ +0,0,0,0,0,0,0,4,5,0,0,6,7,0,8,9,0,0,0,0,0,0,0,4 +,5,0,0,6,7,0,8,9,0,0,0,0,0,0,0,4,5,0,0,6,7,0,8,9 +,0,0,0,0,0,0,0,4,5,0,0,6,7,0,8,9,0,0,0,0,0,0,0,4 +,5,0,0,6,7,0,8,9,0,0,0,0,0,0,0,4,5,0,0,6,7,0,8,9 +,0,0,0,0,0,0,0,4,5,0,0,6,7,0,8,9,0,0,0,0,0,0,0,4 +,5,0,0,6,7,0,8,9,0,0,0,0,0,0,0,4,5,0,0,6,7,0,8,9 +,0,0,0,0,0,0,0,4,5,0,0,6,7,0,8,9,0,0,0,0,0,0,0,4 +,5,0,0,6,7,0,8,9,0,0,0,0,0,0,0,4,5,0,0,6,7,0,8,9 +,0,0,0,0,0,0,0,4,5,0,0,6,7,0,8,9,0,0,0,0,0,0,0,4 +,5,0,0,6,7,0,8,9,0,0,0,0,0,0,0,4,5,0,0,6,7,0,8,9 +,0,0,0,0,0,0,0,4,5,0,0,6,7,0,8,9},{ +0},{ +0},{ +0},{ +0,0,0,0,119,0,0,120,116,116,117,117,0,0,118,118,115,115,115,115,114,114,114,114 +,113,113,113,113,113,113,113,113,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112 +,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,107,107,107,107,107,107,107,107 +,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107 +,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110 +,110,110,110,110,110,110,110,110,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109 +,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109 +,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109 +,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108 +,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108 +,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108},{ +0,0,0,0,116,117,0,118,115,115,114,114,113,113,113,113,112,112,112,112,112,112,112,112 +,111,111,111,111,111,111,111,111,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107 +,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,109,109,109,109,109,109,109,109 +,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109 +,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108 +,108,108,108,108,108,108,108,108,0,0,0,0,116,117,0,118,115,115,114,114,113,113,113,113 +,112,112,112,112,112,112,112,112,111,111,111,111,111,111,111,111,107,107,107,107,107,107,107,107 +,107,107,107,107,107,107,107,107,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110 +,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109 +,109,109,109,109,109,109,109,109,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108 +,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108},{ +0,0,0,0,115,114,113,113,112,112,112,112,111,111,111,111,107,107,107,107,107,107,107,107 +,110,110,110,110,110,110,110,110,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109 +,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,0,0,0,0,115,114,113,113 +,112,112,112,112,111,111,111,111,107,107,107,107,107,107,107,107,110,110,110,110,110,110,110,110 +,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,108,108,108,108,108,108,108,108 +,108,108,108,108,108,108,108,108,0,0,0,0,115,114,113,113,112,112,112,112,111,111,111,111 +,107,107,107,107,107,107,107,107,110,110,110,110,110,110,110,110,109,109,109,109,109,109,109,109 +,109,109,109,109,109,109,109,109,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108 +,0,0,0,0,115,114,113,113,112,112,112,112,111,111,111,111,107,107,107,107,107,107,107,107 +,110,110,110,110,110,110,110,110,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109 +,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108},{ +0,0,0,113,112,112,111,111,107,107,107,107,110,110,110,110,109,109,109,109,109,109,109,109 +,108,108,108,108,108,108,108,108,0,0,0,113,112,112,111,111,107,107,107,107,110,110,110,110 +,109,109,109,109,109,109,109,109,108,108,108,108,108,108,108,108,0,0,0,113,112,112,111,111 +,107,107,107,107,110,110,110,110,109,109,109,109,109,109,109,109,108,108,108,108,108,108,108,108 +,0,0,0,113,112,112,111,111,107,107,107,107,110,110,110,110,109,109,109,109,109,109,109,109 +,108,108,108,108,108,108,108,108,0,0,0,113,112,112,111,111,107,107,107,107,110,110,110,110 +,109,109,109,109,109,109,109,109,108,108,108,108,108,108,108,108,0,0,0,113,112,112,111,111 +,107,107,107,107,110,110,110,110,109,109,109,109,109,109,109,109,108,108,108,108,108,108,108,108 +,0,0,0,113,112,112,111,111,107,107,107,107,110,110,110,110,109,109,109,109,109,109,109,109 +,108,108,108,108,108,108,108,108,0,0,0,113,112,112,111,111,107,107,107,107,110,110,110,110 +,109,109,109,109,109,109,109,109,108,108,108,108,108,108,108,108},{ +0,0,112,111,107,107,110,110,109,109,109,109,108,108,108,108,0,0,112,111,107,107,110,110 +,109,109,109,109,108,108,108,108,0,0,112,111,107,107,110,110,109,109,109,109,108,108,108,108 +,0,0,112,111,107,107,110,110,109,109,109,109,108,108,108,108,0,0,112,111,107,107,110,110 +,109,109,109,109,108,108,108,108,0,0,112,111,107,107,110,110,109,109,109,109,108,108,108,108 +,0,0,112,111,107,107,110,110,109,109,109,109,108,108,108,108,0,0,112,111,107,107,110,110 +,109,109,109,109,108,108,108,108,0,0,112,111,107,107,110,110,109,109,109,109,108,108,108,108 +,0,0,112,111,107,107,110,110,109,109,109,109,108,108,108,108,0,0,112,111,107,107,110,110 +,109,109,109,109,108,108,108,108,0,0,112,111,107,107,110,110,109,109,109,109,108,108,108,108 +,0,0,112,111,107,107,110,110,109,109,109,109,108,108,108,108,0,0,112,111,107,107,110,110 +,109,109,109,109,108,108,108,108,0,0,112,111,107,107,110,110,109,109,109,109,108,108,108,108 +,0,0,112,111,107,107,110,110,109,109,109,109,108,108,108,108},{ +0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108 +,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108 +,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108 +,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108 +,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108 +,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108 +,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108 +,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108 +,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108 +,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108 +,0,0,107,110,109,109,108,108,0,0,107,110,109,109,108,108},{ +0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108 +,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108 +,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108 +,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108 +,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108 +,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108 +,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108 +,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108 +,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108 +,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108 +,0,0,109,108,0,0,109,108,0,0,109,108,0,0,109,108},{ +0},{ +0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93 +,93,93,93,93,93,93,93,93,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96 +,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,98,98,98,98,98,98,98,98 +,98,98,98,98,98,98,98,98,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 +,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,101,101,101,101,101,101,101,101 +,101,101,101,101,101,101,101,101,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94 +,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,95,95,95,95,95,95,95,95 +,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95 +,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,103,103,103,103,103,103,103,103 +,103,103,103,103,103,103,103,103,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104 +,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105},{ +88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88 +,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88 +,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88 +,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88 +,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88 +,88,88,88,88,88,88,88,88,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89 +,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89 +,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89 +,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89 +,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89 +,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89},{ +90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90 +,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90 +,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90 +,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90 +,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90 +,90,90,90,90,90,90,90,90,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92 +,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92 +,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92 +,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92 +,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92 +,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92},{ +76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76 +,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76 +,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76 +,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76 +,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76 +,76,76,76,76,76,76,76,76,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77 +,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77 +,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77 +,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77 +,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77 +,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77},{ +78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78 +,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78 +,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78 +,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78 +,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78 +,78,78,78,78,78,78,78,78,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79 +,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79 +,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79 +,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79 +,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79 +,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79},{ +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80 +,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80 +,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80 +,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80 +,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80 +,80,80,80,80,80,80,80,80,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81 +,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81 +,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81 +,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81 +,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81 +,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81},{ +82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82 +,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82 +,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82 +,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82 +,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82 +,82,82,82,82,82,82,82,82,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83 +,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83 +,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83 +,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83 +,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83 +,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83},{ +84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84 +,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84 +,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84 +,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84 +,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84 +,84,84,84,84,84,84,84,84,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85 +,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85 +,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85 +,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85 +,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85 +,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85},{ +86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86 +,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86 +,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86 +,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86 +,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86 +,86,86,86,86,86,86,86,86,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87 +,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87 +,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87 +,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87 +,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87 +,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87},{ +0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93 +,96,96,96,96,96,96,96,96,97,97,97,97,97,97,97,97,98,98,98,98,98,98,98,98 +,99,99,99,99,99,99,99,99,100,100,100,100,100,100,100,100,101,101,101,101,101,101,101,101 +,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,95,95,95,95,95,95,95,95 +,95,95,95,95,95,95,95,95,102,102,102,102,102,102,102,102,103,103,103,103,103,103,103,103 +,104,104,104,104,104,104,104,104,105,105,105,105,105,105,105,105},{ +31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31 +,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31 +,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31 +,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31 +,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31 +,31,31,31,31,31,31,31,31,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32 +,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32 +,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32 +,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32 +,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32 +,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32},{ +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47 +,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47 +,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47 +,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47 +,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47 +,47,47,47,47,47,47,47,47,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48 +,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48 +,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48 +,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48 +,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48 +,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48},{ +49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49 +,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49 +,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49 +,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49 +,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49 +,49,49,49,49,49,49,49,49,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50},{ +35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35 +,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35 +,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35 +,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35 +,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35 +,35,35,35,35,35,35,35,35,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36 +,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36 +,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36 +,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36 +,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36 +,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36},{ +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37 +,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37 +,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37 +,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37 +,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37 +,37,37,37,37,37,37,37,37,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38 +,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38 +,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38 +,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38 +,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38 +,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38},{ +39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39 +,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39 +,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39 +,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39 +,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39 +,39,39,39,39,39,39,39,39,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40 +,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40 +,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40 +,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40 +,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40 +,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40},{ +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33 +,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33 +,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33 +,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33 +,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33 +,33,33,33,33,33,33,33,33,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34 +,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34 +,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34 +,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34 +,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34 +,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34},{ +55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55 +,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55 +,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55 +,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55 +,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55 +,55,55,55,55,55,55,55,55,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56 +,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56 +,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56 +,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56 +,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56 +,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56},{ +41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41 +,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41 +,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41 +,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41 +,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41 +,41,41,41,41,41,41,41,41,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42 +,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42 +,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42 +,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42 +,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42 +,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42},{ +43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43 +,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43 +,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43 +,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43 +,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43 +,43,43,43,43,43,43,43,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44 +,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44 +,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44 +,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44 +,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44 +,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44},{ +45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45 +,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45 +,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45 +,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45 +,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45 +,45,45,45,45,45,45,45,45,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46 +,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46 +,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46 +,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46 +,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46 +,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46},{ +63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63 +,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63 +,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63 +,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63 +,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63 +,63,63,63,63,63,63,63,63,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64 +,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64 +,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64 +,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64 +,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64 +,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64},{ +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65 +,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65 +,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65 +,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65 +,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65 +,65,65,65,65,65,65,65,65,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70 +,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70 +,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70 +,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70 +,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70 +,70,70,70,70,70,70,70,70,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71 +,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71 +,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71 +,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71 +,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71 +,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71},{ +61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61 +,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61 +,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61 +,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61 +,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61 +,61,61,61,61,61,61,61,61,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62 +,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62 +,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62 +,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62 +,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62 +,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62},{ +88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88 +,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88 +,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,89,89,89,89,89,89,89,89 +,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89 +,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89 +,89,89,89,89,89,89,89,89,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90 +,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90 +,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90 +,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92 +,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92 +,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92},{ +51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51 +,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51 +,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51 +,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51 +,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51 +,51,51,51,51,51,51,51,51,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52 +,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52 +,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52 +,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52 +,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52 +,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52},{ +53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53 +,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53 +,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53 +,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53 +,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53 +,53,53,53,53,53,53,53,53,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54 +,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54 +,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54 +,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54 +,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54 +,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54},{ +57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57 +,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57 +,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57 +,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57 +,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57 +,57,57,57,57,57,57,57,57,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58 +,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58 +,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58 +,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58 +,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58 +,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58},{ +59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59 +,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59 +,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59 +,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59 +,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59 +,59,59,59,59,59,59,59,59,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60 +,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60 +,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60 +,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60 +,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60 +,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60},{ +72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72 +,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72 +,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72 +,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72 +,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72 +,72,72,72,72,72,72,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73 +,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73 +,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73 +,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73 +,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73 +,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73},{ +76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76 +,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76 +,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,77,77,77,77,77,77,77,77 +,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77 +,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77 +,77,77,77,77,77,77,77,77,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75 +,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75 +,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75 +,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75 +,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75 +,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75},{ +74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74 +,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74 +,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74 +,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74 +,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74 +,74,74,74,74,74,74,74,74,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78 +,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78 +,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78 +,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79 +,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79 +,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79},{ +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80 +,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80 +,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,81,81,81,81,81,81,81,81 +,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81 +,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81 +,81,81,81,81,81,81,81,81,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82 +,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82 +,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82 +,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83 +,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83 +,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83},{ +84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84 +,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84 +,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,85,85,85,85,85,85,85,85 +,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85 +,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85 +,85,85,85,85,85,85,85,85,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86 +,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86 +,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86 +,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87 +,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87 +,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87},{ +0,210,210,210,210,210,210,210,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,93,93,93,93,93,93,93,93 +,96,96,96,96,97,97,97,97,98,98,98,98,99,99,99,99,100,100,100,100,101,101,101,101 +,94,94,94,94,94,94,94,94,95,95,95,95,95,95,95,95,102,102,102,102,103,103,103,103 +,104,104,104,104,105,105,105,105,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31 +,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31 +,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31 +,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32 +,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32 +,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32},{ +47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47 +,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47 +,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,48,48,48,48,48,48,48,48 +,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48 +,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48 +,48,48,48,48,48,48,48,48,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24 +,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24 +,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24 +,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24 +,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24 +,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24},{ +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25 +,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25 +,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25 +,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25 +,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25 +,25,25,25,25,25,25,25,25,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49 +,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49 +,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49 +,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50},{ +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22 +,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22 +,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22 +,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22 +,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22 +,22,22,22,22,22,22,22,22,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35 +,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35 +,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35 +,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36 +,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36 +,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36},{ +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37 +,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37 +,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,38,38,38,38,38,38,38,38 +,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38 +,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38 +,38,38,38,38,38,38,38,38,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39 +,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39 +,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39 +,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40 +,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40 +,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40},{ +21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21 +,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21 +,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21 +,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21 +,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21 +,21,21,21,21,21,21,21,21,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33 +,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33 +,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33 +,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34 +,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34 +,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34},{ +55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55 +,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55 +,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,56,56,56,56,56,56,56,56 +,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56 +,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56 +,56,56,56,56,56,56,56,56,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28 +,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28 +,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28 +,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28 +,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28 +,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28},{ +41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41 +,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41 +,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,42,42,42,42,42,42,42,42 +,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42 +,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42 +,42,42,42,42,42,42,42,42,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43 +,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43 +,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43 +,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44 +,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44 +,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44},{ +45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45 +,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45 +,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,46,46,46,46,46,46,46,46 +,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46 +,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46 +,46,46,46,46,46,46,46,46,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23 +,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23 +,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23 +,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23 +,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23 +,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23},{ +30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 +,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 +,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 +,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 +,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 +,30,30,30,30,30,30,30,30,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63 +,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63 +,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63 +,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64 +,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64 +,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64},{ +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65 +,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65 +,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70 +,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70 +,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70 +,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71 +,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71 +,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71},{ +29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 +,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 +,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 +,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 +,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 +,29,29,29,29,29,29,29,29,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61 +,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61 +,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61 +,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62 +,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62 +,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62},{ +88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88 +,88,88,88,88,88,88,88,88,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89 +,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,90,90,90,90,90,90,90,90 +,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90 +,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92 +,92,92,92,92,92,92,92,92,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20 +,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20 +,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20 +,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20 +,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20 +,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20},{ +26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26 +,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26 +,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26 +,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26 +,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26 +,26,26,26,26,26,26,26,26,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51 +,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51 +,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51 +,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52 +,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52 +,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52},{ +53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53 +,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53 +,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,54,54,54,54,54,54,54,54 +,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54 +,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54 +,54,54,54,54,54,54,54,54,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27 +,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27 +,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27 +,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27 +,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27 +,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27},{ +57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57 +,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57 +,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,58,58,58,58,58,58,58,58 +,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58 +,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58 +,58,58,58,58,58,58,58,58,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59 +,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59 +,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59 +,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60 +,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60 +,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60},{ +72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72 +,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72 +,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,73,73,73,73,73,73,73,73 +,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73 +,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73 +,73,73,73,73,73,73,73,73,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76 +,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,77,77,77,77,77,77,77,77 +,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77 +,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75 +,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75 +,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75},{ +74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74 +,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74 +,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,78,78,78,78,78,78,78,78 +,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78 +,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79 +,79,79,79,79,79,79,79,79,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80 +,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,81,81,81,81,81,81,81,81 +,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81 +,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82 +,82,82,82,82,82,82,82,82,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83 +,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83},{ +84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84 +,84,84,84,84,84,84,84,84,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85 +,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,86,86,86,86,86,86,86,86 +,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86 +,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87 +,87,87,87,87,87,87,87,87,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69 +,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69 +,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69 +,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69 +,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69 +,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69},{ +0,210,210,210,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,93,93,93,93,96,96,97,97,98,98,99,99,100,100,101,101 +,94,94,94,94,95,95,95,95,102,102,103,103,104,104,105,105,31,31,31,31,31,31,31,31 +,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31 +,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32 +,32,32,32,32,32,32,32,32,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47 +,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,48,48,48,48,48,48,48,48 +,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48 +,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24 +,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24 +,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24},{ +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25 +,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25 +,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,49,49,49,49,49,49,49,49 +,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49 +,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +,50,50,50,50,50,50,50,50,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 +,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 +,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 +,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 +,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 +,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15},{ +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22 +,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22 +,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,35,35,35,35,35,35,35,35 +,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35 +,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36 +,36,36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37 +,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,38,38,38,38,38,38,38,38 +,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38 +,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39 +,39,39,39,39,39,39,39,39,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40 +,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40},{ +21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21 +,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21 +,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,33,33,33,33,33,33,33,33 +,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33 +,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34 +,34,34,34,34,34,34,34,34,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 +,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 +,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 +,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 +,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 +,14,14,14,14,14,14,14,14,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55 +,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,56,56,56,56,56,56,56,56 +,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56 +,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28 +,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28 +,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28},{ +41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41 +,41,41,41,41,41,41,41,41,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42 +,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,43,43,43,43,43,43,43,43 +,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43 +,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44 +,44,44,44,44,44,44,44,44,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45 +,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,46,46,46,46,46,46,46,46 +,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46 +,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23 +,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23 +,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23},{ +30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 +,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 +,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,63,63,63,63,63,63,63,63 +,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63 +,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64 +,64,64,64,64,64,64,64,64,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65 +,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70 +,70,70,70,70,70,70,70,70,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71 +,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71},{ +29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 +,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 +,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,61,61,61,61,61,61,61,61 +,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61 +,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62 +,62,62,62,62,62,62,62,62,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88 +,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,90,90,90,90,90,90,90,90 +,90,90,90,90,90,90,90,90,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92 +,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20 +,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20 +,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20},{ +26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26 +,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26 +,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,51,51,51,51,51,51,51,51 +,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51 +,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52 +,52,52,52,52,52,52,52,52,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53 +,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,54,54,54,54,54,54,54,54 +,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54 +,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27 +,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27 +,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27},{ +57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57 +,57,57,57,57,57,57,57,57,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58 +,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,59,59,59,59,59,59,59,59 +,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59 +,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60 +,60,60,60,60,60,60,60,60,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68 +,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68 +,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68 +,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68 +,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68 +,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68},{ +91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91 +,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91 +,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91 +,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91 +,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91 +,91,91,91,91,91,91,91,91,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72 +,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,73,73,73,73,73,73,73,73 +,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73 +,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,77,77,77,77,77,77,77,77 +,77,77,77,77,77,77,77,77,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75 +,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75},{ +74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74 +,74,74,74,74,74,74,74,74,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78 +,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,80,80,80,80,80,80,80,80 +,80,80,80,80,80,80,80,80,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81 +,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,83,83,83,83,83,83,83,83 +,83,83,83,83,83,83,83,83,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84 +,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,86,86,86,86,86,86,86,86 +,86,86,86,86,86,86,86,86,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87 +,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69 +,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69 +,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69},{ +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18 +,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18 +,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18 +,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18 +,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18 +,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19 +,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19 +,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19 +,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19 +,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19 +,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19},{ +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16 +,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16 +,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16 +,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16 +,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16 +,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 +,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 +,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 +,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 +,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 +,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17},{ +0,210,1,1,1,1,1,1,1,1,1,1,1,1,1,1,93,93,96,97,98,99,100,101 +,94,94,95,95,102,103,104,105,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31 +,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,47,47,47,47,47,47,47,47 +,47,47,47,47,47,47,47,47,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48 +,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24 +,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25 +,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,49,49,49,49,49,49,49,49 +,49,49,49,49,49,49,49,49,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 +,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 +,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15},{ +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22 +,22,22,22,22,22,22,22,22,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35 +,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,37,37,37,37,37,37,37,37 +,37,37,37,37,37,37,37,37,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38 +,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,40,40,40,40,40,40,40,40 +,40,40,40,40,40,40,40,40,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21 +,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,33,33,33,33,33,33,33,33 +,33,33,33,33,33,33,33,33,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 +,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 +,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,55,55,55,55,55,55,55,55 +,55,55,55,55,55,55,55,55,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56 +,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28 +,28,28,28,28,28,28,28,28,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41 +,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,43,43,43,43,43,43,43,43 +,43,43,43,43,43,43,43,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44 +,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,46,46,46,46,46,46,46,46 +,46,46,46,46,46,46,46,46,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23 +,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23},{ +30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 +,30,30,30,30,30,30,30,30,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63 +,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,65,65,65,65,65,65,65,65 +,65,65,65,65,65,65,65,65,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,71,71,71,71,71,71,71,71 +,71,71,71,71,71,71,71,71,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12},{ +13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 +,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 +,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 +,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 +,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 +,13,13,13,13,13,13,13,13,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 +,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,61,61,61,61,61,61,61,61 +,61,61,61,61,61,61,61,61,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62 +,88,88,88,88,88,88,88,88,89,89,89,89,89,89,89,89,90,90,90,90,90,90,90,90 +,92,92,92,92,92,92,92,92,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20 +,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20},{ +26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26 +,26,26,26,26,26,26,26,26,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51 +,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,53,53,53,53,53,53,53,53 +,53,53,53,53,53,53,53,53,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54 +,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27 +,27,27,27,27,27,27,27,27,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57 +,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,59,59,59,59,59,59,59,59 +,59,59,59,59,59,59,59,59,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60 +,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68 +,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68 +,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68},{ +91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91 +,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91 +,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,72,72,72,72,72,72,72,72 +,72,72,72,72,72,72,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73 +,76,76,76,76,76,76,76,76,77,77,77,77,77,77,77,77,75,75,75,75,75,75,75,75 +,75,75,75,75,75,75,75,75,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74 +,78,78,78,78,78,78,78,78,79,79,79,79,79,79,79,79,80,80,80,80,80,80,80,80 +,81,81,81,81,81,81,81,81,82,82,82,82,82,82,82,82,83,83,83,83,83,83,83,83 +,84,84,84,84,84,84,84,84,85,85,85,85,85,85,85,85,86,86,86,86,86,86,86,86 +,87,87,87,87,87,87,87,87,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69 +,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69},{ +67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67 +,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67 +,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67 +,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67 +,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67 +,67,67,67,67,67,67,67,67,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10},{ +11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18 +,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18 +,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18 +,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19 +,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19 +,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19},{ +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16 +,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16 +,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17 +,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 +,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 +,17,17,17,17,17,17,17,17,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66 +,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66 +,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66 +,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66 +,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66 +,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66},{ +0,1,1,1,1,1,1,1,93,0,0,0,94,95,0,0,31,31,31,31,31,31,31,31 +,32,32,32,32,32,32,32,32,47,47,47,47,47,47,47,47,48,48,48,48,48,48,48,48 +,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25 +,25,25,25,25,25,25,25,25,49,49,49,49,49,49,49,49,50,50,50,50,50,50,50,50 +,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 +,15,15,15,15,15,15,15,15,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22 +,35,35,35,35,35,35,35,35,36,36,36,36,36,36,36,36,37,37,37,37,37,37,37,37 +,38,38,38,38,38,38,38,38,39,39,39,39,39,39,39,39,40,40,40,40,40,40,40,40 +,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,33,33,33,33,33,33,33,33 +,34,34,34,34,34,34,34,34,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 +,14,14,14,14,14,14,14,14,55,55,55,55,55,55,55,55,56,56,56,56,56,56,56,56 +,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,41,41,41,41,41,41,41,41 +,42,42,42,42,42,42,42,42,43,43,43,43,43,43,43,43,44,44,44,44,44,44,44,44 +,45,45,45,45,45,45,45,45,46,46,46,46,46,46,46,46,23,23,23,23,23,23,23,23 +,23,23,23,23,23,23,23,23,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30 +,63,63,63,63,63,63,63,63,64,64,64,64,64,64,64,64,65,65,65,65,65,65,65,65 +,2,2,2,2,2,2,2,2,70,70,70,70,70,70,70,70,71,71,71,71,71,71,71,71 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12},{ +13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 +,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 +,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,29,29,29,29,29,29,29,29 +,29,29,29,29,29,29,29,29,61,61,61,61,61,61,61,61,62,62,62,62,62,62,62,62 +,88,88,88,88,89,89,89,89,90,90,90,90,92,92,92,92,20,20,20,20,20,20,20,20 +,20,20,20,20,20,20,20,20,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26 +,51,51,51,51,51,51,51,51,52,52,52,52,52,52,52,52,53,53,53,53,53,53,53,53 +,54,54,54,54,54,54,54,54,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27 +,57,57,57,57,57,57,57,57,58,58,58,58,58,58,58,58,59,59,59,59,59,59,59,59 +,60,60,60,60,60,60,60,60,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68 +,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68},{ +91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91 +,91,91,91,91,91,91,91,91,72,72,72,72,72,72,72,72,73,73,73,73,73,73,73,73 +,76,76,76,76,77,77,77,77,75,75,75,75,75,75,75,75,74,74,74,74,74,74,74,74 +,78,78,78,78,79,79,79,79,80,80,80,80,81,81,81,81,82,82,82,82,83,83,83,83 +,84,84,84,84,85,85,85,85,86,86,86,86,87,87,87,87,69,69,69,69,69,69,69,69 +,69,69,69,69,69,69,69,69,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4},{ +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67 +,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67 +,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10},{ +11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,18,18,18,18,18,18,18,18 +,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18 +,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19 +,19,19,19,19,19,19,19,19,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6},{ +7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16 +,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17 +,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 +,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66 +,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66 +,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66},{ +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9},{ +0,1,1,1,0,0,0,0,31,31,31,31,32,32,32,32,47,47,47,47,48,48,48,48 +,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,49,49,49,49,50,50,50,50 +,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,22,22,22,22,22,22,22,22 +,35,35,35,35,36,36,36,36,37,37,37,37,38,38,38,38,39,39,39,39,40,40,40,40 +,21,21,21,21,21,21,21,21,33,33,33,33,34,34,34,34,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 +,55,55,55,55,56,56,56,56,28,28,28,28,28,28,28,28,41,41,41,41,42,42,42,42 +,43,43,43,43,44,44,44,44,45,45,45,45,46,46,46,46,23,23,23,23,23,23,23,23 +,30,30,30,30,30,30,30,30,63,63,63,63,64,64,64,64,65,65,65,65,2,2,2,2 +,70,70,70,70,71,71,71,71,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12},{ +13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 +,13,13,13,13,13,13,13,13,29,29,29,29,29,29,29,29,61,61,61,61,62,62,62,62 +,88,88,89,89,90,90,92,92,20,20,20,20,20,20,20,20,26,26,26,26,26,26,26,26 +,51,51,51,51,52,52,52,52,53,53,53,53,54,54,54,54,27,27,27,27,27,27,27,27 +,57,57,57,57,58,58,58,58,59,59,59,59,60,60,60,60,68,68,68,68,68,68,68,68 +,68,68,68,68,68,68,68,68,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91 +,72,72,72,72,73,73,73,73,76,76,77,77,75,75,75,75,74,74,74,74,78,78,79,79 +,80,80,81,81,82,82,83,83,84,84,85,85,86,86,87,87,69,69,69,69,69,69,69,69 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4},{ +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,67,67,67,67,67,67,67,67 +,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,18,18,18,18,18,18,18,18 +,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6},{ +7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,16,16,16,16,16,16,16,16 +,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 +,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66 +,66,66,66,66,66,66,66,66,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9},{ +0,1,0,0,31,31,32,32,47,47,48,48,24,24,24,24,25,25,25,25,49,49,50,50 +,15,15,15,15,15,15,15,15,22,22,22,22,35,35,36,36,37,37,38,38,39,39,40,40 +,21,21,21,21,33,33,34,34,3,3,3,3,3,3,3,3,14,14,14,14,14,14,14,14 +,55,55,56,56,28,28,28,28,41,41,42,42,43,43,44,44,45,45,46,46,23,23,23,23 +,30,30,30,30,63,63,64,64,65,65,2,2,70,70,71,71,12,12,12,12,12,12,12,12 +,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 +,29,29,29,29,61,61,62,62,88,89,90,92,20,20,20,20,26,26,26,26,51,51,52,52 +,53,53,54,54,27,27,27,27,57,57,58,58,59,59,60,60,68,68,68,68,68,68,68,68 +,91,91,91,91,91,91,91,91,72,72,73,73,76,77,75,75,74,74,78,79,80,81,82,83 +,84,85,86,87,69,69,69,69,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4},{ +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,16,16,16,16,16,16,16,16 +,17,17,17,17,17,17,17,17,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9},{ +0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197 +,197,197,197,197,197,197,197,197,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200 +,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,202,202,202,202,202,202,202,202 +,202,202,202,202,202,202,202,202,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203 +,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,205,205,205,205,205,205,205,205 +,205,205,205,205,205,205,205,205,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198 +,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,199,199,199,199,199,199,199,199 +,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199 +,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,207,207,207,207,207,207,207,207 +,207,207,207,207,207,207,207,207,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208 +,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209},{ +124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124 +,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124 +,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,158,158,158,158,158,158,158,158 +,158,158,158,158,158,158,158,158,179,179,179,179,179,179,179,179,180,180,180,180,180,180,180,180 +,181,181,181,181,181,181,181,181,182,182,182,182,182,182,182,182,161,161,161,161,161,161,161,161 +,161,161,161,161,161,161,161,161,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162 +,189,189,189,189,189,189,189,189,190,190,190,190,190,190,190,190,191,191,191,191,191,191,191,191 +,192,192,192,192,192,192,192,192,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165 +,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,193,193,193,193,193,193,193,193 +,194,194,194,194,194,194,194,194,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130 +,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130},{ +131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131 +,131,131,131,131,131,131,131,131,195,195,195,195,195,195,195,195,196,196,196,196,196,196,196,196 +,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,175,175,175,175,175,175,175,175 +,175,175,175,175,175,175,175,175,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176 +,177,177,177,177,177,177,177,177,178,178,178,178,178,178,178,178,159,159,159,159,159,159,159,159 +,159,159,159,159,159,159,159,159,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160 +,183,183,183,183,183,183,183,183,184,184,184,184,184,184,184,184,185,185,185,185,185,185,185,185 +,186,186,186,186,186,186,186,186,187,187,187,187,187,187,187,187,188,188,188,188,188,188,188,188 +,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170 +,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170 +,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170},{ +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129 +,129,129,129,129,129,129,129,129,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156 +,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,150,150,150,150,150,150,150,150 +,150,150,150,150,150,150,150,150,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151 +,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,153,153,153,153,153,153,153,153 +,153,153,153,153,153,153,153,153,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163 +,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,167,167,167,167,167,167,167,167 +,167,167,167,167,167,167,167,167,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173 +,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122 +,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122 +,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122},{ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123 +,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123 +,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,154,154,154,154,154,154,154,154 +,154,154,154,154,154,154,154,154,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155 +,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,169,169,169,169,169,169,169,169 +,169,169,169,169,169,169,169,169,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136 +,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,138,138,138,138,138,138,138,138 +,138,138,138,138,138,138,138,138,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139 +,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,147,147,147,147,147,147,147,147 +,147,147,147,147,147,147,147,147,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128 +,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128},{ +121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121 +,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121 +,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121 +,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121 +,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121 +,121,121,121,121,121,121,121,121,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171 +,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,132,132,132,132,132,132,132,132 +,132,132,132,132,132,132,132,132,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133 +,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,135,135,135,135,135,135,135,135 +,135,135,135,135,135,135,135,135,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125 +,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125},{ +126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126 +,126,126,126,126,126,126,126,126,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140 +,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,142,142,142,142,142,142,142,142 +,142,142,142,142,142,142,142,142,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143 +,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,145,145,145,145,145,145,145,145 +,145,145,145,145,145,145,145,145,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127 +,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,148,148,148,148,148,148,148,148 +,148,148,148,148,148,148,148,148,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149 +,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106 +,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106 +,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106},{ +0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197 +,200,200,200,200,200,200,200,200,201,201,201,201,201,201,201,201,202,202,202,202,202,202,202,202 +,203,203,203,203,203,203,203,203,204,204,204,204,204,204,204,204,205,205,205,205,205,205,205,205 +,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,199,199,199,199,199,199,199,199 +,199,199,199,199,199,199,199,199,206,206,206,206,206,206,206,206,207,207,207,207,207,207,207,207 +,208,208,208,208,208,208,208,208,209,209,209,209,209,209,209,209},{ +124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124 +,124,124,124,124,124,124,124,124,158,158,158,158,158,158,158,158,179,179,179,179,180,180,180,180 +,181,181,181,181,182,182,182,182,161,161,161,161,161,161,161,161,162,162,162,162,162,162,162,162 +,189,189,189,189,190,190,190,190,191,191,191,191,192,192,192,192,165,165,165,165,165,165,165,165 +,166,166,166,166,166,166,166,166,193,193,193,193,194,194,194,194,130,130,130,130,130,130,130,130 +,130,130,130,130,130,130,130,130,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131 +,195,195,195,195,196,196,196,196,174,174,174,174,174,174,174,174,175,175,175,175,175,175,175,175 +,176,176,176,176,176,176,176,176,177,177,177,177,178,178,178,178,159,159,159,159,159,159,159,159 +,160,160,160,160,160,160,160,160,183,183,183,183,184,184,184,184,185,185,185,185,186,186,186,186 +,187,187,187,187,188,188,188,188,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170 +,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170},{ +119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119 +,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119 +,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119 +,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119 +,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119 +,119,119,119,119,119,119,119,119,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129 +,156,156,156,156,156,156,156,156,157,157,157,157,157,157,157,157,150,150,150,150,150,150,150,150 +,151,151,151,151,151,151,151,151,152,152,152,152,152,152,152,152,153,153,153,153,153,153,153,153 +,163,163,163,163,163,163,163,163,164,164,164,164,164,164,164,164,167,167,167,167,167,167,167,167 +,173,173,173,173,173,173,173,173,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122 +,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122},{ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123 +,123,123,123,123,123,123,123,123,154,154,154,154,154,154,154,154,155,155,155,155,155,155,155,155 +,168,168,168,168,168,168,168,168,169,169,169,169,169,169,169,169,136,136,136,136,136,136,136,136 +,137,137,137,137,137,137,137,137,138,138,138,138,138,138,138,138,139,139,139,139,139,139,139,139 +,146,146,146,146,146,146,146,146,147,147,147,147,147,147,147,147,128,128,128,128,128,128,128,128 +,128,128,128,128,128,128,128,128,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120 +,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120 +,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120 +,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120 +,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120 +,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120},{ +121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121 +,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121 +,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,171,171,171,171,171,171,171,171 +,172,172,172,172,172,172,172,172,132,132,132,132,132,132,132,132,133,133,133,133,133,133,133,133 +,134,134,134,134,134,134,134,134,135,135,135,135,135,135,135,135,125,125,125,125,125,125,125,125 +,125,125,125,125,125,125,125,125,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126 +,140,140,140,140,140,140,140,140,141,141,141,141,141,141,141,141,142,142,142,142,142,142,142,142 +,143,143,143,143,143,143,143,143,144,144,144,144,144,144,144,144,145,145,145,145,145,145,145,145 +,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,148,148,148,148,148,148,148,148 +,149,149,149,149,149,149,149,149,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106 +,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106},{ +0,210,210,210,210,210,210,210,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,197,197,197,197,197,197,197,197 +,200,200,200,200,201,201,201,201,202,202,202,202,203,203,203,203,204,204,204,204,205,205,205,205 +,198,198,198,198,198,198,198,198,199,199,199,199,199,199,199,199,206,206,206,206,207,207,207,207 +,208,208,208,208,209,209,209,209,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124 +,158,158,158,158,179,179,180,180,181,181,182,182,161,161,161,161,162,162,162,162,189,189,190,190 +,191,191,192,192,165,165,165,165,166,166,166,166,193,193,194,194,130,130,130,130,130,130,130,130 +,131,131,131,131,131,131,131,131,195,195,196,196,174,174,174,174,175,175,175,175,176,176,176,176 +,177,177,178,178,159,159,159,159,160,160,160,160,183,183,184,184,185,185,186,186,187,187,188,188 +,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170},{ +119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119 +,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119 +,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,129,129,129,129,129,129,129,129 +,156,156,156,156,157,157,157,157,150,150,150,150,151,151,151,151,152,152,152,152,153,153,153,153 +,163,163,163,163,164,164,164,164,167,167,167,167,173,173,173,173,122,122,122,122,122,122,122,122 +,122,122,122,122,122,122,122,122,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123 +,154,154,154,154,155,155,155,155,168,168,168,168,169,169,169,169,136,136,136,136,137,137,137,137 +,138,138,138,138,139,139,139,139,146,146,146,146,147,147,147,147,128,128,128,128,128,128,128,128 +,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120 +,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120 +,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120},{ +116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116 +,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116 +,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116 +,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116 +,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116 +,116,116,116,116,116,116,116,116,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117 +,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117 +,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117 +,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117 +,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117 +,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117},{ +121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121 +,121,121,121,121,121,121,121,121,171,171,171,171,172,172,172,172,132,132,132,132,133,133,133,133 +,134,134,134,134,135,135,135,135,125,125,125,125,125,125,125,125,126,126,126,126,126,126,126,126 +,140,140,140,140,141,141,141,141,142,142,142,142,143,143,143,143,144,144,144,144,145,145,145,145 +,127,127,127,127,127,127,127,127,148,148,148,148,149,149,149,149,106,106,106,106,106,106,106,106 +,106,106,106,106,106,106,106,106,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118 +,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118 +,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118 +,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118 +,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118 +,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118},{ +0,210,210,210,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,197,197,197,197,200,200,201,201,202,202,203,203,204,204,205,205 +,198,198,198,198,199,199,199,199,206,206,207,207,208,208,209,209,124,124,124,124,124,124,124,124 +,158,158,179,180,181,182,161,161,162,162,189,190,191,192,165,165,166,166,193,194,130,130,130,130 +,131,131,131,131,195,196,174,174,175,175,176,176,177,178,159,159,160,160,183,184,185,186,187,188 +,170,170,170,170,170,170,170,170,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119 +,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,129,129,129,129,156,156,157,157 +,150,150,151,151,152,152,153,153,163,163,164,164,167,167,173,173,122,122,122,122,122,122,122,122 +,123,123,123,123,123,123,123,123,154,154,155,155,168,168,169,169,136,136,137,137,138,138,139,139 +,146,146,147,147,128,128,128,128,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120 +,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120},{ +116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116 +,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116 +,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,117,117,117,117,117,117,117,117 +,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117 +,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117 +,117,117,117,117,117,117,117,117,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121 +,171,171,172,172,132,132,133,133,134,134,135,135,125,125,125,125,126,126,126,126,140,140,141,141 +,142,142,143,143,144,144,145,145,127,127,127,127,148,148,149,149,106,106,106,106,106,106,106,106 +,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118 +,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118 +,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118},{ +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115 +,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115 +,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115 +,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115 +,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115 +,115,115,115,115,115,115,115,115,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114 +,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114 +,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114 +,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114 +,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114 +,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114},{ +0,210,1,1,1,1,1,1,1,1,1,1,1,1,1,1,197,197,200,201,202,203,204,205 +,198,198,199,199,206,207,208,209,124,124,124,124,158,0,0,161,162,0,0,165,166,0,130,130 +,131,131,0,174,175,176,0,159,160,0,0,0,170,170,170,170,119,119,119,119,119,119,119,119 +,119,119,119,119,119,119,119,119,129,129,156,157,150,151,152,153,163,164,167,173,122,122,122,122 +,123,123,123,123,154,155,168,169,136,137,138,139,146,147,128,128,120,120,120,120,120,120,120,120 +,120,120,120,120,120,120,120,120,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116 +,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,117,117,117,117,117,117,117,117 +,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117 +,121,121,121,121,121,121,121,121,171,172,132,133,134,135,125,125,126,126,140,141,142,143,144,145 +,127,127,148,149,106,106,106,106,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118 +,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118},{ +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115 +,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115 +,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,114,114,114,114,114,114,114,114 +,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114 +,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114 +,114,114,114,114,114,114,114,114,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113 +,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113 +,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113 +,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113 +,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113 +,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113},{ +0,1,1,1,1,1,1,1,197,0,0,0,198,199,0,0,124,124,0,0,0,0,0,130 +,131,0,0,0,0,0,170,170,119,119,119,119,119,119,119,119,129,0,0,0,0,0,122,122 +,123,123,0,0,0,0,0,128,120,120,120,120,120,120,120,120,116,116,116,116,116,116,116,116 +,116,116,116,116,116,116,116,116,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117 +,121,121,121,121,0,0,0,125,126,0,0,0,127,0,106,106,118,118,118,118,118,118,118,118 +,118,118,118,118,118,118,118,118,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115 +,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,114,114,114,114,114,114,114,114 +,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114 +,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113 +,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113 +,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113},{ +112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112 +,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112 +,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112 +,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112 +,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112 +,112,112,112,112,112,112,112,112,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111 +,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111 +,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111 +,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111 +,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111 +,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111},{ +0,1,1,1,0,0,0,0,124,0,0,0,0,0,0,170,119,119,119,119,0,0,0,122 +,123,0,0,0,120,120,120,120,116,116,116,116,116,116,116,116,117,117,117,117,117,117,117,117 +,121,121,0,0,0,0,0,106,118,118,118,118,118,118,118,118,115,115,115,115,115,115,115,115 +,115,115,115,115,115,115,115,115,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114 +,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113 +,113,113,113,113,113,113,113,113,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112 +,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112 +,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112 +,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111 +,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111 +,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111},{ +107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107 +,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107 +,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107 +,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107 +,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107 +,107,107,107,107,107,107,107,107,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110 +,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110 +,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110 +,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110 +,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110 +,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110},{ +0,1,0,0,0,0,0,0,119,119,0,0,0,0,120,120,116,116,116,116,117,117,117,117 +,121,0,0,0,118,118,118,118,115,115,115,115,115,115,115,115,114,114,114,114,114,114,114,114 +,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,112,112,112,112,112,112,112,112 +,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112 +,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111 +,111,111,111,111,111,111,111,111,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107 +,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107 +,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107 +,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110 +,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110 +,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110},{ +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109 +,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109 +,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109 +,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109 +,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109 +,109,109,109,109,109,109,109,109,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108 +,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108 +,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108 +,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108 +,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108 +,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108},{ +0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210},{ +96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96 +,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96 +,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96 +,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96 +,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96 +,96,96,96,96,96,96,96,96,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97 +,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97 +,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97 +,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97 +,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97 +,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97},{ +98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98 +,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98 +,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98 +,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98 +,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98 +,98,98,98,98,98,98,98,98,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 +,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 +,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 +,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 +,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 +,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99},{ +100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100 +,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100 +,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100 +,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100 +,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100 +,100,100,100,100,100,100,100,100,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 +,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 +,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 +,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 +,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 +,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101},{ +102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 +,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 +,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 +,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 +,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 +,102,102,102,102,102,102,102,102,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103 +,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103 +,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103 +,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103 +,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103 +,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103},{ +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104 +,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104 +,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104 +,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104 +,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104 +,104,104,104,104,104,104,104,104,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105 +,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105 +,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105 +,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105 +,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105 +,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105},{ +0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93 +,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93 +,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93 +,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93 +,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93 +,93,93,93,93,93,93,93,93,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96 +,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96 +,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96 +,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97 +,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97 +,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97},{ +98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98 +,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98 +,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,99,99,99,99,99,99,99,99 +,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 +,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 +,99,99,99,99,99,99,99,99,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100 +,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100 +,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100 +,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 +,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 +,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101},{ +94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94 +,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94 +,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94 +,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94 +,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94 +,94,94,94,94,94,94,94,94,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95 +,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95 +,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95 +,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95 +,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95 +,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95},{ +102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 +,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 +,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,103,103,103,103,103,103,103,103 +,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103 +,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103 +,103,103,103,103,103,103,103,103,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104 +,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104 +,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104 +,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105 +,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105 +,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105},{ +0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93 +,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93 +,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,96,96,96,96,96,96,96,96 +,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96 +,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97 +,97,97,97,97,97,97,97,97,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98 +,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,99,99,99,99,99,99,99,99 +,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 +,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100 +,100,100,100,100,100,100,100,100,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 +,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101},{ +94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94 +,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94 +,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,95,95,95,95,95,95,95,95 +,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95 +,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95 +,95,95,95,95,95,95,95,95,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 +,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,103,103,103,103,103,103,103,103 +,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103 +,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104 +,104,104,104,104,104,104,104,104,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105 +,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105},{ +0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210},{ +179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179 +,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179 +,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179 +,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179 +,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179 +,179,179,179,179,179,179,179,179,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180 +,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180 +,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180 +,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180 +,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180 +,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180},{ +181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181 +,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181 +,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181 +,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181 +,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181 +,181,181,181,181,181,181,181,181,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182 +,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182 +,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182 +,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182 +,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182 +,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182},{ +189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189 +,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189 +,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189 +,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189 +,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189 +,189,189,189,189,189,189,189,189,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190 +,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190 +,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190 +,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190 +,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190 +,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190},{ +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191 +,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191 +,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191 +,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191 +,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191 +,191,191,191,191,191,191,191,191,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192 +,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192 +,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192 +,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192 +,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192 +,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192},{ +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193 +,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193 +,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193 +,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193 +,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193 +,193,193,193,193,193,193,193,193,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194 +,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194 +,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194 +,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194 +,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194 +,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194},{ +195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195 +,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195 +,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195 +,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195 +,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195 +,195,195,195,195,195,195,195,195,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196 +,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196 +,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196 +,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196 +,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196 +,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196},{ +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177 +,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177 +,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177 +,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177 +,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177 +,177,177,177,177,177,177,177,177,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178 +,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178 +,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178 +,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178 +,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178 +,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178},{ +183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183 +,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183 +,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183 +,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183 +,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183 +,183,183,183,183,183,183,183,183,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184 +,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184 +,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184 +,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184 +,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184 +,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184},{ +185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185 +,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185 +,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185 +,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185 +,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185 +,185,185,185,185,185,185,185,185,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186 +,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186 +,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186 +,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186 +,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186 +,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186},{ +187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187 +,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187 +,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187 +,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187 +,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187 +,187,187,187,187,187,187,187,187,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188 +,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188 +,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188 +,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188 +,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188 +,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188},{ +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200 +,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200 +,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200 +,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200 +,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200 +,200,200,200,200,200,200,200,200,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201 +,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201 +,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201 +,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201 +,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201 +,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201},{ +202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202 +,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202 +,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202 +,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202 +,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202 +,202,202,202,202,202,202,202,202,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203 +,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203 +,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203 +,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203 +,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203 +,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203},{ +204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204 +,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204 +,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204 +,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204 +,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204 +,204,204,204,204,204,204,204,204,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205 +,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205 +,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205 +,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205 +,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205 +,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205},{ +206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206 +,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206 +,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206 +,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206 +,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206 +,206,206,206,206,206,206,206,206,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207 +,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207 +,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207 +,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207 +,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207 +,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207},{ +208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208 +,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208 +,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208 +,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208 +,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208 +,208,208,208,208,208,208,208,208,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209 +,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209 +,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209 +,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209 +,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209 +,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209},{ +158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158 +,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158 +,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158 +,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158 +,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158 +,158,158,158,158,158,158,158,158,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179 +,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179 +,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179 +,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180 +,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180 +,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180},{ +181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181 +,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181 +,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,182,182,182,182,182,182,182,182 +,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182 +,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182 +,182,182,182,182,182,182,182,182,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161 +,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161 +,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161 +,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161 +,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161 +,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161},{ +162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162 +,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162 +,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162 +,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162 +,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162 +,162,162,162,162,162,162,162,162,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189 +,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189 +,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189 +,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190 +,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190 +,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190},{ +191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191 +,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191 +,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,192,192,192,192,192,192,192,192 +,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192 +,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192 +,192,192,192,192,192,192,192,192,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165 +,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165 +,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165 +,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165 +,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165 +,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165},{ +166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166 +,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166 +,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166 +,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166 +,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166 +,166,166,166,166,166,166,166,166,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193 +,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193 +,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193 +,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194 +,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194 +,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194},{ +195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195 +,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195 +,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,196,196,196,196,196,196,196,196 +,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196 +,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196 +,196,196,196,196,196,196,196,196,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174 +,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174 +,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174 +,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174 +,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174 +,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174},{ +175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175 +,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175 +,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175 +,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175 +,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175 +,175,175,175,175,175,175,175,175,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176 +,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176 +,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176 +,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176 +,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176 +,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176},{ +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177 +,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177 +,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,178,178,178,178,178,178,178,178 +,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178 +,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178 +,178,178,178,178,178,178,178,178,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159 +,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159 +,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159 +,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159 +,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159 +,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159},{ +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160 +,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160 +,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160 +,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160 +,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160 +,160,160,160,160,160,160,160,160,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183 +,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183 +,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183 +,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184 +,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184 +,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184},{ +185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185 +,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185 +,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,186,186,186,186,186,186,186,186 +,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186 +,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186 +,186,186,186,186,186,186,186,186,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187 +,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187 +,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187 +,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188 +,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188 +,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188},{ +156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156 +,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156 +,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156 +,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156 +,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156 +,156,156,156,156,156,156,156,156,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157 +,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157 +,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157 +,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157 +,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157 +,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157},{ +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150 +,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150 +,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150 +,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150 +,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150 +,150,150,150,150,150,150,150,150,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151 +,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151 +,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151 +,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151 +,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151 +,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151},{ +152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152 +,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152 +,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152 +,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152 +,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152 +,152,152,152,152,152,152,152,152,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153 +,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153 +,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153 +,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153 +,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153 +,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153},{ +163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163 +,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163 +,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163 +,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163 +,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163 +,163,163,163,163,163,163,163,163,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164 +,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164 +,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164 +,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164 +,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164 +,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164},{ +167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167 +,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167 +,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167 +,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167 +,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167 +,167,167,167,167,167,167,167,167,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173 +,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173 +,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173 +,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173 +,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173 +,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173},{ +154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154 +,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154 +,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154 +,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154 +,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154 +,154,154,154,154,154,154,154,154,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155 +,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155 +,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155 +,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155 +,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155 +,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155},{ +168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168 +,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168 +,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168 +,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168 +,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168 +,168,168,168,168,168,168,168,168,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169 +,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169 +,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169 +,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169 +,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169 +,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169},{ +136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136 +,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136 +,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136 +,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136 +,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136 +,136,136,136,136,136,136,136,136,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137 +,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137 +,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137 +,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137 +,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137 +,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137},{ +138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138 +,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138 +,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138 +,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138 +,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138 +,138,138,138,138,138,138,138,138,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139 +,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139 +,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139 +,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139 +,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139 +,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139},{ +146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146 +,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146 +,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146 +,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146 +,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146 +,146,146,146,146,146,146,146,146,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147 +,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147 +,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147 +,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147 +,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147 +,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147},{ +171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171 +,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171 +,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171 +,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171 +,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171 +,171,171,171,171,171,171,171,171,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172 +,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172 +,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172 +,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172 +,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172 +,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172},{ +132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132 +,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132 +,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132 +,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132 +,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132 +,132,132,132,132,132,132,132,132,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133 +,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133 +,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133 +,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133 +,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133 +,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133},{ +134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134 +,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134 +,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134 +,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134 +,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134 +,134,134,134,134,134,134,134,134,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135 +,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135 +,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135 +,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135 +,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135 +,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135},{ +140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140 +,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140 +,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140 +,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140 +,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140 +,140,140,140,140,140,140,140,140,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141 +,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141 +,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141 +,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141 +,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141 +,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141},{ +142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142 +,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142 +,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142 +,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142 +,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142 +,142,142,142,142,142,142,142,142,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143 +,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143 +,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143 +,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143 +,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143 +,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143},{ +144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144 +,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144 +,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144 +,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144 +,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144 +,144,144,144,144,144,144,144,144,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145 +,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145 +,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145 +,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145 +,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145 +,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145},{ +148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148 +,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148 +,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148 +,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148 +,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148 +,148,148,148,148,148,148,148,148,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149 +,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149 +,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149 +,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149 +,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149 +,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149},{ +0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197 +,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197 +,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197 +,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197 +,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197 +,197,197,197,197,197,197,197,197,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200 +,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200 +,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200 +,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201 +,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201 +,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201},{ +202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202 +,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202 +,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,203,203,203,203,203,203,203,203 +,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203 +,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203 +,203,203,203,203,203,203,203,203,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204 +,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204 +,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204 +,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205 +,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205 +,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205},{ +198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198 +,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198 +,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198 +,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198 +,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198 +,198,198,198,198,198,198,198,198,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199 +,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199 +,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199 +,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199 +,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199 +,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199},{ +206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206 +,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206 +,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,207,207,207,207,207,207,207,207 +,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207 +,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207 +,207,207,207,207,207,207,207,207,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208 +,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208 +,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208 +,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209 +,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209 +,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209},{ +158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158 +,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158 +,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,179,179,179,179,179,179,179,179 +,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179 +,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180 +,180,180,180,180,180,180,180,180,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181 +,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,182,182,182,182,182,182,182,182 +,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182 +,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161 +,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161 +,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161},{ +162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162 +,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162 +,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,189,189,189,189,189,189,189,189 +,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189 +,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190 +,190,190,190,190,190,190,190,190,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191 +,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,192,192,192,192,192,192,192,192 +,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192 +,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165 +,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165 +,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165},{ +166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166 +,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166 +,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,193,193,193,193,193,193,193,193 +,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193 +,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194 +,194,194,194,194,194,194,194,194,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130 +,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130 +,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130 +,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130 +,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130 +,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130},{ +131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131 +,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131 +,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131 +,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131 +,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131 +,131,131,131,131,131,131,131,131,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195 +,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,196,196,196,196,196,196,196,196 +,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196 +,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174 +,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174 +,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174},{ +175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175 +,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175 +,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,176,176,176,176,176,176,176,176 +,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176 +,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176 +,176,176,176,176,176,176,176,176,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177 +,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,178,178,178,178,178,178,178,178 +,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178 +,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159 +,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159 +,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159},{ +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160 +,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160 +,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,183,183,183,183,183,183,183,183 +,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183 +,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184 +,184,184,184,184,184,184,184,184,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185 +,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,186,186,186,186,186,186,186,186 +,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186 +,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187 +,187,187,187,187,187,187,187,187,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188 +,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188},{ +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129 +,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129 +,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129 +,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129 +,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129 +,129,129,129,129,129,129,129,129,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156 +,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156 +,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156 +,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157 +,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157 +,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157},{ +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150 +,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150 +,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,151,151,151,151,151,151,151,151 +,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151 +,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151 +,151,151,151,151,151,151,151,151,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152 +,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152 +,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152 +,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153 +,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153 +,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153},{ +163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163 +,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163 +,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,164,164,164,164,164,164,164,164 +,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164 +,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164 +,164,164,164,164,164,164,164,164,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167 +,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167 +,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167 +,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173 +,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173 +,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173},{ +154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154 +,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154 +,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,155,155,155,155,155,155,155,155 +,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155 +,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155 +,155,155,155,155,155,155,155,155,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168 +,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168 +,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168 +,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169 +,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169 +,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169},{ +136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136 +,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136 +,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,137,137,137,137,137,137,137,137 +,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137 +,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137 +,137,137,137,137,137,137,137,137,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138 +,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138 +,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138 +,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139 +,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139 +,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139},{ +146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146 +,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146 +,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,147,147,147,147,147,147,147,147 +,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147 +,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147 +,147,147,147,147,147,147,147,147,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128 +,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128 +,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128 +,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128 +,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128 +,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128},{ +171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171 +,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171 +,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,172,172,172,172,172,172,172,172 +,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172 +,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172 +,172,172,172,172,172,172,172,172,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132 +,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132 +,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132 +,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133 +,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133 +,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133},{ +134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134 +,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134 +,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,135,135,135,135,135,135,135,135 +,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135 +,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135 +,135,135,135,135,135,135,135,135,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125 +,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125 +,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125 +,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125 +,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125 +,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125},{ +126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126 +,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126 +,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126 +,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126 +,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126 +,126,126,126,126,126,126,126,126,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140 +,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140 +,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140 +,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141 +,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141 +,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141},{ +142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142 +,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142 +,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,143,143,143,143,143,143,143,143 +,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143 +,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143 +,143,143,143,143,143,143,143,143,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144 +,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144 +,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144 +,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145 +,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145 +,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145},{ +127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127 +,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127 +,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127 +,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127 +,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127 +,127,127,127,127,127,127,127,127,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148 +,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148 +,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148 +,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149 +,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149 +,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149},{ +0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210 +,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197 +,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197 +,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,200,200,200,200,200,200,200,200 +,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200 +,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201 +,201,201,201,201,201,201,201,201,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202 +,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,203,203,203,203,203,203,203,203 +,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203 +,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204 +,204,204,204,204,204,204,204,204,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205 +,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205},{ +198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198 +,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198 +,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,199,199,199,199,199,199,199,199 +,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199 +,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199 +,199,199,199,199,199,199,199,199,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206 +,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,207,207,207,207,207,207,207,207 +,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207 +,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208 +,208,208,208,208,208,208,208,208,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209 +,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209},{ +124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124 +,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124 +,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124 +,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124 +,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124 +,124,124,124,124,124,124,124,124,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158 +,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,179,179,179,179,179,179,179,179 +,179,179,179,179,179,179,179,179,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180 +,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,182,182,182,182,182,182,182,182 +,182,182,182,182,182,182,182,182,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161 +,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161},{ +162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162 +,162,162,162,162,162,162,162,162,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189 +,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,191,191,191,191,191,191,191,191 +,191,191,191,191,191,191,191,191,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192 +,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165 +,165,165,165,165,165,165,165,165,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166 +,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,193,193,193,193,193,193,193,193 +,193,193,193,193,193,193,193,193,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194 +,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130 +,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130 +,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130},{ +131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131 +,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131 +,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,195,195,195,195,195,195,195,195 +,195,195,195,195,195,195,195,195,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196 +,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174 +,174,174,174,174,174,174,174,174,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175 +,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,176,176,176,176,176,176,176,176 +,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176 +,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,178,178,178,178,178,178,178,178 +,178,178,178,178,178,178,178,178,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159 +,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159},{ +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160 +,160,160,160,160,160,160,160,160,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183 +,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,185,185,185,185,185,185,185,185 +,185,185,185,185,185,185,185,185,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186 +,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,188,188,188,188,188,188,188,188 +,188,188,188,188,188,188,188,188,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170 +,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170 +,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170 +,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170 +,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170 +,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170},{ +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129 +,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129 +,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,156,156,156,156,156,156,156,156 +,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156 +,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157 +,157,157,157,157,157,157,157,157,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150 +,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,151,151,151,151,151,151,151,151 +,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151 +,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152 +,152,152,152,152,152,152,152,152,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153 +,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153},{ +163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163 +,163,163,163,163,163,163,163,163,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164 +,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,167,167,167,167,167,167,167,167 +,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167 +,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173 +,173,173,173,173,173,173,173,173,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122 +,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122 +,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122 +,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122 +,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122 +,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122},{ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123 +,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123 +,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123 +,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123 +,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123 +,123,123,123,123,123,123,123,123,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154 +,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,155,155,155,155,155,155,155,155 +,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155 +,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168 +,168,168,168,168,168,168,168,168,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169 +,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169},{ +136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136 +,136,136,136,136,136,136,136,136,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137 +,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,138,138,138,138,138,138,138,138 +,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138 +,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139 +,139,139,139,139,139,139,139,139,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146 +,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,147,147,147,147,147,147,147,147 +,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147 +,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128 +,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128 +,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128},{ +171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171 +,171,171,171,171,171,171,171,171,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172 +,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,132,132,132,132,132,132,132,132 +,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132 +,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133 +,133,133,133,133,133,133,133,133,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134 +,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,135,135,135,135,135,135,135,135 +,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135 +,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125 +,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125 +,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125},{ +126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126 +,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126 +,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,140,140,140,140,140,140,140,140 +,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140 +,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141 +,141,141,141,141,141,141,141,141,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142 +,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,143,143,143,143,143,143,143,143 +,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143 +,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144 +,144,144,144,144,144,144,144,144,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145 +,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145},{ +127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127 +,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127 +,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,148,148,148,148,148,148,148,148 +,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148 +,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149 +,149,149,149,149,149,149,149,149,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106 +,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106 +,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106 +,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106 +,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106 +,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106} +}; +const u_char TIFFFax1DNextState[230][256] = {{ +16,17,0,0,0,0,7,7,7,7,0,0,6,6,6,6,7,7,0,0,0,0,0,0 +,7,7,0,0,6,6,6,6,6,6,6,6,0,0,7,7,0,0,0,0,0,0,7,7 +,7,7,0,0,0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,7,7,0,0,18,19,7,7,7,7,0,0,0,0,7,7,0,0,0,0,6,6,6,6 +,6,6,6,6,0,0,20,0,0,21,22,23,24,25,7,7,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,6,6,6,6,6,6,6,6,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,6,6 +,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4},{ +26,27,28,0,0,29,7,7,0,30,31,32,0,33,7,7,7,7,34,0,35,36,37,0 +,0,38,39,40,6,6,6,6,6,6,6,6,0,41,42,0,0,43,44,0,45,46,7,7 +,7,7,47,48,49,50,51,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,7,7,7,7,6,6,6,6,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,26,27,28,0,0,29,7,7,0,30,31,32,0,33,7,7 +,7,7,34,0,35,36,37,0,0,38,39,40,6,6,6,6,6,6,6,6,0,41,42,0 +,0,43,44,0,45,46,7,7,7,7,47,48,49,50,51,0,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,7,7,7,7,6,6,6,6 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5},{ +52,53,54,0,55,56,57,0,0,58,59,60,61,62,7,7,7,7,63,64,65,66,67,0 +,0,68,69,70,6,6,6,6,6,6,6,6,7,7,7,7,7,7,0,0,6,6,6,6 +,6,6,6,6,0,0,7,7,6,6,6,6,6,6,6,6,52,53,54,0,55,56,57,0 +,0,58,59,60,61,62,7,7,7,7,63,64,65,66,67,0,0,68,69,70,6,6,6,6 +,6,6,6,6,7,7,7,7,7,7,0,0,6,6,6,6,6,6,6,6,0,0,7,7 +,6,6,6,6,6,6,6,6,52,53,54,0,55,56,57,0,0,58,59,60,61,62,7,7 +,7,7,63,64,65,66,67,0,0,68,69,70,6,6,6,6,6,6,6,6,7,7,7,7 +,7,7,0,0,6,6,6,6,6,6,6,6,0,0,7,7,6,6,6,6,6,6,6,6 +,52,53,54,0,55,56,57,0,0,58,59,60,61,62,7,7,7,7,63,64,65,66,67,0 +,0,68,69,70,6,6,6,6,6,6,6,6,7,7,7,7,7,7,0,0,6,6,6,6 +,6,6,6,6,0,0,7,7,6,6,6,6,6,6,6,6},{ +71,72,73,74,75,76,77,0,0,78,79,80,81,82,7,7,7,7,0,0,0,83,7,7 +,7,7,84,0,7,7,7,7,71,72,73,74,75,76,77,0,0,78,79,80,81,82,7,7 +,7,7,0,0,0,83,7,7,7,7,84,0,7,7,7,7,71,72,73,74,75,76,77,0 +,0,78,79,80,81,82,7,7,7,7,0,0,0,83,7,7,7,7,84,0,7,7,7,7 +,71,72,73,74,75,76,77,0,0,78,79,80,81,82,7,7,7,7,0,0,0,83,7,7 +,7,7,84,0,7,7,7,7,71,72,73,74,75,76,77,0,0,78,79,80,81,82,7,7 +,7,7,0,0,0,83,7,7,7,7,84,0,7,7,7,7,71,72,73,74,75,76,77,0 +,0,78,79,80,81,82,7,7,7,7,0,0,0,83,7,7,7,7,84,0,7,7,7,7 +,71,72,73,74,75,76,77,0,0,78,79,80,81,82,7,7,7,7,0,0,0,83,7,7 +,7,7,84,0,7,7,7,7,71,72,73,74,75,76,77,0,0,78,79,80,81,82,7,7 +,7,7,0,0,0,83,7,7,7,7,84,0,7,7,7,7},{ +85,86,87,88,89,90,91,0,0,92,93,0,0,94,0,0,85,86,87,88,89,90,91,0 +,0,92,93,0,0,94,0,0,85,86,87,88,89,90,91,0,0,92,93,0,0,94,0,0 +,85,86,87,88,89,90,91,0,0,92,93,0,0,94,0,0,85,86,87,88,89,90,91,0 +,0,92,93,0,0,94,0,0,85,86,87,88,89,90,91,0,0,92,93,0,0,94,0,0 +,85,86,87,88,89,90,91,0,0,92,93,0,0,94,0,0,85,86,87,88,89,90,91,0 +,0,92,93,0,0,94,0,0,85,86,87,88,89,90,91,0,0,92,93,0,0,94,0,0 +,85,86,87,88,89,90,91,0,0,92,93,0,0,94,0,0,85,86,87,88,89,90,91,0 +,0,92,93,0,0,94,0,0,85,86,87,88,89,90,91,0,0,92,93,0,0,94,0,0 +,85,86,87,88,89,90,91,0,0,92,93,0,0,94,0,0,85,86,87,88,89,90,91,0 +,0,92,93,0,0,94,0,0,85,86,87,88,89,90,91,0,0,92,93,0,0,94,0,0 +,85,86,87,88,89,90,91,0,0,92,93,0,0,94},{ +95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102 +,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102 +,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102 +,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102 +,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102 +,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102 +,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102 +,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102 +,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102 +,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102 +,95,96,97,98,99,100,101,102,95,96,97,98,99,100,101,102},{ +103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106 +,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106 +,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106 +,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106 +,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106 +,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106 +,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106 +,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106 +,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106 +,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106 +,103,104,105,106,103,104,105,106,103,104,105,106,103,104,105,106},{ +107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108 +,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108 +,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108 +,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108 +,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108 +,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108 +,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108 +,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108 +,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108 +,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108 +,107,108,107,108,107,108,107,108,107,108,107,108,107,108,107,108},{ +109,110,111,112,0,113,114,0,7,7,7,7,115,116,7,7,6,6,6,6,6,6,6,6 +,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +117,118,119,120,0,0,121,0,7,7,7,7,6,6,6,6,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,117,118,119,120,0,0,121,0,7,7,7,7,6,6,6,6 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +122,123,124,125,0,0,7,7,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,122,123,124,125,0,0,7,7 +,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,122,123,124,125,0,0,7,7,6,6,6,6,6,6,6,6 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,122,123,124,125,0,0,7,7,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4},{ +126,127,128,0,7,7,7,7,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,126,127,128,0,7,7,7,7,6,6,6,6,6,6,6,6 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,126,127,128,0,7,7,7,7 +,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,126,127,128,0,7,7,7,7,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,126,127,128,0,7,7,7,7,6,6,6,6,6,6,6,6 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,126,127,128,0,7,7,7,7 +,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,126,127,128,0,7,7,7,7,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,126,127,128,0,7,7,7,7,6,6,6,6,6,6,6,6 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5},{ +129,130,0,0,7,7,7,7,6,6,6,6,6,6,6,6,129,130,0,0,7,7,7,7 +,6,6,6,6,6,6,6,6,129,130,0,0,7,7,7,7,6,6,6,6,6,6,6,6 +,129,130,0,0,7,7,7,7,6,6,6,6,6,6,6,6,129,130,0,0,7,7,7,7 +,6,6,6,6,6,6,6,6,129,130,0,0,7,7,7,7,6,6,6,6,6,6,6,6 +,129,130,0,0,7,7,7,7,6,6,6,6,6,6,6,6,129,130,0,0,7,7,7,7 +,6,6,6,6,6,6,6,6,129,130,0,0,7,7,7,7,6,6,6,6,6,6,6,6 +,129,130,0,0,7,7,7,7,6,6,6,6,6,6,6,6,129,130,0,0,7,7,7,7 +,6,6,6,6,6,6,6,6,129,130,0,0,7,7,7,7,6,6,6,6,6,6,6,6 +,129,130,0,0,7,7,7,7,6,6,6,6,6,6,6,6,129,130,0,0,7,7,7,7 +,6,6,6,6,6,6,6,6,129,130,0,0,7,7,7,7,6,6,6,6,6,6,6,6 +,129,130,0,0,7,7,7,7,6,6,6,6,6,6,6,6},{ +131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7 +,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7 +,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7 +,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7 +,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7 +,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7 +,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7 +,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7 +,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7 +,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7 +,131,132,0,0,7,7,7,7,131,132,0,0,7,7,7,7},{ +133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0 +,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0 +,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0 +,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0 +,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0 +,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0 +,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0 +,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0 +,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0 +,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0,133,134,0,0 +,133,134,0,0,133,134,0,0,133,134,0,0,133,134},{ +135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136 +,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136 +,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136 +,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136 +,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136 +,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136 +,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136 +,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136 +,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136 +,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136 +,135,136,135,136,135,136,135,136,135,136,135,136,135,136,135,136},{ +137,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +137,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +137,0,7,7,6,6,6,6,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,5,5,5,5,5,5,5,5 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +137,0,7,7,5,5,5,5,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7 +,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +137,0,6,6,5,5,5,5,4,4,4,4,4,4,4,4,7,7,0,0,0,0,0,0 +,7,7,7,7,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +137,7,6,6,5,5,5,5,0,138,139,140,0,0,141,142,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +143,7,6,6,144,145,146,147,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6 +,7,7,7,7,7,7,7,7,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,6,6,6,6,6,6,6,6,7,7,7,7,6,6,6,6,6,6,6,6,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,5,5,5,5,5,5,5,5 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +148,7,149,150,7,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,7,7,7,7 +,5,5,5,5,5,5,5,5,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7 +,6,6,6,6,7,7,7,7,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,7,7,7,7,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,6,6,6,6 +,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,6,6,6,6,7,7,7,7,0,0,0,0,6,6,6,6,6,6,6,6,7,7,7,7 +,7,7,7,7,6,6,6,6,7,7,7,7,7,7,7,7,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,7,7,7,7,0,0,7,7,7,7,0,0,0,0,0,0 +,0,0,0,0,6,6,6,6,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +151,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +151,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +151,0,7,7,6,6,6,6,12,12,12,12,12,12,12,12,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 +,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,5,5,5,5,5,5,5,5 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,6,6,6,6,7,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,7,7,7,7 +,7,7,7,7,6,6,6,6,6,6,6,6,7,7,7,7,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6 +,7,7,7,7,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,5,5,5,5,5,5,5,5 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 +,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +151,0,7,7,13,13,13,13,12,12,12,12,12,12,12,12,11,11,11,11,11,11,11,11 +,11,11,11,11,11,11,11,11,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7 +,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,5,5,5,5,5,5,5,5 +,7,7,0,0,0,0,7,7,7,7,0,0,0,0,7,7,7,7,0,0,6,6,6,6 +,6,6,6,6,0,0,7,7,7,7,7,7,0,0,7,7,7,7,0,0,0,0,0,0 +,5,5,5,5,5,5,5,5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,6,6,6,6,7,7,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,5,5,5,5,5,5,5,5 +,5,5,5,5,5,5,5,5,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +,7,7,7,7,6,6,6,6,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,7,7,7,7,7,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,7,7,7,7 +,7,7,7,7,7,7,7,7,6,6,6,6,7,7,7,7,5,5,5,5,5,5,5,5 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +151,0,14,14,13,13,13,13,12,12,12,12,12,12,12,12,7,7,0,0,0,0,0,0 +,7,7,7,7,0,0,0,0,6,6,6,6,0,152,153,0,0,154,155,0,0,156,7,7 +,7,7,157,0,0,0,158,0,0,159,160,161,6,6,6,6,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,7,7,0,0,0,0,0,0,0,0,0,0,6,6,6,6 +,6,6,6,6,0,0,0,0,0,0,0,0,0,0,7,7,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,5,5,5,5,5,5,5,5,0,0,0,0,0,0,7,7,7,7,0,0,0,0,0,0 +,7,7,0,0,6,6,6,6,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +151,15,14,14,13,13,13,13,0,162,163,164,0,0,165,166,7,7,167,168,169,170,171,0 +,0,172,173,174,175,176,7,7,5,5,5,5,5,5,5,5,0,177,178,179,180,181,7,7 +,7,7,182,183,184,185,186,0,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,6,6,6,6,187,188,189,0,0,190,191,192,0,193,7,7,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +194,15,14,14,195,196,197,198,0,199,200,201,202,203,204,0,6,6,6,6,205,206,207,0 +,0,208,209,210,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,7,7,211,212,213,214,215,0,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +216,15,217,218,219,220,221,222,7,7,223,224,225,226,7,7,6,6,6,6,6,6,6,6 +,0,227,228,229,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +137,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +137,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +137,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +151,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +151,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +151,0,7,7,6,6,6,6,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +,9,9,9,9,9,9,9,9,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 +,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 +,4,4,4,4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},{ +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 +,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} +}; +#else +extern const u_char TIFFFax2DMode[][256]; +extern const u_char TIFFFax2DNextState[][256]; +extern const u_char TIFFFaxUncompAction[][256]; +extern const u_char TIFFFaxUncompNextState[][256]; +extern const u_char TIFFFax1DAction[][256]; +extern const u_char TIFFFax1DNextState[][256]; +#endif diff --git a/panda/src/tiff/mkg3states.c b/panda/src/tiff/mkg3states.c new file mode 100644 index 0000000000..945a9245d2 --- /dev/null +++ b/panda/src/tiff/mkg3states.c @@ -0,0 +1,806 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * Program to construct Group 3 & Group 4 decoding tables. + * + * This code is derived from code by Michael P. Marking. In + * particular, the algorithms to generate the null_mode and + * horiz_mode state tables are his. See the comments below + * for more information. + * + * BEGIN (from the original source) + LEGAL + * Copyright 1989, 1990 Michael P. Marking, Post Office Box 8039, + * Scottsdale, Arizona 85252-8039. All rights reserved. + * + * License is granted by the copyright holder to distribute and use this + * code without payment of royalties or the necessity of notification as + * long as this notice (all the text under "LEGAL") is included. + * + * Reference: $Id$ + * + * This program is offered without any warranty of any kind. It includes + * no warranty of merchantability or fitness for any purpose. Testing and + * suitability for any use are the sole responsibility of the user. + * + INFORMATION + * Although there is no support offered with this program, the author will + * endeavor to correct errors. Updates will also be made available from + * time to time. + * + * Contact: Michael P. Marking, Post Office Box 8039, Scottsdale, Arizona + * 85252-8039 USA. Replies are not guaranteed to be swift. Beginning + * July 1990, e-mail may be sent to uunet!ipel!marking. + * + * Also beginning in July 1990, this code will be archived at the + * ipel!phoenix BBS in file g3g4.zoo. The 24-hour telephone number + * for 300/1200/2400 is (602)274-0462. When logging in, specify user + * "public", system "bbs", and password "public". + * + * This code is also available from the C Users Group in volume 317. + * + * END (from the original source) + */ +#include +#include +#include "tiffcomp.h" + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#define WHITE 0 +#define BLACK 1 + +/* + * G3 2D and G4 decoding modes. Note that + * the vertical modes are ordered so that + * (mode - MODE_VERT_V0) gives the vertical + * adjustment for the b1 parameter. + */ +#define MODE_NULL 0 +#define MODE_PASS 1 +#define MODE_HORIZ 2 +#define MODE_VERT_VL3 3 +#define MODE_VERT_VL2 4 +#define MODE_VERT_VL1 5 +#define MODE_VERT_V0 6 +#define MODE_VERT_VR1 7 +#define MODE_VERT_VR2 8 +#define MODE_VERT_VR3 9 +#define MODE_UNCOMP 10 +#define MODE_ERROR 11 +#define MODE_ERROR_1 12 + +unsigned long +append_0(unsigned long prefix) +{ + return (prefix + (1L<<16)); +} + +unsigned long +append_1(unsigned long prefix) +{ + static unsigned short prefix_bit[16] = { + 0x8000, 0x4000, 0x2000, 0x1000, + 0x0800, 0x0400, 0x0200, 0x0100, + 0x0080, 0x0040, 0x0020, 0x0010, + 0x0008, 0x0004, 0x0002, 0x0001 + }; + unsigned char len = (prefix >> 16) & 0xf; + return (append_0(prefix) + prefix_bit[len]); +} + +#define G3CODES +#include "t4.h" + +short +search_table(unsigned long prefix, const tableentry* tab, int n) +{ + unsigned short len = (prefix >> 16) & 0xf; + unsigned short code = (prefix & 0xffff) >> (16 - len); + + while (n-- > 0) { + if (tab->length == len && tab->code == code) + return ((short) tab->runlen); + tab++; + } + return (G3CODE_INCOMP); +} + +#define NCODES(a) (sizeof (a) / sizeof (a[0])) +short +white_run_length(unsigned long prefix) +{ + return (search_table(prefix, TIFFFaxWhiteCodes, NCODES(TIFFFaxWhiteCodes))); +} + +short +black_run_length(unsigned long prefix) +{ + return (search_table(prefix, TIFFFaxBlackCodes, NCODES(TIFFFaxBlackCodes))); +} +#undef NCODES + +#define MAX_NULLPREFIX 200 /* max # of null-mode prefixes */ +typedef unsigned char NullModeTable[MAX_NULLPREFIX][256]; +#define MAX_HORIZPREFIX 250 /* max # of incomplete 1-D prefixes */ +typedef unsigned char HorizModeTable[MAX_HORIZPREFIX][256]; + + /* the bit string corresponding to this row of the decoding table */ +long null_mode_prefix[MAX_NULLPREFIX]; +NullModeTable null_mode; /* MODE_*, indexed by bit and byte */ +NullModeTable null_mode_next_state; /* next row of decoding tables to use */ + /* number of prefixes or rows in the G4 decoding tables */ +short null_mode_prefix_count = 0; + +/* + * 2D uncompressed mode codes. Note + * that two groups of codes are arranged + * so that the decoder can caluclate the + * length of the run by subtracting the + * code from a known base value. + */ +#define UNCOMP_INCOMP 0 +/* runs of [0]*1 */ +#define UNCOMP_RUN0 1 +#define UNCOMP_RUN1 2 +#define UNCOMP_RUN2 3 +#define UNCOMP_RUN3 4 +#define UNCOMP_RUN4 5 +#define UNCOMP_RUN5 6 +#define UNCOMP_RUN6 7 +/* runs of [0]* w/ terminating color */ +#define UNCOMP_TRUN0 8 +#define UNCOMP_TRUN1 9 +#define UNCOMP_TRUN2 10 +#define UNCOMP_TRUN3 11 +#define UNCOMP_TRUN4 12 +/* special code for unexpected EOF */ +#define UNCOMP_EOF 13 +/* invalid code encountered */ +#define UNCOMP_INVALID 14 + +long uncomp_mode_prefix[MAX_NULLPREFIX]; +NullModeTable uncomp_mode; +NullModeTable uncomp_mode_next_state; +short uncomp_mode_prefix_count = 0; + +/* + * Decoding action values for horiz_mode. + */ +#define ACT_INCOMP 0 /* incompletely decoded code */ +#define ACT_INVALID 1 /* invalide code */ +#define ACT_WRUNT 2 /* terminating white run code */ +#define ACT_WRUN 65 /* non-terminating white run code */ +#define ACT_BRUNT 106 /* terminating black run code */ +#define ACT_BRUN 169 /* non-terminating black run code */ +#define ACT_EOL 210 /* end-of-line code */ +HorizModeTable horiz_mode; + +short +horiz_mode_code_black(short runlen) +{ + return (runlen < 64 ? runlen + ACT_BRUNT : (runlen / 64) + ACT_BRUN); +} + +short +horiz_mode_code_white(short runlen) +{ + return (runlen < 64 ? runlen + ACT_WRUNT : (runlen / 64) + ACT_WRUN); +} + +/* + * If the corresponding horiz_mode entry is ACT_INCOMP + * this entry is a row number for decoding the next byte; + * otherwise, it is the bit number with which to continue + * decoding the next codeword. + */ +HorizModeTable horiz_mode_next_state; + /* prefixes corresponding to the rows of the decoding table */ +long horiz_mode_prefix[MAX_HORIZPREFIX]; + /* color of next run, BLACK or WHITE */ +char horiz_mode_color[MAX_HORIZPREFIX]; +short horiz_mode_prefix_count = 0; + +static unsigned char bit_mask[8] = + { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; + +void build_null_mode_tables(void); +short find_horiz_mode_prefix(long, char); +short find_null_mode_prefix(long); +short null_mode_type(long); +void build_horiz_mode_tables(void); +short horiz_mode_code_black(short); +short horiz_mode_code_white(short); +void build_uncomp_mode_tables(void); +void write_tables(FILE*); + +int verbose = FALSE; +char *storage_class = ""; +int packoutput = TRUE; + +void +main(int argc, char** argv) +{ + while (argc > 1 && argv[1][0] == '-') { + if (strcmp(argv[1], "-v") == 0) { + verbose = TRUE; + argc--, argv++; + } else if (strcmp(argv[1], "-c") == 0) { + storage_class = "const "; + argc--, argv++; + } else if (strcmp(argv[1], "-p") == 0) { + packoutput = FALSE; + argc--, argv++; + } + } + build_null_mode_tables(); /* null mode decoding tables */ + if (verbose) { + fprintf(stderr, "%d null mode prefixes defined\n", + (int) null_mode_prefix_count); + fprintf(stderr, "building uncompressed mode scripts...\n"); + } + build_uncomp_mode_tables(); /* uncompressed mode decoding tables */ + if (verbose) { + fprintf(stderr, "%d uncompressed mode prefixes defined\n", + (int) uncomp_mode_prefix_count); + fprintf(stderr, "building 1D scripts...\n"); + } + build_horiz_mode_tables(); /* 1D decoding tables */ + if (verbose) + fprintf(stderr, "%d incomplete prefixes defined\n", + (int) horiz_mode_prefix_count); + write_tables(stdout); + exit(0); +} + +void +write_null_mode_table(FILE* fd, NullModeTable table, char* name) +{ + int i, j, lastNonZero; + char* outersep; + char* sep; + + fprintf(fd, "%su_char\t%s[%d][256] = {", storage_class, + name, (int) null_mode_prefix_count); + outersep = ""; + if (!packoutput) { + for (i = 0; i < null_mode_prefix_count; i++) { + fprintf(fd, "%s\n/* prefix %d */ {\n", outersep, i); + sep = " "; + for (j = 0; j < 256; j++) { + fprintf(fd, "%s%2d", sep, (int) table[i][j]); + if (((j+1) % 16) == 0) { + fprintf(fd, ", /* %3d-%3d */\n", j-15, j); + sep = " "; + } else + sep = ","; + } + fprintf(fd, "}"); + outersep = ","; + } + } else { + for (i = 0; i < null_mode_prefix_count; i++) { + fprintf(fd, "%s{\n", outersep); + for (j = 255; j > 0; j--) + if (table[i][j] != 0) + break; + sep = ""; + lastNonZero = j; + for (j = 0; j <= lastNonZero; j++) { + fprintf(fd, "%s%d", sep, (int) table[i][j]); + if (((j+1) % 24) == 0) + putc('\n', fd); + sep = ","; + } + fprintf(fd, "}"); + outersep = ","; + } + } + fprintf(fd, "\n};\n"); +} + +void +write_horiz_mode_table(FILE* fd, HorizModeTable table, char* name) +{ + int i, j, lastNonZero; + char* outersep; + char* sep; + + fprintf(fd, "%s u_char\t%s[%d][256] = {", storage_class, + name, (int) horiz_mode_prefix_count); + outersep = ""; + if (!packoutput) { + for (i = 0; i < horiz_mode_prefix_count; i++) { + fprintf(fd, "%s\n/* prefix %d */ {\n", outersep, i); + sep = " "; + for (j = 0; j < 256; j++) { + fprintf(fd, "%s%3d", sep, (int) table[i][j]); + if (((j+1) % 14) == 0) { + fprintf(fd, ", /* %3d-%3d */\n", j-13, j); + sep = " "; + } else + sep = ","; + } + fprintf(fd, "\n}"); + outersep = ","; + } + } else { + outersep = ""; + for (i = 0; i < horiz_mode_prefix_count; i++) { + fprintf(fd, "%s{\n", outersep); + for (j = 255; j > 0; j--) + if (table[i][j] != 0) + break; + sep = ""; + lastNonZero = j; + for (j = 0; j <= lastNonZero; j++) { + fprintf(fd, "%s%d", sep, (int) table[i][j]); + if (((j+1) % 24) == 0) + putc('\n', fd); + sep = ","; + } + fprintf(fd, "}"); + outersep = ","; + } + } + fprintf(fd, "\n};\n"); +} + +void +write_define(FILE* fd, char* name, int value, char* comment) +{ + fprintf(fd, "#define\t%s\t%d", name, value); + if (!packoutput && comment) + fprintf(fd, "\t/* %s */", comment); + fprintf(fd, "\n"); +} + +void +write_preamble(FILE* fd) +{ + fprintf(fd, "%s\n", +"/* DO NOT EDIT THIS FILE, IT WAS AUTOMATICALLY CREATED BY mkg3state */"); + write_define(fd, "ACT_INCOMP", ACT_INCOMP, "incompletely decoded code"); + write_define(fd, "ACT_INVALID", ACT_INVALID, "invalide code"); + write_define(fd, "ACT_WRUNT", ACT_WRUNT, "terminating white run code"); + write_define(fd, "ACT_WRUN", ACT_WRUN, "non-terminating white run code"); + write_define(fd, "ACT_BRUNT", ACT_BRUNT, "terminating black run code"); + write_define(fd, "ACT_BRUN", ACT_BRUN, "non-terminating black run code"); + write_define(fd, "ACT_EOL", ACT_EOL, "end-of-line code"); + fprintf(fd, "\n"); + fprintf(fd, "/* modes that the decoder can be in */\n"); + write_define(fd, "MODE_NULL", MODE_NULL, NULL); + write_define(fd, "MODE_PASS", MODE_PASS, NULL); + write_define(fd, "MODE_HORIZ", MODE_HORIZ, NULL); + write_define(fd, "MODE_VERT_V0", MODE_VERT_V0, NULL); + write_define(fd, "MODE_VERT_VR1", MODE_VERT_VR1, NULL); + write_define(fd, "MODE_VERT_VR2", MODE_VERT_VR2, NULL); + write_define(fd, "MODE_VERT_VR3", MODE_VERT_VR3, NULL); + write_define(fd, "MODE_VERT_VL1", MODE_VERT_VL1, NULL); + write_define(fd, "MODE_VERT_VL2", MODE_VERT_VL2, NULL); + write_define(fd, "MODE_VERT_VL3", MODE_VERT_VL3, NULL); + write_define(fd, "MODE_UNCOMP", MODE_UNCOMP, NULL); + write_define(fd, "MODE_ERROR", MODE_ERROR, NULL); + write_define(fd, "MODE_ERROR_1", MODE_ERROR_1, NULL); + fprintf(fd, "\n"); + fprintf(fd, "#define\tRUNLENGTH(ix) (TIFFFaxWhiteCodes[ix].runlen)\n"); + fprintf(fd, "\n"); + write_define(fd, "UNCOMP_INCOMP", UNCOMP_INCOMP, NULL); + fprintf(fd, "/* runs of [0]*1 */\n"); + write_define(fd, "UNCOMP_RUN0", UNCOMP_RUN0, NULL); + write_define(fd, "UNCOMP_RUN1", UNCOMP_RUN1, NULL); + write_define(fd, "UNCOMP_RUN2", UNCOMP_RUN2, NULL); + write_define(fd, "UNCOMP_RUN3", UNCOMP_RUN3, NULL); + write_define(fd, "UNCOMP_RUN4", UNCOMP_RUN4, NULL); + write_define(fd, "UNCOMP_RUN5", UNCOMP_RUN5, NULL); + write_define(fd, "UNCOMP_RUN6", UNCOMP_RUN6, NULL); + fprintf(fd, "/* runs of [0]* w/ terminating color */\n"); + write_define(fd, "UNCOMP_TRUN0", UNCOMP_TRUN0, NULL); + write_define(fd, "UNCOMP_TRUN1", UNCOMP_TRUN1, NULL); + write_define(fd, "UNCOMP_TRUN2", UNCOMP_TRUN2, NULL); + write_define(fd, "UNCOMP_TRUN3", UNCOMP_TRUN3, NULL); + write_define(fd, "UNCOMP_TRUN4", UNCOMP_TRUN4, NULL); + fprintf(fd, "/* special code for unexpected EOF */\n"); + write_define(fd, "UNCOMP_EOF", UNCOMP_EOF, NULL); + fprintf(fd, "/* invalid code encountered */\n"); + write_define(fd, "UNCOMP_INVALID", UNCOMP_INVALID, NULL); + fprintf(fd, "/* codes >= terminate uncompress mode */\n"); + fprintf(fd, "#define\tUNCOMP_EXIT UNCOMP_TRUN0\n"); + fprintf(fd, "\n"); +} + +void +extern_table(FILE* fd, char* name) +{ + fprintf(fd, "extern\t%su_char %s[][256];\n", storage_class, name); +} + +void +write_tables(FILE* fd) +{ + write_preamble(fd); + fprintf(fd, "#ifdef G3STATES\n"); + write_null_mode_table(fd, null_mode, "TIFFFax2DMode"); + write_null_mode_table(fd, null_mode_next_state, "TIFFFax2DNextState"); + write_null_mode_table(fd, uncomp_mode, "TIFFFaxUncompAction"); + write_null_mode_table(fd, uncomp_mode_next_state, "TIFFFaxUncompNextState"); + write_horiz_mode_table(fd, horiz_mode, "TIFFFax1DAction"); + write_horiz_mode_table(fd, horiz_mode_next_state, "TIFFFax1DNextState"); + fprintf(fd, "#else\n"); + extern_table(fd, "TIFFFax2DMode"); + extern_table(fd, "TIFFFax2DNextState"); + extern_table(fd, "TIFFFaxUncompAction"); + extern_table(fd, "TIFFFaxUncompNextState"); + extern_table(fd, "TIFFFax1DAction"); + extern_table(fd, "TIFFFax1DNextState"); + fprintf(fd, "#endif\n"); +} + +short +find_null_mode_prefix(long prefix) +{ + short j1; + + if (prefix == 0L) + return (0); + for (j1 = 8; j1 < null_mode_prefix_count; j1++) + if (prefix == null_mode_prefix[j1]) + return (j1); + if (null_mode_prefix_count == MAX_NULLPREFIX) { + fprintf(stderr, "ERROR: null mode prefix table overflow\n"); + exit(1); + } + if (verbose) + fprintf(stderr, "adding null mode prefix[%d] 0x%lx\n", + (int) null_mode_prefix_count, prefix); + null_mode_prefix[null_mode_prefix_count++] = prefix; + return (null_mode_prefix_count-1); +} + +short +find_horiz_mode_prefix(long prefix, char color) +{ + short j1; + + for (j1 = 0; j1 < horiz_mode_prefix_count; j1++) + if (prefix == horiz_mode_prefix[j1] && horiz_mode_color[j1] == color) + return (j1); + /* + * It wasn't found, so add it to the tables, but first, is there room? + */ + if (horiz_mode_prefix_count == MAX_HORIZPREFIX) { + fprintf(stderr, "ERROR: 1D prefix table overflow\n"); + exit(1); + } + /* OK, there's room... */ + if (verbose) + fprintf(stderr, "\nhoriz mode prefix %d, color %c = 0x%lx ", + (int) horiz_mode_prefix_count, "WB"[color], prefix); + horiz_mode_prefix[horiz_mode_prefix_count] = prefix; + horiz_mode_color[horiz_mode_prefix_count] = color; + horiz_mode_prefix_count++; + return (horiz_mode_prefix_count - 1); +} + +short +find_uncomp_mode_prefix(long prefix) +{ + short j1; + + if (prefix == 0L) + return (0); + for (j1 = 8; j1 < uncomp_mode_prefix_count; j1++) + if (prefix == uncomp_mode_prefix[j1]) + return (j1); + if (uncomp_mode_prefix_count == MAX_NULLPREFIX) { + fprintf(stderr, "ERROR: uncomp mode prefix table overflow\n"); + exit(1); + } + if (verbose) + fprintf(stderr, "adding uncomp mode prefix[%d] 0x%lx\n", + (int) uncomp_mode_prefix_count, prefix); + uncomp_mode_prefix[uncomp_mode_prefix_count++] = prefix; + return (uncomp_mode_prefix_count-1); +} + +short +null_mode_type(long prefix) +{ + switch (prefix) { + case 0x18000L: return (MODE_VERT_V0); /* 1 */ + case 0x36000L: return (MODE_VERT_VR1); /* 011 */ + case 0x34000L: return (MODE_VERT_VL1); /* 010 */ + case 0x32000L: return (MODE_HORIZ); /* 001 */ + case 0x41000L: return (MODE_PASS); /* 0001 */ + case 0x60C00L: return (MODE_VERT_VR2); /* 0000 11 */ + case 0x60800L: return (MODE_VERT_VL2); /* 0000 10 */ + case 0x70600L: return (MODE_VERT_VR3); /* 0000 011 */ + case 0x70400L: return (MODE_VERT_VL3); /* 0000 010 */ + case 0x80200L: return (MODE_ERROR); /* 0000 0010 */ + case 0x90300L: return (MODE_ERROR); /* 0000 0011 0 */ + case 0xA0380L: return (MODE_ERROR); /* 0000 0011 10 */ + case 0xA03C0L: return (MODE_UNCOMP); /* 0000 0011 11 */ + /* + * Under the assumption that there are no + * errors in the file, then this bit string + * can only be the beginning of an EOL code. + */ + case 0x70000L: return (MODE_ERROR_1); /* 0000 000 */ + } + return (-1); +} + +short +uncomp_mode_type(long prefix) +{ + short code; + short len; + switch (prefix) { + case 0x18000L: return (UNCOMP_RUN1); /* 1 */ + case 0x24000L: return (UNCOMP_RUN2); /* 01 */ + case 0x32000L: return (UNCOMP_RUN3); /* 001 */ + case 0x41000L: return (UNCOMP_RUN4); /* 0001 */ + case 0x50800L: return (UNCOMP_RUN5); /* 0000 1 */ + case 0x60400L: return (UNCOMP_RUN6); /* 0000 01 */ + case 0x70200L: return (UNCOMP_TRUN0); /* 0000 001 */ + case 0x80100L: return (UNCOMP_TRUN1); /* 0000 0001 */ + case 0x90080L: return (UNCOMP_TRUN2); /* 0000 0000 1 */ + case 0xA0040L: return (UNCOMP_TRUN3); /* 0000 0000 01 */ + case 0xB0020L: return (UNCOMP_TRUN4); /* 0000 0000 001 */ + } + code = prefix & 0xffffL; + len = (prefix >> 16) & 0xf; + return ((code || len > 10) ? UNCOMP_INVALID : -1); +} + +#define BASESTATE(b) ((unsigned char) ((b) & 0x7)) + +void +build_null_mode_tables(void) +{ + short prefix; + + /* + * Note: the first eight entries correspond to + * a null prefix and starting bit numbers 0-7. + */ + null_mode_prefix_count = 8; + for (prefix = 0; prefix < null_mode_prefix_count; prefix++) { + short byte; + for (byte = 0; byte < 256; byte++) { + short firstbit; + short bit; + long curprefix; + char found_code = FALSE; + + if (prefix < 8) { + curprefix = 0L; + firstbit = prefix; + } else { + curprefix = null_mode_prefix[prefix]; + firstbit = 0; + } + for (bit = firstbit; bit < 8 && !found_code; bit++) { + short mode; + + if (bit_mask[bit] & byte) + curprefix = append_1(curprefix); + else + curprefix = append_0(curprefix); + switch (mode = null_mode_type(curprefix)) { + case MODE_PASS: + case MODE_HORIZ: + case MODE_VERT_V0: + case MODE_VERT_VR1: + case MODE_VERT_VR2: + case MODE_VERT_VR3: + case MODE_VERT_VL1: + case MODE_VERT_VL2: + case MODE_VERT_VL3: + case MODE_UNCOMP: + case MODE_ERROR: + case MODE_ERROR_1: + /* + * NOTE: if the bit number is 8, then the table + * entry will be zero, which indicates a new byte + * is to be fetched during the decoding process + */ + found_code = TRUE; + null_mode[prefix][byte] = (unsigned char) mode; + null_mode_next_state[prefix][byte] = BASESTATE(bit+1); + break; + } + } + if (!found_code) { + null_mode_next_state[prefix][byte] = (unsigned char) + find_null_mode_prefix(curprefix); + /* + * This indicates to the decoder that + * no valid code has yet been identified. + */ + null_mode[prefix][byte] = MODE_NULL; + } + } + } +} + +void +build_horiz_mode_tables(void) +{ + unsigned short byte; + short prefix; + + /* + * The first 8 are for white, + * the second 8 are for black, + * beginning with bits 0-7. + */ + horiz_mode_prefix_count = 16; + for (prefix = 0; prefix < horiz_mode_prefix_count; prefix++) + for (byte = 0; byte < 256; byte++) { + short bits_digested = 0; + short bit; + short firstbit; + char color; + unsigned long curprefix; + + if (prefix < 8) { + color = WHITE; + curprefix = 0L; + firstbit = prefix; + } else if (prefix < 16) { + color = BLACK; + curprefix = 0L; + firstbit = prefix - 8; + } else { + color = horiz_mode_color[prefix]; + curprefix = horiz_mode_prefix[prefix]; + firstbit = 0; + } + for (bit = firstbit; bit < 8 && !bits_digested; bit++) { + if (bit_mask[bit] & byte) + curprefix = append_1(curprefix); + else + curprefix = append_0(curprefix); + /* + * The following conversion allows for arbitrary strings of + * zeroes to precede the end-of-line code 0000 0000 0001. + * It assumes no errors in the data, and is based on + * the assumption that the code replaced (12 consecutive + * zeroes) can only be "legally" encountered before the + * end-of-line code. This assumption is valid only for + * a Group 3 image; the combination will never occur + * in horizontal mode in a proper Group 4 image. + */ + if (curprefix == 0xC0000L) + curprefix = 0xB0000L; + if (color == WHITE) { + short runlength = white_run_length(curprefix); + + if (runlength == G3CODE_INVALID) { + horiz_mode[prefix][byte] = (unsigned char) ACT_INVALID; + horiz_mode_next_state[prefix][byte] = (unsigned char) bit; + bits_digested = bit + 1; + } else if (runlength == G3CODE_EOL) { /* Group 3 only */ + horiz_mode[prefix][byte] = (unsigned char) ACT_EOL; + horiz_mode_next_state[prefix][byte] = BASESTATE(bit+1); + bits_digested = bit + 1; + } else if (runlength != G3CODE_INCOMP) { + horiz_mode[prefix][byte] = (unsigned char) + horiz_mode_code_white(runlength); + horiz_mode_next_state[prefix][byte] = BASESTATE(bit+1); + bits_digested = bit + 1; + } + } else { /* color == BLACK */ + short runlength = black_run_length(curprefix); + + if (runlength == G3CODE_INVALID) { + horiz_mode[prefix][byte] = (unsigned char) ACT_INVALID; + horiz_mode_next_state[prefix][byte] = (unsigned char) (bit+8); + bits_digested = bit + 1; + } else if (runlength == G3CODE_EOL) { /* Group 3 only */ + horiz_mode[prefix][byte] = (unsigned char) ACT_EOL; + horiz_mode_next_state[prefix][byte] = BASESTATE(bit+1); + bits_digested = bit + 1; + } else if (runlength != G3CODE_INCOMP) { + horiz_mode[prefix][byte] = (unsigned char) + horiz_mode_code_black(runlength); + horiz_mode_next_state[prefix][byte] = BASESTATE(bit+1); + bits_digested = bit + 1; + } + } + } + if (!bits_digested) { /* no codewords after examining byte */ + horiz_mode[prefix][byte] = (unsigned char) ACT_INCOMP; + horiz_mode_next_state[prefix][byte] = (unsigned char) + find_horiz_mode_prefix(curprefix, color); + } + } +} + +void +build_uncomp_mode_tables(void) +{ + short prefix; + + /* + * Note: the first eight entries correspond to + * a null prefix and starting bit numbers 0-7. + */ + uncomp_mode_prefix_count = 8; + for (prefix = 0; prefix < uncomp_mode_prefix_count; prefix++) { + short byte; + for (byte = 0; byte < 256; byte++) { + short firstbit; + short bit; + long curprefix; + char found_code = FALSE; + + if (prefix < 8) { + curprefix = 0L; + firstbit = prefix; + } else { + curprefix = uncomp_mode_prefix[prefix]; + firstbit = 0; + } + for (bit = firstbit; bit < 8 && !found_code; bit++) { + short mode; + + if (bit_mask[bit] & byte) + curprefix = append_1(curprefix); + else + curprefix = append_0(curprefix); + mode = uncomp_mode_type(curprefix); + if (mode != -1) { + /* + * NOTE: if the bit number is 8, then the table + * entry will be zero, which indicates a new byte + * is to be fetched during the decoding process + */ + found_code = TRUE; + uncomp_mode[prefix][byte] = (unsigned char) mode; + uncomp_mode_next_state[prefix][byte] = BASESTATE(bit+1); + break; + } + } + if (!found_code) { + uncomp_mode_next_state[prefix][byte] = (unsigned char) + find_uncomp_mode_prefix(curprefix); + /* + * This indicates to the decoder that + * no valid code has yet been identified. + */ + uncomp_mode[prefix][byte] = UNCOMP_INCOMP; + } + } + } +} diff --git a/panda/src/tiff/mkspans.c b/panda/src/tiff/mkspans.c new file mode 100644 index 0000000000..5e5f1dfecb --- /dev/null +++ b/panda/src/tiff/mkspans.c @@ -0,0 +1,50 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +#include "tiffcomp.h" + +/* + * Hack program to construct tables used to find + * runs of zeros and ones in Group 3 Fax encoding. + */ + +dumparray(name, runs) + char *name; + unsigned char runs[256]; +{ + register int i; + register char *sep; + printf("static u_char %s[256] = {\n", name); + sep = " "; + for (i = 0; i < 256; i++) { + printf("%s%d", sep, runs[i]); + if (((i + 1) % 16) == 0) { + printf(", /* 0x%02x - 0x%02x */\n", i-15, i); + sep = " "; + } else + sep = ", "; + } + printf("\n};\n"); +} + +main() +{ + unsigned char runs[2][256]; + + memset(runs[0], 0, 256*sizeof (char)); + memset(runs[1], 0, 256*sizeof (char)); + { register int run, runlen, i; + runlen = 1; + for (run = 0x80; run != 0xff; run = (run>>1)|0x80) { + for (i = run-1; i >= 0; i--) { + runs[1][run|i] = runlen; + runs[0][(~(run|i)) & 0xff] = runlen; + } + runlen++; + } + runs[1][0xff] = runs[0][0] = 8; + } + dumparray("bruns", runs[0]); + dumparray("wruns", runs[1]); +} diff --git a/panda/src/tiff/t4.h b/panda/src/tiff/t4.h new file mode 100644 index 0000000000..22df8a120b --- /dev/null +++ b/panda/src/tiff/t4.h @@ -0,0 +1,285 @@ +/* $Header$ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _T4_ +#define _T4_ +/* + * CCITT T.4 1D Huffman runlength codes and + * related definitions. Given the small sizes + * of these tables it might does not seem + * worthwhile to make code & length 8 bits. + */ +typedef struct tableentry { + unsigned short length; /* bit length of g3 code */ + unsigned short code; /* g3 code */ + short runlen; /* run length in bits */ +} tableentry; + +#define EOL 0x001 /* EOL code value - 0000 0000 0000 1 */ + +/* status values returned instead of a run length */ +#define G3CODE_INVALID -1 +#define G3CODE_INCOMP -2 +#define G3CODE_EOL -3 +#define G3CODE_EOF -4 + +/* + * Note that these tables are ordered such that the + * index into the table is known to be either the + * run length, or (run length / 64) + a fixed offset. + * + * NB: The G3CODE_INVALID entries are only used + * during state generation (see mkg3states.c). + */ +#ifdef G3CODES +const tableentry TIFFFaxWhiteCodes[] = { + { 8, 0x35, 0 }, /* 0011 0101 */ + { 6, 0x7, 1 }, /* 0001 11 */ + { 4, 0x7, 2 }, /* 0111 */ + { 4, 0x8, 3 }, /* 1000 */ + { 4, 0xB, 4 }, /* 1011 */ + { 4, 0xC, 5 }, /* 1100 */ + { 4, 0xE, 6 }, /* 1110 */ + { 4, 0xF, 7 }, /* 1111 */ + { 5, 0x13, 8 }, /* 1001 1 */ + { 5, 0x14, 9 }, /* 1010 0 */ + { 5, 0x7, 10 }, /* 0011 1 */ + { 5, 0x8, 11 }, /* 0100 0 */ + { 6, 0x8, 12 }, /* 0010 00 */ + { 6, 0x3, 13 }, /* 0000 11 */ + { 6, 0x34, 14 }, /* 1101 00 */ + { 6, 0x35, 15 }, /* 1101 01 */ + { 6, 0x2A, 16 }, /* 1010 10 */ + { 6, 0x2B, 17 }, /* 1010 11 */ + { 7, 0x27, 18 }, /* 0100 111 */ + { 7, 0xC, 19 }, /* 0001 100 */ + { 7, 0x8, 20 }, /* 0001 000 */ + { 7, 0x17, 21 }, /* 0010 111 */ + { 7, 0x3, 22 }, /* 0000 011 */ + { 7, 0x4, 23 }, /* 0000 100 */ + { 7, 0x28, 24 }, /* 0101 000 */ + { 7, 0x2B, 25 }, /* 0101 011 */ + { 7, 0x13, 26 }, /* 0010 011 */ + { 7, 0x24, 27 }, /* 0100 100 */ + { 7, 0x18, 28 }, /* 0011 000 */ + { 8, 0x2, 29 }, /* 0000 0010 */ + { 8, 0x3, 30 }, /* 0000 0011 */ + { 8, 0x1A, 31 }, /* 0001 1010 */ + { 8, 0x1B, 32 }, /* 0001 1011 */ + { 8, 0x12, 33 }, /* 0001 0010 */ + { 8, 0x13, 34 }, /* 0001 0011 */ + { 8, 0x14, 35 }, /* 0001 0100 */ + { 8, 0x15, 36 }, /* 0001 0101 */ + { 8, 0x16, 37 }, /* 0001 0110 */ + { 8, 0x17, 38 }, /* 0001 0111 */ + { 8, 0x28, 39 }, /* 0010 1000 */ + { 8, 0x29, 40 }, /* 0010 1001 */ + { 8, 0x2A, 41 }, /* 0010 1010 */ + { 8, 0x2B, 42 }, /* 0010 1011 */ + { 8, 0x2C, 43 }, /* 0010 1100 */ + { 8, 0x2D, 44 }, /* 0010 1101 */ + { 8, 0x4, 45 }, /* 0000 0100 */ + { 8, 0x5, 46 }, /* 0000 0101 */ + { 8, 0xA, 47 }, /* 0000 1010 */ + { 8, 0xB, 48 }, /* 0000 1011 */ + { 8, 0x52, 49 }, /* 0101 0010 */ + { 8, 0x53, 50 }, /* 0101 0011 */ + { 8, 0x54, 51 }, /* 0101 0100 */ + { 8, 0x55, 52 }, /* 0101 0101 */ + { 8, 0x24, 53 }, /* 0010 0100 */ + { 8, 0x25, 54 }, /* 0010 0101 */ + { 8, 0x58, 55 }, /* 0101 1000 */ + { 8, 0x59, 56 }, /* 0101 1001 */ + { 8, 0x5A, 57 }, /* 0101 1010 */ + { 8, 0x5B, 58 }, /* 0101 1011 */ + { 8, 0x4A, 59 }, /* 0100 1010 */ + { 8, 0x4B, 60 }, /* 0100 1011 */ + { 8, 0x32, 61 }, /* 0011 0010 */ + { 8, 0x33, 62 }, /* 0011 0011 */ + { 8, 0x34, 63 }, /* 0011 0100 */ + { 5, 0x1B, 64 }, /* 1101 1 */ + { 5, 0x12, 128 }, /* 1001 0 */ + { 6, 0x17, 192 }, /* 0101 11 */ + { 7, 0x37, 256 }, /* 0110 111 */ + { 8, 0x36, 320 }, /* 0011 0110 */ + { 8, 0x37, 384 }, /* 0011 0111 */ + { 8, 0x64, 448 }, /* 0110 0100 */ + { 8, 0x65, 512 }, /* 0110 0101 */ + { 8, 0x68, 576 }, /* 0110 1000 */ + { 8, 0x67, 640 }, /* 0110 0111 */ + { 9, 0xCC, 704 }, /* 0110 0110 0 */ + { 9, 0xCD, 768 }, /* 0110 0110 1 */ + { 9, 0xD2, 832 }, /* 0110 1001 0 */ + { 9, 0xD3, 896 }, /* 0110 1001 1 */ + { 9, 0xD4, 960 }, /* 0110 1010 0 */ + { 9, 0xD5, 1024 }, /* 0110 1010 1 */ + { 9, 0xD6, 1088 }, /* 0110 1011 0 */ + { 9, 0xD7, 1152 }, /* 0110 1011 1 */ + { 9, 0xD8, 1216 }, /* 0110 1100 0 */ + { 9, 0xD9, 1280 }, /* 0110 1100 1 */ + { 9, 0xDA, 1344 }, /* 0110 1101 0 */ + { 9, 0xDB, 1408 }, /* 0110 1101 1 */ + { 9, 0x98, 1472 }, /* 0100 1100 0 */ + { 9, 0x99, 1536 }, /* 0100 1100 1 */ + { 9, 0x9A, 1600 }, /* 0100 1101 0 */ + { 6, 0x18, 1664 }, /* 0110 00 */ + { 9, 0x9B, 1728 }, /* 0100 1101 1 */ + { 11, 0x8, 1792 }, /* 0000 0001 000 */ + { 11, 0xC, 1856 }, /* 0000 0001 100 */ + { 11, 0xD, 1920 }, /* 0000 0001 101 */ + { 12, 0x12, 1984 }, /* 0000 0001 0010 */ + { 12, 0x13, 2048 }, /* 0000 0001 0011 */ + { 12, 0x14, 2112 }, /* 0000 0001 0100 */ + { 12, 0x15, 2176 }, /* 0000 0001 0101 */ + { 12, 0x16, 2240 }, /* 0000 0001 0110 */ + { 12, 0x17, 2304 }, /* 0000 0001 0111 */ + { 12, 0x1C, 2368 }, /* 0000 0001 1100 */ + { 12, 0x1D, 2432 }, /* 0000 0001 1101 */ + { 12, 0x1E, 2496 }, /* 0000 0001 1110 */ + { 12, 0x1F, 2560 }, /* 0000 0001 1111 */ + { 12, 0x1, G3CODE_EOL }, /* 0000 0000 0001 */ + { 9, 0x1, G3CODE_INVALID }, /* 0000 0000 1 */ + { 10, 0x1, G3CODE_INVALID }, /* 0000 0000 01 */ + { 11, 0x1, G3CODE_INVALID }, /* 0000 0000 001 */ + { 12, 0x0, G3CODE_INVALID }, /* 0000 0000 0000 */ +}; + +const tableentry TIFFFaxBlackCodes[] = { + { 10, 0x37, 0 }, /* 0000 1101 11 */ + { 3, 0x2, 1 }, /* 010 */ + { 2, 0x3, 2 }, /* 11 */ + { 2, 0x2, 3 }, /* 10 */ + { 3, 0x3, 4 }, /* 011 */ + { 4, 0x3, 5 }, /* 0011 */ + { 4, 0x2, 6 }, /* 0010 */ + { 5, 0x3, 7 }, /* 0001 1 */ + { 6, 0x5, 8 }, /* 0001 01 */ + { 6, 0x4, 9 }, /* 0001 00 */ + { 7, 0x4, 10 }, /* 0000 100 */ + { 7, 0x5, 11 }, /* 0000 101 */ + { 7, 0x7, 12 }, /* 0000 111 */ + { 8, 0x4, 13 }, /* 0000 0100 */ + { 8, 0x7, 14 }, /* 0000 0111 */ + { 9, 0x18, 15 }, /* 0000 1100 0 */ + { 10, 0x17, 16 }, /* 0000 0101 11 */ + { 10, 0x18, 17 }, /* 0000 0110 00 */ + { 10, 0x8, 18 }, /* 0000 0010 00 */ + { 11, 0x67, 19 }, /* 0000 1100 111 */ + { 11, 0x68, 20 }, /* 0000 1101 000 */ + { 11, 0x6C, 21 }, /* 0000 1101 100 */ + { 11, 0x37, 22 }, /* 0000 0110 111 */ + { 11, 0x28, 23 }, /* 0000 0101 000 */ + { 11, 0x17, 24 }, /* 0000 0010 111 */ + { 11, 0x18, 25 }, /* 0000 0011 000 */ + { 12, 0xCA, 26 }, /* 0000 1100 1010 */ + { 12, 0xCB, 27 }, /* 0000 1100 1011 */ + { 12, 0xCC, 28 }, /* 0000 1100 1100 */ + { 12, 0xCD, 29 }, /* 0000 1100 1101 */ + { 12, 0x68, 30 }, /* 0000 0110 1000 */ + { 12, 0x69, 31 }, /* 0000 0110 1001 */ + { 12, 0x6A, 32 }, /* 0000 0110 1010 */ + { 12, 0x6B, 33 }, /* 0000 0110 1011 */ + { 12, 0xD2, 34 }, /* 0000 1101 0010 */ + { 12, 0xD3, 35 }, /* 0000 1101 0011 */ + { 12, 0xD4, 36 }, /* 0000 1101 0100 */ + { 12, 0xD5, 37 }, /* 0000 1101 0101 */ + { 12, 0xD6, 38 }, /* 0000 1101 0110 */ + { 12, 0xD7, 39 }, /* 0000 1101 0111 */ + { 12, 0x6C, 40 }, /* 0000 0110 1100 */ + { 12, 0x6D, 41 }, /* 0000 0110 1101 */ + { 12, 0xDA, 42 }, /* 0000 1101 1010 */ + { 12, 0xDB, 43 }, /* 0000 1101 1011 */ + { 12, 0x54, 44 }, /* 0000 0101 0100 */ + { 12, 0x55, 45 }, /* 0000 0101 0101 */ + { 12, 0x56, 46 }, /* 0000 0101 0110 */ + { 12, 0x57, 47 }, /* 0000 0101 0111 */ + { 12, 0x64, 48 }, /* 0000 0110 0100 */ + { 12, 0x65, 49 }, /* 0000 0110 0101 */ + { 12, 0x52, 50 }, /* 0000 0101 0010 */ + { 12, 0x53, 51 }, /* 0000 0101 0011 */ + { 12, 0x24, 52 }, /* 0000 0010 0100 */ + { 12, 0x37, 53 }, /* 0000 0011 0111 */ + { 12, 0x38, 54 }, /* 0000 0011 1000 */ + { 12, 0x27, 55 }, /* 0000 0010 0111 */ + { 12, 0x28, 56 }, /* 0000 0010 1000 */ + { 12, 0x58, 57 }, /* 0000 0101 1000 */ + { 12, 0x59, 58 }, /* 0000 0101 1001 */ + { 12, 0x2B, 59 }, /* 0000 0010 1011 */ + { 12, 0x2C, 60 }, /* 0000 0010 1100 */ + { 12, 0x5A, 61 }, /* 0000 0101 1010 */ + { 12, 0x66, 62 }, /* 0000 0110 0110 */ + { 12, 0x67, 63 }, /* 0000 0110 0111 */ + { 10, 0xF, 64 }, /* 0000 0011 11 */ + { 12, 0xC8, 128 }, /* 0000 1100 1000 */ + { 12, 0xC9, 192 }, /* 0000 1100 1001 */ + { 12, 0x5B, 256 }, /* 0000 0101 1011 */ + { 12, 0x33, 320 }, /* 0000 0011 0011 */ + { 12, 0x34, 384 }, /* 0000 0011 0100 */ + { 12, 0x35, 448 }, /* 0000 0011 0101 */ + { 13, 0x6C, 512 }, /* 0000 0011 0110 0 */ + { 13, 0x6D, 576 }, /* 0000 0011 0110 1 */ + { 13, 0x4A, 640 }, /* 0000 0010 0101 0 */ + { 13, 0x4B, 704 }, /* 0000 0010 0101 1 */ + { 13, 0x4C, 768 }, /* 0000 0010 0110 0 */ + { 13, 0x4D, 832 }, /* 0000 0010 0110 1 */ + { 13, 0x72, 896 }, /* 0000 0011 1001 0 */ + { 13, 0x73, 960 }, /* 0000 0011 1001 1 */ + { 13, 0x74, 1024 }, /* 0000 0011 1010 0 */ + { 13, 0x75, 1088 }, /* 0000 0011 1010 1 */ + { 13, 0x76, 1152 }, /* 0000 0011 1011 0 */ + { 13, 0x77, 1216 }, /* 0000 0011 1011 1 */ + { 13, 0x52, 1280 }, /* 0000 0010 1001 0 */ + { 13, 0x53, 1344 }, /* 0000 0010 1001 1 */ + { 13, 0x54, 1408 }, /* 0000 0010 1010 0 */ + { 13, 0x55, 1472 }, /* 0000 0010 1010 1 */ + { 13, 0x5A, 1536 }, /* 0000 0010 1101 0 */ + { 13, 0x5B, 1600 }, /* 0000 0010 1101 1 */ + { 13, 0x64, 1664 }, /* 0000 0011 0010 0 */ + { 13, 0x65, 1728 }, /* 0000 0011 0010 1 */ + { 11, 0x8, 1792 }, /* 0000 0001 000 */ + { 11, 0xC, 1856 }, /* 0000 0001 100 */ + { 11, 0xD, 1920 }, /* 0000 0001 101 */ + { 12, 0x12, 1984 }, /* 0000 0001 0010 */ + { 12, 0x13, 2048 }, /* 0000 0001 0011 */ + { 12, 0x14, 2112 }, /* 0000 0001 0100 */ + { 12, 0x15, 2176 }, /* 0000 0001 0101 */ + { 12, 0x16, 2240 }, /* 0000 0001 0110 */ + { 12, 0x17, 2304 }, /* 0000 0001 0111 */ + { 12, 0x1C, 2368 }, /* 0000 0001 1100 */ + { 12, 0x1D, 2432 }, /* 0000 0001 1101 */ + { 12, 0x1E, 2496 }, /* 0000 0001 1110 */ + { 12, 0x1F, 2560 }, /* 0000 0001 1111 */ + { 12, 0x1, G3CODE_EOL }, /* 0000 0000 0001 */ + { 9, 0x1, G3CODE_INVALID }, /* 0000 0000 1 */ + { 10, 0x1, G3CODE_INVALID }, /* 0000 0000 01 */ + { 11, 0x1, G3CODE_INVALID }, /* 0000 0000 001 */ + { 12, 0x0, G3CODE_INVALID }, /* 0000 0000 0000 */ +}; +#else +extern const tableentry TIFFFaxWhiteCodes[]; +extern const tableentry TIFFFaxBlackCodes[]; +#endif +#endif /* _T4_ */ diff --git a/panda/src/tiff/tif_apple.c b/panda/src/tiff/tif_apple.c new file mode 100644 index 0000000000..c432d7e774 --- /dev/null +++ b/panda/src/tiff/tif_apple.c @@ -0,0 +1,219 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library Macintosh-specific routines. + * + * These routines use only Toolbox and high-level File Manager traps. + * They make no calls to the THINK C "unix" compatibility library. Also, + * malloc is not used directly but it is still referenced internally by + * the ANSI library in rare cases. Heap fragmentation by the malloc ring + * buffer is therefore minimized. + * + * O_RDONLY and O_RDWR are treated identically here. The tif_mode flag is + * checked in TIFFWriteCheck(). + * + * Create below fills in a blank creator signature and sets the file type + * to 'TIFF'. It is much better for the application to do this by Create'ing + * the file first and TIFFOpen'ing it later. + */ + +#include "tiffiop.h" +#include +#include +#include + +#ifdef applec +#define CtoPstr c2pstr +#endif + +static tsize_t +_tiffReadProc(thandle_t fd, tdata_t buf, tsize_t size) +{ + return (FSRead((short) fd, (long*) &size, (char*) buf) == noErr ? + size : (tsize_t) -1); +} + +static tsize_t +_tiffWriteProc(thandle_t fd, tdata_t buf, tsize_t size) +{ + return (FSWrite((short) fd, (long*) &size, (char*) buf) == noErr ? + size : (tsize_t) -1); +} + +static toff_t +_tiffSeekProc(thandle_t fd, toff_t off, int whence) +{ + long fpos, size; + + if (GetEOF((short) fd, &size) != noErr) + return EOF; + (void) GetFPos((short) fd, &fpos); + + switch (whence) { + case SEEK_CUR: + if (off + fpos > size) + SetEOF((short) fd, off + fpos); + if (SetFPos((short) fd, fsFromMark, off) != noErr) + return EOF; + break; + case SEEK_END: + if (off > 0) + SetEOF((short) fd, off + size); + if (SetFPos((short) fd, fsFromStart, off + size) != noErr) + return EOF; + break; + case SEEK_SET: + if (off > size) + SetEOF((short) fd, off); + if (SetFPos((short) fd, fsFromStart, off) != noErr) + return EOF; + break; + } + + return (toff_t)(GetFPos((short) fd, &fpos) == noErr ? fpos : EOF); +} + +static int +_tiffMapProc(thandle_t fd, tdata_t* pbase, toff_t* psize) +{ + return (0); +} + +static void +_tiffUnmapProc(thandle_t fd, tdata_t base, toff_t size) +{ +} + +static int +_tiffCloseProc(thandle_t fd) +{ + return (FSClose((short) fd)); +} + +static toff_t +_tiffSizeProc(thandle_t fd) +{ + long size; + + if (GetEOF((short) fd, &size) != noErr) { + TIFFError("_tiffSizeProc", "%s: Cannot get file size"); + return (-1L); + } + return ((toff_t) size); +} + +void * +_TIFFmalloc(size_t s) +{ + return (NewPtr(s)); +} + +void +_TIFFfree(void* p) +{ + DisposePtr(p); +} + +void * +_TIFFrealloc(void* p, size_t s) +{ + Ptr n = p; + + SetPtrSize(p, s); + if (MemError() && (n = NewPtr(s)) != NULL) { + BlockMove(p, n, GetPtrSize(p)); + DisposePtr(p); + } + return (n); +} + +/* + * Open a TIFF file descriptor for read/writing. + */ +TIFF* +TIFFFdOpen(int fd, const char* name, const char* mode) +{ + TIFF* tif; + + tif = TIFFClientOpen(name, mode, (thandle_t) fd, + _tiffReadProc, _tiffWriteProc, _tiffSeekProc, _tiffCloseProc, + _tiffSizeProc, _tiffMapProc, _tiffUnmapProc); + if (tif) + tif->tif_fd = fd; + return (tif); +} + +/* + * Open a TIFF file for read/writing. + */ +TIFF* +TIFFOpen(const char* name, const char* mode) +{ + static const char module[] = "TIFFOpen"; + Str255 pname; + FInfo finfo; + short fref; + OSErr err; + + strcpy((char*) pname, name); + CtoPstr((char*) pname); + + switch (_TIFFgetMode(mode, module)) { + default: + return ((TIFF*) 0); + case O_RDWR | O_CREAT | O_TRUNC: + if (GetFInfo(pname, 0, &finfo) == noErr) + FSDelete(pname, 0); + /* fall through */ + case O_RDWR | O_CREAT: + if ((err = GetFInfo(pname, 0, &finfo)) == fnfErr) { + if (Create(pname, 0, ' ', 'TIFF') != noErr) + goto badCreate; + if (FSOpen(pname, 0, &fref) != noErr) + goto badOpen; + } else if (err == noErr) { + if (FSOpen(pname, 0, &fref) != noErr) + goto badOpen; + } else + goto badOpen; + break; + case O_RDONLY: + case O_RDWR: + if (FSOpen(pname, 0, &fref) != noErr) + goto badOpen; + break; + } + return (TIFFFdOpen((int) fref, name, mode)); +badCreate: + TIFFError(module, "%s: Cannot create", name); + return ((TIFF*) 0); +badOpen: + TIFFError(module, "%s: Cannot open", name); + return ((TIFF*) 0); +} diff --git a/panda/src/tiff/tif_aux.c b/panda/src/tiff/tif_aux.c new file mode 100644 index 0000000000..1ebd34afe9 --- /dev/null +++ b/panda/src/tiff/tif_aux.c @@ -0,0 +1,204 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Auxiliary Support Routines. + */ +#include "tiffiop.h" + +#ifdef COLORIMETRY_SUPPORT +#include + +static void +TIFFDefaultTransferFunction(TIFFDirectory* td) +{ + uint16 **tf = td->td_transferfunction; + long i, n = 1<td_bitspersample; + + tf[0] = (uint16 *)_TIFFmalloc(n * sizeof (uint16)); + tf[0][0] = 0; + for (i = 1; i < n; i++) + tf[0][i] = (uint16)floor(65535.*pow(i/(n-1.), 2.2) + .5); + if (td->td_samplesperpixel - td->td_extrasamples > 1) { + tf[1] = (uint16 *)_TIFFmalloc(n * sizeof (uint16)); + memcpy(tf[1], tf[0], n * sizeof (uint16)); + tf[2] = (uint16 *)_TIFFmalloc(n * sizeof (uint16)); + memcpy(tf[2], tf[0], n * sizeof (uint16)); + } +} + +static void +TIFFDefaultRefBlackWhite(TIFFDirectory* td) +{ + int i; + + td->td_refblackwhite = (float *)_TIFFmalloc(6*sizeof (float)); + for (i = 0; i < 3; i++) { + td->td_refblackwhite[2*i+0] = 0; + td->td_refblackwhite[2*i+1] = 1L<td_bitspersample; + } +} +#endif + +/* + * Like TIFFGetField, but return any default + * value if the tag is not present in the directory. + * + * NB: We use the value in the directory, rather than + * explcit values so that defaults exist only one + * place in the library -- in TIFFDefaultDirectory. + */ +TIFFVGetFieldDefaulted(TIFF* tif, ttag_t tag, va_list ap) +{ + TIFFDirectory *td = &tif->tif_dir; + + if (TIFFVGetField(tif, tag, ap)) + return (1); + switch (tag) { + case TIFFTAG_SUBFILETYPE: + *va_arg(ap, uint16 *) = td->td_subfiletype; + return (1); + case TIFFTAG_BITSPERSAMPLE: + *va_arg(ap, uint16 *) = td->td_bitspersample; + return (1); + case TIFFTAG_THRESHHOLDING: + *va_arg(ap, uint16 *) = td->td_threshholding; + return (1); + case TIFFTAG_FILLORDER: + *va_arg(ap, uint16 *) = td->td_fillorder; + return (1); + case TIFFTAG_ORIENTATION: + *va_arg(ap, uint16 *) = td->td_orientation; + return (1); + case TIFFTAG_SAMPLESPERPIXEL: + *va_arg(ap, uint16 *) = td->td_samplesperpixel; + return (1); + case TIFFTAG_ROWSPERSTRIP: + *va_arg(ap, uint32 *) = td->td_rowsperstrip; + return (1); + case TIFFTAG_MINSAMPLEVALUE: + *va_arg(ap, uint16 *) = td->td_minsamplevalue; + return (1); + case TIFFTAG_MAXSAMPLEVALUE: + *va_arg(ap, uint16 *) = td->td_maxsamplevalue; + return (1); + case TIFFTAG_PLANARCONFIG: + *va_arg(ap, uint16 *) = td->td_planarconfig; + return (1); + case TIFFTAG_GROUP4OPTIONS: + *va_arg(ap, uint32 *) = td->td_group4options; + return (1); + case TIFFTAG_RESOLUTIONUNIT: + *va_arg(ap, uint16 *) = td->td_resolutionunit; + return (1); + case TIFFTAG_PREDICTOR: + *va_arg(ap, uint16 *) = td->td_predictor; + return (1); +#ifdef CMYK_SUPPORT + case TIFFTAG_DOTRANGE: + *va_arg(ap, uint16 *) = 0; + *va_arg(ap, uint16 *) = (1<td_bitspersample)-1; + return (1); + case TIFFTAG_INKSET: + *va_arg(ap, uint16 *) = td->td_inkset; + return (1); +#endif + case TIFFTAG_EXTRASAMPLES: + *va_arg(ap, uint16 *) = td->td_extrasamples; + *va_arg(ap, uint16 **) = td->td_sampleinfo; + return (1); + case TIFFTAG_MATTEING: + *va_arg(ap, uint16 *) = + (td->td_extrasamples == 1 && + td->td_sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA); + return (1); + case TIFFTAG_TILEDEPTH: + *va_arg(ap, uint32 *) = td->td_tiledepth; + return (1); + case TIFFTAG_DATATYPE: + *va_arg(ap, uint16 *) = td->td_sampleformat-1; + return (1); + case TIFFTAG_IMAGEDEPTH: + *va_arg(ap, uint16 *) = td->td_imagedepth; + return (1); +#ifdef YCBCR_SUPPORT + case TIFFTAG_YCBCRCOEFFICIENTS: + if (!td->td_ycbcrcoeffs) { + td->td_ycbcrcoeffs = (float *) + _TIFFmalloc(3*sizeof (float)); + /* defaults are from CCIR Recommendation 601-1 */ + td->td_ycbcrcoeffs[0] = .299; + td->td_ycbcrcoeffs[1] = .587; + td->td_ycbcrcoeffs[2] = .114; + } + *va_arg(ap, float **) = td->td_ycbcrcoeffs; + return (1); + case TIFFTAG_YCBCRSUBSAMPLING: + *va_arg(ap, uint16 *) = td->td_ycbcrsubsampling[0]; + *va_arg(ap, uint16 *) = td->td_ycbcrsubsampling[1]; + return (1); + case TIFFTAG_YCBCRPOSITIONING: + *va_arg(ap, uint16 *) = td->td_ycbcrpositioning; + return (1); +#endif +#ifdef COLORIMETRY_SUPPORT + case TIFFTAG_TRANSFERFUNCTION: + if (!td->td_transferfunction[0]) + TIFFDefaultTransferFunction(td); + *va_arg(ap, uint16 **) = td->td_transferfunction[0]; + if (td->td_samplesperpixel - td->td_extrasamples > 1) { + *va_arg(ap, uint16 **) = td->td_transferfunction[1]; + *va_arg(ap, uint16 **) = td->td_transferfunction[2]; + } + return (1); + case TIFFTAG_REFERENCEBLACKWHITE: + if (!td->td_refblackwhite) + TIFFDefaultRefBlackWhite(td); + *va_arg(ap, float **) = td->td_refblackwhite; + return (1); +#endif + } + return (0); +} + +/* + * Like TIFFGetField, but return any default + * value if the tag is not present in the directory. + */ +TIFFGetFieldDefaulted(TIFF* tif, ttag_t tag, ...) +{ + int ok; + va_list ap; + + va_start(ap, tag); + ok = TIFFVGetFieldDefaulted(tif, tag, ap); + va_end(ap); + return (ok); +} diff --git a/panda/src/tiff/tif_ccittrle.c b/panda/src/tiff/tif_ccittrle.c new file mode 100644 index 0000000000..add9495e9a --- /dev/null +++ b/panda/src/tiff/tif_ccittrle.c @@ -0,0 +1,75 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * CCITT Group 3 1-D Modified Huffman + * Run Length Encoding Compression Support + */ +#include "tiffiop.h" +#include "tif_fax3.h" + +TIFFInitCCITTRLE(TIFF* tif) +{ + TIFFInitCCITTFax3(tif); /* reuse G3 compression */ + tif->tif_preencode = NULL; + tif->tif_postencode = NULL; + tif->tif_encoderow = TIFFNoRowEncode; + tif->tif_encodestrip = TIFFNoStripEncode; + tif->tif_encodetile = TIFFNoTileEncode; + tif->tif_close = NULL; + /* + * This magic causes the regular G3 decompression + * code to not skip to the EOL mark at the end of + * a row, and to flush input data to a byte boundary + * at the end of each row. + */ + tif->tif_options |= FAX3_NOEOL|FAX3_BYTEALIGN; + return (1); +} + +int +TIFFInitCCITTRLEW(TIFF* tif) +{ + TIFFInitCCITTFax3(tif); /* reuse G3 compression */ + tif->tif_preencode = NULL; + tif->tif_postencode = NULL; + tif->tif_encoderow = TIFFNoRowEncode; + tif->tif_encodestrip = TIFFNoStripEncode; + tif->tif_encodetile = TIFFNoTileEncode; + tif->tif_close = NULL; + /* + * This magic causes the regular G3 decompression + * code to not skip to the EOL mark at the end of + * a row, and to flush input data to a byte boundary + * at the end of each row. + */ + tif->tif_options |= FAX3_NOEOL|FAX3_WORDALIGN; + return (1); +} diff --git a/panda/src/tiff/tif_close.c b/panda/src/tiff/tif_close.c new file mode 100644 index 0000000000..de761b7e9e --- /dev/null +++ b/panda/src/tiff/tif_close.c @@ -0,0 +1,51 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + */ +#include "tiffiop.h" + +void +TIFFClose(TIFF* tif) +{ + if (tif->tif_mode != O_RDONLY) + /* + * Flush buffered data and directory (if dirty). + */ + TIFFFlush(tif); + if (tif->tif_cleanup) + (*tif->tif_cleanup)(tif); + TIFFFreeDirectory(tif); + if (tif->tif_rawdata && (tif->tif_flags&TIFF_MYBUFFER)) + _TIFFfree(tif->tif_rawdata); + if (isMapped(tif)) + TIFFUnmapFileContents(tif, tif->tif_base, tif->tif_size); + (void) TIFFCloseFile(tif); + _TIFFfree((char *)tif); +} diff --git a/panda/src/tiff/tif_compress.c b/panda/src/tiff/tif_compress.c new file mode 100644 index 0000000000..4883a7c520 --- /dev/null +++ b/panda/src/tiff/tif_compress.c @@ -0,0 +1,158 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library + * + * Compression Scheme Configuration Support. + */ +#include "tiffiop.h" + +typedef struct { + char* name; + int scheme; + TIFFBoolMethod init; +} cscheme_t; +static const cscheme_t CompressionSchemes[] = { + { "Null", COMPRESSION_NONE, TIFFInitDumpMode }, +#ifdef LZW_SUPPORT + { "LZW", COMPRESSION_LZW, TIFFInitLZW }, +#endif +#ifdef PACKBITS_SUPPORT + { "PackBits", COMPRESSION_PACKBITS, TIFFInitPackBits }, +#endif +#ifdef THUNDER_SUPPORT + { "ThunderScan", COMPRESSION_THUNDERSCAN,TIFFInitThunderScan }, +#endif +#ifdef NEXT_SUPPORT + { "NeXT", COMPRESSION_NEXT, TIFFInitNeXT }, +#endif +#ifdef JPEG_SUPPORT + { "JPEG", COMPRESSION_JPEG, TIFFInitJPEG }, +#endif +#ifdef CCITT_SUPPORT + { "CCITT RLE", COMPRESSION_CCITTRLE, TIFFInitCCITTRLE }, + { "CCITT RLE/W", COMPRESSION_CCITTRLEW, TIFFInitCCITTRLEW }, + { "CCITT Group3", COMPRESSION_CCITTFAX3, TIFFInitCCITTFax3 }, + { "CCITT Group4", COMPRESSION_CCITTFAX4, TIFFInitCCITTFax4 }, +#endif +}; +#define NSCHEMES (sizeof (CompressionSchemes) / sizeof (CompressionSchemes[0])) + +static const cscheme_t * +findScheme(int scheme) +{ + register const cscheme_t *c; + + for (c = CompressionSchemes; c < &CompressionSchemes[NSCHEMES]; c++) + if (c->scheme == scheme) + return (c); + return ((const cscheme_t *)0); +} + +static int +TIFFNoEncode(TIFF* tif, char* method) +{ + const cscheme_t *c = findScheme(tif->tif_dir.td_compression); + TIFFError(tif->tif_name, + "%s %s encoding is not implemented", c->name, method); + return (-1); +} + +int +TIFFNoRowEncode(TIFF* tif, tidata_t pp, tsize_t cc, tsample_t s) +{ + return (TIFFNoEncode(tif, "scanline")); +} + +int +TIFFNoStripEncode(TIFF* tif, tidata_t pp, tsize_t cc, tsample_t s) +{ + return (TIFFNoEncode(tif, "strip")); +} + +int +TIFFNoTileEncode(TIFF* tif, tidata_t pp, tsize_t cc, tsample_t s) +{ + return (TIFFNoEncode(tif, "tile")); +} + +static int +TIFFNoDecode(TIFF* tif, char* method) +{ + const cscheme_t *c = findScheme(tif->tif_dir.td_compression); + TIFFError(tif->tif_name, + "%s %s decoding is not implemented", c->name, method); + return (-1); +} + +int +TIFFNoRowDecode(TIFF* tif, tidata_t pp, tsize_t cc, tsample_t s) +{ + return (TIFFNoDecode(tif, "scanline")); +} + +int +TIFFNoStripDecode(TIFF* tif, tidata_t pp, tsize_t cc, tsample_t s) +{ + return (TIFFNoDecode(tif, "strip")); +} + +int +TIFFNoTileDecode(TIFF* tif, tidata_t pp, tsize_t cc, tsample_t s) +{ + return (TIFFNoDecode(tif, "tile")); +} + +int +TIFFSetCompressionScheme(TIFF* tif, int scheme) +{ + const cscheme_t *c = findScheme(scheme); + + if (!c) { + TIFFError(tif->tif_name, + "Unknown data compression algorithm %u (0x%x)", + scheme, scheme); + return (0); + } + tif->tif_predecode = NULL; + tif->tif_decoderow = TIFFNoRowDecode; + tif->tif_decodestrip = TIFFNoStripDecode; + tif->tif_decodetile = TIFFNoTileDecode; + tif->tif_preencode = NULL; + tif->tif_postencode = NULL; + tif->tif_encoderow = TIFFNoRowEncode; + tif->tif_encodestrip = TIFFNoStripEncode; + tif->tif_encodetile = TIFFNoTileEncode; + tif->tif_close = NULL; + tif->tif_seek = NULL; + tif->tif_cleanup = NULL; + tif->tif_flags &= ~TIFF_NOBITREV; + tif->tif_options = 0; + return ((*c->init)(tif)); +} diff --git a/panda/src/tiff/tif_dir.c b/panda/src/tiff/tif_dir.c new file mode 100644 index 0000000000..4c0004b950 --- /dev/null +++ b/panda/src/tiff/tif_dir.c @@ -0,0 +1,996 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Directory Tag Get & Set Routines. + * (and also some miscellaneous stuff) + */ +#include "tiffiop.h" + +static void +setString(char** cpp, char* cp) +{ + if (*cpp) + _TIFFfree(*cpp), *cpp = 0; + if (cp) { + size_t len = strlen(cp)+1; + if (*cpp = _TIFFmalloc(len)) + memcpy(*cpp, cp, len); + } +} + +static void +setShortArray(uint16** wpp, uint16* wp, long n) +{ + if (*wpp) + _TIFFfree((char *)*wpp), *wpp = 0; + n *= sizeof (uint16); + if (wp && (*wpp = (uint16 *)_TIFFmalloc(n))) + memcpy(*wpp, wp, n); +} + +static void +setLongArray(uint32** wpp, uint32* wp, long n) +{ + if (*wpp) + _TIFFfree((char *)*wpp), *wpp = 0; + n *= sizeof (uint32); + if (wp && (*wpp = (uint32 *)_TIFFmalloc(n))) + memcpy(*wpp, wp, n); +} + +static void +setFloatArray(float** wpp, float* wp, long n) +{ + if (*wpp) + _TIFFfree((char *)*wpp), *wpp = 0; + n *= sizeof (float); + if (wp && (*wpp = (float *)_TIFFmalloc(n))) + memcpy(*wpp, wp, n); +} + +#ifdef JPEG_SUPPORT +/* + * Install a JPEG Quantization table. + * Note that we reorder the elements + * of the array in the zig-zag order + * that is expected by the compression code + * and that is to be stored in the file. + */ +static void +setJPEGQTable(u_char*** wpp, u_char** wp, int nc) +{ + static u_char zigzag[64] = { + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 + }; + char *tab; + int i, j; + + if (*wpp) + _TIFFfree((char *)*wpp), *wpp = 0; + *wpp = (u_char **) + _TIFFmalloc(nc * (sizeof (u_char *) + 64*sizeof (u_char))); + tab = (((char *)*wpp) + nc*sizeof (u_char *)); + for (i = 0; i < nc; i++) { + (*wpp)[i] = (u_char *)tab; + for (j = 0; j < 64; j++) + tab[zigzag[j]] = wp[i][j]; + tab += 64*sizeof (u_char); + } +} + +/* + * Install a JPEG Coefficient table. + */ +static void +setJPEGCTable(u_char*** cpp, u_char** cp, int nc) +{ + u_char *tab; + int i, j, nw; + + if (*cpp) + _TIFFfree(*cpp), *cpp = 0; + /* + * Calculate the size of the table by counting + * the number of codes specified in the bits array. + */ + nw = 0; + for (i = 0; i < nc; i++) { + nw += 16; /* 16 bytes for bits array */ + for (j = 0; j < 16; j++)/* sum up count of codes */ + nw += cp[i][j]; + } + *cpp = (u_char **)_TIFFmalloc(nc*sizeof (u_char *) + nw); + tab = ((u_char *)*cpp) + nc*sizeof (u_char *); + /* + * Setup internal array and copy user data. + */ + for (i = 0; i < nc; i++) { + (*cpp)[i] = tab; + for (nw = 16, j = 0; j < 16; j++) + nw += cp[i][j]; + memcpy(tab, cp[i], nw); + tab += nw; + } +} +#endif + +/* + * Install extra samples information. + */ +static int +setExtraSamples(TIFFDirectory* td, va_list ap, int* v) +{ + uint16* va; + int i; + + *v = va_arg(ap, int); + if (*v > td->td_samplesperpixel) + return (0); + va = va_arg(ap, uint16*); + if (va == NULL) /* typically missing param */ + return (0); + for (i = 0; i < *v; i++) + if (va[i] > EXTRASAMPLE_UNASSALPHA) + return (0); + td->td_extrasamples = *v; + setShortArray(&td->td_sampleinfo, va, td->td_extrasamples); + return (1); +} + +static int +TIFFSetField1(TIFF* tif, ttag_t tag, va_list ap) +{ + TIFFDirectory *td = &tif->tif_dir; + int status = 1; + uint32 v32; + int i, v; + + switch (tag) { + case TIFFTAG_SUBFILETYPE: + td->td_subfiletype = va_arg(ap, uint32); + break; + case TIFFTAG_IMAGEWIDTH: + td->td_imagewidth = va_arg(ap, uint32); + break; + case TIFFTAG_IMAGELENGTH: + td->td_imagelength = va_arg(ap, uint32); + break; + case TIFFTAG_BITSPERSAMPLE: + td->td_bitspersample = va_arg(ap, int); + /* + * If the data require post-decoding processing + * to byte-swap samples, set it up here. Note + * that since tags are required to be ordered, + * compression code can override this behaviour + * in the setup method if it wants to roll the + * post decoding work in with its normal work. + */ + if (tif->tif_flags & TIFF_SWAB) { + if (td->td_bitspersample == 16) + tif->tif_postdecode = TIFFSwab16BitData; + else if (td->td_bitspersample == 32) + tif->tif_postdecode = TIFFSwab32BitData; + } + break; + case TIFFTAG_COMPRESSION: + v = va_arg(ap, int) & 0xffff; + /* + * If we're changing the compression scheme, + * the notify the previous module so that it + * can cleanup any state it's setup. + */ + if (TIFFFieldSet(tif, FIELD_COMPRESSION)) { + if (td->td_compression == v) + break; + if (tif->tif_cleanup) + (*tif->tif_cleanup)(tif); + } + /* + * Setup new compression routine state. + */ + if (status = TIFFSetCompressionScheme(tif, v)) + td->td_compression = v; + break; + case TIFFTAG_PHOTOMETRIC: + td->td_photometric = va_arg(ap, int); + break; + case TIFFTAG_THRESHHOLDING: + td->td_threshholding = va_arg(ap, int); + break; + case TIFFTAG_FILLORDER: + v = va_arg(ap, int); + if (v != FILLORDER_LSB2MSB && v != FILLORDER_MSB2LSB) + goto badvalue; + td->td_fillorder = v; + break; + case TIFFTAG_DOCUMENTNAME: + setString(&td->td_documentname, va_arg(ap, char *)); + break; + case TIFFTAG_ARTIST: + setString(&td->td_artist, va_arg(ap, char *)); + break; + case TIFFTAG_DATETIME: + setString(&td->td_datetime, va_arg(ap, char *)); + break; + case TIFFTAG_HOSTCOMPUTER: + setString(&td->td_hostcomputer, va_arg(ap, char *)); + break; + case TIFFTAG_IMAGEDESCRIPTION: + setString(&td->td_imagedescription, va_arg(ap, char *)); + break; + case TIFFTAG_MAKE: + setString(&td->td_make, va_arg(ap, char *)); + break; + case TIFFTAG_MODEL: + setString(&td->td_model, va_arg(ap, char *)); + break; + case TIFFTAG_SOFTWARE: + setString(&td->td_software, va_arg(ap, char *)); + break; + case TIFFTAG_ORIENTATION: + v = va_arg(ap, int); + if (v < ORIENTATION_TOPLEFT || ORIENTATION_LEFTBOT < v) { + TIFFWarning(tif->tif_name, + "Bad value %ld for \"%s\" tag ignored", + v, TIFFFieldWithTag(tag)->field_name); + } else + td->td_orientation = v; + break; + case TIFFTAG_SAMPLESPERPIXEL: + /* XXX should cross check -- e.g. if pallette, then 1 */ + v = va_arg(ap, int); + if (v == 0) + goto badvalue; + td->td_samplesperpixel = v; + break; + case TIFFTAG_ROWSPERSTRIP: + v32 = va_arg(ap, uint32); + if (v32 == 0) + goto badvalue32; + td->td_rowsperstrip = v32; + if (!TIFFFieldSet(tif, FIELD_TILEDIMENSIONS)) { + td->td_tilelength = v32; + td->td_tilewidth = td->td_imagewidth; + } + break; + case TIFFTAG_MINSAMPLEVALUE: + td->td_minsamplevalue = va_arg(ap, int) & 0xffff; + break; + case TIFFTAG_MAXSAMPLEVALUE: + td->td_maxsamplevalue = va_arg(ap, int) & 0xffff; + break; + case TIFFTAG_XRESOLUTION: + td->td_xresolution = va_arg(ap, dblparam_t); + break; + case TIFFTAG_YRESOLUTION: + td->td_yresolution = va_arg(ap, dblparam_t); + break; + case TIFFTAG_PLANARCONFIG: + v = va_arg(ap, int); + if (v != PLANARCONFIG_CONTIG && v != PLANARCONFIG_SEPARATE) + goto badvalue; + td->td_planarconfig = v; + break; + case TIFFTAG_PAGENAME: + setString(&td->td_pagename, va_arg(ap, char *)); + break; + case TIFFTAG_XPOSITION: + td->td_xposition = va_arg(ap, dblparam_t); + break; + case TIFFTAG_YPOSITION: + td->td_yposition = va_arg(ap, dblparam_t); + break; + case TIFFTAG_GROUP3OPTIONS: + td->td_group3options = va_arg(ap, uint32); + break; + case TIFFTAG_GROUP4OPTIONS: + td->td_group4options = va_arg(ap, uint32); + break; + case TIFFTAG_RESOLUTIONUNIT: + v = va_arg(ap, int); + if (v < RESUNIT_NONE || RESUNIT_CENTIMETER < v) + goto badvalue; + td->td_resolutionunit = v; + break; + case TIFFTAG_PAGENUMBER: + td->td_pagenumber[0] = va_arg(ap, int); + td->td_pagenumber[1] = va_arg(ap, int); + break; + case TIFFTAG_HALFTONEHINTS: + td->td_halftonehints[0] = va_arg(ap, int); + td->td_halftonehints[1] = va_arg(ap, int); + break; + case TIFFTAG_COLORMAP: + v = 1L<td_bitspersample; + setShortArray(&td->td_colormap[0], va_arg(ap, uint16*), v); + setShortArray(&td->td_colormap[1], va_arg(ap, uint16*), v); + setShortArray(&td->td_colormap[2], va_arg(ap, uint16*), v); + break; + case TIFFTAG_PREDICTOR: + td->td_predictor = va_arg(ap, int); + break; + case TIFFTAG_EXTRASAMPLES: + if (!setExtraSamples(td, ap, &v)) + goto badvalue; + break; + case TIFFTAG_MATTEING: + td->td_extrasamples = (va_arg(ap, int) != 0); + if (td->td_extrasamples) { + uint16 sv = EXTRASAMPLE_ASSOCALPHA; + setShortArray(&td->td_sampleinfo, &sv, 1); + } + break; + case TIFFTAG_BADFAXLINES: + td->td_badfaxlines = va_arg(ap, uint32); + break; + case TIFFTAG_CLEANFAXDATA: + td->td_cleanfaxdata = va_arg(ap, int); + break; + case TIFFTAG_CONSECUTIVEBADFAXLINES: + td->td_badfaxrun = va_arg(ap, uint32); + break; + case TIFFTAG_TILEWIDTH: + v32 = va_arg(ap, uint32); + if (v32 % 16) + goto badvalue32; + td->td_tilewidth = v32; + tif->tif_flags |= TIFF_ISTILED; + break; + case TIFFTAG_TILELENGTH: + v32 = va_arg(ap, uint32); + if (v32 % 16) + goto badvalue32; + td->td_tilelength = v32; + tif->tif_flags |= TIFF_ISTILED; + break; + case TIFFTAG_TILEDEPTH: + v32 = va_arg(ap, uint32); + if (v == 0) + goto badvalue32; + td->td_tiledepth = v32; + break; + case TIFFTAG_DATATYPE: + case TIFFTAG_SAMPLEFORMAT: + v = va_arg(ap, int); + if (tag == TIFFTAG_DATATYPE && v == 0) + v = SAMPLEFORMAT_VOID; + if (v < SAMPLEFORMAT_UINT || SAMPLEFORMAT_VOID < v) + goto badvalue; + td->td_sampleformat = v; + break; + case TIFFTAG_IMAGEDEPTH: + td->td_imagedepth = va_arg(ap, uint32); + break; +#ifdef YCBCR_SUPPORT + case TIFFTAG_YCBCRCOEFFICIENTS: + setFloatArray(&td->td_ycbcrcoeffs, va_arg(ap, float *), 3); + break; + case TIFFTAG_YCBCRPOSITIONING: + td->td_ycbcrpositioning = va_arg(ap, int); + break; + case TIFFTAG_YCBCRSUBSAMPLING: + td->td_ycbcrsubsampling[0] = va_arg(ap, int); + td->td_ycbcrsubsampling[1] = va_arg(ap, int); + break; +#endif +#ifdef JPEG_SUPPORT + case TIFFTAG_JPEGPROC: + td->td_jpegproc = va_arg(ap, int); + break; + case TIFFTAG_JPEGRESTARTINTERVAL: + td->td_jpegrestartinterval = va_arg(ap, int); + break; + case TIFFTAG_JPEGQTABLES: + setJPEGQTable(&td->td_qtab, va_arg(ap, u_char **), + td->td_samplesperpixel); + break; + case TIFFTAG_JPEGDCTABLES: + setJPEGCTable(&td->td_dctab, va_arg(ap, u_char **), + td->td_samplesperpixel); + break; + case TIFFTAG_JPEGACTABLES: + setJPEGCTable(&td->td_actab, va_arg(ap, u_char **), + td->td_samplesperpixel); + break; +#endif +#ifdef COLORIMETRY_SUPPORT + case TIFFTAG_WHITEPOINT: + setFloatArray(&td->td_whitepoint, va_arg(ap, float *), 2); + break; + case TIFFTAG_PRIMARYCHROMATICITIES: + setFloatArray(&td->td_primarychromas, va_arg(ap, float *), 6); + break; + case TIFFTAG_TRANSFERFUNCTION: + v = (td->td_samplesperpixel - td->td_extrasamples) > 1 ? 3 : 1; + for (i = 0; i < v; i++) + setShortArray(&td->td_transferfunction[i], + va_arg(ap, uint16*), 1L<td_bitspersample); + break; + case TIFFTAG_REFERENCEBLACKWHITE: + /* XXX should check for null range */ + setFloatArray(&td->td_refblackwhite, va_arg(ap, float *), 6); + break; +#endif +#ifdef CMYK_SUPPORT + case TIFFTAG_INKSET: + td->td_inkset = va_arg(ap, int); + break; + case TIFFTAG_DOTRANGE: + /* XXX should check for null range */ + td->td_dotrange[0] = va_arg(ap, int); + td->td_dotrange[1] = va_arg(ap, int); + break; + case TIFFTAG_INKNAMES: + setString(&td->td_inknames, va_arg(ap, char *)); + break; + case TIFFTAG_TARGETPRINTER: + setString(&td->td_targetprinter, va_arg(ap, char *)); + break; +#endif + default: + TIFFError(tif->tif_name, + "Internal error, tag value botch, tag \"%s\"", + TIFFFieldWithTag(tag)->field_name); + status = 0; + break; + } + if (status) { + TIFFSetFieldBit(tif, TIFFFieldWithTag(tag)->field_bit); + tif->tif_flags |= TIFF_DIRTYDIRECT; + } + va_end(ap); + return (status); +badvalue: + TIFFError(tif->tif_name, "%d: Bad value for \"%s\"", v, + TIFFFieldWithTag(tag)->field_name); + va_end(ap); + return (0); +badvalue32: + TIFFError(tif->tif_name, "%ld: Bad value for \"%s\"", v32, + TIFFFieldWithTag(tag)->field_name); + va_end(ap); + return (0); +} + +/* + * Return 1/0 according to whether or not + * it is permissible to set the tag's value. + * Note that we allow ImageLength to be changed + * so that we can append and extend to images. + * Any other tag may not be altered once writing + * has commenced, unless its value has no effect + * on the format of the data that is written. + */ +static int +OkToChangeTag(TIFF* tif, ttag_t tag) +{ + if (tag != TIFFTAG_IMAGELENGTH && + (tif->tif_flags & TIFF_BEENWRITING)) { + const TIFFFieldInfo *fip = TIFFFindFieldInfo(tag, TIFF_ANY); + /* + * Consult info table to see if tag can be changed + * after we've started writing. We only allow changes + * to those tags that don't/shouldn't affect the + * compression and/or format of the data. + */ + if (fip && !fip->field_oktochange) + return (0); + } + return (1); +} + +/* + * Record the value of a field in the + * internal directory structure. The + * field will be written to the file + * when/if the directory structure is + * updated. + */ +int +TIFFSetField(TIFF* tif, ttag_t tag, ...) +{ + int status = 0; + + if (OkToChangeTag(tif, tag)) { + va_list ap; + + va_start(ap, tag); + status = TIFFSetField1(tif, tag, ap); + va_end(ap); + } else { + const TIFFFieldInfo *fip = TIFFFindFieldInfo(tag, TIFF_ANY); + if (fip) + TIFFError("TIFFSetField", + "%s: Cannot modify tag \"%s\" while writing", + tif->tif_name, fip->field_name); + } + return (status); +} + +/* + * Like TIFFSetField, but taking a varargs + * parameter list. This routine is useful + * for building higher-level interfaces on + * top of the library. + */ +int +TIFFVSetField(TIFF* tif, ttag_t tag, va_list ap) +{ + int status = 0; + + if (!OkToChangeTag(tif, tag)) { + const TIFFFieldInfo *fip = TIFFFindFieldInfo(tag, TIFF_ANY); + if (fip) + TIFFError("TIFFVSetField", + "%s: Cannot modify tag \"%s\" while writing", + tif->tif_name, fip->field_name); + } else + status = TIFFSetField1(tif, tag, ap); + return (status); +} + +static void +TIFFGetField1(TIFFDirectory* td, ttag_t tag, va_list ap) +{ + switch (tag) { + case TIFFTAG_SUBFILETYPE: + *va_arg(ap, uint32*) = td->td_subfiletype; + break; + case TIFFTAG_IMAGEWIDTH: + *va_arg(ap, uint32*) = td->td_imagewidth; + break; + case TIFFTAG_IMAGELENGTH: + *va_arg(ap, uint32*) = td->td_imagelength; + break; + case TIFFTAG_BITSPERSAMPLE: + *va_arg(ap, uint16*) = td->td_bitspersample; + break; + case TIFFTAG_COMPRESSION: + *va_arg(ap, uint16*) = td->td_compression; + break; + case TIFFTAG_PHOTOMETRIC: + *va_arg(ap, uint16*) = td->td_photometric; + break; + case TIFFTAG_THRESHHOLDING: + *va_arg(ap, uint16*) = td->td_threshholding; + break; + case TIFFTAG_FILLORDER: + *va_arg(ap, uint16*) = td->td_fillorder; + break; + case TIFFTAG_DOCUMENTNAME: + *va_arg(ap, char **) = td->td_documentname; + break; + case TIFFTAG_ARTIST: + *va_arg(ap, char **) = td->td_artist; + break; + case TIFFTAG_DATETIME: + *va_arg(ap, char **) = td->td_datetime; + break; + case TIFFTAG_HOSTCOMPUTER: + *va_arg(ap, char **) = td->td_hostcomputer; + break; + case TIFFTAG_IMAGEDESCRIPTION: + *va_arg(ap, char **) = td->td_imagedescription; + break; + case TIFFTAG_MAKE: + *va_arg(ap, char **) = td->td_make; + break; + case TIFFTAG_MODEL: + *va_arg(ap, char **) = td->td_model; + break; + case TIFFTAG_SOFTWARE: + *va_arg(ap, char **) = td->td_software; + break; + case TIFFTAG_ORIENTATION: + *va_arg(ap, uint16*) = td->td_orientation; + break; + case TIFFTAG_SAMPLESPERPIXEL: + *va_arg(ap, uint16*) = td->td_samplesperpixel; + break; + case TIFFTAG_ROWSPERSTRIP: + *va_arg(ap, uint32*) = td->td_rowsperstrip; + break; + case TIFFTAG_MINSAMPLEVALUE: + *va_arg(ap, uint16*) = td->td_minsamplevalue; + break; + case TIFFTAG_MAXSAMPLEVALUE: + *va_arg(ap, uint16*) = td->td_maxsamplevalue; + break; + case TIFFTAG_XRESOLUTION: + *va_arg(ap, float *) = td->td_xresolution; + break; + case TIFFTAG_YRESOLUTION: + *va_arg(ap, float *) = td->td_yresolution; + break; + case TIFFTAG_PLANARCONFIG: + *va_arg(ap, uint16*) = td->td_planarconfig; + break; + case TIFFTAG_XPOSITION: + *va_arg(ap, float *) = td->td_xposition; + break; + case TIFFTAG_YPOSITION: + *va_arg(ap, float *) = td->td_yposition; + break; + case TIFFTAG_PAGENAME: + *va_arg(ap, char **) = td->td_pagename; + break; + case TIFFTAG_GROUP3OPTIONS: + *va_arg(ap, uint32*) = td->td_group3options; + break; + case TIFFTAG_GROUP4OPTIONS: + *va_arg(ap, uint32*) = td->td_group4options; + break; + case TIFFTAG_RESOLUTIONUNIT: + *va_arg(ap, uint16*) = td->td_resolutionunit; + break; + case TIFFTAG_PAGENUMBER: + *va_arg(ap, uint16*) = td->td_pagenumber[0]; + *va_arg(ap, uint16*) = td->td_pagenumber[1]; + break; + case TIFFTAG_HALFTONEHINTS: + *va_arg(ap, uint16*) = td->td_halftonehints[0]; + *va_arg(ap, uint16*) = td->td_halftonehints[1]; + break; + case TIFFTAG_COLORMAP: + *va_arg(ap, uint16**) = td->td_colormap[0]; + *va_arg(ap, uint16**) = td->td_colormap[1]; + *va_arg(ap, uint16**) = td->td_colormap[2]; + break; + case TIFFTAG_PREDICTOR: + *va_arg(ap, uint16*) = td->td_predictor; + break; + case TIFFTAG_STRIPOFFSETS: + case TIFFTAG_TILEOFFSETS: + *va_arg(ap, uint32**) = td->td_stripoffset; + break; + case TIFFTAG_STRIPBYTECOUNTS: + case TIFFTAG_TILEBYTECOUNTS: + *va_arg(ap, uint32**) = td->td_stripbytecount; + break; + case TIFFTAG_MATTEING: + *va_arg(ap, uint16*) = + (td->td_extrasamples == 1 && + td->td_sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA); + break; + case TIFFTAG_EXTRASAMPLES: + *va_arg(ap, uint16*) = td->td_extrasamples; + *va_arg(ap, uint16**) = td->td_sampleinfo; + break; + case TIFFTAG_BADFAXLINES: + *va_arg(ap, uint32*) = td->td_badfaxlines; + break; + case TIFFTAG_CLEANFAXDATA: + *va_arg(ap, uint16*) = td->td_cleanfaxdata; + break; + case TIFFTAG_CONSECUTIVEBADFAXLINES: + *va_arg(ap, uint32*) = td->td_badfaxrun; + break; + case TIFFTAG_TILEWIDTH: + *va_arg(ap, uint32*) = td->td_tilewidth; + break; + case TIFFTAG_TILELENGTH: + *va_arg(ap, uint32*) = td->td_tilelength; + break; + case TIFFTAG_TILEDEPTH: + *va_arg(ap, uint32*) = td->td_tiledepth; + break; + case TIFFTAG_DATATYPE: + *va_arg(ap, uint16*) = + (td->td_sampleformat == SAMPLEFORMAT_VOID ? + 0 : td->td_sampleformat); + break; + case TIFFTAG_SAMPLEFORMAT: + *va_arg(ap, uint16*) = td->td_sampleformat; + break; + case TIFFTAG_IMAGEDEPTH: + *va_arg(ap, uint32*) = td->td_imagedepth; + break; +#ifdef YCBCR_SUPPORT + case TIFFTAG_YCBCRCOEFFICIENTS: + *va_arg(ap, float **) = td->td_ycbcrcoeffs; + break; + case TIFFTAG_YCBCRPOSITIONING: + *va_arg(ap, uint16*) = td->td_ycbcrpositioning; + break; + case TIFFTAG_YCBCRSUBSAMPLING: + *va_arg(ap, uint16*) = td->td_ycbcrsubsampling[0]; + *va_arg(ap, uint16*) = td->td_ycbcrsubsampling[1]; + break; +#endif +#ifdef JPEG_SUPPORT + case TIFFTAG_JPEGPROC: + *va_arg(ap, uint16*) = td->td_jpegproc; + break; + case TIFFTAG_JPEGRESTARTINTERVAL: + *va_arg(ap, uint16*) = td->td_jpegrestartinterval; + break; + case TIFFTAG_JPEGQTABLES: + *va_arg(ap, u_char ***) = td->td_qtab; + break; + case TIFFTAG_JPEGDCTABLES: + *va_arg(ap, u_char ***) = td->td_dctab; + break; + case TIFFTAG_JPEGACTABLES: + *va_arg(ap, u_char ***) = td->td_actab; + break; +#endif +#ifdef COLORIMETRY_SUPPORT + case TIFFTAG_WHITEPOINT: + *va_arg(ap, float **) = td->td_whitepoint; + break; + case TIFFTAG_PRIMARYCHROMATICITIES: + *va_arg(ap, float **) = td->td_primarychromas; + break; + case TIFFTAG_TRANSFERFUNCTION: + *va_arg(ap, uint16**) = td->td_transferfunction[0]; + if (td->td_samplesperpixel - td->td_extrasamples > 1) { + *va_arg(ap, uint16**) = td->td_transferfunction[1]; + *va_arg(ap, uint16**) = td->td_transferfunction[2]; + } + break; + case TIFFTAG_REFERENCEBLACKWHITE: + *va_arg(ap, float **) = td->td_refblackwhite; + break; +#endif +#ifdef CMYK_SUPPORT + case TIFFTAG_INKSET: + *va_arg(ap, uint16*) = td->td_inkset; + break; + case TIFFTAG_DOTRANGE: + *va_arg(ap, uint16*) = td->td_dotrange[0]; + *va_arg(ap, uint16*) = td->td_dotrange[1]; + break; + case TIFFTAG_INKNAMES: + *va_arg(ap, char **) = td->td_inknames; + break; + case TIFFTAG_TARGETPRINTER: + *va_arg(ap, char **) = td->td_targetprinter; + break; +#endif + default: + TIFFError("TIFFGetField1", + "Internal error, no value returned for tag \"%s\"", + TIFFFieldWithTag(tag)->field_name); + break; + } + va_end(ap); +} + +/* + * Return the value of a field in the + * internal directory structure. + */ +int +TIFFGetField(TIFF* tif, ttag_t tag, ...) +{ + const TIFFFieldInfo *fip = TIFFFindFieldInfo(tag, TIFF_ANY); + + if (fip) { + u_short bit = fip->field_bit; + if (bit != FIELD_IGNORE && TIFFFieldSet(tif, bit)) { + va_list ap; + va_start(ap, tag); + TIFFGetField1(&tif->tif_dir, tag, ap); + va_end(ap); + return (1); + } + } else + TIFFError("TIFFGetField", "Unknown field, tag 0x%x", tag); + return (0); +} + +/* + * Like TIFFGetField, but taking a varargs + * parameter list. This routine is useful + * for building higher-level interfaces on + * top of the library. + */ +int +TIFFVGetField(TIFF* tif, ttag_t tag, va_list ap) +{ + const TIFFFieldInfo *fip = TIFFFindFieldInfo(tag, TIFF_ANY); + + if (fip) { + u_short bit = fip->field_bit; + if (bit != FIELD_IGNORE && TIFFFieldSet(tif, bit)) { + TIFFGetField1(&tif->tif_dir, tag, ap); + return (1); + } + } else + TIFFError("TIFFGetField", "Unknown field, tag 0x%x", tag); + return (0); +} + +/* + * Internal interface to TIFFGetField... + */ +void +_TIFFgetfield(TIFFDirectory* td, ttag_t tag, ...) +{ + va_list ap; + va_start(ap, tag); + TIFFGetField1(td, tag, ap); + va_end(ap); +} + +#define CleanupField(member) { \ + if (td->member) { \ + _TIFFfree((char *)td->member); \ + td->member = 0; \ + } \ +} + +/* + * Release storage associated with a directory. + */ +void +TIFFFreeDirectory(TIFF* tif) +{ + register TIFFDirectory *td = &tif->tif_dir; + + CleanupField(td_colormap[0]); + CleanupField(td_colormap[1]); + CleanupField(td_colormap[2]); + CleanupField(td_documentname); + CleanupField(td_artist); + CleanupField(td_datetime); + CleanupField(td_hostcomputer); + CleanupField(td_imagedescription); + CleanupField(td_make); + CleanupField(td_model); + CleanupField(td_software); + CleanupField(td_pagename); + CleanupField(td_sampleinfo); +#ifdef YCBCR_SUPPORT + CleanupField(td_ycbcrcoeffs); +#endif +#ifdef JPEG_SUPPORT + CleanupField(td_qtab); + CleanupField(td_dctab); + CleanupField(td_actab); +#endif +#ifdef CMYK_SUPPORT + CleanupField(td_inknames); + CleanupField(td_targetprinter); +#endif +#ifdef COLORIMETRY_SUPPORT + CleanupField(td_whitepoint); + CleanupField(td_primarychromas); + CleanupField(td_refblackwhite); + CleanupField(td_transferfunction[0]); + CleanupField(td_transferfunction[1]); + CleanupField(td_transferfunction[2]); +#endif + CleanupField(td_stripoffset); + CleanupField(td_stripbytecount); +} +#undef CleanupField + +/* + * Setup a default directory structure. + */ +int +TIFFDefaultDirectory(TIFF* tif) +{ + register TIFFDirectory *td = &tif->tif_dir; + + memset(td, 0, sizeof (*td)); + td->td_fillorder = FILLORDER_MSB2LSB; + td->td_bitspersample = 1; + td->td_threshholding = THRESHHOLD_BILEVEL; + td->td_orientation = ORIENTATION_TOPLEFT; + td->td_samplesperpixel = 1; + td->td_predictor = 1; + td->td_rowsperstrip = 0xffffffff; + td->td_tilewidth = 0xffffffff; + td->td_tilelength = 0xffffffff; + td->td_tiledepth = 1; + td->td_resolutionunit = RESUNIT_INCH; + td->td_sampleformat = SAMPLEFORMAT_VOID; + td->td_imagedepth = 1; +#ifdef YCBCR_SUPPORT + td->td_ycbcrsubsampling[0] = 2; + td->td_ycbcrsubsampling[1] = 2; + td->td_ycbcrpositioning = YCBCRPOSITION_CENTERED; +#endif +#ifdef CMYK_SUPPORT + td->td_inkset = INKSET_CMYK; +#endif + (void) TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + /* + * NB: The directory is marked dirty as a result of setting + * up the default compression scheme. However, this really + * isn't correct -- we want TIFF_DIRTYDIRECT to be set only + * if the user does something. We could just do the setup + * by hand, but it seems better to use the normal mechanism + * (i.e. TIFFSetField). + */ + tif->tif_flags &= ~TIFF_DIRTYDIRECT; + return (1); +} + +/* + * Set the n-th directory as the current directory. + * NB: Directories are numbered starting at 0. + */ +int +TIFFSetDirectory(TIFF* tif, tdir_t dirn) +{ + static const char module[] = "TIFFSetDirectory"; + uint16 dircount; + uint32 nextdir; + tdir_t n; + + nextdir = tif->tif_header.tiff_diroff; + for (n = dirn; n > 0 && nextdir != 0; n--) { + if (!SeekOK(tif, nextdir) || + !ReadOK(tif, &dircount, sizeof (uint16))) { + TIFFError(module, "%s: Error fetching directory count", + tif->tif_name); + return (0); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabShort(&dircount); + TIFFSeekFile(tif, dircount*sizeof (TIFFDirEntry), L_INCR); + if (!ReadOK(tif, &nextdir, sizeof (uint32))) { + TIFFError(module, "%s: Error fetching directory link", + tif->tif_name); + return (0); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong(&nextdir); + } + tif->tif_nextdiroff = nextdir; + /* + * Set curdir to the actual directory index. The + * -1 is because TIFFReadDirectory will increment + * tif_curdir after successfully reading the directory. + */ + tif->tif_curdir = (dirn - n) - 1; + return (TIFFReadDirectory(tif)); +} + +/* + * Return an indication of whether or not we are + * at the last directory in the file. + */ +int +TIFFLastDirectory(TIFF* tif) +{ + return (tif->tif_nextdiroff == 0); +} diff --git a/panda/src/tiff/tif_dirinfo.c b/panda/src/tiff/tif_dirinfo.c new file mode 100644 index 0000000000..d9540dc4f8 --- /dev/null +++ b/panda/src/tiff/tif_dirinfo.c @@ -0,0 +1,303 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Known Directory Tag Support. + */ +#include "tiffiop.h" + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +/* + * NB: THIS ARRAY IS ASSUMED TO BE SORTED BY TAG. + * Also, if a tag can have both LONG and SHORT types + * then the LONG must be placed before the SHORT for + * writing to work properly. + */ +const TIFFFieldInfo tiffFieldInfo[] = { + { TIFFTAG_SUBFILETYPE, 1, 1, TIFF_LONG, FIELD_SUBFILETYPE, + TRUE, "SubfileType" }, +/* XXX SHORT for compatibility w/ old versions of the library */ + { TIFFTAG_SUBFILETYPE, 1, 1, TIFF_SHORT, FIELD_SUBFILETYPE, + TRUE, "SubfileType" }, + { TIFFTAG_OSUBFILETYPE, 1, 1, TIFF_SHORT, FIELD_SUBFILETYPE, + TRUE, "OldSubfileType" }, + { TIFFTAG_IMAGEWIDTH, 1, 1, TIFF_LONG, FIELD_IMAGEDIMENSIONS, + FALSE, "ImageWidth" }, + { TIFFTAG_IMAGEWIDTH, 1, 1, TIFF_SHORT, FIELD_IMAGEDIMENSIONS, + FALSE, "ImageWidth" }, + { TIFFTAG_IMAGELENGTH, 1, 1, TIFF_LONG, FIELD_IMAGEDIMENSIONS, + TRUE, "ImageLength" }, + { TIFFTAG_IMAGELENGTH, 1, 1, TIFF_SHORT, FIELD_IMAGEDIMENSIONS, + TRUE, "ImageLength" }, + { TIFFTAG_BITSPERSAMPLE, -1,-1, TIFF_SHORT, FIELD_BITSPERSAMPLE, + FALSE, "BitsPerSample" }, + { TIFFTAG_COMPRESSION, -1, 1, TIFF_SHORT, FIELD_COMPRESSION, + FALSE, "Compression" }, + { TIFFTAG_PHOTOMETRIC, 1, 1, TIFF_SHORT, FIELD_PHOTOMETRIC, + FALSE, "PhotometricInterpretation" }, + { TIFFTAG_THRESHHOLDING, 1, 1, TIFF_SHORT, FIELD_THRESHHOLDING, + TRUE, "Threshholding" }, + { TIFFTAG_CELLWIDTH, 1, 1, TIFF_SHORT, FIELD_IGNORE, + TRUE, "CellWidth" }, + { TIFFTAG_CELLLENGTH, 1, 1, TIFF_SHORT, FIELD_IGNORE, + TRUE, "CellLength" }, + { TIFFTAG_FILLORDER, 1, 1, TIFF_SHORT, FIELD_FILLORDER, + FALSE, "FillOrder" }, + { TIFFTAG_DOCUMENTNAME, -1,-1, TIFF_ASCII, FIELD_DOCUMENTNAME, + TRUE, "DocumentName" }, + { TIFFTAG_IMAGEDESCRIPTION, -1,-1, TIFF_ASCII, FIELD_IMAGEDESCRIPTION, + TRUE, "ImageDescription" }, + { TIFFTAG_MAKE, -1,-1, TIFF_ASCII, FIELD_MAKE, + TRUE, "Make" }, + { TIFFTAG_MODEL, -1,-1, TIFF_ASCII, FIELD_MODEL, + TRUE, "Model" }, + { TIFFTAG_STRIPOFFSETS, -1,-1, TIFF_LONG, FIELD_STRIPOFFSETS, + FALSE, "StripOffsets" }, + { TIFFTAG_STRIPOFFSETS, -1,-1, TIFF_SHORT, FIELD_STRIPOFFSETS, + FALSE, "StripOffsets" }, + { TIFFTAG_ORIENTATION, 1, 1, TIFF_SHORT, FIELD_ORIENTATION, + FALSE, "Orientation" }, + { TIFFTAG_SAMPLESPERPIXEL, 1, 1, TIFF_SHORT, FIELD_SAMPLESPERPIXEL, + FALSE, "SamplesPerPixel" }, + { TIFFTAG_ROWSPERSTRIP, 1, 1, TIFF_LONG, FIELD_ROWSPERSTRIP, + FALSE, "RowsPerStrip" }, + { TIFFTAG_ROWSPERSTRIP, 1, 1, TIFF_SHORT, FIELD_ROWSPERSTRIP, + FALSE, "RowsPerStrip" }, + { TIFFTAG_STRIPBYTECOUNTS, -1,-1, TIFF_LONG, FIELD_STRIPBYTECOUNTS, + FALSE, "StripByteCounts" }, + { TIFFTAG_STRIPBYTECOUNTS, -1,-1, TIFF_SHORT, FIELD_STRIPBYTECOUNTS, + FALSE, "StripByteCounts" }, + { TIFFTAG_MINSAMPLEVALUE, -2,-1, TIFF_SHORT, FIELD_MINSAMPLEVALUE, + TRUE, "MinSampleValue" }, + { TIFFTAG_MAXSAMPLEVALUE, -2,-1, TIFF_SHORT, FIELD_MAXSAMPLEVALUE, + TRUE, "MaxSampleValue" }, + { TIFFTAG_XRESOLUTION, 1, 1, TIFF_RATIONAL, FIELD_RESOLUTION, + FALSE, "XResolution" }, + { TIFFTAG_YRESOLUTION, 1, 1, TIFF_RATIONAL, FIELD_RESOLUTION, + FALSE, "YResolution" }, + { TIFFTAG_PLANARCONFIG, 1, 1, TIFF_SHORT, FIELD_PLANARCONFIG, + FALSE, "PlanarConfiguration" }, + { TIFFTAG_PAGENAME, -1,-1, TIFF_ASCII, FIELD_PAGENAME, + TRUE, "PageName" }, + { TIFFTAG_XPOSITION, 1, 1, TIFF_RATIONAL, FIELD_POSITION, + TRUE, "XPosition" }, + { TIFFTAG_YPOSITION, 1, 1, TIFF_RATIONAL, FIELD_POSITION, + TRUE, "YPosition" }, + { TIFFTAG_FREEOFFSETS, -1,-1, TIFF_LONG, FIELD_IGNORE, + FALSE, "FreeOffsets" }, + { TIFFTAG_FREEBYTECOUNTS, -1,-1, TIFF_LONG, FIELD_IGNORE, + FALSE, "FreeByteCounts" }, + { TIFFTAG_GRAYRESPONSEUNIT, 1, 1, TIFF_SHORT, FIELD_IGNORE, + TRUE, "GrayResponseUnit" }, + { TIFFTAG_GRAYRESPONSECURVE,-1,-1, TIFF_SHORT, FIELD_IGNORE, + TRUE, "GrayResponseCurve" }, + { TIFFTAG_GROUP3OPTIONS, 1, 1, TIFF_LONG, FIELD_GROUP3OPTIONS, + FALSE, "Group3Options" }, + { TIFFTAG_GROUP4OPTIONS, 1, 1, TIFF_LONG, FIELD_GROUP4OPTIONS, + FALSE, "Group4Options" }, + { TIFFTAG_RESOLUTIONUNIT, 1, 1, TIFF_SHORT, FIELD_RESOLUTIONUNIT, + FALSE, "ResolutionUnit" }, + { TIFFTAG_PAGENUMBER, 2, 2, TIFF_SHORT, FIELD_PAGENUMBER, + TRUE, "PageNumber" }, + { TIFFTAG_COLORRESPONSEUNIT, 1, 1, TIFF_SHORT, FIELD_IGNORE, + TRUE, "ColorResponseUnit" }, +#ifdef COLORIMETRY_SUPPORT + { TIFFTAG_TRANSFERFUNCTION, -1,-1, TIFF_SHORT, FIELD_TRANSFERFUNCTION, + TRUE, "TransferFunction" }, +#endif + { TIFFTAG_SOFTWARE, -1,-1, TIFF_ASCII, FIELD_SOFTWARE, + TRUE, "Software" }, + { TIFFTAG_DATETIME, 20,20, TIFF_ASCII, FIELD_DATETIME, + TRUE, "DateTime" }, + { TIFFTAG_ARTIST, -1,-1, TIFF_ASCII, FIELD_ARTIST, + TRUE, "Artist" }, + { TIFFTAG_HOSTCOMPUTER, -1,-1, TIFF_ASCII, FIELD_HOSTCOMPUTER, + TRUE, "HostComputer" }, + { TIFFTAG_PREDICTOR, 1, 1, TIFF_SHORT, FIELD_PREDICTOR, + FALSE, "Predictor" }, +#ifdef COLORIMETRY_SUPPORT + { TIFFTAG_WHITEPOINT, 2, 2, TIFF_RATIONAL,FIELD_WHITEPOINT, + TRUE, "WhitePoint" }, + { TIFFTAG_PRIMARYCHROMATICITIES,6,6,TIFF_RATIONAL,FIELD_PRIMARYCHROMAS, + TRUE, "PrimaryChromaticities" }, +#endif + { TIFFTAG_COLORMAP, -1,-1, TIFF_SHORT, FIELD_COLORMAP, + TRUE, "ColorMap" }, + { TIFFTAG_HALFTONEHINTS, 2, 2, TIFF_SHORT, FIELD_HALFTONEHINTS, + TRUE, "HalftoneHints" }, + { TIFFTAG_TILEWIDTH, 1, 1, TIFF_LONG, FIELD_TILEDIMENSIONS, + FALSE, "TileWidth" }, + { TIFFTAG_TILEWIDTH, 1, 1, TIFF_SHORT, FIELD_TILEDIMENSIONS, + FALSE, "TileWidth" }, + { TIFFTAG_TILELENGTH, 1, 1, TIFF_LONG, FIELD_TILEDIMENSIONS, + FALSE, "TileLength" }, + { TIFFTAG_TILELENGTH, 1, 1, TIFF_SHORT, FIELD_TILEDIMENSIONS, + FALSE, "TileLength" }, + { TIFFTAG_TILEOFFSETS, -1, 1, TIFF_LONG, FIELD_STRIPOFFSETS, + FALSE, "TileOffsets" }, + { TIFFTAG_TILEBYTECOUNTS, -1, 1, TIFF_LONG, FIELD_STRIPBYTECOUNTS, + FALSE, "TileByteCounts" }, + { TIFFTAG_TILEBYTECOUNTS, -1, 1, TIFF_SHORT, FIELD_STRIPBYTECOUNTS, + FALSE, "TileByteCounts" }, + { TIFFTAG_BADFAXLINES, 1, 1, TIFF_LONG, FIELD_BADFAXLINES, + TRUE, "BadFaxLines" }, + { TIFFTAG_BADFAXLINES, 1, 1, TIFF_SHORT, FIELD_BADFAXLINES, + TRUE, "BadFaxLines" }, + { TIFFTAG_CLEANFAXDATA, 1, 1, TIFF_SHORT, FIELD_CLEANFAXDATA, + TRUE, "CleanFaxData" }, + { TIFFTAG_CONSECUTIVEBADFAXLINES,1,1, TIFF_LONG,FIELD_BADFAXRUN, + TRUE, "ConsecutiveBadFaxLines" }, + { TIFFTAG_CONSECUTIVEBADFAXLINES,1,1, TIFF_SHORT,FIELD_BADFAXRUN, + TRUE, "ConsecutiveBadFaxLines" }, +#ifdef CMYK_SUPPORT /* 6.0 CMYK tags */ + { TIFFTAG_INKSET, 1, 1, TIFF_SHORT, FIELD_INKSET, + FALSE, "InkSet" }, + { TIFFTAG_INKNAMES, -1,-1, TIFF_ASCII, FIELD_INKNAMES, + TRUE, "InkNames" }, + { TIFFTAG_DOTRANGE, 2, 2, TIFF_SHORT, FIELD_DOTRANGE, + FALSE, "DotRange" }, + { TIFFTAG_DOTRANGE, 2, 2, TIFF_BYTE, FIELD_DOTRANGE, + FALSE, "DotRange" }, + { TIFFTAG_TARGETPRINTER, -1,-1, TIFF_ASCII, FIELD_TARGETPRINTER, + TRUE, "TargetPrinter" }, +#endif + { TIFFTAG_EXTRASAMPLES, -1,-1, TIFF_SHORT, FIELD_EXTRASAMPLES, + FALSE, "ExtraSamples" }, +/* XXX for bogus Adobe Photoshop v2.5 files */ + { TIFFTAG_EXTRASAMPLES, -1,-1, TIFF_BYTE, FIELD_EXTRASAMPLES, + FALSE, "ExtraSamples" }, + { TIFFTAG_SAMPLEFORMAT, -1,-1, TIFF_SHORT, FIELD_SAMPLEFORMAT, + FALSE, "SampleFormat" }, +#ifdef notdef + { TIFFTAG_SMINSAMPLEVALUE, -2,-1, TIFF_ANY, FIELD_SMINSAMPLEVALUE, + TRUE, "SMinSampleValue" }, + { TIFFTAG_SMAXSAMPLEVALUE, -2,-1, TIFF_ANY, FIELD_SMAXSAMPLEVALUE, + TRUE, "SMaxSampleValue" }, +#endif +#ifdef JPEG_SUPPORT /* 6.0 JPEG tags */ + { TIFFTAG_JPEGPROC, 1, 1, TIFF_SHORT, FIELD_JPEGPROC, + FALSE, "JPEGProc" }, + { TIFFTAG_JPEGIFOFFSET, 1, 1, TIFF_LONG, FIELD_IGNORE, + FALSE, "JPEGInterchangeFormat" }, + { TIFFTAG_JPEGIFBYTECOUNT, 1, 1, TIFF_LONG, FIELD_IGNORE, + FALSE, "JPEGInterchangeFormatLength" }, + { TIFFTAG_JPEGRESTARTINTERVAL,1,1, TIFF_SHORT,FIELD_JPEGRESTARTINTERVAL, + FALSE, "JPEGRestartInterval" }, + { TIFFTAG_JPEGQTABLES, -2,-1, TIFF_LONG, FIELD_JPEGQTABLES, + FALSE, "JPEGQTables" }, + { TIFFTAG_JPEGDCTABLES, -2,-1, TIFF_LONG, FIELD_JPEGDCTABLES, + FALSE, "JPEGDCTables" }, + { TIFFTAG_JPEGACTABLES, -2,-1, TIFF_LONG, FIELD_JPEGACTABLES, + FALSE, "JPEGACTables" }, +#endif +#ifdef YCBCR_SUPPORT /* 6.0 YCbCr tags */ + { TIFFTAG_YCBCRCOEFFICIENTS, 3, 3, TIFF_RATIONAL, FIELD_YCBCRCOEFFICIENTS, + FALSE, "YCbCrCoefficients" }, + { TIFFTAG_YCBCRSUBSAMPLING, 2, 2, TIFF_SHORT, FIELD_YCBCRSUBSAMPLING, + FALSE, "YCbCrSubsampling" }, + { TIFFTAG_YCBCRPOSITIONING, 1, 1, TIFF_SHORT, FIELD_YCBCRPOSITIONING, + FALSE, "YCbCrPositioning" }, +#endif +#ifdef COLORIMETRY_SUPPORT + { TIFFTAG_REFERENCEBLACKWHITE,6,6,TIFF_RATIONAL, FIELD_REFBLACKWHITE, + FALSE, "ReferenceBlackWhite" }, +/* XXX temporarily accept LONG for backwards compatibility */ + { TIFFTAG_REFERENCEBLACKWHITE,6,6,TIFF_LONG, FIELD_REFBLACKWHITE, + FALSE, "ReferenceBlackWhite" }, +#endif +/* begin SGI tags */ + { TIFFTAG_MATTEING, 1, 1, TIFF_SHORT, FIELD_EXTRASAMPLES, + FALSE, "Matteing" }, + { TIFFTAG_DATATYPE, -2,-1, TIFF_SHORT, FIELD_SAMPLEFORMAT, + FALSE, "DataType" }, + { TIFFTAG_IMAGEDEPTH, 1, 1, TIFF_LONG, FIELD_IMAGEDEPTH, + FALSE, "ImageDepth" }, + { TIFFTAG_IMAGEDEPTH, 1, 1, TIFF_SHORT, FIELD_IMAGEDEPTH, + FALSE, "ImageDepth" }, + { TIFFTAG_TILEDEPTH, 1, 1, TIFF_LONG, FIELD_TILEDEPTH, + FALSE, "TileDepth" }, + { TIFFTAG_TILEDEPTH, 1, 1, TIFF_SHORT, FIELD_TILEDEPTH, + FALSE, "TileDepth" }, +/* end SGI tags */ + { 0 } +}; + +const int tiffDataWidth[] = { + 1, /* nothing */ + 1, /* TIFF_BYTE */ + 1, /* TIFF_ASCII */ + 2, /* TIFF_SHORT */ + 4, /* TIFF_LONG */ + 8, /* TIFF_RATIONAL */ + 1, /* TIFF_SBYTE */ + 1, /* TIFF_UNDEFINED */ + 2, /* TIFF_SSHORT */ + 4, /* TIFF_SLONG */ + 8, /* TIFF_SRATIONAL */ + 4, /* TIFF_FLOAT */ + 8, /* TIFF_DOUBLE */ +}; + +const TIFFFieldInfo * +TIFFFindFieldInfo(ttag_t tag, TIFFDataType dt) +{ + static const TIFFFieldInfo *last = NULL; + register const TIFFFieldInfo *fip; + + if (last && last->field_tag == tag && + (dt == TIFF_ANY || dt == last->field_type)) + return (last); + /* NB: if table gets big, use sorted search (e.g. binary search) */ + for (fip = tiffFieldInfo; fip->field_tag; fip++) + if (fip->field_tag == tag && + (dt == TIFF_ANY || fip->field_type == dt)) + return (last = fip); + return ((const TIFFFieldInfo *)0); +} + +#include +#include + +const TIFFFieldInfo * +TIFFFieldWithTag(ttag_t tag) +{ + const TIFFFieldInfo *fip = TIFFFindFieldInfo(tag, TIFF_ANY); + if (!fip) { + TIFFError("TIFFFieldWithTag", + "Internal error, unknown tag 0x%x", (u_int) tag); + assert(fip != NULL); + /*NOTREACHED*/ + } + return (fip); +} diff --git a/panda/src/tiff/tif_dirread.c b/panda/src/tiff/tif_dirread.c new file mode 100644 index 0000000000..bb35965330 --- /dev/null +++ b/panda/src/tiff/tif_dirread.c @@ -0,0 +1,1248 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Directory Read Support Routines. + */ +#include "tiffiop.h" + +#define IGNORE 0 /* tag placeholder used below */ + +#if HAVE_IEEEFP +#define TIFFCvtIEEEFloatToNative(tif, n, fp) +#endif + +static void EstimateStripByteCounts(TIFF*, TIFFDirEntry*, uint16); +static void MissingRequired(TIFF*, const char*); +static int CheckDirCount(TIFF*, TIFFDirEntry*, uint32); +static tsize_t TIFFFetchData(TIFF*, TIFFDirEntry*, char*); +static tsize_t TIFFFetchString(TIFF*, TIFFDirEntry*, char*); +static float TIFFFetchRational(TIFF*, TIFFDirEntry*); +static int TIFFFetchNormalTag(TIFF*, TIFFDirEntry*); +static int TIFFFetchPerSampleShorts(TIFF*, TIFFDirEntry*, int*); +static int TIFFFetchShortArray(TIFF*, TIFFDirEntry*, uint16*); +static int TIFFFetchStripThing(TIFF*, TIFFDirEntry*, long, uint32**); +static int TIFFFetchExtraSamples(TIFF*, TIFFDirEntry*); +static int TIFFFetchRefBlackWhite(TIFF*, TIFFDirEntry*); +static int TIFFFetchJPEGQTables(TIFF*, TIFFDirEntry*); +static int TIFFFetchJPEGCTables(TIFF*, TIFFDirEntry*, u_char***); +static float TIFFFetchFloat(TIFF*, TIFFDirEntry*); +static int TIFFFetchFloatArray(TIFF*, TIFFDirEntry*, float*); +static int TIFFFetchShortPair(TIFF*, TIFFDirEntry*); +#ifdef STRIPCHOP_SUPPORT +static void ChopUpSingleUncompressedStrip(TIFF*); +#endif + +static char * +CheckMalloc(TIFF* tif, size_t n, const char* what) +{ + char *cp = _TIFFmalloc(n); + if (cp == NULL) + TIFFError(tif->tif_name, "No space %s", what); + return (cp); +} + +/* + * Read the next TIFF directory from a file + * and convert it to the internal format. + * We read directories sequentially. + */ +int +TIFFReadDirectory(TIFF* tif) +{ + register TIFFDirEntry *dp; + register int n; + register TIFFDirectory *td; + TIFFDirEntry *dir; + int iv; + long v; + const TIFFFieldInfo *fip; + uint16 dircount; + uint32 nextdiroff; + char *cp; + int diroutoforderwarning = 0; + + tif->tif_diroff = tif->tif_nextdiroff; + if (tif->tif_diroff == 0) /* no more directories */ + return (0); + /* + * Cleanup any previous compression state. + */ + if (tif->tif_cleanup) + (*tif->tif_cleanup)(tif); + tif->tif_curdir++; + nextdiroff = 0; + if (!isMapped(tif)) { + if (!SeekOK(tif, tif->tif_diroff)) { + TIFFError(tif->tif_name, + "Seek error accessing TIFF directory"); + return (0); + } + if (!ReadOK(tif, &dircount, sizeof (uint16))) { + TIFFError(tif->tif_name, + "Can not read TIFF directory count"); + return (0); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabShort(&dircount); + dir = (TIFFDirEntry *)CheckMalloc(tif, + dircount * sizeof (TIFFDirEntry), "to read TIFF directory"); + if (dir == NULL) + return (0); + if (!ReadOK(tif, dir, dircount*sizeof (TIFFDirEntry))) { + TIFFError(tif->tif_name, "Can not read TIFF directory"); + goto bad; + } + /* + * Read offset to next directory for sequential scans. + */ + (void) ReadOK(tif, &nextdiroff, sizeof (uint32)); + } else { + toff_t off = tif->tif_diroff; + + if (off + sizeof (short) > tif->tif_size) { + TIFFError(tif->tif_name, + "Can not read TIFF directory count"); + return (0); + } else + memcpy(&dircount, tif->tif_base + off, sizeof (uint16)); + off += sizeof (uint16); + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabShort(&dircount); + dir = (TIFFDirEntry *)CheckMalloc(tif, + dircount * sizeof (TIFFDirEntry), "to read TIFF directory"); + if (dir == NULL) + return (0); + if (off + dircount*sizeof (TIFFDirEntry) > tif->tif_size) { + TIFFError(tif->tif_name, "Can not read TIFF directory"); + goto bad; + } else + memcpy(dir, tif->tif_base + off, + dircount*sizeof (TIFFDirEntry)); + off += dircount* sizeof (TIFFDirEntry); + if (off + sizeof (uint32) < tif->tif_size) + memcpy(&nextdiroff, tif->tif_base+off, sizeof (uint32)); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong(&nextdiroff); + tif->tif_nextdiroff = nextdiroff; + + tif->tif_flags &= ~TIFF_BEENWRITING; /* reset before new dir */ + /* + * Setup default value and then make a pass over + * the fields to check type and tag information, + * and to extract info required to size data + * structures. A second pass is made afterwards + * to read in everthing not taken in the first pass. + */ + td = &tif->tif_dir; + /* free any old stuff and reinit */ + TIFFFreeDirectory(tif); + TIFFDefaultDirectory(tif); + tif->tif_postdecode = TIFFNoPostDecode; + /* + * Electronic Arts writes gray-scale TIFF files + * without a PlanarConfiguration directory entry. + * Thus we setup a default value here, even though + * the TIFF spec says there is no default value. + */ + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + for (fip = tiffFieldInfo, dp = dir, n = dircount; n > 0; n--, dp++) { + if (tif->tif_flags & TIFF_SWAB) { + TIFFSwabArrayOfShort(&dp->tdir_tag, 2); + TIFFSwabArrayOfLong(&dp->tdir_count, 2); + } + /* + * Find the field information entry for this tag. + */ + /* + * Silicon Beach (at least) writes unordered + * directory tags (violating the spec). Handle + * it here, but be obnoxious (maybe they'll fix it?). + */ + if (dp->tdir_tag < fip->field_tag) { + if (!diroutoforderwarning) { + TIFFWarning(tif->tif_name, + "invalid TIFF directory; tags are not sorted in ascending order"); + diroutoforderwarning = 1; + } + fip = tiffFieldInfo; /* O(n^2) */ + } + while (fip->field_tag && fip->field_tag < dp->tdir_tag) + fip++; + if (!fip->field_tag || fip->field_tag != dp->tdir_tag) { + TIFFWarning(tif->tif_name, + "unknown field with tag %d (0x%x) ignored", + dp->tdir_tag, dp->tdir_tag); + dp->tdir_tag = IGNORE; + fip = tiffFieldInfo; /* restart search */ + continue; + } + /* + * Null out old tags that we ignore. + */ + if (fip->field_bit == FIELD_IGNORE) { + ignore: + dp->tdir_tag = IGNORE; + continue; + } + /* + * Check data type. + */ + while (dp->tdir_type != (u_short)fip->field_type) { + if (fip->field_type == TIFF_ANY) /* wildcard */ + break; + fip++; + if (!fip->field_tag || fip->field_tag != dp->tdir_tag) { + TIFFWarning(tif->tif_name, + "wrong data type %d for \"%s\"; tag ignored", + dp->tdir_type, fip[-1].field_name); + goto ignore; + } + } + /* + * Check count if known in advance. + */ + if (fip->field_readcount != TIFF_VARIABLE) { + uint32 expected = (fip->field_readcount == TIFF_SPP) ? + (uint32) td->td_samplesperpixel : + (uint32) fip->field_readcount; + if (!CheckDirCount(tif, dp, expected)) + goto ignore; + } + + switch (dp->tdir_tag) { + case TIFFTAG_STRIPOFFSETS: + case TIFFTAG_STRIPBYTECOUNTS: + case TIFFTAG_TILEOFFSETS: + case TIFFTAG_TILEBYTECOUNTS: + TIFFSetFieldBit(tif, fip->field_bit); + break; + case TIFFTAG_IMAGEWIDTH: + case TIFFTAG_IMAGELENGTH: + case TIFFTAG_IMAGEDEPTH: + case TIFFTAG_TILELENGTH: + case TIFFTAG_TILEWIDTH: + case TIFFTAG_TILEDEPTH: + case TIFFTAG_PLANARCONFIG: + case TIFFTAG_SAMPLESPERPIXEL: + case TIFFTAG_ROWSPERSTRIP: + if (!TIFFFetchNormalTag(tif, dp)) + goto bad; + dp->tdir_tag = IGNORE; + break; + case TIFFTAG_EXTRASAMPLES: + (void) TIFFFetchExtraSamples(tif, dp); + dp->tdir_tag = IGNORE; + break; + } + } + + /* + * Allocate directory structure and setup defaults. + */ + if (!TIFFFieldSet(tif, FIELD_IMAGEDIMENSIONS)) { + MissingRequired(tif, "ImageLength"); + goto bad; + } + if (!TIFFFieldSet(tif, FIELD_PLANARCONFIG)) { + MissingRequired(tif, "PlanarConfiguration"); + goto bad; + } + /* + * Setup appropriate structures (by strip or by tile) + */ + if (!TIFFFieldSet(tif, FIELD_TILEDIMENSIONS)) { + td->td_stripsperimage = (td->td_rowsperstrip == 0xffffffff ? + (td->td_imagelength != 0 ? 1 : 0) : + howmany(td->td_imagelength, td->td_rowsperstrip)); + td->td_tilewidth = td->td_imagewidth; + td->td_tilelength = td->td_rowsperstrip; + td->td_tiledepth = td->td_imagedepth; + tif->tif_flags &= ~TIFF_ISTILED; + } else { + td->td_stripsperimage = TIFFNumberOfTiles(tif); + tif->tif_flags |= TIFF_ISTILED; + } + td->td_nstrips = td->td_stripsperimage; + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) + td->td_nstrips *= td->td_samplesperpixel; + if (!TIFFFieldSet(tif, FIELD_STRIPOFFSETS)) { + MissingRequired(tif, + isTiled(tif) ? "TileOffsets" : "StripOffsets"); + goto bad; + } + + /* + * Second pass: extract other information. + */ + for (dp = dir, n = dircount; n > 0; n--, dp++) { + if (dp->tdir_tag == IGNORE) + continue; + switch (dp->tdir_tag) { + case TIFFTAG_COMPRESSION: + case TIFFTAG_MINSAMPLEVALUE: + case TIFFTAG_MAXSAMPLEVALUE: + case TIFFTAG_BITSPERSAMPLE: + /* + * The 5.0 spec says the Compression tag has + * one value, while earlier specs say it has + * one value per sample. Because of this, we + * accept the tag if one value is supplied. + * + * The MinSampleValue, MaxSampleValue and + * BitsPerSample tags are supposed to be written + * as one value/sample, but some vendors incorrectly + * write one value only -- so we accept that + * as well (yech). + */ + if (dp->tdir_count == 1) { + v = TIFFExtractData(tif, + dp->tdir_type, dp->tdir_offset); + if (!TIFFSetField(tif, dp->tdir_tag, (int)v)) + goto bad; + break; + } + /* fall thru... */ + case TIFFTAG_DATATYPE: + case TIFFTAG_SAMPLEFORMAT: + if (!TIFFFetchPerSampleShorts(tif, dp, &iv) || + !TIFFSetField(tif, dp->tdir_tag, iv)) + goto bad; + break; + case TIFFTAG_STRIPOFFSETS: + case TIFFTAG_TILEOFFSETS: + if (!TIFFFetchStripThing(tif, dp, + td->td_nstrips, &td->td_stripoffset)) + goto bad; + break; + case TIFFTAG_STRIPBYTECOUNTS: + case TIFFTAG_TILEBYTECOUNTS: + if (!TIFFFetchStripThing(tif, dp, + td->td_nstrips, &td->td_stripbytecount)) + goto bad; + break; + case TIFFTAG_COLORMAP: + case TIFFTAG_TRANSFERFUNCTION: + /* + * TransferFunction can have either 1x or 3x data + * values; Colormap can have only 3x items. + */ + v = 1L<td_bitspersample; + if (dp->tdir_tag == TIFFTAG_COLORMAP || + dp->tdir_count != v) { + if (!CheckDirCount(tif, dp, 3*v)) + break; + } + v *= sizeof (uint16); + cp = CheckMalloc(tif, dp->tdir_count * sizeof (uint16), + "to read \"TransferFunction\" tag"); + if (cp != NULL) { + if (TIFFFetchData(tif, dp, cp)) { + /* + * This deals with there being only + * one array to apply to all samples. + */ + if (dp->tdir_count == (1L<td_bitspersample)) + v = 0; + TIFFSetField(tif, dp->tdir_tag, + cp, cp+v, cp+2*v); + } + _TIFFfree(cp); + } + break; + case TIFFTAG_PAGENUMBER: + case TIFFTAG_HALFTONEHINTS: + case TIFFTAG_YCBCRSUBSAMPLING: + case TIFFTAG_DOTRANGE: + (void) TIFFFetchShortPair(tif, dp); + break; +#ifdef COLORIMETRY_SUPPORT + case TIFFTAG_REFERENCEBLACKWHITE: + (void) TIFFFetchRefBlackWhite(tif, dp); + break; +#endif +#ifdef JPEG_SUPPORT + case TIFFTAG_JPEGQTABLES: + if (TIFFFetchJPEGQTables(tif, dp)) + TIFFSetFieldBit(tif, FIELD_JPEGQTABLES); + break; + case TIFFTAG_JPEGDCTABLES: + if (TIFFFetchJPEGCTables(tif, dp, &td->td_dctab)) + TIFFSetFieldBit(tif, FIELD_JPEGDCTABLES); + break; + case TIFFTAG_JPEGACTABLES: + if (TIFFFetchJPEGCTables(tif, dp, &td->td_actab)) + TIFFSetFieldBit(tif, FIELD_JPEGACTABLES); + break; +#endif +/* BEGIN REV 4.0 COMPATIBILITY */ + case TIFFTAG_OSUBFILETYPE: + v = 0; + switch (TIFFExtractData(tif, dp->tdir_type, + dp->tdir_offset)) { + case OFILETYPE_REDUCEDIMAGE: + v = FILETYPE_REDUCEDIMAGE; + break; + case OFILETYPE_PAGE: + v = FILETYPE_PAGE; + break; + } + if (v) + (void) TIFFSetField(tif, + TIFFTAG_SUBFILETYPE, (int)v); + break; +/* END REV 4.0 COMPATIBILITY */ + default: + (void) TIFFFetchNormalTag(tif, dp); + break; + } + } + /* + * Verify Palette image has a Colormap. + */ + if (td->td_photometric == PHOTOMETRIC_PALETTE && + !TIFFFieldSet(tif, FIELD_COLORMAP)) { + MissingRequired(tif, "Colormap"); + goto bad; + } + /* + * Attempt to deal with a missing StripByteCounts tag. + */ + if (!TIFFFieldSet(tif, FIELD_STRIPBYTECOUNTS)) { + /* + * Some manufacturers violate the spec by not giving + * the size of the strips. In this case, assume there + * is one uncompressed strip of data. + */ + if (td->td_nstrips > 1) { + MissingRequired(tif, "StripByteCounts"); + goto bad; + } + TIFFWarning(tif->tif_name, +"TIFF directory is missing required \"%s\" field, calculating from imagelength", + TIFFFieldWithTag(TIFFTAG_STRIPBYTECOUNTS)->field_name); + EstimateStripByteCounts(tif, dir, dircount); +#define BYTECOUNTLOOKSBAD \ + (td->td_stripbytecount[0] == 0 || \ + (td->td_compression == COMPRESSION_NONE && \ + td->td_stripbytecount[0] > TIFFGetFileSize(tif) - td->td_stripoffset[0])) + } else if (td->td_nstrips == 1 && BYTECOUNTLOOKSBAD) { + /* + * Plexus (and others) sometimes give a value + * of zero for a tag when they don't know what + * the correct value is! Try and handle the + * simple case of estimating the size of a one + * strip image. + */ + TIFFWarning(tif->tif_name, + "Bogus \"%s\" field, ignoring and calculating from imagelength", + TIFFFieldWithTag(TIFFTAG_STRIPBYTECOUNTS)->field_name); + EstimateStripByteCounts(tif, dir, dircount); + } + if (dir) + _TIFFfree((char *)dir); + if (!TIFFFieldSet(tif, FIELD_MAXSAMPLEVALUE)) + td->td_maxsamplevalue = (1L<td_bitspersample)-1; + /* + * Setup default compression scheme. + */ + if (!TIFFFieldSet(tif, FIELD_COMPRESSION)) + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); +#ifdef STRIPCHOP_SUPPORT + /* + * Some manufacturers make life difficult by writing + * large amounts of uncompressed data as a single strip. + * This is contrary to the recommendations of the spec. + * The following makes an attempt at breaking such images + * into strips closer to the recommended 8k bytes. A + * side effect, however, is that the RowsPerStrip tag + * value may be changed. + */ + if (td->td_nstrips == 1 && td->td_compression == COMPRESSION_NONE && + td->td_tilewidth == td->td_imagewidth) + ChopUpSingleUncompressedStrip(tif); +#endif + /* + * Reinitialize i/o since we are starting on a new directory. + */ + tif->tif_row = -1; + tif->tif_curstrip = -1; + tif->tif_col = -1; + tif->tif_curtile = -1; + tif->tif_tilesize = TIFFTileSize(tif); + tif->tif_scanlinesize = TIFFScanlineSize(tif); + return (1); +bad: + if (dir) + _TIFFfree((char *)dir); + return (0); +} + +static void +EstimateStripByteCounts(TIFF* tif, TIFFDirEntry* dir, uint16 dircount) +{ + register TIFFDirEntry *dp; + register TIFFDirectory *td = &tif->tif_dir; + + td->td_stripbytecount = (uint32*) + CheckMalloc(tif, sizeof (uint32), "for \"StripByteCounts\" array"); + if (td->td_compression != COMPRESSION_NONE) { + uint32 space = sizeof (TIFFHeader) + + sizeof (uint16) + + (dircount * sizeof (TIFFDirEntry)) + + sizeof (uint32); + toff_t filesize = TIFFGetFileSize(tif); + uint16 n; + + /* calculate amount of space used by indirect values */ + for (dp = dir, n = dircount; n > 0; n--, dp++) { + int cc = dp->tdir_count * tiffDataWidth[dp->tdir_type]; + if (cc > sizeof (uint32)) + space += cc; + } + td->td_stripbytecount[0] = filesize - space; + /* + * This gross hack handles the case were the offset to + * the strip is past the place where we think the strip + * should begin. Since a strip of data must be contiguous, + * it's safe to assume that we've overestimated the amount + * of data in the strip and trim this number back accordingly. + */ + if (td->td_stripoffset[0] + td->td_stripbytecount[0] > filesize) + td->td_stripbytecount[0] = + filesize - td->td_stripoffset[0]; + } else { + uint32 rowbytes = howmany(td->td_bitspersample * + td->td_samplesperpixel * td->td_imagewidth, 8); + td->td_stripbytecount[0] = td->td_imagelength * rowbytes; + } + TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS); + if (!TIFFFieldSet(tif, FIELD_ROWSPERSTRIP)) + td->td_rowsperstrip = td->td_imagelength; +} + +static void +MissingRequired(TIFF* tif, const char* tagname) +{ + TIFFError(tif->tif_name, + "TIFF directory is missing required \"%s\" field", tagname); +} + +/* + * Check the count field of a directory + * entry against a known value. The caller + * is expected to skip/ignore the tag if + * there is a mismatch. + */ +static int +CheckDirCount(TIFF* tif, TIFFDirEntry* dir, uint32 count) +{ + if (count != dir->tdir_count) { + TIFFWarning(tif->tif_name, + "incorrect count for field \"%s\" (%lu, expecting %lu); tag ignored", + TIFFFieldWithTag(dir->tdir_tag)->field_name, + dir->tdir_count, count); + return (0); + } + return (1); +} + +/* + * Fetch a contiguous directory item. + */ +static tsize_t +TIFFFetchData(TIFF* tif, TIFFDirEntry* dir, char* cp) +{ + int w = tiffDataWidth[dir->tdir_type]; + tsize_t cc = dir->tdir_count * w; + + if (!isMapped(tif)) { + if (!SeekOK(tif, dir->tdir_offset)) + goto bad; + if (!ReadOK(tif, cp, cc)) + goto bad; + } else { + if (dir->tdir_offset + cc > tif->tif_size) + goto bad; + memcpy(cp, tif->tif_base + dir->tdir_offset, cc); + } + if (tif->tif_flags & TIFF_SWAB) { + switch (dir->tdir_type) { + case TIFF_SHORT: + case TIFF_SSHORT: + TIFFSwabArrayOfShort((uint16*)cp, dir->tdir_count); + break; + case TIFF_LONG: + case TIFF_SLONG: + case TIFF_FLOAT: + TIFFSwabArrayOfLong((uint32*)cp, dir->tdir_count); + break; + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + TIFFSwabArrayOfLong((uint32*)cp, 2*dir->tdir_count); + break; + } + } + return (cc); +bad: + TIFFError(tif->tif_name, "Error fetching data for field \"%s\"", + TIFFFieldWithTag(dir->tdir_tag)->field_name); + return ((tsize_t) 0); +} + +/* + * Fetch an ASCII item from the file. + */ +static tsize_t +TIFFFetchString(TIFF* tif, TIFFDirEntry* dir, char* cp) +{ + if (dir->tdir_count <= 4) { + uint32 l = dir->tdir_offset; + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong(&l); + memcpy(cp, &l, dir->tdir_count); + return (1); + } + return (TIFFFetchData(tif, dir, cp)); +} + +/* + * Convert numerator+denominator to float. + */ +static int +cvtRational(TIFF* tif, TIFFDirEntry* dir, uint32 num, uint32 denom, float* rv) +{ + if (denom == 0) { + TIFFError(tif->tif_name, + "%s: Rational with zero denominator (num = %lu)", + TIFFFieldWithTag(dir->tdir_tag)->field_name, num); + return (0); + } else { + if (dir->tdir_type == TIFF_RATIONAL) + *rv = ((float)num / (float)denom); + else + *rv = ((float)(int32)num / (float)(int32)denom); + return (1); + } +} + +/* + * Fetch a rational item from the file + * at offset off and return the value + * as a floating point number. + */ +static float +TIFFFetchRational(TIFF* tif, TIFFDirEntry* dir) +{ + uint32 l[2]; + float v; + + return (!TIFFFetchData(tif, dir, (char *)l) || + !cvtRational(tif, dir, l[0], l[1], &v) ? 1. : v); +} + +/* + * Fetch a single floating point value + * from the offset field and return it + * as a native float. + */ +static float +TIFFFetchFloat(TIFF* tif, TIFFDirEntry* dir) +{ + float v = (float) + TIFFExtractData(tif, dir->tdir_type, dir->tdir_offset); + TIFFCvtIEEEFloatToNative(tif, 1, &v); + return (v); +} + +/* + * Fetch an array of BYTE or SBYTE values. + */ +static int +TIFFFetchByteArray(TIFF* tif, TIFFDirEntry* dir, uint16* v) +{ + if (dir->tdir_count <= 4) { + /* + * Extract data from offset field. + */ + if (tif->tif_header.tiff_magic == TIFF_BIGENDIAN) { + switch (dir->tdir_count) { + case 4: v[3] = dir->tdir_offset & 0xff; + case 3: v[2] = (dir->tdir_offset >> 8) & 0xff; + case 2: v[1] = (dir->tdir_offset >> 16) & 0xff; + case 1: v[0] = dir->tdir_offset >> 24; + } + } else { + switch (dir->tdir_count) { + case 4: v[3] = dir->tdir_offset >> 24; + case 3: v[2] = (dir->tdir_offset >> 16) & 0xff; + case 2: v[1] = (dir->tdir_offset >> 8) & 0xff; + case 1: v[0] = dir->tdir_offset & 0xff; + } + } + return (1); + } else + return (TIFFFetchData(tif, dir, (char *)v)); /* XXX */ +} + +/* + * Fetch an array of SHORT or SSHORT values. + */ +static int +TIFFFetchShortArray(TIFF* tif, TIFFDirEntry* dir, uint16* v) +{ + if (dir->tdir_count <= 2) { + if (tif->tif_header.tiff_magic == TIFF_BIGENDIAN) { + switch (dir->tdir_count) { + case 2: v[1] = dir->tdir_offset & 0xffff; + case 1: v[0] = dir->tdir_offset >> 16; + } + } else { + switch (dir->tdir_count) { + case 2: v[1] = dir->tdir_offset >> 16; + case 1: v[0] = dir->tdir_offset & 0xffff; + } + } + return (1); + } else + return (TIFFFetchData(tif, dir, (char *)v)); +} + +/* + * Fetch a pair of SHORT or BYTE values. + */ +static int +TIFFFetchShortPair(TIFF* tif, TIFFDirEntry* dir) +{ + uint16 v[2]; + int ok = 0; + + switch (dir->tdir_type) { + case TIFF_SHORT: + case TIFF_SSHORT: + ok = TIFFFetchShortArray(tif, dir, v); + break; + case TIFF_BYTE: + case TIFF_SBYTE: + ok = TIFFFetchByteArray(tif, dir, v); + break; + } + if (ok) + TIFFSetField(tif, dir->tdir_tag, v[0], v[1]); + return (ok); +} + +/* + * Fetch an array of LONG or SLONG values. + */ +static int +TIFFFetchLongArray(TIFF* tif, TIFFDirEntry* dir, uint32* v) +{ + if (dir->tdir_count == 1) { + v[0] = dir->tdir_offset; + return (1); + } else + return (TIFFFetchData(tif, dir, (char *)v)); +} + +/* + * Fetch an array of RATIONAL or SRATIONAL values. + */ +static int +TIFFFetchRationalArray(TIFF* tif, TIFFDirEntry* dir, float* v) +{ + int ok = 0; + uint32 *l; + + l = (uint32*)CheckMalloc(tif, + dir->tdir_count*tiffDataWidth[dir->tdir_type], + "to fetch array of rationals"); + if (l) { + if (TIFFFetchData(tif, dir, (char *)l)) { + uint32 i; + for (i = 0; i < dir->tdir_count; i++) { + ok = cvtRational(tif, dir, + l[2*i+0], l[2*i+1], &v[i]); + if (!ok) + break; + } + } + _TIFFfree((char *)l); + } + return (ok); +} + +/* + * Fetch an array of FLOAT values. + */ +static int +TIFFFetchFloatArray(TIFF* tif, TIFFDirEntry* dir, float* v) +{ + if (TIFFFetchData(tif, dir, (char *)v)) { + TIFFCvtIEEEFloatToNative(tif, dir->tdir_count, v); + return (1); + } else + return (0); +} + +/* + * Fetch a tag that is not handled by special case code. + * + * NB: DOUBLE and UNDEFINED types are not handled. + */ +static int +TIFFFetchNormalTag(TIFF* tif, TIFFDirEntry* dp) +{ + static char mesg[] = "to fetch tag value"; + int ok = 0; + + if (dp->tdir_count > 1) { /* array of values */ + char* cp = NULL; + + switch (dp->tdir_type) { + case TIFF_BYTE: + case TIFF_SBYTE: + /* NB: always expand BYTE values to shorts */ + cp = CheckMalloc(tif, + dp->tdir_count * sizeof (uint16), mesg); + ok = cp && TIFFFetchByteArray(tif, dp, (uint16*)cp); + break; + case TIFF_SHORT: + case TIFF_SSHORT: + cp = CheckMalloc(tif, + dp->tdir_count * sizeof (uint16), mesg); + ok = cp && TIFFFetchShortArray(tif, dp, (uint16*)cp); + break; + case TIFF_LONG: + case TIFF_SLONG: + cp = CheckMalloc(tif, + dp->tdir_count * sizeof (uint32), mesg); + ok = cp && TIFFFetchLongArray(tif, dp, (uint32*)cp); + break; + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + cp = CheckMalloc(tif, + dp->tdir_count * sizeof (float), mesg); + ok = cp && TIFFFetchRationalArray(tif, dp, (float *)cp); + break; + case TIFF_FLOAT: + cp = CheckMalloc(tif, + dp->tdir_count * sizeof (float), mesg); + ok = cp && TIFFFetchFloatArray(tif, dp, (float *)cp); + break; + case TIFF_ASCII: + /* + * Some vendors write strings w/o the trailing + * NULL byte, so always append one just in case. + */ + cp = CheckMalloc(tif, dp->tdir_count+1, mesg); + if (ok = (cp && TIFFFetchString(tif, dp, cp))) + cp[dp->tdir_count] = '\0'; /* XXX */ + break; + } + if (ok) + ok = TIFFSetField(tif, dp->tdir_tag, cp); + if (cp != NULL) + _TIFFfree(cp); + } else if (CheckDirCount(tif, dp, 1)) { /* singleton value */ + char c[2]; + TIFFDataType type; + switch (dp->tdir_type) { + case TIFF_BYTE: + case TIFF_SBYTE: + case TIFF_SHORT: + case TIFF_SSHORT: + /* + * If the tag is also acceptable as a LONG or SLONG + * then TIFFSetField will expect an uint32 parameter + * passed to it (through varargs). Thus, for machines + * where sizeof (int) != sizeof (uint32) we must do + * a careful check here. It's hard to say if this + * is worth optimizing. + * + * NB: We use TIFFFieldWithTag here knowing that + * it returns us the first entry in the table + * for the tag and that that entry is for the + * widest potential data type the tag may have. + */ + type = TIFFFieldWithTag(dp->tdir_tag)->field_type; + if (type != TIFF_LONG && type != TIFF_SLONG) { + ok = TIFFSetField(tif, dp->tdir_tag, (int) + TIFFExtractData(tif, dp->tdir_type, dp->tdir_offset)); + break; + } + /* fall thru... */ + case TIFF_LONG: + case TIFF_SLONG: + ok = TIFFSetField(tif, dp->tdir_tag, (uint32) + TIFFExtractData(tif, dp->tdir_type, dp->tdir_offset)); + break; + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + ok = TIFFSetField(tif, dp->tdir_tag, + TIFFFetchRational(tif, dp)); + break; + case TIFF_FLOAT: + ok = TIFFSetField(tif, dp->tdir_tag, + TIFFFetchFloat(tif, dp)); + break; + case TIFF_ASCII: + if (ok = (TIFFFetchString(tif, dp, c))) { + c[1] = '\0'; /* XXX paranoid */ + ok = TIFFSetField(tif, dp->tdir_tag, c); + } + break; + } + } + return (ok); +} + +#define NITEMS(x) (sizeof (x) / sizeof (x[0])) +/* + * Fetch samples/pixel short values for + * the specified tag and verify that + * all values are the same. + */ +static int +TIFFFetchPerSampleShorts(TIFF* tif, TIFFDirEntry* dir, int* pl) +{ + int samples = tif->tif_dir.td_samplesperpixel; + int status = 0; + + if (CheckDirCount(tif, dir, (uint32) samples)) { + uint16 buf[10]; + uint16* v = buf; + + if (samples > NITEMS(buf)) + v = (uint16*)_TIFFmalloc(samples * sizeof (uint16)); + if (TIFFFetchShortArray(tif, dir, v)) { + int i; + for (i = 1; i < samples; i++) + if (v[i] != v[0]) { + TIFFError(tif->tif_name, + "Cannot handle different per-sample values for field \"%s\"", + TIFFFieldWithTag(dir->tdir_tag)->field_name); + goto bad; + } + *pl = v[0]; + status = 1; + } + bad: + if (v != buf) + _TIFFfree((char *)v); + } + return (status); +} +#undef NITEMS + +/* + * Fetch a set of offsets or lengths. + * While this routine says "strips", + * in fact it's also used for tiles. + */ +static int +TIFFFetchStripThing(TIFF* tif, TIFFDirEntry* dir, long nstrips, uint32** lpp) +{ + register uint32 *lp; + int status; + + if (!CheckDirCount(tif, dir, nstrips)) + return (0); + /* + * Allocate space for strip information. + */ + if (*lpp == NULL && + (*lpp = (uint32 *)CheckMalloc(tif, + nstrips * sizeof (uint32), "for strip array")) == NULL) + return (0); + lp = *lpp; + if (dir->tdir_type == (int)TIFF_SHORT) { + /* + * Handle uint16->uint32 expansion. + */ + uint16 *dp = (uint16 *)CheckMalloc(tif, + dir->tdir_count* sizeof (uint16), "to fetch strip tag"); + if (dp == NULL) + return (0); + if (status = TIFFFetchShortArray(tif, dir, dp)) { + register uint16 *wp = dp; + while (nstrips-- > 0) + *lp++ = *wp++; + } + _TIFFfree((char *)dp); + } else + status = TIFFFetchLongArray(tif, dir, lp); + return (status); +} + +#define NITEMS(x) (sizeof (x) / sizeof (x[0])) +/* + * Fetch and set the ExtraSamples tag. + */ +static int +TIFFFetchExtraSamples(TIFF* tif, TIFFDirEntry* dir) +{ + uint16 buf[10], *v = buf; + int status; + + if (dir->tdir_count > NITEMS(buf)) + v = (uint16*)_TIFFmalloc(dir->tdir_count * sizeof (uint16)); + if (dir->tdir_type == TIFF_BYTE) + status = TIFFFetchByteArray(tif, dir, v); + else + status = TIFFFetchShortArray(tif, dir, v); + if (status) + status = TIFFSetField(tif, dir->tdir_tag, dir->tdir_count, v); + if (v != buf) + _TIFFfree((char *)v); + return (status); +} +#undef NITEMS + +#ifdef COLORIMETRY_SUPPORT +/* + * Fetch and set the RefBlackWhite tag. + */ +static int +TIFFFetchRefBlackWhite(TIFF* tif, TIFFDirEntry* dir) +{ + static char mesg[] = "for \"ReferenceBlackWhite\" array"; + char* cp; + int ok; + + if (dir->tdir_type == TIFF_RATIONAL) + return (TIFFFetchNormalTag(tif, dir)); + /* + * Handle LONG's for backward compatibility. + */ + cp = CheckMalloc(tif, dir->tdir_count * sizeof (uint32), mesg); + if (ok = (cp && TIFFFetchLongArray(tif, dir, (uint32*)cp))) { + float *fp = (float *) + CheckMalloc(tif, dir->tdir_count * sizeof (float), mesg); + if (ok = (fp != NULL)) { + int i; + for (i = 0; i < dir->tdir_count; i++) + fp[i] = (float)((uint32*)cp)[i]; + ok = TIFFSetField(tif, dir->tdir_tag, fp); + _TIFFfree((char *)fp); + } + } + if (cp) + _TIFFfree(cp); + return (ok); +} +#endif + +#ifdef JPEG_SUPPORT +/* + * Fetch the JPEG Quantization tables + * for the specified directory entry. + * Storage for the td_qtab array is + * allocated as a side effect. + */ +static int +TIFFFetchJPEGQTables(TIFF* tif, TIFFDirEntry* dir) +{ + TIFFDirectory *td = &tif->tif_dir; + uint32 off[4]; + int i, j; + TIFFDirEntry tdir; + char *qmat; + + if (dir->tdir_count > 1) { + /* XXX verify count <= 4 */ + if (!TIFFFetchData(tif, dir, (char *)off)) + return (0); + } else + off[0] = dir->tdir_offset; + /* + * We don't share per-component q matrices because + * (besides complicating this logic even more), it + * would make it very painful if the user does a ``set''. + */ + td->td_qtab = (u_char **)CheckMalloc(tif, + dir->tdir_count*(sizeof (u_char *) + 64*sizeof (u_char)), + "for JPEG Q table"); + if (td->td_qtab == NULL) + return (0); + tdir.tdir_type = TIFF_BYTE; + tdir.tdir_count = 64; + qmat = (((char *)td->td_qtab) + dir->tdir_count*sizeof (u_char *)); + for (i = 0; i < dir->tdir_count; i++) { + td->td_qtab[i] = (u_char *)qmat; + tdir.tdir_offset = off[i]; + if (!TIFFFetchData(tif, &tdir, qmat)) + return (0); + qmat += 64*sizeof (u_char); + } + return (1); +} + +/* + * Fetch JPEG Huffman code tables for the + * specified directory entry. Storage for + * the tables are allocated as a side effect. + */ +static int +TIFFFetchJPEGCTables(TIFF* tif, TIFFDirEntry* dir, u_char*** ptab) +{ + uint32 off[4]; + int i, j, ncodes; + TIFFDirEntry tdir; + char *tab; + + if (dir->tdir_count > 1) { + /* XXX verify count <= 4 */ + if (!TIFFFetchData(tif, dir, (char *)off)) + return (0); + } else + off[0] = dir->tdir_offset; + /* + * We don't share per-component tables because + * (besides complicating this logic even more), it + * would make it very painful if the user does a + * ``set''. Note also that we don't try to optimize + * storage of the tables -- we just allocate enough + * space to hold the largest possible. All this + * stuff is so complicated 'cuz the tag is defined + * to be compatible with the JPEG table format, + * rather than something that fits well into the + * structure of TIFF -- argh! + */ + *ptab = (u_char **)CheckMalloc(tif, dir->tdir_count* + (sizeof (u_char *) + (16+256)*sizeof (u_char)), + "for JPEG Huffman table"); + if (*ptab == NULL) + return (0); + tdir.tdir_type = TIFF_BYTE; + tab = (((char *)*ptab) + dir->tdir_count*sizeof (u_char *)); + for (i = 0; i < dir->tdir_count; i++) { + (*ptab)[i] = (u_char *)tab; + tdir.tdir_offset = off[i]; + tdir.tdir_count = 16; + /* + * We must fetch the array that holds the + * count of codes for each bit length first + * and the count up the number of codes that + * are in the variable length table. This + * information is implicit in the JPEG format + * 'cuz it's preceded by a length field. + */ + if (!TIFFFetchData(tif, &tdir, tab)) /* count array */ + return (0); + for (ncodes = 0, j = 0; j < 16; j++) + ncodes += tab[j]; + /* + * Adjust offsets and fetch codes separately. + */ + tdir.tdir_offset += 16; + tdir.tdir_count = ncodes; + tab += 16; + if (!TIFFFetchData(tif, &tdir, tab)) + return (0); + tab += ncodes; + } + return (1); +} +#endif + +#ifdef STRIPCHOP_SUPPORT +/* + * Replace a single strip (tile) of uncompressed data by + * multiple strips (tiles), each approximately 8Kbytes. + * This is useful for dealing with large images or + * for dealing with machines with a limited amount + * memory. + */ +static void +ChopUpSingleUncompressedStrip(TIFF* tif) +{ + register TIFFDirectory *td = &tif->tif_dir; + uint32 bytecount = td->td_stripbytecount[0]; + uint32 offset = td->td_stripoffset[0]; + tsize_t rowbytes = TIFFVTileSize(tif, 1), stripbytes; + tstrip_t strip, nstrips, rowsperstrip; + uint32* newcounts; + uint32* newoffsets; + + /* + * Make the rows hold at least one + * scanline, but fill 8k if possible. + */ + if (rowbytes > 8192) { + stripbytes = rowbytes; + rowsperstrip = 1; + } else { + rowsperstrip = 8192 / rowbytes; + stripbytes = rowbytes * rowsperstrip; + } + nstrips = howmany(bytecount, stripbytes); + newcounts = (uint32*) CheckMalloc(tif, nstrips * sizeof (uint32), + "for chopped \"StripByteCounts\" array"); + newoffsets = (uint32*) CheckMalloc(tif, nstrips * sizeof (uint32), + "for chopped \"StripOffsets\" array"); + if (newcounts == NULL || newoffsets == NULL) { + /* + * Unable to allocate new strip information, give + * up and use the original one strip information. + */ + if (newcounts != NULL) + _TIFFfree(newcounts); + if (newoffsets != NULL) + _TIFFfree(newoffsets); + return; + } + /* + * Fill the strip information arrays with + * new bytecounts and offsets that reflect + * the broken-up format. + */ + for (strip = 0; strip < nstrips; strip++) { + if (stripbytes > bytecount) + stripbytes = bytecount; + newcounts[strip] = stripbytes; + newoffsets[strip] = offset; + offset += stripbytes; + bytecount -= stripbytes; + } + /* + * Replace old single strip info with multi-strip info. + */ + td->td_stripsperimage = td->td_nstrips = nstrips; + td->td_rowsperstrip = rowsperstrip; + td->td_tilelength = rowsperstrip; + + _TIFFfree(td->td_stripbytecount); + _TIFFfree(td->td_stripoffset); + td->td_stripbytecount = newcounts; + td->td_stripoffset = newoffsets; +} +#endif /* STRIPCHOP_SUPPORT */ diff --git a/panda/src/tiff/tif_dirwrite.c b/panda/src/tiff/tif_dirwrite.c new file mode 100644 index 0000000000..287f4ae697 --- /dev/null +++ b/panda/src/tiff/tif_dirwrite.c @@ -0,0 +1,791 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Directory Write Support Routines. + */ +#include "tiffiop.h" + +#if HAVE_IEEEFP +#define TIFFCvtNativeToIEEEFloat(tif, n, fp) +#endif + +static int TIFFWriteNormalTag(TIFF*, TIFFDirEntry*, const TIFFFieldInfo*); +static void TIFFSetupShortLong(TIFF*, ttag_t, TIFFDirEntry*, uint32); +static int TIFFSetupShortPair(TIFF*, ttag_t, TIFFDirEntry*); +static int TIFFWriteRational(TIFF*, + TIFFDataType, ttag_t, TIFFDirEntry*, float); +static int TIFFWritePerSampleShorts(TIFF*, ttag_t, TIFFDirEntry*); +static int TIFFWriteShortTable(TIFF*, ttag_t, TIFFDirEntry*, uint32, uint16**); +static int TIFFWriteShortArray(TIFF*, + TIFFDataType, ttag_t, TIFFDirEntry*, uint32, uint16*); +static int TIFFWriteLongArray(TIFF *, + TIFFDataType, ttag_t, TIFFDirEntry*, uint32, uint32*); +static int TIFFWriteRationalArray(TIFF *, + TIFFDataType, ttag_t, TIFFDirEntry*, uint32, float*); +static int TIFFWriteFloatArray(TIFF *, + TIFFDataType, ttag_t, TIFFDirEntry*, uint32, float*); +static int TIFFWriteString(TIFF*, ttag_t, TIFFDirEntry*, char*); +#ifdef JPEG_SUPPORT +static int TIFFWriteJPEGQTables(TIFF*, TIFFDirEntry*); +static int TIFFWriteJPEGCTables(TIFF*, ttag_t, TIFFDirEntry*, u_char**); +#endif +#ifdef COLORIMETRY_SUPPORT +static int TIFFWriteTransferFunction(TIFF*, TIFFDirEntry*); +#endif +static int TIFFWriteData(TIFF*, TIFFDirEntry*, char*); +static int TIFFLinkDirectory(TIFF*); + +#define WriteRationalPair(type, tag1, v1, tag2, v2) { \ + if (!TIFFWriteRational(tif, type, tag1, dir, v1)) \ + goto bad; \ + if (!TIFFWriteRational(tif, type, tag2, dir+1, v2)) \ + goto bad; \ + dir++; \ +} + +/* + * Write the contents of the current directory + * to the specified file. This routine doesn't + * handle overwriting a directory with auxiliary + * storage that's been changed. + */ +int +TIFFWriteDirectory(TIFF* tif) +{ + short dircount, tag; + int nfields, dirsize; + char *data; + const TIFFFieldInfo *fip; + TIFFDirEntry *dir; + TIFFDirectory *td; + u_long b, fields[FIELD_SETLONGS]; + + if (tif->tif_mode == O_RDONLY) + return (1); + /* + * Clear write state so that subsequent images with + * different characteristics get the right buffers + * setup for them. + */ + if (tif->tif_flags & TIFF_POSTENCODE) { + tif->tif_flags &= ~TIFF_POSTENCODE; + if (tif->tif_postencode && !(*tif->tif_postencode)(tif)) { + TIFFError(tif->tif_name, + "Error post-encoding before directory write"); + return (0); + } + } + if (tif->tif_close) + (*tif->tif_close)(tif); + if (tif->tif_cleanup) + (*tif->tif_cleanup)(tif); + /* + * Flush any data that might have been written + * by the compression close+cleanup routines. + */ + if (tif->tif_rawcc > 0 && !TIFFFlushData1(tif)) { + TIFFError(tif->tif_name, + "Error flushing data before directory write"); + return (0); + } + if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata) { + _TIFFfree(tif->tif_rawdata); + tif->tif_rawdata = NULL; + tif->tif_rawcc = 0; + } + tif->tif_flags &= ~(TIFF_BEENWRITING|TIFF_BUFFERSETUP); + + td = &tif->tif_dir; + /* + * Size the directory so that we can calculate + * offsets for the data items that aren't kept + * in-place in each field. + */ + nfields = 0; + for (b = 0; b <= FIELD_LAST; b++) + if (TIFFFieldSet(tif, b)) + nfields += (b < FIELD_SUBFILETYPE ? 2 : 1); + dirsize = nfields * sizeof (TIFFDirEntry); + data = _TIFFmalloc(dirsize); + if (data == NULL) { + TIFFError(tif->tif_name, + "Cannot write directory, out of space"); + return (0); + } + /* + * Directory hasn't been placed yet, put + * it at the end of the file and link it + * into the existing directory structure. + */ + if (tif->tif_diroff == 0 && !TIFFLinkDirectory(tif)) + goto bad; + tif->tif_dataoff = + tif->tif_diroff + sizeof (short) + dirsize + sizeof (long); + if (tif->tif_dataoff & 1) + tif->tif_dataoff++; + (void) TIFFSeekFile(tif, tif->tif_dataoff, L_SET); + tif->tif_curdir++; + dir = (TIFFDirEntry *)data; + /* + * Setup external form of directory + * entries and write data items. + */ + memcpy(fields, td->td_fieldsset, sizeof (fields)); + /* + * Write out ExtraSamples tag only if + * extra samples are present in the data. + */ + if (FieldSet(fields, FIELD_EXTRASAMPLES) && td->td_extrasamples) { + ResetFieldBit(fields, FIELD_EXTRASAMPLES); + nfields--; + dirsize -= sizeof (TIFFDirEntry); + } /*XXX*/ + for (fip = tiffFieldInfo; fip->field_tag; fip++) { + if (fip->field_bit == FIELD_IGNORE || + !FieldSet(fields, fip->field_bit)) + continue; + switch (fip->field_bit) { + case FIELD_STRIPOFFSETS: + /* + * We use one field bit for both strip and tile + * offsets, and so must be careful in selecting + * the appropriate field descriptor (so that tags + * are written in sorted order). + */ + tag = isTiled(tif) ? + TIFFTAG_TILEOFFSETS : TIFFTAG_STRIPOFFSETS; + if (tag != fip->field_tag) + continue; + if (!TIFFWriteLongArray(tif, TIFF_LONG, tag, dir, + (uint32) td->td_nstrips, td->td_stripoffset)) + goto bad; + break; + case FIELD_STRIPBYTECOUNTS: + /* + * We use one field bit for both strip and tile + * byte counts, and so must be careful in selecting + * the appropriate field descriptor (so that tags + * are written in sorted order). + */ + tag = isTiled(tif) ? + TIFFTAG_TILEBYTECOUNTS : TIFFTAG_STRIPBYTECOUNTS; + if (tag != fip->field_tag) + continue; + if (!TIFFWriteLongArray(tif, TIFF_LONG, tag, dir, + (uint32) td->td_nstrips, td->td_stripbytecount)) + goto bad; + break; + case FIELD_ROWSPERSTRIP: + TIFFSetupShortLong(tif, TIFFTAG_ROWSPERSTRIP, + dir, td->td_rowsperstrip); + break; + case FIELD_COLORMAP: + if (!TIFFWriteShortTable(tif, TIFFTAG_COLORMAP, dir, + 3, td->td_colormap)) + goto bad; + break; + case FIELD_IMAGEDIMENSIONS: + TIFFSetupShortLong(tif, TIFFTAG_IMAGEWIDTH, + dir++, td->td_imagewidth); + TIFFSetupShortLong(tif, TIFFTAG_IMAGELENGTH, + dir, td->td_imagelength); + break; + case FIELD_TILEDIMENSIONS: + TIFFSetupShortLong(tif, TIFFTAG_TILEWIDTH, + dir++, td->td_tilewidth); + TIFFSetupShortLong(tif, TIFFTAG_TILELENGTH, + dir, td->td_tilelength); + break; + case FIELD_POSITION: + WriteRationalPair(TIFF_RATIONAL, + TIFFTAG_XPOSITION, td->td_xposition, + TIFFTAG_YPOSITION, td->td_yposition); + break; + case FIELD_RESOLUTION: + WriteRationalPair(TIFF_RATIONAL, + TIFFTAG_XRESOLUTION, td->td_xresolution, + TIFFTAG_YRESOLUTION, td->td_yresolution); + break; + case FIELD_BITSPERSAMPLE: + case FIELD_MINSAMPLEVALUE: + case FIELD_MAXSAMPLEVALUE: + case FIELD_SAMPLEFORMAT: + if (!TIFFWritePerSampleShorts(tif, fip->field_tag, dir)) + goto bad; + break; + case FIELD_PAGENUMBER: + case FIELD_HALFTONEHINTS: +#ifdef YCBCR_SUPPORT + case FIELD_YCBCRSUBSAMPLING: +#endif +#ifdef CMYK_SUPPORT + case FIELD_DOTRANGE: +#endif + if (!TIFFSetupShortPair(tif, fip->field_tag, dir)) + goto bad; + break; +#ifdef JPEG_SUPPORT + case FIELD_JPEGQTABLES: + if (!TIFFWriteJPEGQTables(tif, dir)) + goto bad; + break; + case FIELD_JPEGDCTABLES: + if (!TIFFWriteJPEGCTables(tif, + TIFFTAG_JPEGDCTABLES, dir, td->td_dctab)) + goto bad; + break; + case FIELD_JPEGACTABLES: + if (!TIFFWriteJPEGCTables(tif, + TIFFTAG_JPEGACTABLES, dir, td->td_actab)) + goto bad; + break; +#endif +#ifdef COLORIMETRY_SUPPORT + case FIELD_TRANSFERFUNCTION: + if (!TIFFWriteTransferFunction(tif, dir)) + goto bad; + break; +#endif + default: + if (!TIFFWriteNormalTag(tif, dir, fip)) + goto bad; + break; + } + dir++; + ResetFieldBit(fields, fip->field_bit); + } + /* + * Write directory. + */ + (void) TIFFSeekFile(tif, tif->tif_diroff, L_SET); + dircount = nfields; + if (!WriteOK(tif, &dircount, sizeof (short))) { + TIFFError(tif->tif_name, "Error writing directory count"); + goto bad; + } + if (!WriteOK(tif, data, dirsize)) { + TIFFError(tif->tif_name, "Error writing directory contents"); + goto bad; + } + if (!WriteOK(tif, &tif->tif_nextdiroff, sizeof (long))) { + TIFFError(tif->tif_name, "Error writing directory link"); + goto bad; + } + TIFFFreeDirectory(tif); + _TIFFfree(data); + tif->tif_flags &= ~TIFF_DIRTYDIRECT; + + /* + * Reset directory-related state for subsequent + * directories. + */ + TIFFDefaultDirectory(tif); + tif->tif_diroff = 0; + tif->tif_curoff = 0; + tif->tif_row = -1; + tif->tif_curstrip = -1; + return (1); +bad: + _TIFFfree(data); + return (0); +} +#undef WriteRationalPair + +/* + * Process tags that are not special cased. + */ +static int +TIFFWriteNormalTag(TIFF* tif, TIFFDirEntry* dir, const TIFFFieldInfo* fip) +{ + TIFFDirectory* td = &tif->tif_dir; + u_short wc = (u_short) fip->field_writecount; + + dir->tdir_tag = fip->field_tag; + dir->tdir_type = (u_short)fip->field_type; + dir->tdir_count = wc; +#define WRITE(x,y) x(tif, fip->field_type, fip->field_tag, dir, wc, y) + switch (fip->field_type) { + case TIFF_SHORT: + case TIFF_SSHORT: + if (wc > 1) { + uint16 *wp; + if (wc == (u_short) TIFF_VARIABLE) { + _TIFFgetfield(td, fip->field_tag, &wc, &wp); + dir->tdir_count = wc; + } else + _TIFFgetfield(td, fip->field_tag, &wp); + if (!WRITE(TIFFWriteShortArray, wp)) + return (0); + } else { + uint16 sv; + _TIFFgetfield(td, fip->field_tag, &sv); + dir->tdir_offset = + TIFFInsertData(tif, dir->tdir_type, sv); + } + break; + case TIFF_LONG: + case TIFF_SLONG: + if (wc > 1) { + uint32 *lp; + if (wc == (u_short) TIFF_VARIABLE) { + _TIFFgetfield(td, fip->field_tag, &wc, &lp); + dir->tdir_count = wc; + } else + _TIFFgetfield(td, fip->field_tag, &lp); + if (!WRITE(TIFFWriteLongArray, lp)) + return (0); + } else { + /* XXX handle LONG->SHORT conversion */ + _TIFFgetfield(td, fip->field_tag, &dir->tdir_offset); + } + break; + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + if (wc > 1) { + float *fp; + if (wc == (u_short) TIFF_VARIABLE) { + _TIFFgetfield(td, fip->field_tag, &wc, &fp); + dir->tdir_count = wc; + } else + _TIFFgetfield(td, fip->field_tag, &fp); + if (!WRITE(TIFFWriteRationalArray, fp)) + return (0); + } else { + float fv; + _TIFFgetfield(td, fip->field_tag, &fv); + if (!TIFFWriteRational(tif, fip->field_type, fip->field_tag, dir, fv)) + return (0); + } + break; + case TIFF_FLOAT: + if (wc > 1) { + float *fp; + if (wc == (u_short) TIFF_VARIABLE) { + _TIFFgetfield(td, fip->field_tag, &wc, &fp); + dir->tdir_count = wc; + } else + _TIFFgetfield(td, fip->field_tag, &fp); + if (!WRITE(TIFFWriteFloatArray, fp)) + return (0); + } else { + float fv; + _TIFFgetfield(td, fip->field_tag, &fv); + TIFFCvtNativeToIEEEFloat(tif, 1, &fv); + /* XXX assumes sizeof (long) == sizeof (float) */ + dir->tdir_offset = *(uint32*) &fv; /* XXX */ + } + break; + case TIFF_ASCII: { + char *cp; + _TIFFgetfield(td, fip->field_tag, &cp); + if (!TIFFWriteString(tif, fip->field_tag, dir, cp)) + return (0); + break; + } + } + return (1); +} +#undef WRITE + +/* + * Setup a directory entry with either a SHORT + * or LONG type according to the value. + */ +static void +TIFFSetupShortLong(TIFF* tif, ttag_t tag, TIFFDirEntry* dir, uint32 v) +{ + dir->tdir_tag = tag; + dir->tdir_count = 1; + if (v > 0xffffL) { + dir->tdir_type = (short)TIFF_LONG; + dir->tdir_offset = v; + } else { + dir->tdir_type = (short)TIFF_SHORT; + dir->tdir_offset = TIFFInsertData(tif, (int)TIFF_SHORT, v); + } +} +#undef MakeShortDirent + +/* + * Setup a RATIONAL directory entry and + * write the associated indirect value. + */ +static int +TIFFWriteRational(TIFF* tif, + TIFFDataType type, ttag_t tag, TIFFDirEntry* dir, float v) +{ + return (TIFFWriteRationalArray(tif, type, tag, dir, 1, &v)); +} + +#define NITEMS(x) (sizeof (x) / sizeof (x[0])) +/* + * Setup a directory entry that references a + * samples/pixel array of SHORT values and + * (potentially) write the associated indirect + * values. + */ +static int +TIFFWritePerSampleShorts(TIFF* tif, ttag_t tag, TIFFDirEntry* dir) +{ + uint16 buf[10], v; + uint16* w = buf; + int i, status, samples = tif->tif_dir.td_samplesperpixel; + + if (samples > NITEMS(buf)) + w = (uint16*)_TIFFmalloc(samples * sizeof (uint16)); + _TIFFgetfield(&tif->tif_dir, tag, &v); + for (i = 0; i < samples; i++) + w[i] = v; + status = TIFFWriteShortArray(tif, TIFF_SHORT, tag, dir, samples, w); + if (w != buf) + _TIFFfree((char*)w); + return (status); +} +#undef NITEMS + +/* + * Setup a pair of shorts that are returned by + * value, rather than as a reference to an array. + */ +static int +TIFFSetupShortPair(TIFF* tif, ttag_t tag, TIFFDirEntry* dir) +{ + uint16 v[2]; + + _TIFFgetfield(&tif->tif_dir, tag, &v[0], &v[1]); + return (TIFFWriteShortArray(tif, TIFF_SHORT, tag, dir, 2, v)); +} + +/* + * Setup a directory entry for an NxM table of shorts, + * where M is known to be 2**bitspersample, and write + * the associated indirect data. + */ +static int +TIFFWriteShortTable(TIFF* tif, + ttag_t tag, TIFFDirEntry* dir, uint32 n, uint16** table) +{ + uint32 i, off; + + dir->tdir_tag = tag; + dir->tdir_type = (short)TIFF_SHORT; + /* XXX -- yech, fool TIFFWriteData */ + dir->tdir_count = 1L<tif_dir.td_bitspersample; + off = tif->tif_dataoff; + for (i = 0; i < n; i++) + if (!TIFFWriteData(tif, dir, (char *)table[i])) + return (0); + dir->tdir_count *= n; + dir->tdir_offset = off; + return (1); +} + +/* + * Setup a directory entry of an ASCII string + * and write any associated indirect value. + */ +static int +TIFFWriteString(TIFF* tif, ttag_t tag, TIFFDirEntry* dir, char* cp) +{ + dir->tdir_tag = tag; + dir->tdir_type = (short)TIFF_ASCII; + dir->tdir_count = strlen(cp) + 1; /* includes \0 byte */ + if (dir->tdir_count > 4) { + if (!TIFFWriteData(tif, dir, cp)) + return (0); + } else + memcpy(&dir->tdir_offset, cp, dir->tdir_count); + return (1); +} + +/* + * Setup a directory entry of an array of SHORT + * or SSHORT and write the associated indirect values. + */ +static int +TIFFWriteShortArray(TIFF* tif, + TIFFDataType type, ttag_t tag, TIFFDirEntry* dir, uint32 n, uint16* v) +{ + dir->tdir_tag = tag; + dir->tdir_type = (short)type; + dir->tdir_count = n; + if (n <= 2) { + if (tif->tif_header.tiff_magic == TIFF_BIGENDIAN) { + dir->tdir_offset = (long)v[0] << 16; + if (n == 2) + dir->tdir_offset |= v[1] & 0xffff; + } else { + dir->tdir_offset = v[0] & 0xffff; + if (n == 2) + dir->tdir_offset |= (long)v[1] << 16; + } + return (1); + } else + return (TIFFWriteData(tif, dir, (char *)v)); +} + +/* + * Setup a directory entry of an array of LONG + * or SLONG and write the associated indirect values. + */ +static int +TIFFWriteLongArray(TIFF* tif, + TIFFDataType type, ttag_t tag, TIFFDirEntry* dir, uint32 n, uint32* v) +{ + dir->tdir_tag = tag; + dir->tdir_type = (short)type; + dir->tdir_count = n; + if (n == 1) { + dir->tdir_offset = v[0]; + return (1); + } else + return (TIFFWriteData(tif, dir, (char *)v)); +} + +/* + * Setup a directory entry of an array of RATIONAL + * or SRATIONAL and write the associated indirect values. + */ +static int +TIFFWriteRationalArray(TIFF* tif, + TIFFDataType type, ttag_t tag, TIFFDirEntry* dir, uint32 n, float* v) +{ + uint32 i; + uint32* t; + int status; + + dir->tdir_tag = tag; + dir->tdir_type = (short)type; + dir->tdir_count = n; + t = (uint32*)_TIFFmalloc(2*n * sizeof (uint32)); + for (i = 0; i < n; i++) { + float fv = v[i]; + int sign = 1; + uint32 den; + + if (fv < 0) { + if (type == TIFF_RATIONAL) { + TIFFWarning(tif->tif_name, + "\"%s\": Information lost writing value (%g) as (unsigned) RATIONAL", + TIFFFieldWithTag(tag)->field_name, v); + fv = 0; + } else + fv = -fv, sign = -1; + } + den = 1L; + if (fv > 0) { + while (fv < 1L<<(31-3) && den < 1L<<(31-3)) + fv *= 1<<3, den *= 1L<<3; + } + t[2*i+0] = sign * (fv + 0.5); + t[2*i+1] = den; + } + status = TIFFWriteData(tif, dir, (char *)t); + _TIFFfree((char *)t); + return (status); +} + +static int +TIFFWriteFloatArray(TIFF* tif, + TIFFDataType type, ttag_t tag, TIFFDirEntry* dir, uint32 n, float* v) +{ + dir->tdir_tag = tag; + dir->tdir_type = (short)type; + dir->tdir_count = n; + TIFFCvtNativeToIEEEFloat(tif, n, v); + if (n == 1) { + dir->tdir_offset = *(uint32*)&v[0]; + return (1); + } else + return (TIFFWriteData(tif, dir, (char *)v)); +} + +#ifdef JPEG_SUPPORT +#define NITEMS(x) (sizeof (x) / sizeof (x[0])) +/* + * Setup a directory entry for JPEG Quantization + * tables and write the associated indirect values. + */ +static int +TIFFWriteJPEGQTables(TIFF* tif, TIFFDirEntry* dir) +{ + int i, status = 0, samples = tif->tif_dir.td_samplesperpixel; + uint32 buf[10], *off = buf; + TIFFDirEntry tdir; + + tdir.tdir_tag = TIFFTAG_JPEGQTABLES; /* for diagnostics */ + tdir.tdir_type = (short)TIFF_BYTE; + tdir.tdir_count = 64; + if (samples > NITEMS(buf)) + off = (uint32*)_TIFFmalloc(samples * sizeof (uint32)); + for (i = 0; i < samples; i++) { + if (!TIFFWriteData(tif, &tdir, (char *)tif->tif_dir.td_qtab[i])) + goto bad; + off[i] = tdir.tdir_offset; + } + status = TIFFWriteLongArray(tif, TIFF_LONG, + TIFFTAG_JPEGQTABLES, dir, samples, off); +bad: + if (off != buf) + _TIFFfree((char*)off); + return (status); +} + +/* + * Setup a directory entry for JPEG Coefficient + * tables and write the associated indirect values. + */ +static int +TIFFWriteJPEGCTables(TIFF* tif, ttag_t tag, TIFFDirEntry* dir, u_char** tab) +{ + int status = 0, samples = tif->tif_dir.td_samplesperpixel; + uint32 buf[10], *off = buf; + TIFFDirEntry tdir; + int i, j, ncodes; + + tdir.tdir_tag = tag; /* for diagnostics */ + tdir.tdir_type = (short)TIFF_BYTE; + if (samples > NITEMS(buf)) + off = (uint32*)_TIFFmalloc(samples * sizeof (uint32)); + for (i = 0; i < samples; i++) { + for (ncodes = 0, j = 0; j < 16; j++) + ncodes += tab[i][j]; + tdir.tdir_count = 16+ncodes; + if (!TIFFWriteData(tif, &tdir, (char *)tab[i])) + goto bad; + off[i] = tdir.tdir_offset; + } + status = TIFFWriteLongArray(tif, TIFF_LONG, tag, dir, samples, off); +bad: + if (off != buf) + _TIFFfree((char*)off); + return (status); +} +#undef NITEMS +#endif + +#ifdef COLORIMETRY_SUPPORT +static int +TIFFWriteTransferFunction(TIFF* tif, TIFFDirEntry* dir) +{ + TIFFDirectory* td = &tif->tif_dir; + uint32 n = (1L<td_bitspersample) * sizeof (uint16); + uint16** tf = td->td_transferfunction; + int ncols; + + /* + * Check if the table can be written as a single column, + * or if it must be written as 3 columns. Note that we + * write a 3-column tag if there are 2 samples/pixel and + * a single column of data won't suffice--hmm. + */ + switch (td->td_samplesperpixel - td->td_extrasamples) { + default: if (memcmp(tf[0], tf[2], n)) { ncols = 3; break; } + case 2: if (memcmp(tf[0], tf[1], n)) { ncols = 3; break; } + case 1: case 0: ncols = 1; + } + return (TIFFWriteShortTable(tif, + TIFFTAG_TRANSFERFUNCTION, dir, ncols, tf)); +} +#endif + +/* + * Write a contiguous directory item. + */ +static int +TIFFWriteData(TIFF* tif, TIFFDirEntry* dir, char* cp) +{ + tsize_t cc; + + dir->tdir_offset = tif->tif_dataoff; + cc = dir->tdir_count * tiffDataWidth[dir->tdir_type]; + if (SeekOK(tif, dir->tdir_offset) && + WriteOK(tif, cp, cc)) { + tif->tif_dataoff += (cc + 1) & ~1; + return (1); + } + TIFFError(tif->tif_name, "Error writing data for field \"%s\"", + TIFFFieldWithTag(dir->tdir_tag)->field_name); + return (0); +} + +/* + * Link the current directory into the + * directory chain for the file. + */ +static int +TIFFLinkDirectory(TIFF* tif) +{ + static const char module[] = "TIFFLinkDirectory"; + uint16 dircount; + uint32 nextdir; + + tif->tif_diroff = (TIFFSeekFile(tif, 0L, L_XTND)+1) &~ 1L; + if (tif->tif_header.tiff_diroff == 0) { + /* + * First directory, overwrite header. + */ + tif->tif_header.tiff_diroff = tif->tif_diroff; + (void) TIFFSeekFile(tif, 0L, L_SET); + if (!WriteOK(tif, &tif->tif_header, + sizeof (tif->tif_header))) { + TIFFError(tif->tif_name, "Error writing TIFF header"); + return (0); + } + return (1); + } + /* + * Not the first directory, search to the last and append. + */ + nextdir = tif->tif_header.tiff_diroff; + do { + if (!SeekOK(tif, nextdir) || + !ReadOK(tif, &dircount, sizeof (uint16))) { + TIFFError(module, "Error fetching directory count"); + return (0); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabShort(&dircount); + TIFFSeekFile(tif, dircount * sizeof (TIFFDirEntry), L_INCR); + if (!ReadOK(tif, &nextdir, sizeof (uint32))) { + TIFFError(module, "Error fetching directory link"); + return (0); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong(&nextdir); + } while (nextdir != 0); + (void) TIFFSeekFile(tif, -sizeof (nextdir), L_INCR); + if (!WriteOK(tif, &tif->tif_diroff, sizeof (tif->tif_diroff))) { + TIFFError(module, "Error writing directory link"); + return (0); + } + return (1); +} diff --git a/panda/src/tiff/tif_dumpmode.c b/panda/src/tiff/tif_dumpmode.c new file mode 100644 index 0000000000..f28d9e9261 --- /dev/null +++ b/panda/src/tiff/tif_dumpmode.c @@ -0,0 +1,113 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * "Null" Compression Algorithm Support. + */ +#include "tiffiop.h" + +/* + * Encode a hunk of pixels. + */ +static int +DumpModeEncode(TIFF* tif, tidata_t pp, tsize_t cc, tsample_t s) +{ + while (cc > 0) { + tsize_t n; + + n = cc; + if (tif->tif_rawcc + n > tif->tif_rawdatasize) + n = tif->tif_rawdatasize - tif->tif_rawcc; + /* + * Avoid copy if client has setup raw + * data buffer to avoid extra copy. + */ + if (tif->tif_rawcp != pp) + memcpy(tif->tif_rawcp, pp, n); + tif->tif_rawcp += n; + tif->tif_rawcc += n; + pp += n; + cc -= n; + if (tif->tif_rawcc >= tif->tif_rawdatasize && + !TIFFFlushData1(tif)) + return (-1); + } + return (1); +} + +/* + * Decode a hunk of pixels. + */ +static int +DumpModeDecode(TIFF* tif, tidata_t buf, tsize_t cc, tsample_t s) +{ + if (tif->tif_rawcc < cc) { + TIFFError(tif->tif_name, + "DumpModeDecode: Not enough data for scanline %d", + tif->tif_row); + return (0); + } + /* + * Avoid copy if client has setup raw + * data buffer to avoid extra copy. + */ + if (tif->tif_rawcp != buf) + memcpy(buf, tif->tif_rawcp, cc); + tif->tif_rawcp += cc; + tif->tif_rawcc -= cc; + return (1); +} + +/* + * Seek forwards nrows in the current strip. + */ +static int +DumpModeSeek(TIFF* tif, uint32 nrows) +{ + tif->tif_rawcp += nrows * tif->tif_scanlinesize; + tif->tif_rawcc -= nrows * tif->tif_scanlinesize; + return (1); +} + +/* + * Initialize dump mode. + */ +int +TIFFInitDumpMode(TIFF* tif) +{ + tif->tif_decoderow = DumpModeDecode; + tif->tif_decodestrip = DumpModeDecode; + tif->tif_decodetile = DumpModeDecode; + tif->tif_encoderow = DumpModeEncode; + tif->tif_encodestrip = DumpModeEncode; + tif->tif_encodetile = DumpModeEncode; + tif->tif_seek = DumpModeSeek; + return (1); +} diff --git a/panda/src/tiff/tif_error.c b/panda/src/tiff/tif_error.c new file mode 100644 index 0000000000..b06b49a8cf --- /dev/null +++ b/panda/src/tiff/tif_error.c @@ -0,0 +1,63 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + */ +#include "tiffiop.h" +#include + +static void +defaultHandler(const char* module, const char* fmt, va_list ap) +{ + if (module != NULL) + fprintf(stderr, "%s: ", module); + vfprintf(stderr, fmt, ap); + fprintf(stderr, ".\n"); +} + +static TIFFErrorHandler _errorHandler = defaultHandler; + +TIFFErrorHandler +TIFFSetErrorHandler(TIFFErrorHandler handler) +{ + TIFFErrorHandler prev = _errorHandler; + _errorHandler = handler; + return (prev); +} + +void +TIFFError(const char* module, const char* fmt, ...) +{ + if (_errorHandler) { + va_list ap; + va_start(ap, fmt); + (*_errorHandler)(module, fmt, ap); + va_end(ap); + } +} diff --git a/panda/src/tiff/tif_fax3.c b/panda/src/tiff/tif_fax3.c new file mode 100644 index 0000000000..faf01d8152 --- /dev/null +++ b/panda/src/tiff/tif_fax3.c @@ -0,0 +1,1077 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * CCITT Group 3 and Group 4 Compression Support. + */ +#include "tiffiop.h" +#include "tif_fax3.h" +#define G3CODES +#include "t4.h" +#define G3STATES +#include "g3states.h" +#include +#include + +typedef struct { + Fax3BaseState b; +} Fax3DecodeState; + +typedef struct { + Fax3BaseState b; + const u_char *wruns; + const u_char *bruns; + short k; /* #rows left that can be 2d encoded */ + short maxk; /* max #rows that can be 2d encoded */ +} Fax3EncodeState; + +static int Fax3Decode1DRow(TIFF*, u_char*, int); +static int Fax3Encode1DRow(TIFF*, u_char*, int); +static int findspan(u_char**, int, int, const u_char*); +static int finddiff(u_char*, int, int, int); + +void +TIFFModeCCITTFax3(TIFF* tif, int isClassF) +{ + if (isClassF) + tif->tif_options |= FAX3_CLASSF; + else + tif->tif_options &= ~FAX3_CLASSF; +} + +static u_char bitMask[8] = + { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; +#define isBitSet(sp) ((sp)->b.data & bitMask[(sp)->b.bit]) + +#define is2DEncoding(tif) \ + (tif->tif_dir.td_group3options & GROUP3OPT_2DENCODING) +#define fetchByte(tif, sp) \ + ((tif)->tif_rawcc--, (sp)->b.bitmap[*(u_char *)(tif)->tif_rawcp++]) + +#define BITCASE(b) \ + case b: \ + code <<= 1; \ + if (data & (1<<(7-b))) code |= 1;\ + len++; \ + if (code > 0) { bit = b+1; break; } + +/* + * Skip over input until an EOL code is found. The + * value of len is passed as 0 except during error + * recovery when decoding 2D data. Note also that + * we don't use the optimized state tables to locate + * an EOL because we can't assume much of anything + * about our state (e.g. bit position). + */ +static void +skiptoeol(TIFF* tif, int len) +{ + Fax3DecodeState *sp = (Fax3DecodeState *)tif->tif_data; + register int bit = sp->b.bit; + register int data = sp->b.data; + int code = 0; + + /* + * Our handling of ``bit'' is painful because + * the rest of the code does not maintain it as + * exactly the bit offset in the current data + * byte (bit == 0 means refill the data byte). + * Thus we have to be careful on entry and + * exit to insure that we maintain a value that's + * understandable elsewhere in the decoding logic. + */ + if (bit == 0) /* force refill */ + bit = 8; + for (;;) { + switch (bit) { + again: BITCASE(0); + BITCASE(1); + BITCASE(2); + BITCASE(3); + BITCASE(4); + BITCASE(5); + BITCASE(6); + BITCASE(7); + default: + if (tif->tif_rawcc <= 0) + return; + data = fetchByte(tif, sp); + goto again; + } + if (len >= 12 && code == EOL) + break; + code = len = 0; + } + sp->b.bit = bit > 7 ? 0 : bit; /* force refill */ + sp->b.data = data; +} + +/* + * Return the next bit in the input stream. This is + * used to extract 2D tag values and the color tag + * at the end of a terminating uncompressed data code. + */ +static int +nextbit(TIFF* tif) +{ + Fax3DecodeState *sp = (Fax3DecodeState *)tif->tif_data; + int bit; + + if (sp->b.bit == 0 && tif->tif_rawcc > 0) + sp->b.data = fetchByte(tif, sp); + bit = isBitSet(sp); + if (++(sp->b.bit) > 7) + sp->b.bit = 0; + return (bit); +} + +/* + * Setup G3-related compression/decompression + * state before data is processed. This routine + * is called once per image -- it sets up different + * state based on whether or not 2D encoding is used. + */ +static void * +Fax3SetupState(TIFF* tif, int space) +{ + TIFFDirectory *td = &tif->tif_dir; + Fax3BaseState *sp; + int cc = space; + long rowbytes, rowpixels; + + if (td->td_bitspersample != 1) { + TIFFError(tif->tif_name, + "Bits/sample must be 1 for Group 3/4 encoding/decoding"); + return (0); + } + /* + * Calculate the scanline/tile widths. + */ + if (isTiled(tif)) { + rowbytes = TIFFTileRowSize(tif); + rowpixels = tif->tif_dir.td_tilewidth; + } else { + rowbytes = TIFFScanlineSize(tif); + rowpixels = tif->tif_dir.td_imagewidth; + } + if (is2DEncoding(tif) || td->td_compression == COMPRESSION_CCITTFAX4) + cc += rowbytes+1; + tif->tif_data = _TIFFmalloc(cc); + if (tif->tif_data == NULL) { + TIFFError("Fax3SetupState", + "%s: No space for Fax3 state block", tif->tif_name); + return (0); + } + sp = (Fax3BaseState *)tif->tif_data; + sp->rowbytes = rowbytes; + sp->rowpixels = rowpixels; + sp->bitmap = TIFFGetBitRevTable(tif->tif_fillorder != td->td_fillorder); + sp->white = (td->td_photometric == PHOTOMETRIC_MINISBLACK); + if (is2DEncoding(tif) || td->td_compression == COMPRESSION_CCITTFAX4) { + /* + * 2d encoding/decoding requires a scanline + * buffer for the ``reference line''; the + * scanline against which delta encoding + * is referenced. The reference line must + * be initialized to be ``white'' (done elsewhere). + */ + sp->refline = (u_char *)tif->tif_data + space + 1; + /* + * Initialize pixel just to the left of the + * reference line to white. This extra pixel + * simplifies the edge-condition logic. + */ + sp->refline[-1] = sp->white ? 0xff : 0x00; + } else + sp->refline = 0; + return (sp); +} + +/* + * Setup state for decoding a strip. + */ +static +Fax3PreDecode(TIFF* tif) +{ + Fax3DecodeState *sp = (Fax3DecodeState *)tif->tif_data; + + if (sp == NULL) { + sp = (Fax3DecodeState *)Fax3SetupState(tif, sizeof (*sp)); + if (!sp) + return (0); + } + sp->b.bit = 0; /* force initial read */ + sp->b.data = 0; + sp->b.tag = G3_1D; + if (sp->b.refline) + memset(sp->b.refline, sp->b.white ? 0xff:0x00, sp->b.rowbytes); + /* + * If image has EOL codes, they precede each line + * of data. We skip over the first one here so that + * when we decode rows, we can use an EOL to signal + * that less than the expected number of pixels are + * present for the scanline. + */ + if ((tif->tif_options & FAX3_NOEOL) == 0) { + skiptoeol(tif, 0); + if (is2DEncoding(tif)) + /* tag should always be 1D! */ + sp->b.tag = nextbit(tif) ? G3_1D : G3_2D; + } + return (1); +} + +/* + * Fill a span with ones. + */ +static void +fillspan(register char* cp, register int x, register int count) +{ + static const unsigned char masks[] = + { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; + + if (count <= 0) + return; + cp += x>>3; + if (x &= 7) { /* align to byte boundary */ + if (count < 8 - x) { + *cp++ |= masks[count] >> x; + return; + } + *cp++ |= 0xff >> x; + count -= 8 - x; + } + while (count >= 8) { + *cp++ = 0xff; + count -= 8; + } + *cp |= masks[count]; +} + +/* + * Decode the requested amount of data. + */ +static +Fax3Decode(TIFF* tif, tidata_t buf, tsize_t occ, tsample_t s) +{ + Fax3DecodeState *sp = (Fax3DecodeState *)tif->tif_data; + int status; + + memset(buf, 0, occ); /* decoding only sets non-zero bits */ + while ((long)occ > 0) { + if (sp->b.tag == G3_1D) + status = Fax3Decode1DRow(tif, buf, sp->b.rowpixels); + else + status = Fax3Decode2DRow(tif, buf, sp->b.rowpixels); + /* + * For premature EOF, stop decoding and return + * the buffer with the remainder white-filled. + */ + if (status < 0) + return (status == G3CODE_EOF); + if (is2DEncoding(tif)) { + /* + * Fetch the tag bit that indicates + * whether the next row is 1d or 2d + * encoded. If 2d-encoded, then setup + * the reference line from the decoded + * scanline just completed. + */ + sp->b.tag = nextbit(tif) ? G3_1D : G3_2D; + if (sp->b.tag == G3_2D) + memcpy(sp->b.refline, buf, sp->b.rowbytes); + } + buf += sp->b.rowbytes; + occ -= sp->b.rowbytes; + if (occ != 0) + tif->tif_row++; + } + return (1); +} + +/* + * Decode a run of white. + */ +static int +decode_white_run(TIFF* tif) +{ + Fax3DecodeState *sp = (Fax3DecodeState *)tif->tif_data; + short state = sp->b.bit; + short action; + int runlen = 0; + + for (;;) { + if (sp->b.bit == 0) { + nextbyte: + if (tif->tif_rawcc <= 0) + return (G3CODE_EOF); + sp->b.data = fetchByte(tif, sp); + } + action = TIFFFax1DAction[state][sp->b.data]; + state = TIFFFax1DNextState[state][sp->b.data]; + if (action == ACT_INCOMP) + goto nextbyte; + if (action == ACT_INVALID) + return (G3CODE_INVALID); + if (action == ACT_EOL) + return (G3CODE_EOL); + sp->b.bit = state; + action = RUNLENGTH(action - ACT_WRUNT); + runlen += action; + if (action < 64) + return (runlen); + } + /*NOTREACHED*/ +} + +/* + * Decode a run of black. + */ +static int +decode_black_run(TIFF* tif) +{ + Fax3DecodeState *sp = (Fax3DecodeState *)tif->tif_data; + short state = sp->b.bit + 8; + short action; + int runlen = 0; + + for (;;) { + if (sp->b.bit == 0) { + nextbyte: + if (tif->tif_rawcc <= 0) + return (G3CODE_EOF); + sp->b.data = fetchByte(tif, sp); + } + action = TIFFFax1DAction[state][sp->b.data]; + state = TIFFFax1DNextState[state][sp->b.data]; + if (action == ACT_INCOMP) + goto nextbyte; + if (action == ACT_INVALID) + return (G3CODE_INVALID); + if (action == ACT_EOL) + return (G3CODE_EOL); + sp->b.bit = state; + action = RUNLENGTH(action - ACT_BRUNT); + runlen += action; + if (action < 64) + return (runlen); + state += 8; + } + /*NOTREACHED*/ +} + +/* + * Process one row of 1d Huffman-encoded data. + */ +static int +Fax3Decode1DRow(TIFF* tif, u_char* buf, int npels) +{ + Fax3DecodeState *sp = (Fax3DecodeState *)tif->tif_data; + int x = 0; + int runlen; + short action; + short color = sp->b.white; + static const char module[] = "Fax3Decode1D"; + + for (;;) { + if (color == sp->b.white) + runlen = decode_white_run(tif); + else + runlen = decode_black_run(tif); + switch (runlen) { + case G3CODE_EOF: + TIFFWarning(module, + "%s: Premature EOF at scanline %d (x %d)", + tif->tif_name, tif->tif_row, x); + return (G3CODE_EOF); + case G3CODE_INVALID: /* invalid code */ + /* + * An invalid code was encountered. + * Flush the remainder of the line + * and allow the caller to decide whether + * or not to continue. Note that this + * only works if we have a G3 image + * with EOL markers. + */ + TIFFError(module, + "%s: Bad code word at scanline %d (x %d)", + tif->tif_name, tif->tif_row, x); + goto done; + case G3CODE_EOL: /* premature end-of-line code */ + TIFFWarning(module, + "%s: Premature EOL at scanline %d (x %d)", + tif->tif_name, tif->tif_row, x); + return (1); /* try to resynchronize... */ + } + if (x+runlen > npels) + runlen = npels-x; + if (runlen > 0) { + if (color) + fillspan((char *)buf, x, runlen); + x += runlen; + if (x >= npels) + break; + } + color = !color; + } +done: + /* + * Cleanup at the end of the row. This convoluted + * logic is merely so that we can reuse the code with + * two other related compression algorithms (2 & 32771). + * + * Note also that our handling of word alignment assumes + * that the buffer is at least word aligned. This is + * the case for most all versions of malloc (typically + * the buffer is returned longword aligned). + */ + if ((tif->tif_options & FAX3_NOEOL) == 0) + skiptoeol(tif, 0); + if (tif->tif_options & FAX3_BYTEALIGN) + sp->b.bit = 0; + if ((tif->tif_options & FAX3_WORDALIGN) && ((long)tif->tif_rawcp & 1)) + (void) fetchByte(tif, sp); + return (x == npels ? 1 : G3CODE_EOL); +} + +/* + * Group 3 2d Decoding support. + */ + +/* + * Return the next uncompressed mode code word. + */ +static int +decode_uncomp_code(TIFF* tif) +{ + Fax3DecodeState *sp = (Fax3DecodeState *)tif->tif_data; + short code; + + do { + if (sp->b.bit == 0 || sp->b.bit > 7) { + if (tif->tif_rawcc <= 0) + return (UNCOMP_EOF); + sp->b.data = fetchByte(tif, sp); + } + code = TIFFFaxUncompAction[sp->b.bit][sp->b.data]; + sp->b.bit = TIFFFaxUncompNextState[sp->b.bit][sp->b.data]; + } while (code == ACT_INCOMP); + return (code); +} + +/* + * Process one row of 2d encoded data. + */ +int +Fax3Decode2DRow(TIFF* tif, u_char* buf, int npels) +{ +#define PIXEL(buf,ix) ((((buf)[(ix)>>3]) >> (7-((ix)&7))) & 1) + Fax3DecodeState *sp = (Fax3DecodeState *)tif->tif_data; + int a0 = -1; + int b1, b2; + int run1, run2; /* for horizontal mode */ + short mode; + short color = sp->b.white; + static const char module[] = "Fax3Decode2D"; + + do { + if (sp->b.bit == 0 || sp->b.bit > 7) { + if (tif->tif_rawcc <= 0) { + TIFFError(module, + "%s: Premature EOF at scanline %d", + tif->tif_name, tif->tif_row); + return (G3CODE_EOF); + } + sp->b.data = fetchByte(tif, sp); + } + mode = TIFFFax2DMode[sp->b.bit][sp->b.data]; + sp->b.bit = TIFFFax2DNextState[sp->b.bit][sp->b.data]; + switch (mode) { + case MODE_NULL: + break; + case MODE_PASS: + b2 = finddiff(sp->b.refline, a0, npels, !color); + b1 = finddiff(sp->b.refline, b2, npels, color); + b2 = finddiff(sp->b.refline, b1, npels, !color); + if (color) { + if (a0 < 0) + a0 = 0; + fillspan((char *)buf, a0, b2 - a0); + } + a0 = b2; + break; + case MODE_HORIZ: + if (color == sp->b.white) { + run1 = decode_white_run(tif); + run2 = decode_black_run(tif); + } else { + run1 = decode_black_run(tif); + run2 = decode_white_run(tif); + } + if (run1 >= 0 && run2 >= 0) { + /* + * Do the appropriate fill. Note that we exit + * this logic with the same color that we enter + * with since we do 2 fills. This explains the + * somewhat obscure logic below. + */ + if (a0 < 0) + a0 = 0; + if (a0 + run1 > npels) + run1 = npels - a0; + if (color) + fillspan((char *)buf, a0, run1); + a0 += run1; + if (a0 + run2 > npels) + run2 = npels - a0; + if (!color) + fillspan((char *)buf, a0, run2); + a0 += run2; + } + break; + case MODE_VERT_V0: + case MODE_VERT_VR1: + case MODE_VERT_VR2: + case MODE_VERT_VR3: + case MODE_VERT_VL1: + case MODE_VERT_VL2: + case MODE_VERT_VL3: + b2 = finddiff(sp->b.refline, a0, npels, !color); + b1 = finddiff(sp->b.refline, b2, npels, color); + b1 += mode - MODE_VERT_V0; + if (color) { + if (a0 < 0) + a0 = 0; + fillspan((char *)buf, a0, b1 - a0); + } + color = !color; + a0 = b1; + break; + case MODE_UNCOMP: + /* + * Uncompressed mode: select from the + * special set of code words. + */ + if (a0 < 0) + a0 = 0; + do { + mode = decode_uncomp_code(tif); + switch (mode) { + case UNCOMP_RUN1: + case UNCOMP_RUN2: + case UNCOMP_RUN3: + case UNCOMP_RUN4: + case UNCOMP_RUN5: + run1 = mode - UNCOMP_RUN0; + fillspan((char *)buf, a0+run1-1, 1); + a0 += run1; + break; + case UNCOMP_RUN6: + a0 += 5; + break; + case UNCOMP_TRUN0: + case UNCOMP_TRUN1: + case UNCOMP_TRUN2: + case UNCOMP_TRUN3: + case UNCOMP_TRUN4: + run1 = mode - UNCOMP_TRUN0; + a0 += run1; + color = nextbit(tif) ? + !sp->b.white : sp->b.white; + break; + case UNCOMP_INVALID: + TIFFError(module, + "%s: Bad uncompressed code word at scanline %d", + tif->tif_name, tif->tif_row); + goto bad; + case UNCOMP_EOF: + TIFFError(module, + "%s: Premature EOF at scanline %d", + tif->tif_name, tif->tif_row); + return (G3CODE_EOF); + } + } while (mode < UNCOMP_EXIT); + break; + case MODE_ERROR_1: + if ((tif->tif_options & FAX3_NOEOL) == 0) { + TIFFWarning(module, + "%s: Premature EOL at scanline %d (x %d)", + tif->tif_name, tif->tif_row, a0); + skiptoeol(tif, 7); /* seen 7 0's already */ + return (1); /* try to synchronize */ + } + /* fall thru... */ + case MODE_ERROR: + TIFFError(module, + "%s: Bad 2D code word at scanline %d", + tif->tif_name, tif->tif_row); + goto bad; + default: + TIFFError(module, + "%s: Panic, bad decoding state at scanline %d", + tif->tif_name, tif->tif_row); + return (0); + } + } while (a0 < npels); +bad: + /* + * Cleanup at the end of row. We check for + * EOL separately so that this code can be + * reused by the Group 4 decoding routine. + */ + if ((tif->tif_options & FAX3_NOEOL) == 0) + skiptoeol(tif, 0); + return (a0 >= npels ? 1 : G3CODE_EOL); +#undef PIXEL +} + +/* + * CCITT Group 3 FAX Encoding. + */ + +/* + * Write a variable-length bit-value to + * the output stream. Values are + * assumed to be at most 16 bits. + */ +void +Fax3PutBits(TIFF* tif, u_int bits, u_int length) +{ + Fax3BaseState *sp = (Fax3BaseState *)tif->tif_data; + static const int mask[9] = + { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; + + while (length > sp->bit) { + sp->data |= bits >> (length - sp->bit); + length -= sp->bit; + Fax3FlushBits(tif, sp); + } + sp->data |= (bits & mask[length]) << (sp->bit - length); + sp->bit -= length; + if (sp->bit == 0) + Fax3FlushBits(tif, sp); +} + +/* + * Write a code to the output stream. + */ +static void +putcode(TIFF* tif, const tableentry* te) +{ + Fax3PutBits(tif, te->code, te->length); +} + +/* + * Write the sequence of codes that describes + * the specified span of zero's or one's. The + * appropriate table that holds the make-up and + * terminating codes is supplied. + */ +static void +putspan(TIFF* tif, int span, const tableentry* tab) +{ + while (span >= 2624) { + const tableentry *te = &tab[63 + (2560>>6)]; + putcode(tif, te); + span -= te->runlen; + } + if (span >= 64) { + const tableentry *te = &tab[63 + (span>>6)]; + assert(te->runlen == 64*(span>>6)); + putcode(tif, te); + span -= te->runlen; + } + putcode(tif, &tab[span]); +} + +/* + * Write an EOL code to the output stream. The zero-fill + * logic for byte-aligning encoded scanlines is handled + * here. We also handle writing the tag bit for the next + * scanline when doing 2d encoding. + */ +void +Fax3PutEOL(TIFF* tif) +{ + Fax3BaseState *sp = (Fax3BaseState *)tif->tif_data; + + if (tif->tif_dir.td_group3options & GROUP3OPT_FILLBITS) { + /* + * Force bit alignment so EOL will terminate on + * a byte boundary. That is, force the bit alignment + * to 16-12 = 4 before putting out the EOL code. + */ + int align = 8 - 4; + if (align != sp->bit) { + if (align > sp->bit) + align = sp->bit + (8 - align); + else + align = sp->bit - align; + Fax3PutBits(tif, 0, align); + } + } + Fax3PutBits(tif, EOL, 12); + if (is2DEncoding(tif)) + Fax3PutBits(tif, sp->tag == G3_1D, 1); +} + +static const u_char zeroruns[256] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1f */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 - 0x2f */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 - 0x3f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x50 - 0x5f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x70 - 0x7f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 - 0xff */ +}; +static const u_char oneruns[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 - 0x8f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 - 0x9f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xa0 - 0xaf */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xb0 - 0xbf */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 - 0xcf */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xd0 - 0xdf */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 - 0xef */ + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, /* 0xf0 - 0xff */ +}; + +/* + * Reset encoding state at the start of a strip. + */ +static +Fax3PreEncode(TIFF* tif) +{ + Fax3EncodeState *sp = (Fax3EncodeState *)tif->tif_data; + + if (sp == NULL) { + sp = (Fax3EncodeState *)Fax3SetupState(tif, sizeof (*sp)); + if (!sp) + return (0); + if (sp->b.white == 0) { + sp->wruns = zeroruns; + sp->bruns = oneruns; + } else { + sp->wruns = oneruns; + sp->bruns = zeroruns; + } + } + sp->b.bit = 8; + sp->b.data = 0; + sp->b.tag = G3_1D; + /* + * This is necessary for Group 4; otherwise it isn't + * needed because the first scanline of each strip ends + * up being copied into the refline. + */ + if (sp->b.refline) + memset(sp->b.refline, sp->b.white ? 0xff:0x00, sp->b.rowbytes); + if (is2DEncoding(tif)) { + float res = tif->tif_dir.td_yresolution; + /* + * The CCITT spec says that when doing 2d encoding, you + * should only do it on K consecutive scanlines, where K + * depends on the resolution of the image being encoded + * (2 for <= 200 lpi, 4 for > 200 lpi). Since the directory + * code initializes td_yresolution to 0, this code will + * select a K of 2 unless the YResolution tag is set + * appropriately. (Note also that we fudge a little here + * and use 150 lpi to avoid problems with units conversion.) + */ + if (tif->tif_dir.td_resolutionunit == RESUNIT_CENTIMETER) + res = (res * .3937) / 2.54; /* convert to inches */ + sp->maxk = (res > 150 ? 4 : 2); + sp->k = sp->maxk-1; + } else + sp->k = sp->maxk = 0; + return (1); +} + +/* + * 1d-encode a row of pixels. The encoding is + * a sequence of all-white or all-black spans + * of pixels encoded with Huffman codes. + */ +static int +Fax3Encode1DRow(TIFF* tif, u_char* bp, int bits) +{ + Fax3EncodeState *sp = (Fax3EncodeState *)tif->tif_data; + int bs = 0, span; + + for (;;) { + span = findspan(&bp, bs, bits, sp->wruns); /* white span */ + putspan(tif, span, TIFFFaxWhiteCodes); + bs += span; + if (bs >= bits) + break; + span = findspan(&bp, bs, bits, sp->bruns); /* black span */ + putspan(tif, span, TIFFFaxBlackCodes); + bs += span; + if (bs >= bits) + break; + } + return (1); +} + +static const tableentry horizcode = + { 3, 0x1 }; /* 001 */ +static const tableentry passcode = + { 4, 0x1 }; /* 0001 */ +static const tableentry vcodes[7] = { + { 7, 0x03 }, /* 0000 011 */ + { 6, 0x03 }, /* 0000 11 */ + { 3, 0x03 }, /* 011 */ + { 1, 0x1 }, /* 1 */ + { 3, 0x2 }, /* 010 */ + { 6, 0x02 }, /* 0000 10 */ + { 7, 0x02 } /* 0000 010 */ +}; + +/* + * 2d-encode a row of pixels. Consult the CCITT + * documentation for the algorithm. + */ +int +Fax3Encode2DRow(TIFF* tif, u_char* bp, u_char* rp, int bits) +{ +#define PIXEL(buf,ix) ((((buf)[(ix)>>3]) >> (7-((ix)&7))) & 1) + short white = ((Fax3BaseState *)tif->tif_data)->white; + int a0 = 0; + int a1 = (PIXEL(bp, 0) != white ? 0 : finddiff(bp, 0, bits, white)); + int b1 = (PIXEL(rp, 0) != white ? 0 : finddiff(rp, 0, bits, white)); + int a2, b2; + + for (;;) { + b2 = finddiff(rp, b1, bits, PIXEL(rp,b1)); + if (b2 >= a1) { + int d = b1 - a1; + if (!(-3 <= d && d <= 3)) { /* horizontal mode */ + a2 = finddiff(bp, a1, bits, PIXEL(bp,a1)); + putcode(tif, &horizcode); + if (a0+a1 == 0 || PIXEL(bp, a0) == white) { + putspan(tif, a1-a0, TIFFFaxWhiteCodes); + putspan(tif, a2-a1, TIFFFaxBlackCodes); + } else { + putspan(tif, a1-a0, TIFFFaxBlackCodes); + putspan(tif, a2-a1, TIFFFaxWhiteCodes); + } + a0 = a2; + } else { /* vertical mode */ + putcode(tif, &vcodes[d+3]); + a0 = a1; + } + } else { /* pass mode */ + putcode(tif, &passcode); + a0 = b2; + } + if (a0 >= bits) + break; + a1 = finddiff(bp, a0, bits, PIXEL(bp,a0)); + b1 = finddiff(rp, a0, bits, !PIXEL(bp,a0)); + b1 = finddiff(rp, b1, bits, PIXEL(bp,a0)); + } + return (1); +#undef PIXEL +} + +/* + * Encode a buffer of pixels. + */ +static int +Fax3Encode(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s) +{ + Fax3EncodeState *sp = (Fax3EncodeState *)tif->tif_data; + + while ((long)cc > 0) { + Fax3PutEOL(tif); + if (is2DEncoding(tif)) { + if (sp->b.tag == G3_1D) { + if (!Fax3Encode1DRow(tif, bp, sp->b.rowpixels)) + return (0); + sp->b.tag = G3_2D; + } else { + if (!Fax3Encode2DRow(tif, bp, sp->b.refline, sp->b.rowpixels)) + return (0); + sp->k--; + } + if (sp->k == 0) { + sp->b.tag = G3_1D; + sp->k = sp->maxk-1; + } else + memcpy(sp->b.refline, bp, sp->b.rowbytes); + } else { + if (!Fax3Encode1DRow(tif, bp, sp->b.rowpixels)) + return (0); + } + bp += sp->b.rowbytes; + cc -= sp->b.rowbytes; + if (cc != 0) + tif->tif_row++; + } + return (1); +} + +static int +Fax3PostEncode(TIFF* tif) +{ + Fax3BaseState *sp = (Fax3BaseState *)tif->tif_data; + + if (sp->bit != 8) + Fax3FlushBits(tif, sp); + return (1); +} + +static void +Fax3Close(TIFF* tif) +{ + if ((tif->tif_options & FAX3_CLASSF) == 0) { /* append RTC */ + int i; + for (i = 0; i < 6; i++) { + Fax3PutBits(tif, EOL, 12); + if (is2DEncoding(tif)) + Fax3PutBits(tif, 1, 1); + } + (void) Fax3PostEncode(tif); + } +} + +static void +Fax3Cleanup(TIFF* tif) +{ + if (tif->tif_data) { + _TIFFfree(tif->tif_data); + tif->tif_data = NULL; + } +} + +/* + * Bit handling utilities. + */ + +/* + * Find a span of ones or zeros using the supplied + * table. The byte-aligned start of the bit string + * is supplied along with the start+end bit indices. + * The table gives the number of consecutive ones or + * zeros starting from the msb and is indexed by byte + * value. + */ +static int +findspan(u_char** bpp, int bs, int be, register const u_char* tab) +{ + register u_char *bp = *bpp; + register int bits = be - bs; + register int n, span; + + /* + * Check partial byte on lhs. + */ + if (bits > 0 && (n = (bs & 7))) { + span = tab[(*bp << n) & 0xff]; + if (span > 8-n) /* table value too generous */ + span = 8-n; + if (span > bits) /* constrain span to bit range */ + span = bits; + if (n+span < 8) /* doesn't extend to edge of byte */ + goto done; + bits -= span; + bp++; + } else + span = 0; + /* + * Scan full bytes for all 1's or all 0's. + */ + while (bits >= 8) { + n = tab[*bp]; + span += n; + bits -= n; + if (n < 8) /* end of run */ + goto done; + bp++; + } + /* + * Check partial byte on rhs. + */ + if (bits > 0) { + n = tab[*bp]; + span += (n > bits ? bits : n); + } +done: + *bpp = bp; + return (span); +} + +/* + * Return the offset of the next bit in the range + * [bs..be] that is different from the specified + * color. The end, be, is returned if no such bit + * exists. + */ +static int +finddiff(u_char* cp, int bs, int be, int color) +{ + cp += bs >> 3; /* adjust byte offset */ + return (bs + findspan(&cp, bs, be, color ? oneruns : zeroruns)); +} + +int +TIFFInitCCITTFax3(TIFF* tif) +{ + tif->tif_predecode = Fax3PreDecode; + tif->tif_decoderow = Fax3Decode; + tif->tif_decodestrip = Fax3Decode; + tif->tif_decodetile = Fax3Decode; + tif->tif_preencode = Fax3PreEncode; + tif->tif_postencode = Fax3PostEncode; + tif->tif_encoderow = Fax3Encode; + tif->tif_encodestrip = Fax3Encode; + tif->tif_encodetile = Fax3Encode; + tif->tif_close = Fax3Close; + tif->tif_cleanup = Fax3Cleanup; + tif->tif_options |= FAX3_CLASSF; /* default */ + tif->tif_flags |= TIFF_NOBITREV; /* we handle bit reversal */ + return (1); +} diff --git a/panda/src/tiff/tif_fax3.h b/panda/src/tiff/tif_fax3.h new file mode 100644 index 0000000000..1641103ad7 --- /dev/null +++ b/panda/src/tiff/tif_fax3.h @@ -0,0 +1,71 @@ +/* $Header$ */ + +/* + * Copyright (c) 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _FAX3_ +#define _FAX3_ +/* + * CCITT Group 3 compression/decompression definitions. + */ +#define FAX3_CLASSF TIFF_OPT0 /* use Class F protocol */ +/* the following are for use by Compression=2, 32771, and 4 (T.6) algorithms */ +#define FAX3_NOEOL TIFF_OPT1 /* no EOL code at end of row */ +#define FAX3_BYTEALIGN TIFF_OPT2 /* force byte alignment at end of row */ +#define FAX3_WORDALIGN TIFF_OPT3 /* force word alignment at end of row */ + +/* + * Compression+decompression state blocks are + * derived from this ``base state'' block. + */ +typedef struct { + short data; /* current i/o byte */ + short bit; /* current i/o bit in byte */ + short white; /* value of the color ``white'' */ + u_long rowbytes; /* XXX maybe should be a long? */ + u_long rowpixels; /* XXX maybe should be a long? */ + enum { /* decoding/encoding mode */ + G3_1D, /* basic 1-d mode */ + G3_2D /* optional 2-d mode */ + } tag; + const u_char *bitmap; /* bit reversal table */ + u_char *refline; /* reference line for 2d decoding */ +} Fax3BaseState; + +/* these routines are used by Group 4 (T.6) */ +extern int Fax3Decode2DRow(TIFF*, u_char*, int); +extern int Fax3Encode2DRow(TIFF*, u_char*, u_char*, int); +extern void Fax3PutBits(TIFF*, u_int, u_int); +extern void Fax3PutEOL(TIFF*); +extern int TIFFInitCCITTFax3(TIFF*); + +#define Fax3FlushBits(tif, sp) { \ + if ((tif)->tif_rawcc >= (tif)->tif_rawdatasize) \ + (void) TIFFFlushData1(tif); \ + *(tif)->tif_rawcp++ = (sp)->bitmap[(sp)->data]; \ + (tif)->tif_rawcc++; \ + (sp)->data = 0; \ + (sp)->bit = 8; \ +} +#endif /* _FAX3_ */ diff --git a/panda/src/tiff/tif_fax4.c b/panda/src/tiff/tif_fax4.c new file mode 100644 index 0000000000..284ca29b02 --- /dev/null +++ b/panda/src/tiff/tif_fax4.c @@ -0,0 +1,114 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * CCITT Group 4 Facsimile-compatible + * Compression Scheme Support. + */ +#include "tiffiop.h" +#include "tif_fax3.h" +#include "t4.h" + +/* + * Decode the requested amount of data. + */ +static int +Fax4Decode(TIFF* tif, tidata_t buf, tsize_t occ, tsample_t s) +{ + Fax3BaseState *sp = (Fax3BaseState *)tif->tif_data; + int status; + + memset(buf, 0, occ); /* decoding only sets non-zero bits */ + while ((long)occ > 0) { + status = Fax3Decode2DRow(tif, buf, sp->rowpixels); + if (status < 0) + return (status == G3CODE_EOF); + memcpy(sp->refline, buf, sp->rowbytes); + buf += sp->rowbytes; + occ -= sp->rowbytes; + if (occ != 0) + tif->tif_row++; + } + return (1); +} + +/* + * Encode the requested amount of data. + */ +static int +Fax4Encode(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s) +{ + Fax3BaseState *sp = (Fax3BaseState *)tif->tif_data; + + while ((long)cc > 0) { + if (!Fax3Encode2DRow(tif, bp, sp->refline, sp->rowpixels)) + return (0); + memcpy(sp->refline, bp, sp->rowbytes); + bp += sp->rowbytes; + cc -= sp->rowbytes; + if (cc != 0) + tif->tif_row++; + } + return (1); +} + +static +Fax4PostEncode(TIFF* tif) +{ + Fax3BaseState *sp = (Fax3BaseState *)tif->tif_data; + + /* terminate strip w/ EOFB */ + Fax3PutBits(tif, EOL, 12); + Fax3PutBits(tif, EOL, 12); + if (sp->bit != 8) + Fax3FlushBits(tif, sp); + return (1); +} + +int +TIFFInitCCITTFax4(TIFF* tif) +{ + TIFFInitCCITTFax3(tif); /* reuse G3 compression */ + tif->tif_decoderow = Fax4Decode; + tif->tif_decodestrip = Fax4Decode; + tif->tif_decodetile = Fax4Decode; + tif->tif_encoderow = Fax4Encode; + tif->tif_encodestrip = Fax4Encode; + tif->tif_encodetile = Fax4Encode; + tif->tif_postencode = Fax4PostEncode; + /* + * FAX3_NOEOL causes the regular G3 decompression + * code to not skip to the EOL mark at the end of + * a row (during normal decoding). FAX3_CLASSF + * suppresses RTC generation at the end of an image. + */ + tif->tif_options = FAX3_NOEOL|FAX3_CLASSF; + return (1); +} diff --git a/panda/src/tiff/tif_flush.c b/panda/src/tiff/tif_flush.c new file mode 100644 index 0000000000..37fd034436 --- /dev/null +++ b/panda/src/tiff/tif_flush.c @@ -0,0 +1,62 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + */ +#include "tiffiop.h" + +int +TIFFFlush(TIFF* tif) +{ + + if (tif->tif_mode != O_RDONLY) { + if (!TIFFFlushData(tif)) + return (0); + if ((tif->tif_flags & TIFF_DIRTYDIRECT) && + !TIFFWriteDirectory(tif)) + return (0); + } + return (1); +} + +/* + * Flush buffered data to the file. + */ +int +TIFFFlushData(TIFF* tif) +{ + if ((tif->tif_flags & TIFF_BEENWRITING) == 0) + return (0); + if (tif->tif_flags & TIFF_POSTENCODE) { + tif->tif_flags &= ~TIFF_POSTENCODE; + if (tif->tif_postencode && !(*tif->tif_postencode)(tif)) + return (0); + } + return (TIFFFlushData1(tif)); +} diff --git a/panda/src/tiff/tif_getimage.c b/panda/src/tiff/tif_getimage.c new file mode 100644 index 0000000000..3b77af6d37 --- /dev/null +++ b/panda/src/tiff/tif_getimage.c @@ -0,0 +1,1161 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library + * + * Read and return a packed RGBA image. + */ +#include "tiffiop.h" + +typedef u_char RGBvalue; + +static u_short bitspersample; +static u_short samplesperpixel; +static u_short photometric; +static u_short orientation; +static u_short extrasamples; +static u_short planarconfig; +/* colormap for pallete images */ +static u_short *redcmap, *greencmap, *bluecmap; +static int stoponerr; /* stop on read error */ +/* YCbCr support */ +static u_short YCbCrHorizSampling; +static u_short YCbCrVertSampling; +static float *YCbCrCoeffs; +static float *refBlackWhite; + +static u_long **BWmap; +static u_long **PALmap; + +static int gt(TIFF*, int, int, u_long*); +static int gtTileContig(TIFF*, u_long*, RGBvalue*, u_long, u_long); +static int gtTileSeparate(TIFF*, u_long*, RGBvalue*, u_long, u_long); +static int gtStripContig(TIFF*, u_long*, RGBvalue*, u_long, u_long); +static int gtStripSeparate(TIFF*, u_long*, RGBvalue*, u_long, u_long); +static int makebwmap(TIFF*, RGBvalue*); +static int makecmap(TIFF*, u_short*, u_short*, u_short*); +static void initYCbCrConversion(void); + +int +TIFFReadRGBAImage(TIFF* tif, + u_long rwidth, u_long rheight, u_long* raster, int stop) +{ + int ok, alpha; + u_long width, height; + u_short *sampleinfo; + + TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample); + switch (bitspersample) { + case 1: case 2: case 4: + case 8: case 16: + break; + default: + TIFFError(TIFFFileName(tif), + "Sorry, can not handle %d-bit images", bitspersample); + return (0); + } + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel); + if (samplesperpixel > 4) { + TIFFError(TIFFFileName(tif), + "Sorry, can not handle images with %d-samples/pixel", + samplesperpixel); + return (0); + } + TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES, + &extrasamples, &sampleinfo); + alpha = (extrasamples == 1 && sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA); + TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planarconfig); + switch (samplesperpixel - extrasamples) { + case 3: + break; + case 1: case 4: +/* XXX */ + if (!alpha || planarconfig != PLANARCONFIG_CONTIG) + break; + /* fall thru... */ + default: + TIFFError(TIFFFileName(tif), + "Sorry, can not handle %d-channel %s images%s", + samplesperpixel, + planarconfig == PLANARCONFIG_CONTIG ? + "packed" : "separated", + alpha ? " with alpha" : ""); + return (0); + } + if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric)) { + switch (samplesperpixel) { + case 1: + photometric = PHOTOMETRIC_MINISBLACK; + break; + case 3: case 4: + photometric = PHOTOMETRIC_RGB; + break; + default: + TIFFError(TIFFFileName(tif), + "Missing needed \"PhotometricInterpretation\" tag"); + return (0); + } + TIFFError(TIFFFileName(tif), + "No \"PhotometricInterpretation\" tag, assuming %s\n", + photometric == PHOTOMETRIC_RGB ? "RGB" : "min-is-black"); + } + switch (photometric) { + case PHOTOMETRIC_MINISWHITE: + case PHOTOMETRIC_MINISBLACK: + case PHOTOMETRIC_RGB: + case PHOTOMETRIC_PALETTE: + case PHOTOMETRIC_YCBCR: + break; + case PHOTOMETRIC_SEPARATED: { + u_short inkset; + TIFFGetFieldDefaulted(tif, TIFFTAG_INKSET, &inkset); + if (inkset != INKSET_CMYK) { + TIFFError(TIFFFileName(tif), + "Sorry, can not handle separated image with %s=%d", + "InkSet", inkset); + return (0); + } + break; + } + default: + TIFFError(TIFFFileName(tif), + "Sorry, can not handle image with %s=%d", + "PhotometricInterpretation", photometric); + return (0); + } + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width); + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height); + /* XXX verify rwidth and rheight against width and height */ + stoponerr = stop; + BWmap = NULL; + PALmap = NULL; + ok = gt(tif, rwidth, height, raster + (rheight-height)*rwidth); + if (BWmap) + _TIFFfree((char *)BWmap); + if (PALmap) + _TIFFfree((char *)PALmap); + return (ok); +} + +static int +checkcmap(int n, u_short* r, u_short* g, u_short* b) +{ + while (n-- > 0) + if (*r++ >= 256 || *g++ >= 256 || *b++ >= 256) + return (16); + return (8); +} + +/* + * Construct a mapping table to convert from the range + * of the data samples to [0,255] --for display. This + * process also handles inverting B&W images when needed. + */ +static int +setupMap(TIFF* tif, + u_short minsamplevalue, u_short maxsamplevalue, RGBvalue** pMap) +{ + register int x, range; + RGBvalue *Map; + + range = maxsamplevalue - minsamplevalue; + Map = (RGBvalue *)_TIFFmalloc((range + 1) * sizeof (RGBvalue)); + if (Map == NULL) { + TIFFError(TIFFFileName(tif), + "No space for photometric conversion table"); + return (0); + } + if (photometric == PHOTOMETRIC_MINISWHITE) { + for (x = 0; x <= range; x++) + Map[x] = ((range - x) * 255) / range; + } else { + for (x = 0; x <= range; x++) + Map[x] = (x * 255) / range; + } + if (bitspersample <= 8 && + (photometric == PHOTOMETRIC_MINISBLACK || + photometric == PHOTOMETRIC_MINISWHITE)) { + /* + * Use photometric mapping table to construct + * unpacking tables for samples <= 8 bits. + */ + if (!makebwmap(tif, Map)) + return (0); + /* no longer need Map, free it */ + _TIFFfree((char *)Map); + Map = NULL; + } + *pMap = Map; + return (1); +} + +static int +gt(TIFF* tif, int w, int h, u_long* raster) +{ + u_short minsamplevalue, maxsamplevalue; + RGBvalue *Map; + int e, ncomps; + + TIFFGetFieldDefaulted(tif, TIFFTAG_MINSAMPLEVALUE, &minsamplevalue); + TIFFGetFieldDefaulted(tif, TIFFTAG_MAXSAMPLEVALUE, &maxsamplevalue); + Map = NULL; + switch (photometric) { + case PHOTOMETRIC_YCBCR: + TIFFGetFieldDefaulted(tif, TIFFTAG_YCBCRCOEFFICIENTS, + &YCbCrCoeffs); + TIFFGetFieldDefaulted(tif, TIFFTAG_YCBCRSUBSAMPLING, + &YCbCrHorizSampling, &YCbCrVertSampling); + TIFFGetFieldDefaulted(tif, TIFFTAG_REFERENCEBLACKWHITE, + &refBlackWhite); + initYCbCrConversion(); + /* fall thru... */ + case PHOTOMETRIC_RGB: + case PHOTOMETRIC_SEPARATED: + if (minsamplevalue == 0 && maxsamplevalue == 255) + break; + /* fall thru... */ + case PHOTOMETRIC_MINISBLACK: + case PHOTOMETRIC_MINISWHITE: + if (!setupMap(tif, minsamplevalue, maxsamplevalue, &Map)) + return (0); + break; + case PHOTOMETRIC_PALETTE: + if (!TIFFGetField(tif, TIFFTAG_COLORMAP, + &redcmap, &greencmap, &bluecmap)) { + TIFFError(TIFFFileName(tif), + "Missing required \"Colormap\" tag"); + return (0); + } + /* + * Convert 16-bit colormap to 8-bit (unless it looks + * like an old-style 8-bit colormap). + */ + if (checkcmap(1< 0; i--) { +#define CVT(x) (((x) * 255) / ((1L<<16)-1)) + redcmap[i] = CVT(redcmap[i]); + greencmap[i] = CVT(greencmap[i]); + bluecmap[i] = CVT(bluecmap[i]); + } + } else + TIFFWarning(TIFFFileName(tif), "Assuming 8-bit colormap"); + if (bitspersample <= 8) { + /* + * Use mapping table and colormap to construct + * unpacking tables for samples < 8 bits. + */ + if (!makecmap(tif, redcmap, greencmap, bluecmap)) + return (0); + } + break; + } + ncomps = samplesperpixel - extrasamples; + if (planarconfig == PLANARCONFIG_SEPARATE && ncomps > 1) { + e = TIFFIsTiled(tif) ? + gtTileSeparate(tif, raster, Map, h, w) : + gtStripSeparate(tif, raster, Map, h, w); + } else { + e = TIFFIsTiled(tif) ? + gtTileContig(tif, raster, Map, h, w) : + gtStripContig(tif, raster, Map, h, w); + } + if (Map) + _TIFFfree((char *)Map); + return (e); +} + +static u_long +setorientation(TIFF* tif, u_long h) +{ + u_long y; + + TIFFGetFieldDefaulted(tif, TIFFTAG_ORIENTATION, &orientation); + switch (orientation) { + case ORIENTATION_BOTRIGHT: + case ORIENTATION_RIGHTBOT: /* XXX */ + case ORIENTATION_LEFTBOT: /* XXX */ + TIFFWarning(TIFFFileName(tif), "using bottom-left orientation"); + orientation = ORIENTATION_BOTLEFT; + /* fall thru... */ + case ORIENTATION_BOTLEFT: + y = 0; + break; + case ORIENTATION_TOPRIGHT: + case ORIENTATION_RIGHTTOP: /* XXX */ + case ORIENTATION_LEFTTOP: /* XXX */ + default: + TIFFWarning(TIFFFileName(tif), "using top-left orientation"); + orientation = ORIENTATION_TOPLEFT; + /* fall thru... */ + case ORIENTATION_TOPLEFT: + y = h-1; + break; + } + return (y); +} + +typedef void (*tileContigRoutine) + (u_long*, u_char*, RGBvalue*, u_long, u_long, int, int); +static tileContigRoutine pickTileContigCase(TIFF*, RGBvalue*); + +/* + * Get an tile-organized image that has + * PlanarConfiguration contiguous if SamplesPerPixel > 1 + * or + * SamplesPerPixel == 1 + */ +static int +gtTileContig(TIFF* tif, u_long* raster, RGBvalue* Map, u_long h, u_long w) +{ + u_long col, row, y; + u_long tw, th; + u_char *buf; + int fromskew, toskew; + u_int nrow; + tileContigRoutine put; + + put = pickTileContigCase(tif, Map); + if (put == 0) + return (0); + buf = (u_char *)_TIFFmalloc(TIFFTileSize(tif)); + if (buf == 0) { + TIFFError(TIFFFileName(tif), "No space for tile buffer"); + return (0); + } + TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw); + TIFFGetField(tif, TIFFTAG_TILELENGTH, &th); + y = setorientation(tif, h); + toskew = (orientation == ORIENTATION_TOPLEFT ? -tw + -w : -tw + w); + for (row = 0; row < h; row += th) { + nrow = (row + th > h ? h - row : th); + for (col = 0; col < w; col += tw) { + if (TIFFReadTile(tif, buf, col, row, 0, 0) < 0 && + stoponerr) + break; + if (col + tw > w) { + /* + * Tile is clipped horizontally. Calculate + * visible portion and skewing factors. + */ + u_long npix = w - col; + fromskew = tw - npix; + (*put)(raster + y*w + col, buf, Map, + npix, nrow, fromskew, toskew + fromskew); + } else + (*put)(raster + y*w + col, buf, Map, + tw, nrow, 0, toskew); + } + y += (orientation == ORIENTATION_TOPLEFT ? -nrow : nrow); + } + _TIFFfree(buf); + return (1); +} + +typedef void (*tileSeparateRoutine) + (u_long*, u_char*, u_char*, u_char*, RGBvalue*, u_long, u_long, int, int); +static tileSeparateRoutine pickTileSeparateCase(TIFF*, RGBvalue*); + +/* + * Get an tile-organized image that has + * SamplesPerPixel > 1 + * PlanarConfiguration separated + * We assume that all such images are RGB. + */ +static int +gtTileSeparate(TIFF* tif, u_long* raster, RGBvalue* Map, u_long h, u_long w) +{ + u_long col, row, y; + u_long tw, th; + u_char *buf; + u_char *r, *g, *b; + u_long tilesize; + int fromskew, toskew; + u_int nrow; + tileSeparateRoutine put; + + put = pickTileSeparateCase(tif, Map); + if (put == 0) + return (0); + tilesize = TIFFTileSize(tif); + buf = (u_char *)_TIFFmalloc(3*tilesize); + if (buf == 0) { + TIFFError(TIFFFileName(tif), "No space for tile buffer"); + return (0); + } + r = buf; + g = r + tilesize; + b = g + tilesize; + TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw); + TIFFGetField(tif, TIFFTAG_TILELENGTH, &th); + y = setorientation(tif, h); + toskew = (orientation == ORIENTATION_TOPLEFT ? -tw + -w : -tw + w); + for (row = 0; row < h; row += th) { + nrow = (row + th > h ? h - row : th); + for (col = 0; col < w; col += tw) { + if (TIFFReadTile(tif, r, col, row,0,0) < 0 && stoponerr) + break; + if (TIFFReadTile(tif, g, col, row,0,1) < 0 && stoponerr) + break; + if (TIFFReadTile(tif, b, col, row,0,2) < 0 && stoponerr) + break; + if (col + tw > w) { + /* + * Tile is clipped horizontally. Calculate + * visible portion and skewing factors. + */ + u_long npix = w - col; + fromskew = tw - npix; + (*put)(raster + y*w + col, r, g, b, Map, + npix, nrow, fromskew, toskew + fromskew); + } else + (*put)(raster + y*w + col, r, g, b, Map, + tw, nrow, 0, toskew); + } + y += (orientation == ORIENTATION_TOPLEFT ? -nrow : nrow); + } + _TIFFfree(buf); + return (1); +} + +/* + * Get a strip-organized image that has + * PlanarConfiguration contiguous if SamplesPerPixel > 1 + * or + * SamplesPerPixel == 1 + */ +static int +gtStripContig(TIFF* tif, u_long* raster, RGBvalue* Map, u_long h, u_long w) +{ + u_long row, y, nrow; + u_char *buf; + tileContigRoutine put; + u_long rowsperstrip; + u_long imagewidth; + u_long scanline; + int fromskew, toskew; + + put = pickTileContigCase(tif, Map); + if (put == 0) + return (0); + buf = (u_char *)_TIFFmalloc(TIFFStripSize(tif)); + if (buf == 0) { + TIFFError(TIFFFileName(tif), "No space for strip buffer"); + return (0); + } + y = setorientation(tif, h); + toskew = (orientation == ORIENTATION_TOPLEFT ? -w + -w : -w + w); + TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip); + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &imagewidth); + scanline = TIFFScanlineSize(tif); + fromskew = (w < imagewidth ? imagewidth - w : 0); + for (row = 0; row < h; row += rowsperstrip) { + nrow = (row + rowsperstrip > h ? h - row : rowsperstrip); + if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, row, 0), + buf, nrow*scanline) < 0 && stoponerr) + break; + (*put)(raster + y*w, buf, Map, w, nrow, fromskew, toskew); + y += (orientation == ORIENTATION_TOPLEFT ? -nrow : nrow); + } + _TIFFfree(buf); + return (1); +} + +/* + * Get a strip-organized image with + * SamplesPerPixel > 1 + * PlanarConfiguration separated + * We assume that all such images are RGB. + */ +static int +gtStripSeparate(TIFF* tif, u_long* raster, RGBvalue* Map, u_long h, u_long w) +{ + u_char *buf; + u_char *r, *g, *b; + u_long row, y, nrow; + u_long scanline; + tileSeparateRoutine put; + u_long rowsperstrip; + u_long imagewidth; + u_long stripsize; + int fromskew, toskew; + + stripsize = TIFFStripSize(tif); + r = buf = (u_char *)_TIFFmalloc(3*stripsize); + if (buf == 0) + return (0); + g = r + stripsize; + b = g + stripsize; + put = pickTileSeparateCase(tif, Map); + if (put == 0) { + TIFFError(TIFFFileName(tif), "Can not handle format"); + return (0); + } + y = setorientation(tif, h); + toskew = (orientation == ORIENTATION_TOPLEFT ? -w + -w : -w + w); + TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip); + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &imagewidth); + scanline = TIFFScanlineSize(tif); + fromskew = (w < imagewidth ? imagewidth - w : 0); + for (row = 0; row < h; row += rowsperstrip) { + nrow = (row + rowsperstrip > h ? h - row : rowsperstrip); + if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, row, 0), + r, nrow*scanline) < 0 && stoponerr) + break; + if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, row, 1), + g, nrow*scanline) < 0 && stoponerr) + break; + if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, row, 2), + b, nrow*scanline) < 0 && stoponerr) + break; + (*put)(raster + y*w, r, g, b, Map, w, nrow, fromskew, toskew); + y += (orientation == ORIENTATION_TOPLEFT ? -nrow : nrow); + } + _TIFFfree(buf); + return (1); +} + +#define PACK(r,g,b) ((u_long)(r)|((u_long)(g)<<8)|((u_long)(b)<<16)) + +/* + * Greyscale images with less than 8 bits/sample are handled + * with a table to avoid lots of shifts and masks. The table + * is setup so that put*bwtile (below) can retrieve 8/bitspersample + * pixel values simply by indexing into the table with one + * number. + */ +static int +makebwmap(TIFF* tif, RGBvalue* Map) +{ + register int i; + int nsamples = 8 / bitspersample; + register u_long *p; + + BWmap = (u_long **)_TIFFmalloc( + 256*sizeof (u_long *)+(256*nsamples*sizeof(u_long))); + if (BWmap == NULL) { + TIFFError(TIFFFileName(tif), "No space for B&W mapping table"); + return (0); + } + p = (u_long *)(BWmap + 256); + for (i = 0; i < 256; i++) { + BWmap[i] = p; + switch (bitspersample) { + register RGBvalue c; +#define GREY(x) c = Map[x]; *p++ = PACK(c,c,c); + case 1: + GREY(i>>7); + GREY((i>>6)&1); + GREY((i>>5)&1); + GREY((i>>4)&1); + GREY((i>>3)&1); + GREY((i>>2)&1); + GREY((i>>1)&1); + GREY(i&1); + break; + case 2: + GREY(i>>6); + GREY((i>>4)&3); + GREY((i>>2)&3); + GREY(i&3); + break; + case 4: + GREY(i>>4); + GREY(i&0xf); + break; + case 8: + GREY(i); + break; + } +#undef GREY + } + return (1); +} + +/* + * Palette images with <= 8 bits/sample are handled + * with a table to avoid lots of shifts and masks. The table + * is setup so that put*cmaptile (below) can retrieve 8/bitspersample + * pixel values simply by indexing into the table with one + * number. + */ +static int +makecmap(TIFF* tif, u_short* rmap, u_short* gmap, u_short* bmap) +{ + register int i; + int nsamples = 8 / bitspersample; + register u_long *p; + + PALmap = (u_long **)_TIFFmalloc( + 256*sizeof (u_long *)+(256*nsamples*sizeof(u_long))); + if (PALmap == NULL) { + TIFFError(TIFFFileName(tif), "No space for Palette mapping table"); + return (0); + } + p = (u_long *)(PALmap + 256); + for (i = 0; i < 256; i++) { + PALmap[i] = p; +#define CMAP(x) \ +c = x; *p++ = PACK(rmap[c]&0xff, gmap[c]&0xff, bmap[c]&0xff); + switch (bitspersample) { + register RGBvalue c; + case 1: + CMAP(i>>7); + CMAP((i>>6)&1); + CMAP((i>>5)&1); + CMAP((i>>4)&1); + CMAP((i>>3)&1); + CMAP((i>>2)&1); + CMAP((i>>1)&1); + CMAP(i&1); + break; + case 2: + CMAP(i>>6); + CMAP((i>>4)&3); + CMAP((i>>2)&3); + CMAP(i&3); + break; + case 4: + CMAP(i>>4); + CMAP(i&0xf); + break; + case 8: + CMAP(i); + break; + } +#undef CMAP + } + return (1); +} + +/* + * The following routines move decoded data returned + * from the TIFF library into rasters filled with packed + * ABGR pixels (i.e. suitable for passing to lrecwrite.) + * + * The routines have been created according to the most + * important cases and optimized. pickTileContigCase and + * pickTileSeparateCase analyze the parameters and select + * the appropriate "put" routine to use. + */ +#define REPEAT8(op) REPEAT4(op); REPEAT4(op) +#define REPEAT4(op) REPEAT2(op); REPEAT2(op) +#define REPEAT2(op) op; op +#define CASE8(x,op) \ + switch (x) { \ + case 7: op; case 6: op; case 5: op; \ + case 4: op; case 3: op; case 2: op; \ + case 1: op; \ + } +#define CASE4(x,op) switch (x) { case 3: op; case 2: op; case 1: op; } + +#define UNROLL8(w, op1, op2) { \ + register u_long x; \ + for (x = w; x >= 8; x -= 8) { \ + op1; \ + REPEAT8(op2); \ + } \ + if (x > 0) { \ + op1; \ + CASE8(x,op2); \ + } \ +} +#define UNROLL4(w, op1, op2) { \ + register u_long x; \ + for (x = w; x >= 4; x -= 4) { \ + op1; \ + REPEAT4(op2); \ + } \ + if (x > 0) { \ + op1; \ + CASE4(x,op2); \ + } \ +} +#define UNROLL2(w, op1, op2) { \ + register u_long x; \ + for (x = w; x >= 2; x -= 2) { \ + op1; \ + REPEAT2(op2); \ + } \ + if (x) { \ + op1; \ + op2; \ + } \ +} + + +#define SKEW(r,g,b,skew) { r += skew; g += skew; b += skew; } + +#define DECLAREContigPutFunc(name) \ +static void name(\ + u_long* cp, \ + u_char* pp, \ + RGBvalue* Map, \ + u_long w, u_long h, \ + int fromskew, int toskew \ +) + +/* + * 8-bit palette => colormap/RGB + */ +DECLAREContigPutFunc(put8bitcmaptile) +{ + while (h-- > 0) { + UNROLL8(w, NULL, *cp++ = PALmap[*pp++][0]); + cp += toskew; + pp += fromskew; + } +} + +/* + * 4-bit palette => colormap/RGB + */ +DECLAREContigPutFunc(put4bitcmaptile) +{ + register u_long *bw; + + fromskew /= 2; + while (h-- > 0) { + UNROLL2(w, bw = PALmap[*pp++], *cp++ = *bw++); + cp += toskew; + pp += fromskew; + } +} + +/* + * 2-bit palette => colormap/RGB + */ +DECLAREContigPutFunc(put2bitcmaptile) +{ + register u_long *bw; + + fromskew /= 4; + while (h-- > 0) { + UNROLL4(w, bw = PALmap[*pp++], *cp++ = *bw++); + cp += toskew; + pp += fromskew; + } +} + +/* + * 1-bit palette => colormap/RGB + */ +DECLAREContigPutFunc(put1bitcmaptile) +{ + register u_long *bw; + + fromskew /= 8; + while (h-- > 0) { + UNROLL8(w, bw = PALmap[*pp++], *cp++ = *bw++); + cp += toskew; + pp += fromskew; + } +} + +/* + * 8-bit greyscale => colormap/RGB + */ +DECLAREContigPutFunc(putgreytile) +{ + while (h-- > 0) { + register u_long x; + for (x = w; x-- > 0;) + *cp++ = BWmap[*pp++][0]; + cp += toskew; + pp += fromskew; + } +} + +/* + * 1-bit bilevel => colormap/RGB + */ +DECLAREContigPutFunc(put1bitbwtile) +{ + register u_long *bw; + + fromskew /= 8; + while (h-- > 0) { + UNROLL8(w, bw = BWmap[*pp++], *cp++ = *bw++); + cp += toskew; + pp += fromskew; + } +} + +/* + * 2-bit greyscale => colormap/RGB + */ +DECLAREContigPutFunc(put2bitbwtile) +{ + register u_long *bw; + + fromskew /= 4; + while (h-- > 0) { + UNROLL4(w, bw = BWmap[*pp++], *cp++ = *bw++); + cp += toskew; + pp += fromskew; + } +} + +/* + * 4-bit greyscale => colormap/RGB + */ +DECLAREContigPutFunc(put4bitbwtile) +{ + register u_long *bw; + + fromskew /= 2; + while (h-- > 0) { + UNROLL2(w, bw = BWmap[*pp++], *cp++ = *bw++); + cp += toskew; + pp += fromskew; + } +} + +/* + * 8-bit packed samples => RGB + */ +DECLAREContigPutFunc(putRGBcontig8bittile) +{ + fromskew *= samplesperpixel; + if (Map) { + while (h-- > 0) { + register u_long x; + for (x = w; x-- > 0;) { + *cp++ = PACK(Map[pp[0]], Map[pp[1]], Map[pp[2]]); + pp += samplesperpixel; + } + pp += fromskew; + cp += toskew; + } + } else { + while (h-- > 0) { + UNROLL8(w, NULL, + *cp++ = PACK(pp[0], pp[1], pp[2]); + pp += samplesperpixel); + cp += toskew; + pp += fromskew; + } + } +} + +/* + * 16-bit packed samples => RGB + */ +DECLAREContigPutFunc(putRGBcontig16bittile) +{ + register u_short *wp = (u_short *)pp; + register u_int x; + + fromskew *= samplesperpixel; + if (Map) { + while (h-- > 0) { + for (x = w; x-- > 0;) { + *cp++ = PACK(Map[wp[0]], Map[wp[1]], Map[wp[2]]); + wp += samplesperpixel; + } + cp += toskew; + wp += fromskew; + } + } else { + while (h-- > 0) { + for (x = w; x-- > 0;) { + *cp++ = PACK(wp[0], wp[1], wp[2]); + wp += samplesperpixel; + } + cp += toskew; + wp += fromskew; + } + } +} + +/* + * 8-bit packed CMYK samples => RGB + * + * NB: The conversion of CMYK->RGB is *very* crude. + */ +DECLAREContigPutFunc(putRGBcontig8bitCMYKtile) +{ + u_short r, g, b, k; + + fromskew *= samplesperpixel; + if (Map) { + while (h-- > 0) { + register u_long x; + for (x = w; x-- > 0;) { + k = 255 - pp[3]; + r = (k*(255-pp[0]))/255; + g = (k*(255-pp[1]))/255; + b = (k*(255-pp[2]))/255; + *cp++ = PACK(Map[r], Map[g], Map[b]); + pp += samplesperpixel; + } + pp += fromskew; + cp += toskew; + } + } else { + while (h-- > 0) { + UNROLL8(w, NULL, + k = 255 - pp[3]; + r = (k*(255-pp[0]))/255; + g = (k*(255-pp[1]))/255; + b = (k*(255-pp[2]))/255; + *cp++ = PACK(r, g, b); + pp += samplesperpixel); + cp += toskew; + pp += fromskew; + } + } +} + +#define DECLARESepPutFunc(name) \ +static void name(\ + u_long* cp, \ + u_char* r, u_char* g, u_char* b, \ + RGBvalue* Map, \ + u_long w, u_long h, \ + int fromskew, int toskew \ +) + +/* + * 8-bit unpacked samples => RGB + */ +DECLARESepPutFunc(putRGBseparate8bittile) +{ + if (Map) { + while (h-- > 0) { + register u_long x; + for (x = w; x > 0; x--) + *cp++ = PACK(Map[*r++], Map[*g++], Map[*b++]); + SKEW(r, g, b, fromskew); + cp += toskew; + } + } else { + while (h-- > 0) { + UNROLL8(w, NULL, *cp++ = PACK(*r++, *g++, *b++)); + SKEW(r, g, b, fromskew); + cp += toskew; + } + } +} + +/* + * 16-bit unpacked samples => RGB + */ +DECLARESepPutFunc(putRGBseparate16bittile) +{ + register u_short *wr = (u_short *)r; + register u_short *wg = (u_short *)g; + register u_short *wb = (u_short *)b; + register u_long x; + + if (Map) { + while (h-- > 0) { + for (x = w; x > 0; x--) + *cp++ = PACK(Map[*wr++],Map[*wg++],Map[*wb++]); + SKEW(wr, wg, wb, fromskew); + cp += toskew; + } + } else { + while (h-- > 0) { + for (x = 0; x < w; x++) + *cp++ = PACK(*wr++, *wg++, *wb++); + SKEW(wr, wg, wb, fromskew); + cp += toskew; + } + } +} + +#define Code2V(c, RB, RW, CR) ((((c)-RB)*(float)CR)/(float)(RW-RB)) +#define CLAMP(f,min,max) \ + (int)((f)+.5 < (min) ? (min) : (f)+.5 > (max) ? (max) : (f)+.5) + +#define LumaRed YCbCrCoeffs[0] +#define LumaGreen YCbCrCoeffs[1] +#define LumaBlue YCbCrCoeffs[2] + +static float D1, D2; +static float D3, D4; + +static void +initYCbCrConversion(void) +{ + D1 = 2 - 2*LumaRed; + D2 = D1*LumaRed / LumaGreen; + D3 = 2 - 2*LumaBlue; + D4 = D3*LumaBlue / LumaGreen; +} + +static void +putRGBContigYCbCrClump( + register u_long* cp, register u_char* pp, + int cw, int ch, + u_long w, + int n, int fromskew, int toskew +) +{ + float Cb, Cr; + int j, k; + + Cb = Code2V(pp[n], refBlackWhite[2], refBlackWhite[3], 127); + Cr = Code2V(pp[n+1], refBlackWhite[4], refBlackWhite[5], 127); + for (j = 0; j < ch; j++) { + for (k = 0; k < cw; k++) { + float Y, R, G, B; + Y = Code2V(*pp++, + refBlackWhite[0], refBlackWhite[1], 255); + R = Y + Cr*D1; + B = Y + Cb*D3; + G = Y - Cb*D4 - Cr*D2; + cp[k] = PACK(CLAMP(R,0,255), + CLAMP(G,0,255), + CLAMP(B,0,255)); + } + cp += w+toskew; + pp += fromskew; + } +} +#undef LumaBlue +#undef LumaGreen +#undef LumaRed +#undef CLAMP +#undef Code2V + +/* + * 8-bit packed YCbCr samples => RGB + */ +DECLAREContigPutFunc(putcontig8bitYCbCrtile) +{ + u_int Coff = YCbCrVertSampling * YCbCrHorizSampling; + u_long *tp; + u_long x; + + /* XXX adjust fromskew */ + while (h >= YCbCrVertSampling) { + tp = cp; + for (x = w; x >= YCbCrHorizSampling; x -= YCbCrHorizSampling) { + putRGBContigYCbCrClump(tp, pp, + YCbCrHorizSampling, YCbCrVertSampling, + w, Coff, 0, toskew); + tp += YCbCrHorizSampling; + pp += Coff+2; + } + if (x > 0) { + putRGBContigYCbCrClump(tp, pp, + x, YCbCrVertSampling, + w, Coff, YCbCrHorizSampling - x, toskew); + pp += Coff+2; + } + cp += YCbCrVertSampling*(w + toskew); + pp += fromskew; + h -= YCbCrVertSampling; + } + if (h > 0) { + tp = cp; + for (x = w; x >= YCbCrHorizSampling; x -= YCbCrHorizSampling) { + putRGBContigYCbCrClump(tp, pp, YCbCrHorizSampling, h, + w, Coff, 0, toskew); + tp += YCbCrHorizSampling; + pp += Coff+2; + } + if (x > 0) + putRGBContigYCbCrClump(tp, pp, x, h, + w, Coff, YCbCrHorizSampling - x, toskew); + } +} + +/* + * Select the appropriate conversion routine for packed data. + */ +static tileContigRoutine +pickTileContigCase(TIFF* tif, RGBvalue* Map) +{ + tileContigRoutine put = 0; + + switch (photometric) { + case PHOTOMETRIC_RGB: + if (bitspersample == 8) + put = putRGBcontig8bittile; + else + put = putRGBcontig16bittile; + break; + case PHOTOMETRIC_SEPARATED: + if (bitspersample == 8) + put = putRGBcontig8bitCMYKtile; + break; + case PHOTOMETRIC_PALETTE: + switch (bitspersample) { + case 8: put = put8bitcmaptile; break; + case 4: put = put4bitcmaptile; break; + case 2: put = put2bitcmaptile; break; + case 1: put = put1bitcmaptile; break; + } + break; + case PHOTOMETRIC_MINISWHITE: + case PHOTOMETRIC_MINISBLACK: + switch (bitspersample) { + case 8: put = putgreytile; break; + case 4: put = put4bitbwtile; break; + case 2: put = put2bitbwtile; break; + case 1: put = put1bitbwtile; break; + } + break; + case PHOTOMETRIC_YCBCR: + switch (bitspersample) { + case 8: put = putcontig8bitYCbCrtile; break; + } + break; + } + if (put == 0) + TIFFError(TIFFFileName(tif), "Can not handle format"); + return (put); +} + +/* + * Select the appropriate conversion routine for unpacked data. + * + * NB: we assume that unpacked single channel data is directed + * to the "packed routines. + */ +static tileSeparateRoutine +pickTileSeparateCase(TIFF* tif, RGBvalue* Map) +{ + tileSeparateRoutine put = 0; + + switch (photometric) { + case PHOTOMETRIC_RGB: + if (bitspersample == 8) + put = putRGBseparate8bittile; + else + put = putRGBseparate16bittile; + break; + } + if (put == 0) + TIFFError(TIFFFileName(tif), "Can not handle format"); + return (put); +} diff --git a/panda/src/tiff/tif_jpeg.c b/panda/src/tiff/tif_jpeg.c new file mode 100644 index 0000000000..ecfc774d52 --- /dev/null +++ b/panda/src/tiff/tif_jpeg.c @@ -0,0 +1,39 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Baseline JPEG Compression Algorithm Support. + */ +#include "tiffiop.h" + +TIFFInitJPEG(TIFF* tif) +{ + return (1); +} diff --git a/panda/src/tiff/tif_lzw.c b/panda/src/tiff/tif_lzw.c new file mode 100644 index 0000000000..75065320cb --- /dev/null +++ b/panda/src/tiff/tif_lzw.c @@ -0,0 +1,1231 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * Rev 5.0 Lempel-Ziv & Welch Compression Support + * + * This code is derived from the compress program whose code is + * derived from software contributed to Berkeley by James A. Woods, + * derived from original work by Spencer Thomas and Joseph Orost. + * + * The original Berkeley copyright notice appears below in its entirety. + */ +#include "tiffiop.h" +#include +#include + +/* + * NB: The 5.0 spec describes a different algorithm than Aldus + * implements. Specifically, Aldus does code length transitions + * one code earlier than should be done (for real LZW). + * Earlier versions of this library implemented the correct + * LZW algorithm, but emitted codes in a bit order opposite + * to the TIFF spec. Thus, to maintain compatibility w/ Aldus + * we interpret MSB-LSB ordered codes to be images written w/ + * old versions of this library, but otherwise adhere to the + * Aldus "off by one" algorithm. + * + * Future revisions to the TIFF spec are expected to "clarify this issue". + */ +#define LZW_COMPAT /* include backwards compatibility code */ +/* + * Each strip of data is supposed to be terminated by a CODE_EOI. + * If the following #define is included, the decoder will also + * check for end-of-strip w/o seeing this code. This makes the + * library more robust, but also slower. + */ +#define LZW_CHECKEOS /* include checks for strips w/o EOI code */ + +#define MAXCODE(n) ((1<<(n))-1) +/* + * The TIFF spec specifies that encoded bit + * strings range from 9 to 12 bits. + */ +#define BITS_MIN 9 /* start with 9 bits */ +#define BITS_MAX 12 /* max of 12 bit strings */ +/* predefined codes */ +#define CODE_CLEAR 256 /* code to clear string table */ +#define CODE_EOI 257 /* end-of-information code */ +#define CODE_FIRST 258 /* first free code entry */ +#define CODE_MAX MAXCODE(BITS_MAX) +#define HSIZE 9001 /* 91% occupancy */ +#define HSHIFT (13-8) +#ifdef LZW_COMPAT +/* NB: +1024 is for compatibility with old files */ +#define CSIZE (MAXCODE(BITS_MAX)+1024) +#else +#define CSIZE (MAXCODE(BITS_MAX)+1) +#endif + +typedef void (*predictorFunc)(char* data, u_long nbytes, int stride); + +/* + * State block for each open TIFF + * file using LZW compression/decompression. + */ +typedef struct { + predictorFunc hordiff; /* horizontal differencing method */ + u_long rowsize; /* width of tile/strip/row */ + u_short stride; /* horizontal diferencing stride */ + u_short nbits; /* # of bits/code */ + u_short maxcode; /* maximum code for lzw_nbits */ + u_short free_ent; /* next free entry in hash table */ + long nextdata; /* next bits of i/o */ + long nextbits; /* # of valid bits in lzw_nextdata */ +} LZWState; + +#define lzw_hordiff base.hordiff +#define lzw_rowsize base.rowsize +#define lzw_stride base.stride +#define lzw_nbits base.nbits +#define lzw_maxcode base.maxcode +#define lzw_free_ent base.free_ent +#define lzw_nextdata base.nextdata +#define lzw_nextbits base.nextbits + +/* + * Decoding-specific state. + */ +typedef struct code_ent { + struct code_ent *next; + u_short length; /* string len, including this token */ + u_char value; /* data value */ + u_char firstchar; /* first token of string */ +} code_t; + +typedef int (*decodeFunc)(TIFF*, tidata_t, tsize_t, tsample_t); + +typedef struct { + LZWState base; + long dec_nbitsmask; /* lzw_nbits 1 bits, right adjusted */ + long dec_restart; /* restart count */ +#ifdef LZW_CHECKEOS + long dec_bitsleft; /* available bits in raw data */ +#endif + decodeFunc dec_decode; /* regular or backwards compatible */ + code_t *dec_codep; /* current recognized code */ + code_t *dec_oldcodep; /* previously recognized code */ + code_t *dec_free_entp; /* next free entry */ + code_t *dec_maxcodep; /* max available entry */ + code_t dec_codetab[CSIZE]; +} LZWDecodeState; + +/* + * Encoding-specific state. + */ +typedef struct { + long hash; + long code; +} hash_t; + +typedef struct { + LZWState base; + int enc_oldcode; /* last code encountered */ + long enc_checkpoint; /* point at which to clear table */ +#define CHECK_GAP 10000 /* enc_ratio check interval */ + long enc_ratio; /* current compression ratio */ + long enc_incount; /* (input) data bytes encoded */ + long enc_outcount; /* encoded (output) bytes */ + tidata_t enc_rawlimit; /* bound on tif_rawdata buffer */ + hash_t enc_hashtab[HSIZE]; +} LZWEncodeState; + +static int LZWEncodePredRow(TIFF*, tidata_t, tsize_t, tsample_t); +static int LZWEncodePredTile(TIFF*, tidata_t, tsize_t, tsample_t); +static int LZWDecode(TIFF*, tidata_t, tsize_t, tsample_t); +#ifdef LZW_COMPAT +static int LZWDecodeCompat(TIFF*, tidata_t, tsize_t, tsample_t); +#endif +static int LZWDecodePredRow(TIFF*, tidata_t, tsize_t, tsample_t); +static int LZWDecodePredTile(TIFF*, tidata_t, tsize_t, tsample_t); +static void cl_hash(LZWEncodeState*); + +static int +LZWCheckPredictor(TIFF* tif, + LZWState* sp, predictorFunc pred8bit, predictorFunc pred16bit) +{ + TIFFDirectory *td = &tif->tif_dir; + + sp->hordiff = NULL; + switch (td->td_predictor) { + case 1: + break; + case 2: + sp->stride = (td->td_planarconfig == PLANARCONFIG_CONTIG ? + td->td_samplesperpixel : 1); + switch (td->td_bitspersample) { + case 8: + sp->hordiff = pred8bit; + break; + case 16: + sp->hordiff = pred16bit; + break; + default: + TIFFError(tif->tif_name, + "Horizontal differencing \"Predictor\" not supported with %d-bit samples", + td->td_bitspersample); + return (0); + } + break; + default: + TIFFError(tif->tif_name, "\"Predictor\" value %d not supported", + td->td_predictor); + return (0); + } + if (sp->hordiff != NULL) { + /* + * Calculate the scanline/tile-width size in bytes. + */ + if (isTiled(tif)) + sp->rowsize = TIFFTileRowSize(tif); + else + sp->rowsize = TIFFScanlineSize(tif); + } else + sp->rowsize = 0; + return (1); +} + +/* + * LZW Decoder. + */ + +#ifdef LZW_CHECKEOS +/* + * This check shouldn't be necessary because each + * strip is suppose to be terminated with CODE_EOI. + */ +#define NextCode(tif, sp, bp, code, get) { \ + if ((sp)->dec_bitsleft < nbits) { \ + TIFFWarning(tif->tif_name, \ + "LZWDecode: Strip %d not terminated with EOI code", \ + tif->tif_curstrip); \ + code = CODE_EOI; \ + } else { \ + get(sp, bp, code); \ + (sp)->dec_bitsleft -= nbits; \ + } \ +} +#else +#define NextCode(tif, sp, bp, code, get) get(sp, bp, code) +#endif + +#define REPEAT4(n, op) \ + switch (n) { \ + default: { int i; for (i = n-4; i > 0; i--) { op; } } \ + case 4: op; \ + case 3: op; \ + case 2: op; \ + case 1: op; \ + case 0: ; \ + } +#define XREPEAT4(n, op) \ + switch (n) { \ + default: { int i; for (i = n-4; i > 0; i--) { op; } } \ + case 2: op; \ + case 1: op; \ + case 0: ; \ + } + +static void +horAcc8(register char* cp, register u_long cc, register int stride) +{ + if (cc > stride) { + cc -= stride; + /* + * Pipeline the most common cases. + */ + if (stride == 3) { + u_int cr = cp[0]; + u_int cg = cp[1]; + u_int cb = cp[2]; + do { + cc -= 3, cp += 3; + cp[0] = (cr += cp[0]); + cp[1] = (cg += cp[1]); + cp[2] = (cb += cp[2]); + } while ((long)cc > 0); + } else if (stride == 4) { + u_int cr = cp[0]; + u_int cg = cp[1]; + u_int cb = cp[2]; + u_int ca = cp[3]; + do { + cc -= 4, cp += 4; + cp[0] = (cr += cp[0]); + cp[1] = (cg += cp[1]); + cp[2] = (cb += cp[2]); + cp[3] = (ca += cp[3]); + } while ((long)cc > 0); + } else { + do { + XREPEAT4(stride, cp[stride] += *cp; cp++) + cc -= stride; + } while ((long)cc > 0); + } + } +} + +static void +swabHorAcc16(char* cp, u_long cc, register int stride) +{ + register uint16* wp = (uint16 *)cp; + register u_long wc = cc / 2; + + if (wc > stride) { + TIFFSwabArrayOfShort(wp, wc); + wc -= stride; + do { + REPEAT4(stride, wp[stride] += wp[0]; wp++) + wc -= stride; + } while (wc > 0); + } +} + +static void +horAcc16(char* cp, u_long cc, register int stride) +{ + register uint16* wp = (uint16 *)cp; + register u_long wc = cc / 2; + + if (wc > stride) { + wc -= stride; + do { + REPEAT4(stride, wp[stride] += wp[0]; wp++) + wc -= stride; + } while (wc > 0); + } +} + +/* + * Setup state for decoding a strip. + */ +static int +LZWPreDecode(TIFF* tif) +{ + register LZWDecodeState *sp = (LZWDecodeState *)tif->tif_data; + + if (sp == NULL) { + tif->tif_data = _TIFFmalloc(sizeof (LZWDecodeState)); + if (tif->tif_data == NULL) { + TIFFError("LZWPreDecode", + "No space for LZW state block"); + return (0); + } + sp = (LZWDecodeState *)tif->tif_data; + sp->dec_decode = NULL; + if (!LZWCheckPredictor(tif, &sp->base, horAcc8, horAcc16)) + return (0); + if (sp->lzw_hordiff) { + /* + * Override default decoding method with + * one that does the predictor stuff. + */ + tif->tif_decoderow = LZWDecodePredRow; + tif->tif_decodestrip = LZWDecodePredTile; + tif->tif_decodetile = LZWDecodePredTile; + /* + * If the data is horizontally differenced + * 16-bit data that requires byte-swapping, + * then it must be byte swapped before the + * accumulation step. We do this with a + * special-purpose routine and override the + * normal post decoding logic that the library + * setup when the directory was read. + */ + if (tif->tif_flags&TIFF_SWAB) { + if (sp->lzw_hordiff == horAcc16) { + sp->lzw_hordiff = swabHorAcc16; + tif->tif_postdecode = TIFFNoPostDecode; + } /* else handle 32-bit case... */ + } + } + /* + * Pre-load the table. + */ + { int code; + for (code = 255; code >= 0; code--) { + sp->dec_codetab[code].value = code; + sp->dec_codetab[code].firstchar = code; + sp->dec_codetab[code].length = 1; + sp->dec_codetab[code].next = NULL; + } + } + } + /* + * Check for old bit-reversed codes. + */ + if (tif->tif_rawdata[0] == 0 && (tif->tif_rawdata[1] & 0x1)) { +#ifdef LZW_COMPAT + if (!sp->dec_decode) { + if (sp->lzw_hordiff == NULL) { + /* + * Override default decoding methods with + * ones that deal with the old coding. + * Otherwise the predictor versions set + * above will call the compatibility routines + * through the dec_decode method. + */ + tif->tif_decoderow = LZWDecodeCompat; + tif->tif_decodestrip = LZWDecodeCompat; + tif->tif_decodetile = LZWDecodeCompat; + } + TIFFWarning(tif->tif_name, + "Old-style LZW codes, convert file"); + } + sp->lzw_maxcode = MAXCODE(BITS_MIN); + sp->dec_decode = LZWDecodeCompat; +#else /* !LZW_COMPAT */ + if (!sp->dec_decode) { + TIFFError(tif->tif_name, + "Old-style LZW codes not supported"); + sp->dec_decode = LZWDecode; + } + return (0); +#endif/* !LZW_COMPAT */ + } else { + sp->lzw_maxcode = MAXCODE(BITS_MIN)-1; + sp->dec_decode = LZWDecode; + } + sp->lzw_nbits = BITS_MIN; + sp->lzw_nextbits = 0; + sp->lzw_nextdata = 0; + + sp->dec_restart = 0; + sp->dec_nbitsmask = MAXCODE(BITS_MIN); +#ifdef LZW_CHECKEOS + sp->dec_bitsleft = tif->tif_rawdatasize << 3; +#endif + sp->dec_free_entp = sp->dec_codetab + CODE_FIRST; + sp->dec_oldcodep = &sp->dec_codetab[-1]; + sp->dec_maxcodep = &sp->dec_codetab[sp->dec_nbitsmask-1]; + return (1); +} + +/* + * Decode a "hunk of data". + */ +#define GetNextCode(sp, bp, code) { \ + nextdata = (nextdata<<8) | *(bp)++; \ + nextbits += 8; \ + if (nextbits < nbits) { \ + nextdata = (nextdata<<8) | *(bp)++; \ + nextbits += 8; \ + } \ + code = (nextdata >> (nextbits-nbits)) & nbitsmask; \ + nextbits -= nbits; \ +} + +static void +codeLoop(TIFF* tif) +{ + TIFFError(tif->tif_name, + "LZWDecode: Bogus encoding, loop in the code table; scanline %d", + tif->tif_row); +} + +static int +LZWDecode(TIFF* tif, tidata_t op0, tsize_t occ0, tsample_t s) +{ + LZWDecodeState *sp = (LZWDecodeState *)tif->tif_data; + char *op = (char*) op0; + long occ = (long) occ0; + char *tp; + u_char *bp; + int code, nbits, nextbits, len; + long nextdata, nbitsmask; + code_t *codep, *free_entp, *maxcodep, *oldcodep; + + assert(sp != NULL); + /* + * Restart interrupted output operation. + */ + if (sp->dec_restart) { + int residue; + + codep = sp->dec_codep; + residue = codep->length - sp->dec_restart; + if (residue > occ) { + /* + * Residue from previous decode is sufficient + * to satisfy decode request. Skip to the + * start of the decoded string, place decoded + * values in the output buffer, and return. + */ + sp->dec_restart += occ; + do { + codep = codep->next; + } while (--residue > occ && codep); + if (codep) { + tp = op + occ; + do { + *--tp = codep->value; + codep = codep->next; + } while (--occ && codep); + } + return (1); + } + /* + * Residue satisfies only part of the decode request. + */ + op += residue, occ -= residue; + tp = op; + do { + int t; + --tp; + t = codep->value; + codep = codep->next; + *tp = t; + } while (--residue && codep); + sp->dec_restart = 0; + } + + bp = (u_char *)tif->tif_rawcp; + nbits = sp->lzw_nbits; + nextdata = sp->lzw_nextdata; + nextbits = sp->lzw_nextbits; + nbitsmask = sp->dec_nbitsmask; + oldcodep = sp->dec_oldcodep; + free_entp = sp->dec_free_entp; + maxcodep = sp->dec_maxcodep; + + while (occ > 0) { + NextCode(tif, sp, bp, code, GetNextCode); + if (code == CODE_EOI) + break; + if (code == CODE_CLEAR) { + free_entp = sp->dec_codetab + CODE_FIRST; + nbits = BITS_MIN; + nbitsmask = MAXCODE(BITS_MIN); + maxcodep = sp->dec_codetab + nbitsmask-1; + NextCode(tif, sp, bp, code, GetNextCode); + if (code == CODE_EOI) + break; + *op++ = code, occ--; + oldcodep = sp->dec_codetab + code; + continue; + } + codep = sp->dec_codetab + code; + + /* + * Add the new entry to the code table. + */ + assert(&sp->dec_codetab[0] <= free_entp && free_entp < &sp->dec_codetab[CSIZE]); + free_entp->next = oldcodep; + free_entp->firstchar = free_entp->next->firstchar; + free_entp->length = free_entp->next->length+1; + free_entp->value = (codep < free_entp) ? + codep->firstchar : free_entp->firstchar; + if (++free_entp > maxcodep) { + if (++nbits > BITS_MAX) /* should not happen */ + nbits = BITS_MAX; + nbitsmask = MAXCODE(nbits); + maxcodep = sp->dec_codetab + nbitsmask-1; + } + oldcodep = codep; + if (code >= 256) { + /* + * Code maps to a string, copy string + * value to output (written in reverse). + */ + if (codep->length > occ) { + /* + * String is too long for decode buffer, + * locate portion that will fit, copy to + * the decode buffer, and setup restart + * logic for the next decoding call. + */ + sp->dec_codep = codep; + do { + codep = codep->next; + } while (codep && codep->length > occ); + if (codep) { + sp->dec_restart = occ; + tp = op + occ; + do { + *--tp = codep->value; + codep = codep->next; + } while (--occ && codep); + if (codep) + codeLoop(tif); + } + break; + } + len = codep->length; + tp = op + len; + do { + int t; + --tp; + t = codep->value; + codep = codep->next; + *tp = t; + } while (codep && tp > op); + if (codep) { + codeLoop(tif); + break; + } + op += len, occ -= len; + } else + *op++ = code, occ--; + } + + tif->tif_rawcp = (tidata_t) bp; + sp->lzw_nbits = nbits; + sp->lzw_nextdata = nextdata; + sp->lzw_nextbits = nextbits; + sp->dec_nbitsmask = nbitsmask; + sp->dec_oldcodep = oldcodep; + sp->dec_free_entp = free_entp; + sp->dec_maxcodep = maxcodep; + + if (occ > 0) { + TIFFError(tif->tif_name, + "LZWDecode: Not enough data at scanline %d (short %d bytes)", + tif->tif_row, occ); + return (0); + } + return (1); +} + +#ifdef LZW_COMPAT +/* + * Decode a "hunk of data" for old images. + */ +#define GetNextCodeCompat(sp, bp, code) { \ + nextdata |= *(bp)++ << nextbits; \ + nextbits += 8; \ + if (nextbits < nbits) { \ + nextdata |= *(bp)++ << nextbits; \ + nextbits += 8; \ + } \ + code = nextdata & nbitsmask; \ + nextdata >>= nbits; \ + nextbits -= nbits; \ +} + +static int +LZWDecodeCompat(TIFF* tif, tidata_t op0, tsize_t occ0, tsample_t s) +{ + LZWDecodeState *sp = (LZWDecodeState *)tif->tif_data; + char *op = (char*) op0; + long occ = (long) occ0; + char *tp; + u_char *bp; + int code, nbits, nextbits; + long nextdata, nbitsmask; + code_t *codep, *free_entp, *maxcodep, *oldcodep; + + assert(sp != NULL); + /* + * Restart interrupted output operation. + */ + if (sp->dec_restart) { + int residue; + + codep = sp->dec_codep; + residue = codep->length - sp->dec_restart; + if (residue > occ) { + /* + * Residue from previous decode is sufficient + * to satisfy decode request. Skip to the + * start of the decoded string, place decoded + * values in the output buffer, and return. + */ + sp->dec_restart += occ; + do { + codep = codep->next; + } while (--residue > occ); + tp = op + occ; + do { + *--tp = codep->value; + codep = codep->next; + } while (--occ); + return (1); + } + /* + * Residue satisfies only part of the decode request. + */ + op += residue, occ -= residue; + tp = op; + do { + *--tp = codep->value; + codep = codep->next; + } while (--residue); + sp->dec_restart = 0; + } + + bp = (u_char *)tif->tif_rawcp; + nbits = sp->lzw_nbits; + nextdata = sp->lzw_nextdata; + nextbits = sp->lzw_nextbits; + nbitsmask = sp->dec_nbitsmask; + oldcodep = sp->dec_oldcodep; + free_entp = sp->dec_free_entp; + maxcodep = sp->dec_maxcodep; + + while (occ > 0) { + NextCode(tif, sp, bp, code, GetNextCodeCompat); + if (code == CODE_EOI) + break; + if (code == CODE_CLEAR) { + free_entp = sp->dec_codetab + CODE_FIRST; + nbits = BITS_MIN; + nbitsmask = MAXCODE(BITS_MIN); + maxcodep = sp->dec_codetab + nbitsmask; + NextCode(tif, sp, bp, code, GetNextCodeCompat); + if (code == CODE_EOI) + break; + *op++ = code, occ--; + oldcodep = sp->dec_codetab + code; + continue; + } + codep = sp->dec_codetab + code; + + /* + * Add the new entry to the code table. + */ + assert(&sp->dec_codetab[0] <= free_entp && free_entp < &sp->dec_codetab[CSIZE]); + free_entp->next = oldcodep; + free_entp->firstchar = free_entp->next->firstchar; + free_entp->length = free_entp->next->length+1; + free_entp->value = (codep < free_entp) ? + codep->firstchar : free_entp->firstchar; + if (++free_entp > maxcodep) { + if (++nbits > BITS_MAX) /* should not happen */ + nbits = BITS_MAX; + nbitsmask = MAXCODE(nbits); + maxcodep = sp->dec_codetab + nbitsmask; + } + oldcodep = codep; + if (code >= 256) { + /* + * Code maps to a string, copy string + * value to output (written in reverse). + */ + if (codep->length > occ) { + /* + * String is too long for decode buffer, + * locate portion that will fit, copy to + * the decode buffer, and setup restart + * logic for the next decoding call. + */ + sp->dec_codep = codep; + do { + codep = codep->next; + } while (codep->length > occ); + sp->dec_restart = occ; + tp = op + occ; + do { + *--tp = codep->value; + codep = codep->next; + } while (--occ); + break; + } + op += codep->length, occ -= codep->length; + tp = op; + do { + *--tp = codep->value; + } while (codep = codep->next); + } else + *op++ = code, occ--; + } + + tif->tif_rawcp = (tidata_t) bp; + sp->lzw_nbits = nbits; + sp->lzw_nextdata = nextdata; + sp->lzw_nextbits = nextbits; + sp->dec_nbitsmask = nbitsmask; + sp->dec_oldcodep = oldcodep; + sp->dec_free_entp = free_entp; + sp->dec_maxcodep = maxcodep; + + if (occ > 0) { + TIFFError(tif->tif_name, + "LZWDecodeCompat: Not enough data at scanline %d (short %d bytes)", + tif->tif_row, occ); + return (0); + } + return (1); +} +#endif /* LZW_COMPAT */ + +/* + * Decode a scanline and apply the predictor routine. + */ +static int +LZWDecodePredRow(TIFF* tif, tidata_t op0, tsize_t occ0, tsample_t s) +{ + LZWDecodeState *sp = (LZWDecodeState *)tif->tif_data; + + assert(sp != NULL); + assert(sp->dec_decode != NULL); + if ((*sp->dec_decode)(tif, op0, occ0, s)) { + (*sp->lzw_hordiff)((char *)op0, occ0, sp->lzw_stride); + return (1); + } else + return (0); +} + +/* + * Decode a tile/strip and apply the predictor routine. + * Note that horizontal differencing must be done on a + * row-by-row basis. The width of a "row" has already + * been calculated at pre-decode time according to the + * strip/tile dimensions. + */ +static int +LZWDecodePredTile(TIFF* tif, tidata_t op0, tsize_t occ0, tsample_t s) +{ + LZWDecodeState *sp = (LZWDecodeState *)tif->tif_data; + u_long rowsize; + + assert(sp != NULL); + assert(sp->dec_decode != NULL); + if (!(*sp->dec_decode)(tif, op0, occ0, s)) + return (0); + rowsize = sp->lzw_rowsize; + assert(rowsize > 0); + while ((long)occ0 > 0) { + (*sp->lzw_hordiff)((char*)op0, rowsize, sp->lzw_stride); + occ0 -= rowsize; + op0 += rowsize; + } + return (1); +} + +/* + * LZW Encoding. + */ + +static void +horDiff8(register char* cp, register u_long cc, register int stride) +{ + if (cc > stride) { + cc -= stride; + /* + * Pipeline the most common cases. + */ + if (stride == 3) { + int r1, g1, b1; + int r2 = cp[0]; + int g2 = cp[1]; + int b2 = cp[2]; + do { + r1 = cp[3]; cp[3] = r1-r2; r2 = r1; + g1 = cp[4]; cp[4] = g1-g2; g2 = g1; + b1 = cp[5]; cp[5] = b1-b2; b2 = b1; + cp += 3; + } while ((long)(cc -= 3) > 0); + } else if (stride == 4) { + int r1, g1, b1, a1; + int r2 = cp[0]; + int g2 = cp[1]; + int b2 = cp[2]; + int a2 = cp[3]; + do { + r1 = cp[4]; cp[4] = r1-r2; r2 = r1; + g1 = cp[5]; cp[5] = g1-g2; g2 = g1; + b1 = cp[6]; cp[6] = b1-b2; b2 = b1; + a1 = cp[7]; cp[7] = a1-a2; a2 = a1; + cp += 4; + } while ((long)(cc -= 4) > 0); + } else { + cp += cc - 1; + do { + REPEAT4(stride, cp[stride] -= cp[0]; cp--) + } while ((long)(cc -= stride) > 0); + } + } +} + +static void +horDiff16(char* cp, u_long cc, register int stride) +{ + register int16 *wp = (int16*)cp; + register u_long wc = cc/2; + + if (wc > stride) { + wc -= stride; + wp += wc - 1; + do { + REPEAT4(stride, wp[stride] -= wp[0]; wp--) + wc -= stride; + } while (wc > 0); + } +} + +/* + * Reset encoding state at the start of a strip. + */ +static int +LZWPreEncode(TIFF* tif) +{ + register LZWEncodeState *sp = (LZWEncodeState *)tif->tif_data; + + if (sp == NULL) { + tif->tif_data = _TIFFmalloc(sizeof (LZWEncodeState)); + if (tif->tif_data == NULL) { + TIFFError("LZWPreEncode", + "No space for LZW state block"); + return (0); + } + sp = (LZWEncodeState *)tif->tif_data; + if (!LZWCheckPredictor(tif, &sp->base, horDiff8, horDiff16)) + return (0); + if (sp->lzw_hordiff != NULL) { + tif->tif_encoderow = LZWEncodePredRow; + tif->tif_encodestrip = LZWEncodePredTile; + tif->tif_encodetile = LZWEncodePredTile; + } + } + sp->lzw_nbits = BITS_MIN; + sp->lzw_maxcode = MAXCODE(BITS_MIN); + sp->lzw_free_ent = CODE_FIRST; + sp->lzw_nextbits = 0; + sp->lzw_nextdata = 0; + sp->enc_checkpoint = CHECK_GAP; + sp->enc_ratio = 0; + sp->enc_incount = 0; + sp->enc_outcount = 0; + /* + * The 4 here insures there is space for 2 max-sized + * codes in LZWEncode and LZWPostDecode. + */ + sp->enc_rawlimit = tif->tif_rawdata + tif->tif_rawdatasize-1 - 4; + cl_hash(sp); /* clear hash table */ + sp->enc_oldcode = -1; /* generates CODE_CLEAR in LZWEncode */ + return (1); +} + +#define CALCRATIO(sp, rat) { \ + if (incount > 0x007fffff) { /* NB: shift will overflow */\ + rat = outcount >> 8; \ + rat = (rat == 0 ? 0x7fffffff : incount/rat); \ + } else \ + rat = (incount<<8) / outcount; \ +} +#define PutNextCode(op, c) { \ + nextdata = (nextdata << nbits) | c; \ + nextbits += nbits; \ + *op++ = nextdata >> (nextbits-8); \ + nextbits -= 8; \ + if (nextbits >= 8) { \ + *op++ = nextdata >> (nextbits-8); \ + nextbits -= 8; \ + } \ + outcount += nbits; \ +} + +/* + * Encode a chunk of pixels. + * + * Uses an open addressing double hashing (no chaining) on the + * prefix code/next character combination. We do a variant of + * Knuth's algorithm D (vol. 3, sec. 6.4) along with G. Knott's + * relatively-prime secondary probe. Here, the modular division + * first probe is gives way to a faster exclusive-or manipulation. + * Also do block compression with an adaptive reset, whereby the + * code table is cleared when the compression ratio decreases, + * but after the table fills. The variable-length output codes + * are re-sized at this point, and a CODE_CLEAR is generated + * for the decoder. + */ +static int +LZWEncode(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s) +{ + register LZWEncodeState *sp = (LZWEncodeState *)tif->tif_data; + register long fcode; + register hash_t *hp; + register int h, c, ent, disp; + long incount, outcount, checkpoint; + long nextdata, nextbits; + int free_ent, maxcode, nbits; + tidata_t op, limit; + + if (sp == NULL) + return (0); + /* + * Load local state. + */ + incount = sp->enc_incount; + outcount = sp->enc_outcount; + checkpoint = sp->enc_checkpoint; + nextdata = sp->lzw_nextdata; + nextbits = sp->lzw_nextbits; + free_ent = sp->lzw_free_ent; + maxcode = sp->lzw_maxcode; + nbits = sp->lzw_nbits; + op = tif->tif_rawcp; + limit = sp->enc_rawlimit; + ent = sp->enc_oldcode; + + if (ent == -1 && cc > 0) { + /* + * NB: This is safe because it can only happen + * at the start of a strip where we know there + * is space in the data buffer. + */ + PutNextCode(op, CODE_CLEAR); + ent = *bp++; cc--; incount++; + } + while (cc > 0) { + c = *bp++; cc--; incount++; + fcode = ((long)c << BITS_MAX) + ent; + h = (c << HSHIFT) ^ ent; /* xor hashing */ + hp = &sp->enc_hashtab[h]; + if (hp->hash == fcode) { + ent = hp->code; + continue; + } + if (hp->hash >= 0) { + /* + * Primary hash failed, check secondary hash. + */ + disp = HSIZE - h; + if (h == 0) + disp = 1; + do { + if ((hp -= disp) < sp->enc_hashtab) + hp += HSIZE; + if (hp->hash == fcode) { + ent = hp->code; + goto hit; + } + } while (hp->hash >= 0); + } + /* + * New entry, emit code and add to table. + */ + /* + * Verify there is space in the buffer for the code + * and any potential Clear code that might be emitted + * below. The value of limit is setup so that there + * are at least 4 bytes free--room for 2 codes. + */ + if (op > limit) { + tif->tif_rawcc = op - tif->tif_rawdata; + TIFFFlushData1(tif); + op = tif->tif_rawdata; + } + PutNextCode(op, ent); + ent = c; + hp->code = free_ent++; + hp->hash = fcode; + if (free_ent == CODE_MAX-1) { + /* table is full, emit clear code and reset */ + cl_hash(sp); + sp->enc_ratio = 0; + incount = 0; + outcount = 0; + free_ent = CODE_FIRST; + PutNextCode(op, CODE_CLEAR); + nbits = BITS_MIN; + maxcode = MAXCODE(BITS_MIN); + } else { + /* + * If the next entry is going to be too big for + * the code size, then increase it, if possible. + */ + if (free_ent > maxcode) { + nbits++; + assert(nbits <= BITS_MAX); + maxcode = MAXCODE(nbits); + } else if (incount >= checkpoint) { + long rat; + /* + * Check compression ratio and, if things seem + * to be slipping, clear the hash table and + * reset state. The compression ratio is a + * 24+8-bit fractional number. + */ + checkpoint = incount+CHECK_GAP; + CALCRATIO(sp, rat); + if (rat <= sp->enc_ratio) { + cl_hash(sp); + sp->enc_ratio = 0; + incount = 0; + outcount = 0; + free_ent = CODE_FIRST; + PutNextCode(op, CODE_CLEAR); + nbits = BITS_MIN; + maxcode = MAXCODE(BITS_MIN); + } else + sp->enc_ratio = rat; + } + } + hit: + ; + } + + /* + * Restore global state. + */ + sp->enc_incount = incount; + sp->enc_outcount = outcount; + sp->enc_checkpoint = checkpoint; + sp->enc_oldcode = ent; + sp->lzw_nextdata = nextdata; + sp->lzw_nextbits = nextbits; + sp->lzw_free_ent = free_ent; + sp->lzw_maxcode = maxcode; + sp->lzw_nbits = nbits; + tif->tif_rawcp = op; + return (1); +} + +static int +LZWEncodePredRow(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s) +{ + LZWEncodeState *sp = (LZWEncodeState *)tif->tif_data; + + assert(sp != NULL); + assert(sp->lzw_hordiff != NULL); +/* XXX horizontal differencing alters user's data XXX */ + (*sp->lzw_hordiff)((char *)bp, cc, sp->lzw_stride); + return (LZWEncode(tif, bp, cc, s)); +} + +static int +LZWEncodePredTile(TIFF* tif, tidata_t bp0, tsize_t cc0, tsample_t s) +{ + LZWEncodeState *sp = (LZWEncodeState *)tif->tif_data; + u_long cc = cc0, rowsize; + u_char *bp = bp0; + + assert(sp != NULL); + assert(sp->lzw_hordiff != NULL); + rowsize = sp->lzw_rowsize; + assert(rowsize > 0); + while ((long)cc > 0) { + (*sp->lzw_hordiff)((char *)bp, rowsize, sp->lzw_stride); + cc -= rowsize; + bp += rowsize; + } + return (LZWEncode(tif, bp0, cc0, s)); +} + +/* + * Finish off an encoded strip by flushing the last + * string and tacking on an End Of Information code. + */ +static int +LZWPostEncode(TIFF* tif) +{ + register LZWEncodeState *sp = (LZWEncodeState *)tif->tif_data; + tidata_t op = tif->tif_rawcp; + long nextbits = sp->lzw_nextbits; + long nextdata = sp->lzw_nextdata; + long outcount = sp->enc_outcount; + int nbits = sp->lzw_nbits; + + if (op > sp->enc_rawlimit) { + tif->tif_rawcc = op - tif->tif_rawdata; + TIFFFlushData1(tif); + op = tif->tif_rawdata; + } + if (sp->enc_oldcode != -1) { + PutNextCode(op, sp->enc_oldcode); + sp->enc_oldcode = -1; + } + PutNextCode(op, CODE_EOI); + if (nextbits > 0) + *op++ = nextdata << (8-nextbits); + tif->tif_rawcc = op - tif->tif_rawdata; + return (1); +} + +/* + * Reset encoding hash table. + */ +static void +cl_hash(LZWEncodeState* sp) +{ + register hash_t *hp = &sp->enc_hashtab[HSIZE-1]; + register long i = HSIZE-8; + + do { + i -= 8; + hp[-7].hash = -1; + hp[-6].hash = -1; + hp[-5].hash = -1; + hp[-4].hash = -1; + hp[-3].hash = -1; + hp[-2].hash = -1; + hp[-1].hash = -1; + hp[ 0].hash = -1; + hp -= 8; + } while (i >= 0); + for (i += 8; i > 0; i--, hp--) + hp->hash = -1; +} + +static void +LZWCleanup(TIFF* tif) +{ + if (tif->tif_data) { + _TIFFfree(tif->tif_data); + tif->tif_data = NULL; + } +} + +int +TIFFInitLZW(TIFF* tif) +{ + tif->tif_predecode = LZWPreDecode; + tif->tif_decoderow = LZWDecode; + tif->tif_decodestrip = LZWDecode; + tif->tif_decodetile = LZWDecode; + tif->tif_preencode = LZWPreEncode; + tif->tif_postencode = LZWPostEncode; + tif->tif_encoderow = LZWEncode; + tif->tif_encodestrip = LZWEncode; + tif->tif_encodetile = LZWEncode; + tif->tif_cleanup = LZWCleanup; + return (1); +} + +/* + * Copyright (c) 1985, 1986 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James A. Woods, derived from original work by Spencer Thomas + * and Joseph Orost. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ diff --git a/panda/src/tiff/tif_machdep.c b/panda/src/tiff/tif_machdep.c new file mode 100644 index 0000000000..1bf988e822 --- /dev/null +++ b/panda/src/tiff/tif_machdep.c @@ -0,0 +1,186 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1992 Sam Leffler + * Copyright (c) 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library Machine Dependent Routines. + */ +#include "tiffiop.h" + +#ifdef tahoe +typedef struct ieeedouble { + u_long sign : 1, + exp : 11, + mant : 20; + u_long mant2; +} ieeedouble; +typedef struct ieeefloat { + u_long sign : 1, + exp : 8, + mant : 23; +} ieeefloat; + +typedef struct { + u_long sign : 1, + exp : 8, + mant : 23; + u_long mant2; +} nativedouble; +typedef struct { + u_long sign : 1, + exp : 8, + mant : 23; +} nativefloat; +/* + * Beware, over/under-flow in conversions will + * result in garbage values -- handling it would + * require a subroutine call or lots more code. + */ +#define NATIVE2IEEEFLOAT(fp) { \ + if ((fp)->native.exp) \ + (fp)->ieee.exp = (fp)->native.exp - 129 + 127; /* alter bias */\ +} +#define IEEEFLOAT2NATIVE(fp) { \ + if ((fp)->ieee.exp) \ + (fp)->native.exp = (fp)->ieee.exp - 127 + 129; /* alter bias */\ +} +#define IEEEDOUBLE2NATIVE(dp) { \ + if ((dp)->native.exp = (dp)->ieee.exp) \ + (dp)->native.exp += -1023 + 129; \ + (dp)->native.mant = ((dp)->ieee.mant<<3)|((dp)->native.mant2>>29); \ + (dp)->native.mant2 <<= 3; \ +} +#endif /* tahoe */ + +#ifdef vax +typedef struct ieeedouble { + u_long mant : 20, + exp : 11, + sign : 1; + u_long mant2; +} ieeedouble; +typedef struct ieeefloat { + u_long mant : 23, + exp : 8, + sign : 1; +} ieeefloat; + +typedef struct { + u_long mant1 : 7, + exp : 8, + sign : 1, + mant2 : 16; + u_long mant3; +} nativedouble; +typedef struct { + u_long mant1 : 7, + exp : 8, + sign : 1, + mant2 : 16; +} nativefloat; +/* + * Beware, these do not handle over/under-flow + * during conversion from ieee to native format. + */ +#define NATIVE2IEEEFLOAT(fp) { \ + float_t t; \ + if (t.ieee.exp = (fp)->native.exp) \ + t.ieee.exp += -129 + 127; \ + t.ieee.sign = (fp)->native.sign; \ + t.ieee.mant = ((fp)->native.mant1<<16)|(fp)->native.mant2; \ + *(fp) = t; \ +} +#define IEEEFLOAT2NATIVE(fp) { \ + float_t t; int v = (fp)->ieee.exp; \ + if (v) v += -127 + 129; /* alter bias of exponent */\ + t.native.exp = v; /* implicit truncation of exponent */\ + t.native.sign = (fp)->ieee.sign; \ + v = (fp)->ieee.mant; \ + t.native.mant1 = v >> 16; \ + t.native.mant2 = v;\ + *(fp) = t; \ +} +#define IEEEDOUBLE2NATIVE(dp) { \ + double_t t; int v = (dp)->ieee.exp; \ + if (v) v += -1023 + 1025; /* if can alter bias of exponent */\ + t.native.exp = v; /* implicit truncation of exponent */\ + v = (dp)->ieee.mant; \ + t.native.sign = (dp)->ieee.sign; \ + t.native.mant1 = v >> 16; \ + t.native.mant2 = v;\ + t.native.mant3 = (dp)->mant2; \ + *(dp) = t; \ +} +#endif /* vax */ + +#if !HAVE_IEEEFP +#if !defined(IEEEFLOAT2NATIVE) || !defined(NATIVE2IEEEFLOAT) +"Help, you've configured the library to not have IEEE floating point,\ +but not defined how to convert between IEEE and native formats!" +#endif + +/* + * These unions are used during floating point + * conversions. The above macros define the + * conversion operations. + */ +typedef union { + ieeedouble ieee; + nativedouble native; + char b[8]; + double d; +} double_t; + +typedef union { + ieeefloat ieee; + nativefloat native; + char b[4]; + float f; +} float_t; + +void +TIFFCvtIEEEFloatToNative(TIFF* tif, u_int n, float* f) +{ + float_t *fp = (float_t *)f; + + while (n-- > 0) { + IEEEFLOAT2NATIVE(fp); + fp++; + } +} + +void +TIFFCvtNativeToIEEEFloat(TIFF* tif, u_int n, float* f) +{ + float_t *fp = (float_t *)f; + + while (n-- > 0) { + NATIVE2IEEEFLOAT(fp); + fp++; + } +} +#endif diff --git a/panda/src/tiff/tif_msdos.c b/panda/src/tiff/tif_msdos.c new file mode 100644 index 0000000000..7aae4f7f97 --- /dev/null +++ b/panda/src/tiff/tif_msdos.c @@ -0,0 +1,141 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library MSDOS-specific Routines. + */ +#include "tiffiop.h" +#if defined(__WATCOMC__) || defined(__BORLANDC__) +#include /* for open, close, etc. function prototypes */ +#endif + +static tsize_t +_tiffReadProc(thandle_t fd, tdata_t buf, tsize_t size) +{ + return (read((int) fd, buf, size)); +} + +static tsize_t +_tiffWriteProc(thandle_t fd, tdata_t buf, tsize_t size) +{ + return (write((int) fd, buf, size)); +} + +static toff_t +_tiffSeekProc(thandle_t fd, toff_t off, int whence) +{ + return (lseek((int) fd, (off_t) off, whence)); +} + +static int +_tiffCloseProc(thandle_t fd) +{ + return (close((int) fd)); +} + +#include + +static toff_t +_tiffSizeProc(thandle_t fd) +{ + struct stat sb; + return (fstat((int) fd, &sb) < 0 ? 0 : sb.st_size); +} + +static int +_tiffMapProc(thandle_t fd, tdata_t* pbase, toff_t* psize) +{ + return (0); +} + +static void +_tiffUnmapProc(thandle_t fd, tdata_t base, toff_t size) +{ +} + +/* + * Open a TIFF file descriptor for read/writing. + */ +TIFF* +TIFFFdOpen(int fd, const char* name, const char* mode) +{ + TIFF* tif; + + tif = TIFFClientOpen(name, mode, + (void*) fd, + _tiffReadProc, _tiffWriteProc, _tiffSeekProc, _tiffCloseProc, + _tiffSizeProc, _tiffMapProc, _tiffUnmapProc); + if (tif) + tif->tif_fd = fd; + return (tif); +} + +/* + * Open a TIFF file for read/writing. + */ +TIFF* +TIFFOpen(const char* name, const char* mode) +{ + static const char module[] = "TIFFOpen"; + int m, fd; + + m = _TIFFgetMode(mode, module); + if (m == -1) + return ((TIFF*)0); + fd = open(name, m|O_BINARY, 0666); + if (fd < 0) { + TIFFError(module, "%s: Cannot open", name); + return ((TIFF*)0); + } + return (TIFFFdOpen(fd, name, mode)); +} + +#ifdef __GNUC__ +extern char *malloc(); +extern char *realloc(); +#else +#include +#endif + +void * +_TIFFmalloc(size_t s) +{ + return (malloc(s)); +} + +void +_TIFFfree(void* p) +{ + free(p); +} + +void * +_TIFFrealloc(void* p, size_t s) +{ + return (realloc(p, s)); +} diff --git a/panda/src/tiff/tif_next.c b/panda/src/tiff/tif_next.c new file mode 100644 index 0000000000..6510e2dcd1 --- /dev/null +++ b/panda/src/tiff/tif_next.c @@ -0,0 +1,140 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * NeXT 2-bit Grey Scale Compression Algorithm Support + */ +#include "tiffiop.h" + +#define SETPIXEL(op, v) { \ + switch (npixels++ & 3) { \ + case 0: op[0] = (v) << 6; break; \ + case 1: op[0] |= (v) << 4; break; \ + case 2: op[0] |= (v) << 2; break; \ + case 3: *op++ |= (v); break; \ + } \ +} + +#define LITERALROW 0x00 +#define LITERALSPAN 0x40 +#define WHITE ((1<<2)-1) + +static int +NeXTDecode(TIFF* tif, tidata_t buf, tsize_t occ, tsample_t s) +{ + register u_char *bp, *op; + register tsize_t cc; + register int n; + tidata_t row; + tsize_t scanline; + + /* + * Each scanline is assumed to start off as all + * white (we assume a PhotometricInterpretation + * of ``min-is-black''). + */ + for (op = buf, cc = occ; cc-- > 0;) + *op++ = 0xff; + + bp = (u_char *)tif->tif_rawcp; + cc = tif->tif_rawcc; + scanline = tif->tif_scanlinesize; + for (row = buf; (long)occ > 0; occ -= scanline, row += scanline) { + n = *bp++, cc--; + switch (n) { + case LITERALROW: + /* + * The entire scanline is given as literal values. + */ + if (cc < scanline) + goto bad; + memcpy(row, bp, scanline); + bp += scanline; + cc -= scanline; + break; + case LITERALSPAN: { + int off; + /* + * The scanline has a literal span + * that begins at some offset. + */ + off = (bp[0] * 256) + bp[1]; + n = (bp[2] * 256) + bp[3]; + if (cc < 4+n) + goto bad; + memcpy(row+off, bp+4, n); + bp += 4+n; + cc -= 4+n; + break; + } + default: { + register int npixels = 0, grey; + u_long imagewidth = tif->tif_dir.td_imagewidth; + + /* + * The scanline is composed of a sequence + * of constant color ``runs''. We shift + * into ``run mode'' and interpret bytes + * as codes of the form + * until we've filled the scanline. + */ + op = row; + for (;;) { + grey = (n>>6) & 0x3; + n &= 0x3f; + while (n-- > 0) + SETPIXEL(op, grey); + if (npixels >= imagewidth) + break; + if (cc == 0) + goto bad; + n = *bp++, cc--; + } + break; + } + } + } + tif->tif_rawcp = (tidata_t) bp; + tif->tif_rawcc = cc; + return (1); +bad: + TIFFError(tif->tif_name, "NeXTDecode: Not enough data for scanline %ld", + (long) tif->tif_row); + return (0); +} + +int +TIFFInitNeXT(TIFF* tif) +{ + tif->tif_decoderow = NeXTDecode; + tif->tif_decodestrip = NeXTDecode; + tif->tif_decodetile = NeXTDecode; + return (1); +} diff --git a/panda/src/tiff/tif_open.c b/panda/src/tiff/tif_open.c new file mode 100644 index 0000000000..27a6bf6a59 --- /dev/null +++ b/panda/src/tiff/tif_open.c @@ -0,0 +1,374 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + */ +#include "tiffiop.h" + +static const long typemask[13] = { + 0L, /* TIFF_NOTYPE */ + 0x000000ffL, /* TIFF_BYTE */ + 0xffffffffL, /* TIFF_ASCII */ + 0x0000ffffL, /* TIFF_SHORT */ + 0xffffffffL, /* TIFF_LONG */ + 0xffffffffL, /* TIFF_RATIONAL */ + 0x000000ffL, /* TIFF_SBYTE */ + 0x000000ffL, /* TIFF_UNDEFINED */ + 0x0000ffffL, /* TIFF_SSHORT */ + 0xffffffffL, /* TIFF_SLONG */ + 0xffffffffL, /* TIFF_SRATIONAL */ + 0xffffffffL, /* TIFF_FLOAT */ + 0xffffffffL, /* TIFF_DOUBLE */ +}; +static const int bigTypeshift[13] = { + 0, /* TIFF_NOTYPE */ + 24, /* TIFF_BYTE */ + 0, /* TIFF_ASCII */ + 16, /* TIFF_SHORT */ + 0, /* TIFF_LONG */ + 0, /* TIFF_RATIONAL */ + 16, /* TIFF_SBYTE */ + 16, /* TIFF_UNDEFINED */ + 24, /* TIFF_SSHORT */ + 0, /* TIFF_SLONG */ + 0, /* TIFF_SRATIONAL */ + 0, /* TIFF_FLOAT */ + 0, /* TIFF_DOUBLE */ +}; +static const int litTypeshift[13] = { + 0, /* TIFF_NOTYPE */ + 0, /* TIFF_BYTE */ + 0, /* TIFF_ASCII */ + 0, /* TIFF_SHORT */ + 0, /* TIFF_LONG */ + 0, /* TIFF_RATIONAL */ + 0, /* TIFF_SBYTE */ + 0, /* TIFF_UNDEFINED */ + 0, /* TIFF_SSHORT */ + 0, /* TIFF_SLONG */ + 0, /* TIFF_SRATIONAL */ + 0, /* TIFF_FLOAT */ + 0, /* TIFF_DOUBLE */ +}; + +/* + * Initialize the bit fill order, the + * shift & mask tables, and the byte + * swapping state according to the file + * contents and the machine architecture. + */ +static void +TIFFInitOrder(register TIFF* tif, int magic, int bigendian) +{ + /* XXX how can we deduce this dynamically? */ + tif->tif_fillorder = FILLORDER_MSB2LSB; + + tif->tif_typemask = typemask; + if (magic == TIFF_BIGENDIAN) { + tif->tif_typeshift = bigTypeshift; + if (!bigendian) + tif->tif_flags |= TIFF_SWAB; + } else { + tif->tif_typeshift = litTypeshift; + if (bigendian) + tif->tif_flags |= TIFF_SWAB; + } +} + +int +_TIFFgetMode(const char* mode, const char* module) +{ + int m = -1; + + switch (mode[0]) { + case 'r': + m = O_RDONLY; + if (mode[1] == '+') + m = O_RDWR; + break; + case 'w': + case 'a': + m = O_RDWR|O_CREAT; + if (mode[0] == 'w') + m |= O_TRUNC; + break; + default: + TIFFError(module, "\"%s\": Bad mode", mode); + break; + } + return (m); +} + +TIFF* +TIFFClientOpen( + const char* name, const char* mode, + thandle_t clientdata, + TIFFReadWriteProc readproc, + TIFFReadWriteProc writeproc, + TIFFSeekProc seekproc, + TIFFCloseProc closeproc, + TIFFSizeProc sizeproc, + TIFFMapFileProc mapproc, + TIFFUnmapFileProc unmapproc +) +{ + static const char module[] = "TIFFClientOpen"; + TIFF *tif; + int m, bigendian; + + m = _TIFFgetMode(mode, module); + if (m == -1) + goto bad2; + tif = (TIFF *)_TIFFmalloc(sizeof (TIFF) + strlen(name) + 1); + if (tif == NULL) { + TIFFError(module, "%s: Out of memory (TIFF structure)", name); + goto bad2; + } + memset(tif, 0, sizeof (*tif)); + tif->tif_name = (char *)tif + sizeof (TIFF); + strcpy(tif->tif_name, name); + tif->tif_mode = m &~ (O_CREAT|O_TRUNC); + tif->tif_curdir = -1; /* non-existent directory */ + tif->tif_curoff = 0; + tif->tif_curstrip = -1; /* invalid strip */ + tif->tif_row = -1; /* read/write pre-increment */ + tif->tif_clientdata = clientdata; + tif->tif_readproc = readproc; + tif->tif_writeproc = writeproc; + tif->tif_seekproc = seekproc; + tif->tif_closeproc = closeproc; + tif->tif_sizeproc = sizeproc; + tif->tif_mapproc = mapproc; + tif->tif_unmapproc = unmapproc; + + { int one = 1; char* cp = (char*)&one; bigendian = (*cp == 0); } + /* + * Read in TIFF header. + */ + if (!ReadOK(tif, &tif->tif_header, sizeof (TIFFHeader))) { + if (tif->tif_mode == O_RDONLY) { + TIFFError(name, "Cannot read TIFF header"); + goto bad; + } + /* + * Setup header and write. + */ + tif->tif_header.tiff_magic = bigendian ? + TIFF_BIGENDIAN : TIFF_LITTLEENDIAN; + tif->tif_header.tiff_version = TIFF_VERSION; + tif->tif_header.tiff_diroff = 0; /* filled in later */ + if (!WriteOK(tif, &tif->tif_header, sizeof (TIFFHeader))) { + TIFFError(name, "Error writing TIFF header"); + goto bad; + } + /* + * Setup the byte order handling. + */ + TIFFInitOrder(tif, tif->tif_header.tiff_magic, bigendian); + /* + * Setup default directory. + */ + if (!TIFFDefaultDirectory(tif)) + goto bad; + tif->tif_diroff = 0; + return (tif); + } + /* + * Setup the byte order handling. + */ + if (tif->tif_header.tiff_magic != TIFF_BIGENDIAN && + tif->tif_header.tiff_magic != TIFF_LITTLEENDIAN) { + TIFFError(name, "Not a TIFF file, bad magic number %d (0x%x)", + tif->tif_header.tiff_magic, + tif->tif_header.tiff_magic); + goto bad; + } + TIFFInitOrder(tif, tif->tif_header.tiff_magic, bigendian); + /* + * Swap header if required. + */ + if (tif->tif_flags & TIFF_SWAB) { + TIFFSwabShort(&tif->tif_header.tiff_version); + TIFFSwabLong(&tif->tif_header.tiff_diroff); + } + /* + * Now check version (if needed, it's been byte-swapped). + * Note that this isn't actually a version number, it's a + * magic number that doesn't change (stupid). + */ + if (tif->tif_header.tiff_version != TIFF_VERSION) { + TIFFError(name, + "Not a TIFF file, bad version number %d (0x%x)", + tif->tif_header.tiff_version, + tif->tif_header.tiff_version); + goto bad; + } + tif->tif_flags |= TIFF_MYBUFFER; + tif->tif_rawcp = tif->tif_rawdata = 0; + tif->tif_rawdatasize = 0; + /* + * Setup initial directory. + */ + switch (mode[0]) { + case 'r': + tif->tif_nextdiroff = tif->tif_header.tiff_diroff; + if (TIFFMapFileContents(tif, (tdata_t*) &tif->tif_base, &tif->tif_size)) + tif->tif_flags |= TIFF_MAPPED; + if (TIFFReadDirectory(tif)) { + tif->tif_rawcc = -1; + tif->tif_flags |= TIFF_BUFFERSETUP; + return (tif); + } + break; + case 'a': + /* + * Don't append to file that has information + * byte swapped -- we will write data that is + * in the opposite order. + */ + if (tif->tif_flags & TIFF_SWAB) { + TIFFError(name, + "Cannot append to file that has opposite byte ordering"); + goto bad; + } + /* + * New directories are automatically append + * to the end of the directory chain when they + * are written out (see TIFFWriteDirectory). + */ + if (!TIFFDefaultDirectory(tif)) + goto bad; + return (tif); + } +bad: + tif->tif_mode = O_RDONLY; /* XXX avoid flush */ + TIFFClose(tif); + return ((TIFF*)0); +bad2: + (void) (*closeproc)(clientdata); + return ((TIFF*)0); +} + +tsize_t +TIFFScanlineSize(TIFF* tif) +{ + TIFFDirectory *td = &tif->tif_dir; + tsize_t scanline; + + scanline = td->td_bitspersample * td->td_imagewidth; + if (td->td_planarconfig == PLANARCONFIG_CONTIG) + scanline *= td->td_samplesperpixel; + return (howmany(scanline, 8)); +} + +/* + * Query functions to access private data. + */ + +/* + * Return open file's name. + */ +const char * +TIFFFileName(TIFF* tif) +{ + return (tif->tif_name); +} + +/* + * Return open file's I/O descriptor. + */ +int +TIFFFileno(TIFF* tif) +{ + return (tif->tif_fd); +} + +/* + * Return read/write mode. + */ +int +TIFFGetMode(TIFF* tif) +{ + return (tif->tif_mode); +} + +/* + * Return nonzero if file is organized in + * tiles; zero if organized as strips. + */ +int +TIFFIsTiled(TIFF* tif) +{ + return (isTiled(tif)); +} + +/* + * Return current row being read/written. + */ +uint32 +TIFFCurrentRow(TIFF* tif) +{ + return (tif->tif_row); +} + +/* + * Return index of the current directory. + */ +tdir_t +TIFFCurrentDirectory(TIFF* tif) +{ + return (tif->tif_curdir); +} + +/* + * Return current strip. + */ +tstrip_t +TIFFCurrentStrip(TIFF* tif) +{ + return (tif->tif_curstrip); +} + +/* + * Return current tile. + */ +ttile_t +TIFFCurrentTile(TIFF* tif) +{ + return (tif->tif_curtile); +} + +/* + * Return nonzero if the file has byte-swapped data. + */ +int +TIFFIsByteSwapped(TIFF* tif) +{ + return ((tif->tif_flags & TIFF_SWAB) != 0); +} diff --git a/panda/src/tiff/tif_packbits.c b/panda/src/tiff/tif_packbits.c new file mode 100644 index 0000000000..ef92a7fe94 --- /dev/null +++ b/panda/src/tiff/tif_packbits.c @@ -0,0 +1,249 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * PackBits Compression Algorithm Support + */ +#include "tiffiop.h" + +#include +#include + +static int +PackBitsPreEncode(TIFF* tif) +{ + /* + * Calculate the scanline/tile-width size in bytes. + */ + if (isTiled(tif)) + tif->tif_data = (tidata_t) TIFFTileRowSize(tif); + else + tif->tif_data = (tidata_t) TIFFScanlineSize(tif); + return (1); +} + +/* + * Encode a run of pixels. + */ +static int +PackBitsEncode(TIFF* tif, tidata_t buf, tsize_t cc, tsample_t s) +{ + u_char* bp = (u_char*) buf; + tidata_t op, ep, lastliteral; + long n, slop; + int b; + enum { BASE, LITERAL, RUN, LITERAL_RUN } state; + + op = tif->tif_rawcp; + ep = tif->tif_rawdata + tif->tif_rawdatasize; + state = BASE; + lastliteral = 0; + while (cc > 0) { + /* + * Find the longest string of identical bytes. + */ + b = *bp++, cc--, n = 1; + for (; cc > 0 && b == *bp; cc--, bp++) + n++; + again: + if (op + 2 >= ep) { /* insure space for new data */ + /* + * Be careful about writing the last + * literal. Must write up to that point + * and then copy the remainder to the + * front of the buffer. + */ + if (state == LITERAL || state == LITERAL_RUN) { + slop = op - lastliteral; + tif->tif_rawcc += lastliteral - tif->tif_rawcp; + if (!TIFFFlushData1(tif)) + return (-1); + op = tif->tif_rawcp; + while (slop-- > 0) + *op++ = *lastliteral++; + lastliteral = tif->tif_rawcp; + } else { + tif->tif_rawcc += op - tif->tif_rawcp; + if (!TIFFFlushData1(tif)) + return (-1); + op = tif->tif_rawcp; + } + } + switch (state) { + case BASE: /* initial state, set run/literal */ + if (n > 1) { + state = RUN; + if (n > 128) { + *op++ = -127; + *op++ = b; + n -= 128; + goto again; + } + *op++ = -(n-1); + *op++ = b; + } else { + lastliteral = op; + *op++ = 0; + *op++ = b; + state = LITERAL; + } + break; + case LITERAL: /* last object was literal string */ + if (n > 1) { + state = LITERAL_RUN; + if (n > 128) { + *op++ = -127; + *op++ = b; + n -= 128; + goto again; + } + *op++ = -(n-1); /* encode run */ + *op++ = b; + } else { /* extend literal */ + if (++(*lastliteral) == 127) + state = BASE; + *op++ = b; + } + break; + case RUN: /* last object was run */ + if (n > 1) { + if (n > 128) { + *op++ = -127; + *op++ = b; + n -= 128; + goto again; + } + *op++ = -(n-1); + *op++ = b; + } else { + lastliteral = op; + *op++ = 0; + *op++ = b; + state = LITERAL; + } + break; + case LITERAL_RUN: /* literal followed by a run */ + /* + * Check to see if previous run should + * be converted to a literal, in which + * case we convert literal-run-literal + * to a single literal. + */ + if (n == 1 && -op[-2] == 1 && *lastliteral < 126) { + state = (((*lastliteral) += 2) == 127 ? + BASE : LITERAL); + op[-2] = op[-1]; /* replicate */ + } else + state = RUN; + goto again; + } + } + tif->tif_rawcc += op - tif->tif_rawcp; + tif->tif_rawcp = op; + return (1); +} + +/* + * Encode a rectangular chunk of pixels. We break it up + * into row-sized pieces to insure that encoded runs do + * not span rows. Otherwise, there can be problems with + * the decoder if data is read, for example, by scanlines + * when it was encoded by strips. + */ +static int +PackBitsEncodeChunk(TIFF* tif, tidata_t bp, tsize_t cc, tsample_t s) +{ + tsize_t rowsize = (tsize_t) tif->tif_data; + + assert(rowsize > 0); + while ((long)cc > 0) { + if (PackBitsEncode(tif, bp, rowsize, s) < 0) + return (-1); + bp += rowsize; + cc -= rowsize; + } + return (1); +} + +static int +PackBitsDecode(TIFF* tif, tidata_t op, tsize_t occ, tsample_t s) +{ + char *bp; + tsize_t cc; + + bp = (char*) tif->tif_rawcp; + cc = tif->tif_rawcc; + while (cc > 0 && (long)occ > 0) { + long n = (long) *bp++; + int b; + /* + * Watch out for compilers that + * don't sign extend chars... + */ + if (n >= 128) + n -= 256; + if (n < 0) { /* replicate next byte -n+1 times */ + cc--; + if (n == -128) /* nop */ + continue; + n = -n + 1; + occ -= n; + for (b = *bp++; n-- > 0;) + *op++ = b; + } else { /* copy next n+1 bytes literally */ + memcpy(op, bp, ++n); + op += n; occ -= n; + bp += n; cc -= n; + } + } + tif->tif_rawcp = (tidata_t) bp; + tif->tif_rawcc = cc; + if (occ > 0) { + TIFFError(tif->tif_name, + "PackBitsDecode: Not enough data for scanline %ld", + (long) tif->tif_row); + return (0); + } + /* check for buffer overruns? */ + return (1); +} + +int +TIFFInitPackBits(TIFF* tif) +{ + tif->tif_decoderow = PackBitsDecode; + tif->tif_decodestrip = PackBitsDecode; + tif->tif_decodetile = PackBitsDecode; + tif->tif_preencode = PackBitsPreEncode; + tif->tif_encoderow = PackBitsEncode; + tif->tif_encodestrip = PackBitsEncodeChunk; + tif->tif_encodetile = PackBitsEncodeChunk; + return (1); +} diff --git a/panda/src/tiff/tif_print.c b/panda/src/tiff/tif_print.c new file mode 100644 index 0000000000..eb415a5ef7 --- /dev/null +++ b/panda/src/tiff/tif_print.c @@ -0,0 +1,590 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Directory Printing Support + */ +#include "tiffiop.h" +#include + +#ifdef JPEG_SUPPORT +static void +JPEGPrintQTable(FILE* fd, u_char* tab) +{ + int i, j; + char *sep; + + fputc('\n', fd); + for (i = 0; i < 8; i++) { + sep = " "; + for (j = 0; j < 8; j++) { + fprintf(fd, "%s%2u", sep, tab[8*i+j]); + sep = ", "; + } + fputc('\n', fd); + } +} + +static void +JPEGPrintCTable(FILE* fd, u_char* tab) +{ + int i, n, count; + char *sep; + + fprintf(fd, "\n Bits:"); + count = 0; + for (i = 0; i < 16; i++) { + fprintf(fd, " %u", tab[i]); + count += tab[i]; + } + n = 0; + for (; count > 0; count--) { + if ((n % 8) == 0) { + fputc('\n', fd); + sep = " "; + } + fprintf(fd, "%s0x%02x", sep, tab[i++]); + sep = ", "; + n++; + + } + if (n % 8) + fputc('\n', fd); +} +#endif + +static const char *photoNames[] = { + "min-is-white", /* PHOTOMETRIC_MINISWHITE */ + "min-is-black", /* PHOTOMETRIC_MINISBLACK */ + "RGB color", /* PHOTOMETRIC_RGB */ + "palette color (RGB from colormap)", /* PHOTOMETRIC_PALETTE */ + "transparency mask", /* PHOTOMETRIC_MASK */ + "separated", /* PHOTOMETRIC_SEPARATED */ + "YCbCr", /* PHOTOMETRIC_YCBCR */ + "7 (0x7)", + "CIE L*a*b*", /* PHOTOMETRIC_CIELAB */ +}; +#define NPHOTONAMES (sizeof (photoNames) / sizeof (photoNames[0])) + +static const char *orientNames[] = { + "0 (0x0)", + "row 0 top, col 0 lhs", /* ORIENTATION_TOPLEFT */ + "row 0 top, col 0 rhs", /* ORIENTATION_TOPRIGHT */ + "row 0 bottom, col 0 rhs", /* ORIENTATION_BOTRIGHT */ + "row 0 bottom, col 0 lhs", /* ORIENTATION_BOTLEFT */ + "row 0 lhs, col 0 top", /* ORIENTATION_LEFTTOP */ + "row 0 rhs, col 0 top", /* ORIENTATION_RIGHTTOP */ + "row 0 rhs, col 0 bottom", /* ORIENTATION_RIGHTBOT */ + "row 0 lhs, col 0 bottom", /* ORIENTATION_LEFTBOT */ +}; +#define NORIENTNAMES (sizeof (orientNames) / sizeof (orientNames[0])) + +/* + * Print the contents of the current directory + * to the specified stdio file stream. + */ +void +TIFFPrintDirectory(TIFF* tif, FILE* fd, long flags) +{ + register TIFFDirectory *td; + char *sep; + int i, j; + long n; + + fprintf(fd, "TIFF Directory at offset 0x%x\n", tif->tif_diroff); + td = &tif->tif_dir; + if (TIFFFieldSet(tif,FIELD_SUBFILETYPE)) { + fprintf(fd, " Subfile Type:"); + sep = " "; + if (td->td_subfiletype & FILETYPE_REDUCEDIMAGE) { + fprintf(fd, "%sreduced-resolution image", sep); + sep = "/"; + } + if (td->td_subfiletype & FILETYPE_PAGE) { + fprintf(fd, "%smulti-page document", sep); + sep = "/"; + } + if (td->td_subfiletype & FILETYPE_MASK) + fprintf(fd, "%stransparency mask", sep); + fprintf(fd, " (%u = 0x%x)\n", + td->td_subfiletype, td->td_subfiletype); + } + if (TIFFFieldSet(tif,FIELD_IMAGEDIMENSIONS)) { + fprintf(fd, " Image Width: %lu Image Length: %lu", + (u_long) td->td_imagewidth, (u_long) td->td_imagelength); + if (TIFFFieldSet(tif,FIELD_IMAGEDEPTH)) + fprintf(fd, " Image Depth: %lu", + (u_long) td->td_imagedepth); + fprintf(fd, "\n"); + } + if (TIFFFieldSet(tif,FIELD_TILEDIMENSIONS)) { + fprintf(fd, " Tile Width: %lu Tile Length: %lu", + (u_long) td->td_tilewidth, (u_long) td->td_tilelength); + if (TIFFFieldSet(tif,FIELD_TILEDEPTH)) + fprintf(fd, " Tile Depth: %lu", + (u_long) td->td_tiledepth); + fprintf(fd, "\n"); + } + if (TIFFFieldSet(tif,FIELD_RESOLUTION)) { + fprintf(fd, " Resolution: %g, %g", + td->td_xresolution, td->td_yresolution); + if (TIFFFieldSet(tif,FIELD_RESOLUTIONUNIT)) { + switch (td->td_resolutionunit) { + case RESUNIT_NONE: + fprintf(fd, " (unitless)"); + break; + case RESUNIT_INCH: + fprintf(fd, " pixels/inch"); + break; + case RESUNIT_CENTIMETER: + fprintf(fd, " pixels/cm"); + break; + default: + fprintf(fd, " (unit %u = 0x%x)", + td->td_resolutionunit, + td->td_resolutionunit); + break; + } + } + fprintf(fd, "\n"); + } + if (TIFFFieldSet(tif,FIELD_POSITION)) + fprintf(fd, " Position: %g, %g\n", + td->td_xposition, td->td_yposition); + if (TIFFFieldSet(tif,FIELD_BITSPERSAMPLE)) + fprintf(fd, " Bits/Sample: %u\n", td->td_bitspersample); + if (TIFFFieldSet(tif,FIELD_SAMPLEFORMAT)) { + fprintf(fd, " Sample Format: "); + switch (td->td_sampleformat) { + case SAMPLEFORMAT_VOID: + fprintf(fd, "void\n"); + break; + case SAMPLEFORMAT_INT: + fprintf(fd, "signed integer\n"); + break; + case SAMPLEFORMAT_UINT: + fprintf(fd, "unsigned integer\n"); + break; + case SAMPLEFORMAT_IEEEFP: + fprintf(fd, "IEEE floating point\n"); + break; + default: + fprintf(fd, "%u (0x%x)\n", + td->td_sampleformat, td->td_sampleformat); + break; + } + } + if (TIFFFieldSet(tif,FIELD_COMPRESSION)) { + fprintf(fd, " Compression Scheme: "); + switch (td->td_compression) { + case COMPRESSION_NONE: + fprintf(fd, "none\n"); + break; + case COMPRESSION_CCITTRLE: + fprintf(fd, "CCITT modified Huffman encoding\n"); + break; + case COMPRESSION_CCITTFAX3: + fprintf(fd, "CCITT Group 3 facsimile encoding\n"); + break; + case COMPRESSION_CCITTFAX4: + fprintf(fd, "CCITT Group 4 facsimile encoding\n"); + break; + case COMPRESSION_CCITTRLEW: + fprintf(fd, "CCITT modified Huffman encoding %s\n", + "w/ word alignment"); + break; + case COMPRESSION_PACKBITS: + fprintf(fd, "Macintosh PackBits encoding\n"); + break; + case COMPRESSION_THUNDERSCAN: + fprintf(fd, "ThunderScan 4-bit encoding\n"); + break; + case COMPRESSION_LZW: + fprintf(fd, "Lempel-Ziv & Welch encoding\n"); + break; + case COMPRESSION_NEXT: + fprintf(fd, "NeXT 2-bit encoding\n"); + break; + case COMPRESSION_JPEG: + fprintf(fd, "JPEG encoding\n"); + break; + default: + fprintf(fd, "%u (0x%x)\n", + td->td_compression, td->td_compression); + break; + } + } + if (TIFFFieldSet(tif,FIELD_PHOTOMETRIC)) { + fprintf(fd, " Photometric Interpretation: "); + if (td->td_photometric < NPHOTONAMES) + fprintf(fd, "%s\n", photoNames[td->td_photometric]); + else + fprintf(fd, "%u (0x%x)\n", + td->td_photometric, td->td_photometric); + } + if (TIFFFieldSet(tif,FIELD_EXTRASAMPLES) && td->td_extrasamples) { + fprintf(fd, " Extra Samples: %u<", td->td_extrasamples); + sep = ""; + for (i = 0; i < td->td_extrasamples; i++) { + switch (td->td_sampleinfo[i]) { + case EXTRASAMPLE_UNSPECIFIED: + fprintf(fd, "%sunspecified", sep); + break; + case EXTRASAMPLE_ASSOCALPHA: + fprintf(fd, "%sassoc-alpha", sep); + break; + case EXTRASAMPLE_UNASSALPHA: + fprintf(fd, "%sunassoc-alpha", sep); + break; + default: + fprintf(fd, "%s%u (0x%x)", sep, + td->td_sampleinfo[i], td->td_sampleinfo[i]); + break; + } + sep = ", "; + } + fprintf(fd, ">\n"); + } +#ifdef CMYK_SUPPORT + if (TIFFFieldSet(tif,FIELD_INKSET)) { + fprintf(fd, " Ink Set: "); + switch (td->td_inkset) { + case INKSET_CMYK: + fprintf(fd, "CMYK\n"); + break; + default: + fprintf(fd, "%u (0x%x)\n", + td->td_inkset, td->td_inkset); + break; + } + } + if (TIFFFieldSet(tif,FIELD_INKNAMES)) { + char *cp; + fprintf(fd, " Ink Names: "); + i = td->td_samplesperpixel; + sep = ""; + for (cp = td->td_inknames; i > 0; cp = strchr(cp, '\0')) { + fprintf(fd, "%s%s", sep, cp); + sep = ", "; + } + } + if (TIFFFieldSet(tif,FIELD_DOTRANGE)) + fprintf(fd, " Dot Range: %u-%u\n", + td->td_dotrange[0], td->td_dotrange[1]); + if (TIFFFieldSet(tif,FIELD_TARGETPRINTER)) + fprintf(fd, " Target Printer: %s\n", td->td_targetprinter); +#endif + if (TIFFFieldSet(tif,FIELD_THRESHHOLDING)) { + fprintf(fd, " Thresholding: "); + switch (td->td_threshholding) { + case THRESHHOLD_BILEVEL: + fprintf(fd, "bilevel art scan\n"); + break; + case THRESHHOLD_HALFTONE: + fprintf(fd, "halftone or dithered scan\n"); + break; + case THRESHHOLD_ERRORDIFFUSE: + fprintf(fd, "error diffused\n"); + break; + default: + fprintf(fd, "%u (0x%x)\n", + td->td_threshholding, td->td_threshholding); + break; + } + } + if (TIFFFieldSet(tif,FIELD_FILLORDER)) { + fprintf(fd, " FillOrder: "); + switch (td->td_fillorder) { + case FILLORDER_MSB2LSB: + fprintf(fd, "msb-to-lsb\n"); + break; + case FILLORDER_LSB2MSB: + fprintf(fd, "lsb-to-msb\n"); + break; + default: + fprintf(fd, "%u (0x%x)\n", + td->td_fillorder, td->td_fillorder); + break; + } + } + if (TIFFFieldSet(tif,FIELD_PREDICTOR)) { + fprintf(fd, " Predictor: "); + switch (td->td_predictor) { + case 1: + fprintf(fd, "none\n"); + break; + case 2: + fprintf(fd, "horizontal differencing\n"); + break; + default: + fprintf(fd, "%u (0x%x)\n", + td->td_predictor, td->td_predictor); + break; + } + } +#ifdef YCBCR_SUPPORT + if (TIFFFieldSet(tif,FIELD_YCBCRSUBSAMPLING)) + fprintf(fd, " YCbCr Subsampling: %u, %u\n", + td->td_ycbcrsubsampling[0], td->td_ycbcrsubsampling[1]); + if (TIFFFieldSet(tif,FIELD_YCBCRPOSITIONING)) { + fprintf(fd, " YCbCr Positioning: "); + switch (td->td_ycbcrpositioning) { + case YCBCRPOSITION_CENTERED: + fprintf(fd, "centered\n"); + break; + case YCBCRPOSITION_COSITED: + fprintf(fd, "cosited\n"); + break; + default: + fprintf(fd, "%u (0x%x)\n", + td->td_ycbcrpositioning, td->td_ycbcrpositioning); + break; + } + } + if (TIFFFieldSet(tif,FIELD_YCBCRCOEFFICIENTS)) + fprintf(fd, " YCbCr Coefficients: %g, %g, %g\n", + td->td_ycbcrcoeffs[0], + td->td_ycbcrcoeffs[1], + td->td_ycbcrcoeffs[2]); +#endif +#ifdef JPEG_SUPPORT + if (TIFFFieldSet(tif,FIELD_JPEGPROC)) { + fprintf(fd, " JPEG Processing Mode: "); + switch (td->td_jpegproc) { + case JPEGPROC_BASELINE: + fprintf(fd, "baseline sequential algorithm\n"); + break; + case JPEGPROC_LOSSLESS: + fprintf(fd, "lossless algorithm with Huffman coding\n"); + break; + default: + fprintf(fd, "%u (0x%x)\n", + td->td_jpegproc, td->td_jpegproc); + break; + } + } + if (TIFFFieldSet(tif,FIELD_JPEGRESTARTINTERVAL)) { + fprintf(fd, " JPEG Restart Interval: "); + if (td->td_jpegrestartinterval) + fprintf(fd, "%u\n", td->td_jpegrestartinterval); + else + fprintf(fd, "(no restart markers)\n"); + } + if (TIFFFieldSet(tif,FIELD_JPEGQTABLES)) { + fprintf(fd, " JPEG Quantization Tables: "); + if (flags & TIFFPRINT_JPEGQTABLES) { + for (i = 0; i < td->td_samplesperpixel; i++) + JPEGPrintQTable(fd, td->td_qtab[i]); + } else + fprintf(fd, "(present)\n"); + } + if (TIFFFieldSet(tif,FIELD_JPEGDCTABLES)) { + fprintf(fd, " JPEG DC Tables: "); + if (flags & TIFFPRINT_JPEGDCTABLES) { + for (i = 0; i < td->td_samplesperpixel; i++) + JPEGPrintCTable(fd, td->td_dctab[i]); + } else + fprintf(fd, "(present)\n"); + } + if (TIFFFieldSet(tif,FIELD_JPEGACTABLES)) { + fprintf(fd, " JPEG AC Tables: "); + if (flags & TIFFPRINT_JPEGACTABLES) { + for (i = 0; i < td->td_samplesperpixel; i++) + JPEGPrintCTable(fd, td->td_actab[i]); + } else + fprintf(fd, "(present)\n"); + } +#endif + if (TIFFFieldSet(tif,FIELD_HALFTONEHINTS)) + fprintf(fd, " Halftone Hints: light %u dark %u\n", + td->td_halftonehints[0], td->td_halftonehints[1]); + if (TIFFFieldSet(tif,FIELD_ARTIST)) + fprintf(fd, " Artist: \"%s\"\n", td->td_artist); + if (TIFFFieldSet(tif,FIELD_DATETIME)) + fprintf(fd, " Date & Time: \"%s\"\n", td->td_datetime); + if (TIFFFieldSet(tif,FIELD_HOSTCOMPUTER)) + fprintf(fd, " Host Computer: \"%s\"\n", td->td_hostcomputer); + if (TIFFFieldSet(tif,FIELD_SOFTWARE)) + fprintf(fd, " Software: \"%s\"\n", td->td_software); + if (TIFFFieldSet(tif,FIELD_DOCUMENTNAME)) + fprintf(fd, " Document Name: \"%s\"\n", td->td_documentname); + if (TIFFFieldSet(tif,FIELD_IMAGEDESCRIPTION)) + fprintf(fd, " Image Description: \"%s\"\n", + td->td_imagedescription); + if (TIFFFieldSet(tif,FIELD_MAKE)) + fprintf(fd, " Make: \"%s\"\n", td->td_make); + if (TIFFFieldSet(tif,FIELD_MODEL)) + fprintf(fd, " Model: \"%s\"\n", td->td_model); + if (TIFFFieldSet(tif,FIELD_ORIENTATION)) { + fprintf(fd, " Orientation: "); + if (td->td_orientation < NORIENTNAMES) + fprintf(fd, "%s\n", orientNames[td->td_orientation]); + else + fprintf(fd, "%u (0x%x)\n", + td->td_orientation, td->td_orientation); + } + if (TIFFFieldSet(tif,FIELD_SAMPLESPERPIXEL)) + fprintf(fd, " Samples/Pixel: %u\n", td->td_samplesperpixel); + if (TIFFFieldSet(tif,FIELD_ROWSPERSTRIP)) { + fprintf(fd, " Rows/Strip: "); + if (td->td_rowsperstrip == 0xffffffffL) + fprintf(fd, "(infinite)\n"); + else + fprintf(fd, "%lu\n", (u_long) td->td_rowsperstrip); + } + if (TIFFFieldSet(tif,FIELD_MINSAMPLEVALUE)) + fprintf(fd, " Min Sample Value: %u\n", td->td_minsamplevalue); + if (TIFFFieldSet(tif,FIELD_MAXSAMPLEVALUE)) + fprintf(fd, " Max Sample Value: %u\n", td->td_maxsamplevalue); + if (TIFFFieldSet(tif,FIELD_PLANARCONFIG)) { + fprintf(fd, " Planar Configuration: "); + switch (td->td_planarconfig) { + case PLANARCONFIG_CONTIG: + fprintf(fd, "single image plane\n"); + break; + case PLANARCONFIG_SEPARATE: + fprintf(fd, "separate image planes\n"); + break; + default: + fprintf(fd, "%u (0x%x)\n", + td->td_planarconfig, td->td_planarconfig); + break; + } + } + if (TIFFFieldSet(tif,FIELD_PAGENAME)) + fprintf(fd, " Page Name: \"%s\"\n", td->td_pagename); + if (TIFFFieldSet(tif,FIELD_GROUP3OPTIONS)) { + fprintf(fd, " Group 3 Options:"); + sep = " "; + if (td->td_group3options & GROUP3OPT_2DENCODING) + fprintf(fd, "%s2-d encoding", sep), sep = "+"; + if (td->td_group3options & GROUP3OPT_FILLBITS) + fprintf(fd, "%sEOL padding", sep), sep = "+"; + if (td->td_group3options & GROUP3OPT_UNCOMPRESSED) + fprintf(fd, "%suncompressed data", sep); + fprintf(fd, " (%lu = 0x%lx)\n", + (u_long) td->td_group3options, + (u_long) td->td_group3options); + } + if (TIFFFieldSet(tif,FIELD_CLEANFAXDATA)) { + fprintf(fd, " Fax Data: "); + switch (td->td_cleanfaxdata) { + case CLEANFAXDATA_CLEAN: + fprintf(fd, "clean\n"); + break; + case CLEANFAXDATA_REGENERATED: + fprintf(fd, "receiver regenerated\n"); + break; + case CLEANFAXDATA_UNCLEAN: + fprintf(fd, "uncorrected errors\n"); + break; + default: + fprintf(fd, "(%u = 0x%x)\n", + td->td_cleanfaxdata, td->td_cleanfaxdata); + break; + } + } + if (TIFFFieldSet(tif,FIELD_BADFAXLINES)) + fprintf(fd, " Bad Fax Lines: %lu\n", + (u_long) td->td_badfaxlines); + if (TIFFFieldSet(tif,FIELD_BADFAXRUN)) + fprintf(fd, " Consecutive Bad Fax Lines: %u\n", + td->td_badfaxrun); + if (TIFFFieldSet(tif,FIELD_GROUP4OPTIONS)) { + fprintf(fd, " Group 4 Options:"); + if (td->td_group4options & GROUP4OPT_UNCOMPRESSED) + fprintf(fd, "uncompressed data"); + fprintf(fd, " (%lu = 0x%lx)\n", + (u_long) td->td_group4options, + (u_long) td->td_group4options); + } + if (TIFFFieldSet(tif,FIELD_PAGENUMBER)) + fprintf(fd, " Page Number: %u-%u\n", + td->td_pagenumber[0], td->td_pagenumber[1]); + if (TIFFFieldSet(tif,FIELD_COLORMAP)) { + fprintf(fd, " Color Map: "); + if (flags & TIFFPRINT_COLORMAP) { + fprintf(fd, "\n"); + n = 1L<td_bitspersample; + for (i = 0; i < n; i++) + fprintf(fd, " %5d: %5u %5u %5u\n", + i, + td->td_colormap[0][i], + td->td_colormap[1][i], + td->td_colormap[2][i]); + } else + fprintf(fd, "(present)\n"); + } +#ifdef COLORIMETRY_SUPPORT + if (TIFFFieldSet(tif,FIELD_WHITEPOINT)) + fprintf(fd, " White Point: %g-%g\n", + td->td_whitepoint[0], td->td_whitepoint[1]); + if (TIFFFieldSet(tif,FIELD_PRIMARYCHROMAS)) + fprintf(fd, " Primary Chromaticities: %g,%g %g,%g %g,%g\n", + td->td_primarychromas[0], td->td_primarychromas[1], + td->td_primarychromas[2], td->td_primarychromas[3], + td->td_primarychromas[4], td->td_primarychromas[5]); + if (TIFFFieldSet(tif,FIELD_REFBLACKWHITE)) { + fprintf(fd, " Reference Black/White:\n"); + for (i = 0; i < td->td_samplesperpixel; i++) + fprintf(fd, " %2d: %5g %5g\n", + i, + td->td_refblackwhite[2*i+0], + td->td_refblackwhite[2*i+1]); + } + if (TIFFFieldSet(tif,FIELD_TRANSFERFUNCTION)) { + fprintf(fd, " Transfer Function: "); + if (flags & TIFFPRINT_CURVES) { + fprintf(fd, "\n"); + n = 1L<td_bitspersample; + for (i = 0; i < n; i++) { + fprintf(fd, " %2d: %5u", + i, td->td_transferfunction[0][i]); + for (j = 1; j < td->td_samplesperpixel; j++) + fprintf(fd, " %5u", + td->td_transferfunction[j][i]); + putc('\n', fd); + } + } else + fprintf(fd, "(present)\n"); + } +#endif + if ((flags & TIFFPRINT_STRIPS) && + TIFFFieldSet(tif,FIELD_STRIPOFFSETS)) { + fprintf(fd, " %u %s:\n", + td->td_nstrips, + isTiled(tif) ? "Tiles" : "Strips"); + for (i = 0; i < td->td_nstrips; i++) + fprintf(fd, " %3d: [%8lu, %8lu]\n", + i, + (u_long) td->td_stripoffset[i], + (u_long) td->td_stripbytecount[i]); + } +} diff --git a/panda/src/tiff/tif_read.c b/panda/src/tiff/tif_read.c new file mode 100644 index 0000000000..6345d83267 --- /dev/null +++ b/panda/src/tiff/tif_read.c @@ -0,0 +1,563 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * Scanline-oriented Read Support + */ +#include "tiffiop.h" +#include +#include + +static int TIFFFillStrip(TIFF*, tstrip_t); +static int TIFFFillTile(TIFF*, ttile_t); +static int TIFFStartStrip(TIFF*, tstrip_t); +static int TIFFStartTile(TIFF*, ttile_t); +static int TIFFCheckRead(TIFF*, int); + +/* + * Seek to a random row+sample in a file. + */ +static int +TIFFSeek(TIFF* tif, uint32 row, tsample_t sample) +{ + register TIFFDirectory *td = &tif->tif_dir; + tstrip_t strip; + + if (row >= td->td_imagelength) { /* out of range */ + TIFFError(tif->tif_name, "%lu: Row out of range, max %lu", + (u_long) row, (u_long) td->td_imagelength); + return (0); + } + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) { + if (sample >= td->td_samplesperpixel) { + TIFFError(tif->tif_name, + "%lu: Sample out of range, max %lu", + (u_long) sample, (u_long) td->td_samplesperpixel); + return (0); + } + strip = sample*td->td_stripsperimage + row/td->td_rowsperstrip; + } else + strip = row / td->td_rowsperstrip; + if (strip != tif->tif_curstrip) { /* different strip, refill */ + if (!TIFFFillStrip(tif, strip)) + return (0); + } else if (row < tif->tif_row) { + /* + * Moving backwards within the same strip: backup + * to the start and then decode forward (below). + * + * NB: If you're planning on lots of random access within a + * strip, it's better to just read and decode the entire + * strip, and then access the decoded data in a random fashion. + */ + if (!TIFFStartStrip(tif, strip)) + return (0); + } + if (row != tif->tif_row) { + if (tif->tif_seek) { + /* + * Seek forward to the desired row. + */ + if (!(*tif->tif_seek)(tif, row - tif->tif_row)) + return (0); + tif->tif_row = row; + } else { + TIFFError(tif->tif_name, + "Compression algorithm does not support random access"); + return (0); + } + } + return (1); +} + +int +TIFFReadScanline(TIFF* tif, tdata_t buf, uint32 row, tsample_t sample) +{ + int e; + + if (!TIFFCheckRead(tif, 0)) + return (-1); + if (e = TIFFSeek(tif, row, sample)) { + /* + * Decompress desired row into user buffer. + */ + e = (*tif->tif_decoderow) + (tif, buf, tif->tif_scanlinesize, sample); + tif->tif_row++; + if (e) + (*tif->tif_postdecode)(tif, buf, tif->tif_scanlinesize); + } + return (e ? 1 : -1); +} + +/* + * Read a strip of data and decompress the specified + * amount into the user-supplied buffer. + */ +tsize_t +TIFFReadEncodedStrip(TIFF* tif, tstrip_t strip, tdata_t buf, tsize_t size) +{ + TIFFDirectory *td = &tif->tif_dir; + uint32 nrows; + tsize_t stripsize; + + if (!TIFFCheckRead(tif, 0)) + return (-1); + if (strip >= td->td_nstrips) { + TIFFError(tif->tif_name, "%ld: Strip out of range, max %ld", + (long) strip, (long) td->td_nstrips); + return (-1); + } + /* + * Calculate the strip size according to the number of + * rows in the strip (check for truncated last strip). + */ + if (strip != td->td_nstrips-1 || + (nrows = td->td_imagelength % td->td_rowsperstrip) == 0) + nrows = td->td_rowsperstrip; + stripsize = TIFFVStripSize(tif, nrows); + if (size == (tsize_t) -1) + size = stripsize; + else if (size > stripsize) + size = stripsize; + if (TIFFFillStrip(tif, strip) && + (*tif->tif_decodestrip)(tif, buf, size, strip / td->td_stripsperimage)) { + (*tif->tif_postdecode)(tif, buf, size); + return (size); + } else + return ((tsize_t) -1); +} + +static tsize_t +TIFFReadRawStrip1(TIFF* tif, + tstrip_t strip, tdata_t buf, tsize_t size, const char* module) +{ + TIFFDirectory *td = &tif->tif_dir; + + if (!isMapped(tif)) { + if (!SeekOK(tif, td->td_stripoffset[strip])) { + TIFFError(module, + "%s: Seek error at scanline %lu, strip %lu", + tif->tif_name, + (u_long) tif->tif_row, (u_long) strip); + return (-1); + } + if (!ReadOK(tif, buf, size)) { + TIFFError(module, "%s: Read error at scanline %lu", + tif->tif_name, (u_long) tif->tif_row); + return (-1); + } + } else { + if (td->td_stripoffset[strip] + size > tif->tif_size) { + TIFFError(module, + "%s: Seek error at scanline %lu, strip %lu", + tif->tif_name, + (u_long) tif->tif_row, (u_long) strip); + return (-1); + } + memcpy(buf, tif->tif_base + td->td_stripoffset[strip], size); + } + return (size); +} + +/* + * Read a strip of data from the file. + */ +tsize_t +TIFFReadRawStrip(TIFF* tif, tstrip_t strip, tdata_t buf, tsize_t size) +{ + static const char module[] = "TIFFReadRawStrip"; + TIFFDirectory *td = &tif->tif_dir; + tsize_t bytecount; + + if (!TIFFCheckRead(tif, 0)) + return (-1); + if (strip >= td->td_nstrips) { + TIFFError(tif->tif_name, "%lu: Strip out of range, max %lu", + (u_long) strip, (u_long) td->td_nstrips); + return (-1); + } + bytecount = td->td_stripbytecount[strip]; + if (size != (tsize_t)-1 && size < bytecount) + bytecount = size; + return (TIFFReadRawStrip1(tif, strip, buf, bytecount, module)); +} + +/* + * Read the specified strip and setup for decoding. + * The data buffer is expanded, as necessary, to + * hold the strip's data. + */ +static int +TIFFFillStrip(TIFF* tif, tstrip_t strip) +{ + static const char module[] = "TIFFFillStrip"; + TIFFDirectory *td = &tif->tif_dir; + tsize_t bytecount; + + bytecount = td->td_stripbytecount[strip]; + if (isMapped(tif) && + (td->td_fillorder == tif->tif_fillorder || (tif->tif_flags & TIFF_NOBITREV))) { + /* + * The image is mapped into memory and we either don't + * need to flip bits or the compression routine is going + * to handle this operation itself. In this case, avoid + * copying the raw data and instead just reference the + * data from the memory mapped file image. This assumes + * that the decompression routines do not modify the + * contents of the raw data buffer (if they try to, + * the application will get a fault since the file is + * mapped read-only). + */ + if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata) + _TIFFfree(tif->tif_rawdata); + tif->tif_flags &= ~TIFF_MYBUFFER; + if (td->td_stripoffset[strip] + bytecount > tif->tif_size) { + /* + * This error message might seem strange, but it's + * what would happen if a read were done instead. + */ + TIFFError(module, "%s: Read error on strip %lu", + tif->tif_name, (u_long) strip); + tif->tif_curstrip = -1; /* unknown state */ + return (0); + } + tif->tif_rawdatasize = bytecount; + tif->tif_rawdata = tif->tif_base + td->td_stripoffset[strip]; + } else { + /* + * Expand raw data buffer, if needed, to + * hold data strip coming from file + * (perhaps should set upper bound on + * the size of a buffer we'll use?). + */ + if (bytecount > tif->tif_rawdatasize) { + tif->tif_curstrip = -1; /* unknown state */ + if ((tif->tif_flags & TIFF_MYBUFFER) == 0) { + TIFFError(module, + "%s: Data buffer too small to hold strip %lu", + tif->tif_name, (u_long) strip); + return (0); + } + if (!TIFFReadBufferSetup(tif, 0, + roundup(bytecount, 1024))) + return (0); + } + if (TIFFReadRawStrip1(tif, strip, (u_char *)tif->tif_rawdata, + bytecount, module) != bytecount) + return (0); + if (td->td_fillorder != tif->tif_fillorder && + (tif->tif_flags & TIFF_NOBITREV) == 0) + TIFFReverseBits(tif->tif_rawdata, bytecount); + } + return (TIFFStartStrip(tif, strip)); +} + +/* + * Tile-oriented Read Support + * Contributed by Nancy Cam (Silicon Graphics). + */ + +/* + * Read and decompress a tile of data. The + * tile is selected by the (x,y,z,s) coordinates. + */ +tsize_t +TIFFReadTile(TIFF* tif, + tdata_t buf, uint32 x, uint32 y, uint32 z, tsample_t s) +{ + if (!TIFFCheckRead(tif, 1) || !TIFFCheckTile(tif, x, y, z, s)) + return (-1); + return (TIFFReadEncodedTile(tif, + TIFFComputeTile(tif, x, y, z, s), buf, (tsize_t) -1)); +} + +/* + * Read a tile of data and decompress the specified + * amount into the user-supplied buffer. + */ +tsize_t +TIFFReadEncodedTile(TIFF* tif, ttile_t tile, tdata_t buf, tsize_t size) +{ + TIFFDirectory *td = &tif->tif_dir; + tsize_t tilesize = tif->tif_tilesize; + + if (!TIFFCheckRead(tif, 1)) + return (-1); + if (tile >= td->td_nstrips) { + TIFFError(tif->tif_name, "%ld: Tile out of range, max %ld", + (long) tile, (u_long) td->td_nstrips); + return (-1); + } + if (size == (tsize_t) -1) + size = tilesize; + else if (size > tilesize) + size = tilesize; + if (TIFFFillTile(tif, tile) && + (*tif->tif_decodetile)(tif, buf, size, tile/td->td_stripsperimage)) { + (*tif->tif_postdecode)(tif, buf, size); + return (size); + } else + return (-1); +} + +static tsize_t +TIFFReadRawTile1(TIFF* tif, + ttile_t tile, tdata_t buf, tsize_t size, const char* module) +{ + TIFFDirectory *td = &tif->tif_dir; + + if (!isMapped(tif)) { + if (!SeekOK(tif, td->td_stripoffset[tile])) { + TIFFError(module, + "%s: Seek error at row %ld, col %ld, tile %ld", + tif->tif_name, + (long) tif->tif_row, + (long) tif->tif_col, + (long) tile); + return ((tsize_t) -1); + } + if (!ReadOK(tif, buf, size)) { + TIFFError(module, "%s: Read error at row %ld, col %ld", + tif->tif_name, + (long) tif->tif_row, + (long) tif->tif_col); + return ((tsize_t) -1); + } + } else { + if (td->td_stripoffset[tile] + size > tif->tif_size) { + TIFFError(module, + "%s: Seek error at row %ld, col %ld, tile %ld", + tif->tif_name, + (long) tif->tif_row, + (long) tif->tif_col, + (long) tile); + return ((tsize_t) -1); + } + memcpy(buf, tif->tif_base + td->td_stripoffset[tile], size); + } + return (size); +} + +/* + * Read a tile of data from the file. + */ +tsize_t +TIFFReadRawTile(TIFF* tif, ttile_t tile, tdata_t buf, tsize_t size) +{ + static const char module[] = "TIFFReadRawTile"; + TIFFDirectory *td = &tif->tif_dir; + tsize_t bytecount; + + if (!TIFFCheckRead(tif, 1)) + return ((tsize_t) -1); + if (tile >= td->td_nstrips) { + TIFFError(tif->tif_name, "%lu: Tile out of range, max %lu", + (u_long) tile, (u_long) td->td_nstrips); + return ((tsize_t) -1); + } + bytecount = td->td_stripbytecount[tile]; + if (size != (tsize_t) -1 && size < bytecount) + bytecount = size; + return (TIFFReadRawTile1(tif, tile, buf, bytecount, module)); +} + +/* + * Read the specified tile and setup for decoding. + * The data buffer is expanded, as necessary, to + * hold the tile's data. + */ +static int +TIFFFillTile(TIFF* tif, ttile_t tile) +{ + static const char module[] = "TIFFFillTile"; + TIFFDirectory *td = &tif->tif_dir; + tsize_t bytecount; + + bytecount = td->td_stripbytecount[tile]; + if (isMapped(tif) && + (td->td_fillorder == tif->tif_fillorder || (tif->tif_flags & TIFF_NOBITREV))) { + /* + * The image is mapped into memory and we either don't + * need to flip bits or the compression routine is going + * to handle this operation itself. In this case, avoid + * copying the raw data and instead just reference the + * data from the memory mapped file image. This assumes + * that the decompression routines do not modify the + * contents of the raw data buffer (if they try to, + * the application will get a fault since the file is + * mapped read-only). + */ + if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata) + _TIFFfree(tif->tif_rawdata); + tif->tif_flags &= ~TIFF_MYBUFFER; + if (td->td_stripoffset[tile] + bytecount > tif->tif_size) { + tif->tif_curtile = -1; /* unknown state */ + return (0); + } + tif->tif_rawdatasize = bytecount; + tif->tif_rawdata = tif->tif_base + td->td_stripoffset[tile]; + } else { + /* + * Expand raw data buffer, if needed, to + * hold data tile coming from file + * (perhaps should set upper bound on + * the size of a buffer we'll use?). + */ + if (bytecount > tif->tif_rawdatasize) { + tif->tif_curtile = -1; /* unknown state */ + if ((tif->tif_flags & TIFF_MYBUFFER) == 0) { + TIFFError(module, + "%s: Data buffer too small to hold tile %ld", + tif->tif_name, (long) tile); + return (0); + } + if (!TIFFReadBufferSetup(tif, 0, + roundup(bytecount, 1024))) + return (0); + } + if (TIFFReadRawTile1(tif, tile, (u_char *)tif->tif_rawdata, + bytecount, module) != bytecount) + return (0); + if (td->td_fillorder != tif->tif_fillorder && + (tif->tif_flags & TIFF_NOBITREV) == 0) + TIFFReverseBits(tif->tif_rawdata, bytecount); + } + return (TIFFStartTile(tif, tile)); +} + +/* + * Setup the raw data buffer in preparation for + * reading a strip of raw data. If the buffer + * is specified as zero, then a buffer of appropriate + * size is allocated by the library. Otherwise, + * the client must guarantee that the buffer is + * large enough to hold any individual strip of + * raw data. + */ +int +TIFFReadBufferSetup(TIFF* tif, tdata_t bp, tsize_t size) +{ + static const char module[] = "TIFFReadBufferSetup"; + + if (tif->tif_rawdata) { + if (tif->tif_flags & TIFF_MYBUFFER) + _TIFFfree(tif->tif_rawdata); + tif->tif_rawdata = NULL; + } + if (bp) { + tif->tif_rawdatasize = size; + tif->tif_rawdata = (tidata_t) bp; + tif->tif_flags &= ~TIFF_MYBUFFER; + } else { + tif->tif_rawdatasize = roundup(size, 1024); + tif->tif_rawdata = (tdata_t) _TIFFmalloc(tif->tif_rawdatasize); + tif->tif_flags |= TIFF_MYBUFFER; + } + if (tif->tif_rawdata == NULL) { + TIFFError(module, + "%s: No space for data buffer at scanline %ld", + tif->tif_name, (long) tif->tif_row); + tif->tif_rawdatasize = 0; + return (0); + } + return (1); +} + +/* + * Set state to appear as if a + * strip has just been read in. + */ +static int +TIFFStartStrip(TIFF* tif, tstrip_t strip) +{ + TIFFDirectory *td = &tif->tif_dir; + + tif->tif_curstrip = strip; + tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip; + tif->tif_rawcp = tif->tif_rawdata; + tif->tif_rawcc = td->td_stripbytecount[strip]; + return (tif->tif_predecode == NULL || (*tif->tif_predecode)(tif)); +} + +/* + * Set state to appear as if a + * tile has just been read in. + */ +static int +TIFFStartTile(TIFF* tif, ttile_t tile) +{ + TIFFDirectory *td = &tif->tif_dir; + + tif->tif_curtile = tile; + tif->tif_row = + (tile % howmany(td->td_imagewidth, td->td_tilewidth)) * + td->td_tilelength; + tif->tif_col = + (tile % howmany(td->td_imagelength, td->td_tilelength)) * + td->td_tilewidth; + tif->tif_rawcp = tif->tif_rawdata; + tif->tif_rawcc = td->td_stripbytecount[tile]; + return (tif->tif_predecode == NULL || (*tif->tif_predecode)(tif)); +} + +static int +TIFFCheckRead(TIFF* tif, int tiles) +{ + if (tif->tif_mode == O_WRONLY) { + TIFFError(tif->tif_name, "File not open for reading"); + return (0); + } + if (tiles ^ isTiled(tif)) { + TIFFError(tif->tif_name, tiles ? + "Can not read tiles from a stripped image" : + "Can not read scanlines from a tiled image"); + return (0); + } + return (1); +} + +void +TIFFNoPostDecode(TIFF* tif, tidata_t buf, tsize_t cc) +{ +} + +void +TIFFSwab16BitData(TIFF* tif, tidata_t buf, tsize_t cc) +{ + assert((cc & 1) == 0); + TIFFSwabArrayOfShort((uint16*) buf, cc/2); +} + +void +TIFFSwab32BitData(TIFF* tif, tidata_t buf, tsize_t cc) +{ + assert((cc & 3) == 0); + TIFFSwabArrayOfLong((uint32*) buf, cc/4); +} diff --git a/panda/src/tiff/tif_strip.c b/panda/src/tiff/tif_strip.c new file mode 100644 index 0000000000..6fda0a8afa --- /dev/null +++ b/panda/src/tiff/tif_strip.c @@ -0,0 +1,116 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Strip-organized Image Support Routines. + */ +#include "tiffiop.h" + +/* + * Compute which strip a (row,sample) value is in. + */ +tstrip_t +TIFFComputeStrip(TIFF* tif, uint32 row, tsample_t sample) +{ + TIFFDirectory *td = &tif->tif_dir; + tstrip_t strip; + + strip = row / td->td_rowsperstrip; + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) { + if (sample >= td->td_samplesperpixel) { + TIFFError(tif->tif_name, + "%u: Sample out of range, max %u", + sample, td->td_samplesperpixel); + return ((tstrip_t) 0); + } + strip += sample*td->td_stripsperimage; + } + return (strip); +} + +/* + * Compute how many strips are in an image. + */ +tstrip_t +TIFFNumberOfStrips(TIFF* tif) +{ + TIFFDirectory *td = &tif->tif_dir; + tstrip_t nstrips; + + nstrips = (td->td_rowsperstrip == 0xffffffff ? + (td->td_imagelength != 0 ? 1 : 0) : + howmany(td->td_imagelength, td->td_rowsperstrip)); + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) + nstrips *= td->td_samplesperpixel; + return (nstrips); +} + +/* + * Compute the # bytes in a variable height, row-aligned strip. + */ +tsize_t +TIFFVStripSize(TIFF* tif, uint32 nrows) +{ + TIFFDirectory *td = &tif->tif_dir; + + if (nrows == (uint32) -1) + nrows = td->td_imagelength; +#ifdef YCBCR_SUPPORT + if (td->td_planarconfig == PLANARCONFIG_CONTIG && + td->td_photometric == PHOTOMETRIC_YCBCR) { + /* + * Packed YCbCr data contain one Cb+Cr for every + * HorizontalSampling*VerticalSampling Y values. + * Must also roundup width and height when calculating + * since images that are not a multiple of the + * horizontal/vertical subsampling area include + * YCbCr data for the extended image. + */ + tsize_t w = + roundup(td->td_imagewidth, td->td_ycbcrsubsampling[0]); + tsize_t scanline = howmany(w*td->td_bitspersample, 8); + tsize_t samplingarea = + td->td_ycbcrsubsampling[0]*td->td_ycbcrsubsampling[1]; + nrows = roundup(nrows, td->td_ycbcrsubsampling[1]); + /* NB: don't need howmany here 'cuz everything is rounded */ + return (nrows*scanline + 2*(nrows*scanline / samplingarea)); + } else +#endif + return (nrows * TIFFScanlineSize(tif)); +} + +/* + * Compute the # bytes in a (row-aligned) strip. + */ +tsize_t +TIFFStripSize(TIFF* tif) +{ + return (TIFFVStripSize(tif, tif->tif_dir.td_rowsperstrip)); +} diff --git a/panda/src/tiff/tif_swab.c b/panda/src/tiff/tif_swab.c new file mode 100644 index 0000000000..56983056b7 --- /dev/null +++ b/panda/src/tiff/tif_swab.c @@ -0,0 +1,192 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library Bit & Byte Swapping Support. + * + * XXX We assume short = 16-bits and long = 32-bits XXX + */ +#include "tiffiop.h" + +#ifndef TIFFSwabShort +void +TIFFSwabShort(uint16* wp) +{ + register u_char *cp = (u_char *)wp; + int t; + + t = cp[1]; cp[1] = cp[0]; cp[0] = t; +} +#endif + +#ifndef TIFFSwabLong +void +TIFFSwabLong(uint32* lp) +{ + register u_char *cp = (u_char *)lp; + int t; + + t = cp[3]; cp[3] = cp[0]; cp[0] = t; + t = cp[2]; cp[2] = cp[1]; cp[1] = t; +} +#endif + +#ifndef TIFFSwabArrayOfShort +void +TIFFSwabArrayOfShort(uint16* wp, register u_long n) +{ + register u_char *cp; + register int t; + + /* XXX unroll loop some */ + while (n-- > 0) { + cp = (unsigned char *)wp; + t = cp[1]; cp[1] = cp[0]; cp[0] = t; + wp++; + } +} +#endif + +#ifndef TIFFSwabArrayOfLong +void +TIFFSwabArrayOfLong(register uint32* lp, register u_long n) +{ + register unsigned char *cp; + register int t; + + /* XXX unroll loop some */ + while (n-- > 0) { + cp = (unsigned char *)lp; + t = cp[3]; cp[3] = cp[0]; cp[0] = t; + t = cp[2]; cp[2] = cp[1]; cp[1] = t; + lp++; + } +} +#endif + +/* + * Bit reversal tables. TIFFBitRevTable[] gives + * the bit reversed value of . Used in various + * places in the library when the FillOrder requires + * bit reversal of byte values (e.g. CCITT Fax 3 + * encoding/decoding). TIFFNoBitRevTable is provided + * for algorithms that want an equivalent table that + * do not reverse bit values. + */ +static const unsigned char TIFFBitRevTable[256] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; +static const unsigned char TIFFNoBitRevTable[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +}; + +const unsigned char* +TIFFGetBitRevTable(int reversed) +{ + return (reversed ? TIFFBitRevTable : TIFFNoBitRevTable); +} + +void +TIFFReverseBits(register u_char* cp, register u_long n) +{ + for (; n > 8; n -= 8) { + cp[0] = TIFFBitRevTable[cp[0]]; + cp[1] = TIFFBitRevTable[cp[1]]; + cp[2] = TIFFBitRevTable[cp[2]]; + cp[3] = TIFFBitRevTable[cp[3]]; + cp[4] = TIFFBitRevTable[cp[4]]; + cp[5] = TIFFBitRevTable[cp[5]]; + cp[6] = TIFFBitRevTable[cp[6]]; + cp[7] = TIFFBitRevTable[cp[7]]; + cp += 8; + } + while (n-- > 0) + *cp = TIFFBitRevTable[*cp], cp++; +} diff --git a/panda/src/tiff/tif_thunder.c b/panda/src/tiff/tif_thunder.c new file mode 100644 index 0000000000..7b4d6f2cdf --- /dev/null +++ b/panda/src/tiff/tif_thunder.c @@ -0,0 +1,152 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * ThunderScan 4-bit Compression Algorithm Support + */ +#include "tiffiop.h" + +/* + * ThunderScan uses an encoding scheme designed for + * 4-bit pixel values. Data is encoded in bytes, with + * each byte split into a 2-bit code word and a 6-bit + * data value. The encoding gives raw data, runs of + * pixels, or pixel values encoded as a delta from the + * previous pixel value. For the latter, either 2-bit + * or 3-bit delta values are used, with the deltas packed + * into a single byte. + */ +#define THUNDER_DATA 0x3f /* mask for 6-bit data */ +#define THUNDER_CODE 0xc0 /* mask for 2-bit code word */ +/* code values */ +#define THUNDER_RUN 0x00 /* run of pixels w/ encoded count */ +#define THUNDER_2BITDELTAS 0x40 /* 3 pixels w/ encoded 2-bit deltas */ +#define DELTA2_SKIP 2 /* skip code for 2-bit deltas */ +#define THUNDER_3BITDELTAS 0x80 /* 2 pixels w/ encoded 3-bit deltas */ +#define DELTA3_SKIP 4 /* skip code for 3-bit deltas */ +#define THUNDER_RAW 0xc0 /* raw data encoded */ + +static const int twobitdeltas[4] = { 0, 1, 0, -1 }; +static const int threebitdeltas[8] = { 0, 1, 2, 3, 0, -3, -2, -1 }; + +#define SETPIXEL(op, v) { \ + lastpixel = (v) & 0xf; \ + if (npixels++ & 1) \ + *op++ |= lastpixel; \ + else \ + op[0] = lastpixel << 4; \ +} + +static int +ThunderDecode(TIFF* tif, tidata_t op, tsize_t maxpixels) +{ + register u_char *bp; + register tsize_t cc; + u_int lastpixel; + tsize_t npixels; + + bp = (u_char *)tif->tif_rawcp; + cc = tif->tif_rawcc; + lastpixel = 0; + npixels = 0; + while (cc > 0 && npixels < maxpixels) { + int n, delta; + + n = *bp++, cc--; + switch (n & THUNDER_CODE) { + case THUNDER_RUN: /* pixel run */ + /* + * Replicate the last pixel n times, + * where n is the lower-order 6 bits. + */ + if (npixels & 1) { + op[0] |= lastpixel; + lastpixel = *op++; npixels++; n--; + } else + lastpixel |= lastpixel << 4; + npixels += n; + for (; n > 0; n -= 2) + *op++ = lastpixel; + if (n == -1) + *--op &= 0xf0; + lastpixel &= 0xf; + break; + case THUNDER_2BITDELTAS: /* 2-bit deltas */ + if ((delta = ((n >> 4) & 3)) != DELTA2_SKIP) + SETPIXEL(op, lastpixel + twobitdeltas[delta]); + if ((delta = ((n >> 2) & 3)) != DELTA2_SKIP) + SETPIXEL(op, lastpixel + twobitdeltas[delta]); + if ((delta = (n & 3)) != DELTA2_SKIP) + SETPIXEL(op, lastpixel + twobitdeltas[delta]); + break; + case THUNDER_3BITDELTAS: /* 3-bit deltas */ + if ((delta = ((n >> 3) & 7)) != DELTA3_SKIP) + SETPIXEL(op, lastpixel + threebitdeltas[delta]); + if ((delta = (n & 7)) != DELTA3_SKIP) + SETPIXEL(op, lastpixel + threebitdeltas[delta]); + break; + case THUNDER_RAW: /* raw data */ + SETPIXEL(op, n); + break; + } + } + tif->tif_rawcp = (tidata_t) bp; + tif->tif_rawcc = cc; + if (npixels != maxpixels) { + TIFFError(tif->tif_name, + "ThunderDecode: %s data at scanline %ld (%lu != %lu)", + npixels < maxpixels ? "Not enough" : "Too much", + (long) tif->tif_row, (long) npixels, (long) maxpixels); + return (0); + } + return (1); +} + +static int +ThunderDecodeRow(TIFF* tif, tidata_t buf, tsize_t occ, tsample_t s) +{ + tidata_t row = buf; + + while ((long)occ > 0) { + if (!ThunderDecode(tif, row, tif->tif_dir.td_imagewidth)) + return (0); + occ -= tif->tif_scanlinesize; + row += tif->tif_scanlinesize; + } + return (1); +} + +int +TIFFInitThunderScan(TIFF* tif) +{ + tif->tif_decoderow = ThunderDecodeRow; + tif->tif_decodestrip = ThunderDecodeRow; + return (1); +} diff --git a/panda/src/tiff/tif_tile.c b/panda/src/tiff/tif_tile.c new file mode 100644 index 0000000000..a0ef79bbdb --- /dev/null +++ b/panda/src/tiff/tif_tile.c @@ -0,0 +1,192 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Tiled Image Support Routines. + */ +#include "tiffiop.h" + +/* + * Compute which tile an (x,y,z,s) value is in. + */ +ttile_t +TIFFComputeTile(TIFF* tif, uint32 x, uint32 y, uint32 z, tsample_t s) +{ + TIFFDirectory *td = &tif->tif_dir; + u_long dx = td->td_tilewidth; + u_long dy = td->td_tilelength; + u_long dz = td->td_tiledepth; + ttile_t tile = 1; + + if (td->td_imagedepth == 1) + z = 0; + if (dx == (u_long) -1) + dx = td->td_imagewidth; + if (dy == (u_long) -1) + dy = td->td_imagelength; + if (dz == (u_long) -1) + dz = td->td_imagedepth; + if (dx != 0 && dy != 0 && dz != 0) { + u_long xpt = howmany(td->td_imagewidth, dx); + u_long ypt = howmany(td->td_imagelength, dy); + u_long zpt = howmany(td->td_imagedepth, dz); + + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) + tile = (xpt*ypt*zpt)*s + + (xpt*ypt)*(z/dz) + + xpt*(y/dy) + + x/dx; + else + tile = (xpt*ypt)*(z/dz) + xpt*(y/dy) + x/dx + s; + } + return (tile); +} + +/* + * Check an (x,y,z,s) coordinate + * against the image bounds. + */ +TIFFCheckTile(TIFF* tif, uint32 x, uint32 y, uint32 z, tsample_t s) +{ + TIFFDirectory *td = &tif->tif_dir; + + if (x >= td->td_imagewidth) { + TIFFError(tif->tif_name, "Col %ld out of range, max %lu", + (long) x, (u_long) td->td_imagewidth); + return (0); + } + if (y >= td->td_imagelength) { + TIFFError(tif->tif_name, "Row %ld out of range, max %lu", + (long) y, (u_long) td->td_imagelength); + return (0); + } + if (z >= td->td_imagedepth) { + TIFFError(tif->tif_name, "Depth %ld out of range, max %lu", + (long) z, (u_long) td->td_imagedepth); + return (0); + } + if (td->td_planarconfig == PLANARCONFIG_SEPARATE && + s >= td->td_samplesperpixel) { + TIFFError(tif->tif_name, "Sample %d out of range, max %u", + (int) s, td->td_samplesperpixel); + return (0); + } + return (1); +} + +/* + * Compute how many tiles are in an image. + */ +ttile_t +TIFFNumberOfTiles(TIFF* tif) +{ + TIFFDirectory *td = &tif->tif_dir; + u_long dx = td->td_tilewidth; + u_long dy = td->td_tilelength; + u_long dz = td->td_tiledepth; + ttile_t ntiles; + + if (dx == (u_long) -1) + dx = td->td_imagewidth; + if (dy == (u_long) -1) + dy = td->td_imagelength; + if (dz == (u_long) -1) + dz = td->td_imagedepth; + ntiles = (dx != 0 && dy != 0 && dz != 0) ? + (howmany(td->td_imagewidth, dx) * howmany(td->td_imagelength, dy) * + howmany(td->td_imagedepth, dz)) : + 0; + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) + ntiles *= td->td_samplesperpixel; + return (ntiles); +} + +/* + * Compute the # bytes in each row of a tile. + */ +tsize_t +TIFFTileRowSize(TIFF* tif) +{ + TIFFDirectory *td = &tif->tif_dir; + tsize_t rowsize; + + if (td->td_tilelength == 0 || td->td_tilewidth == 0) + return ((tsize_t) 0); + rowsize = td->td_bitspersample * td->td_tilewidth; + if (td->td_planarconfig == PLANARCONFIG_CONTIG) + rowsize *= td->td_samplesperpixel; + return (howmany(rowsize, 8)); +} + +/* + * Compute the # bytes in a variable length, row-aligned tile. + */ +tsize_t +TIFFVTileSize(TIFF* tif, uint32 nrows) +{ + TIFFDirectory *td = &tif->tif_dir; + tsize_t tilesize; + + if (td->td_tilelength == 0 || td->td_tilewidth == 0 || + td->td_tiledepth == 0) + return ((tsize_t) 0); +#ifdef YCBCR_SUPPORT + if (td->td_planarconfig == PLANARCONFIG_CONTIG && + td->td_photometric == PHOTOMETRIC_YCBCR) { + /* + * Packed YCbCr data contain one Cb+Cr for every + * HorizontalSampling*VerticalSampling Y values. + * Must also roundup width and height when calculating + * since images that are not a multiple of the + * horizontal/vertical subsampling area include + * YCbCr data for the extended image. + */ + tsize_t w = + roundup(td->td_tilewidth, td->td_ycbcrsubsampling[0]); + tsize_t rowsize = howmany(w*td->td_bitspersample, 8); + tsize_t samplingarea = + td->td_ycbcrsubsampling[0]*td->td_ycbcrsubsampling[1]; + nrows = roundup(nrows, td->td_ycbcrsubsampling[1]); + /* NB: don't need howmany here 'cuz everything is rounded */ + tilesize = nrows*rowsize + 2*(nrows*rowsize / samplingarea); + } else +#endif + tilesize = nrows * TIFFTileRowSize(tif); + return (tilesize * td->td_tiledepth); +} + +/* + * Compute the # bytes in a row-aligned tile. + */ +tsize_t +TIFFTileSize(TIFF* tif) +{ + return (TIFFVTileSize(tif, tif->tif_dir.td_tilelength)); +} diff --git a/panda/src/tiff/tif_unix.c b/panda/src/tiff/tif_unix.c new file mode 100644 index 0000000000..0be2918630 --- /dev/null +++ b/panda/src/tiff/tif_unix.c @@ -0,0 +1,170 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library UNIX-specific Routines. + */ +#include "tiffiop.h" +#ifndef PENV_WIN32 +#include +#endif +#include + +static tsize_t +_tiffReadProc(thandle_t fd, tdata_t buf, tsize_t size) +{ + return (read((int) fd, buf, (size_t) size)); +} + +static tsize_t +_tiffWriteProc(thandle_t fd, tdata_t buf, tsize_t size) +{ + return (write((int) fd, buf, (size_t) size)); +} + +static toff_t +_tiffSeekProc(thandle_t fd, off_t off, int whence) +{ + return ((toff_t) lseek((int) fd, (off_t) off, whence)); +} + +static int +_tiffCloseProc(thandle_t fd) +{ + return (close((int) fd)); +} + +#include + +static toff_t +_tiffSizeProc(thandle_t fd) +{ +#ifdef _AM29K + long fsize; + return ((fsize = lseek((int) fd, 0, SEEK_END)) < 0 ? 0 : fsize); +#else + struct stat sb; + return (toff_t) (fstat((int) fd, &sb) < 0 ? 0 : sb.st_size); +#endif +} + +#ifdef MMAP_SUPPORT +#include + +static int +_tiffMapProc(thandle_t fd, tdata_t* pbase, toff_t* psize) +{ + toff_t size = _tiffSizeProc(fd); + if (size != (toff_t) -1) { + *pbase = (tdata_t) + mmap(0, size, PROT_READ, MAP_SHARED, (int) fd, 0); + if (*pbase != (tdata_t) -1) { + *psize = size; + return (1); + } + } + return (0); +} + +static void +_tiffUnmapProc(thandle_t fd, tdata_t base, toff_t size) +{ + (void) munmap(base, (off_t) size); +} +#else /* !MMAP_SUPPORT */ +static int +_tiffMapProc(thandle_t fd, tdata_t* pbase, toff_t* psize) +{ + return (0); +} + +static void +_tiffUnmapProc(thandle_t fd, tdata_t base, toff_t size) +{ +} +#endif /* !MMAP_SUPPORT */ + +/* + * Open a TIFF file descriptor for read/writing. + */ +TIFF* +TIFFFdOpen(int fd, const char* name, const char* mode) +{ + TIFF* tif; + + tif = TIFFClientOpen(name, mode, + (thandle_t) fd, + _tiffReadProc, _tiffWriteProc, + _tiffSeekProc, _tiffCloseProc, _tiffSizeProc, + _tiffMapProc, _tiffUnmapProc); + if (tif) + tif->tif_fd = fd; + return (tif); +} + +/* + * Open a TIFF file for read/writing. + */ +TIFF* +TIFFOpen(const char* name, const char* mode) +{ + static const char module[] = "TIFFOpen"; + int m, fd; + + m = _TIFFgetMode(mode, module); + if (m == -1) + return ((TIFF*)0); +#ifdef _AM29K + fd = open(name, m); +#else + fd = open(name, m, 0666); +#endif + if (fd < 0) { + TIFFError(module, "%s: Cannot open", name); + return ((TIFF *)0); + } + return (TIFFFdOpen(fd, name, mode)); +} + +void* +_TIFFmalloc(size_t s) +{ + return (malloc(s)); +} + +void +_TIFFfree(void* p) +{ + free(p); +} + +void* +_TIFFrealloc(void* p, size_t s) +{ + return (realloc(p, s)); +} diff --git a/panda/src/tiff/tif_version.c b/panda/src/tiff/tif_version.c new file mode 100644 index 0000000000..79550f97fb --- /dev/null +++ b/panda/src/tiff/tif_version.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 1992 Sam Leffler + * Copyright (c) 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ +#include "tiffiop.h" + +static const char TIFFVersion[] = "\ +LIBTIFF, Version 3.3 BETA\n\ +Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler\n\ +Copyright (c) 1991, 1992 Silicon Graphics, Inc.\ +"; + +const char* +TIFFGetVersion(void) +{ + return (TIFFVersion); +} diff --git a/panda/src/tiff/tif_vms.c b/panda/src/tiff/tif_vms.c new file mode 100644 index 0000000000..8429a3b8a6 --- /dev/null +++ b/panda/src/tiff/tif_vms.c @@ -0,0 +1,259 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library VMS-specific Routines. + */ +#include "tiffiop.h" + +static tsize_t +_tiffReadProc(thandle_t fd, tdata_t buf, tsize_t size) +{ + return (read((int) fd, buf, size)); +} + +static tsize_t +_tiffWriteProc(thandle_t fd, tdata_t buf, u_long size) +{ + return (write((int) fd, buf, size)); +} + +static toff_t +_tiffSeekProc(thandle_t fd, toff_t off, int whence) +{ + return ((toff_t) lseek((int) fd, (off_t) off, whence)); +} + +static int +_tiffCloseProc(thandle_t fd) +{ + return (close((int) fd)); +} + +#include + +static toff_t +_tiffSizeProc(thandle_t fd) +{ + struct stat sb; + return (toff_t) (fstat((int) fd, &sb) < 0 ? 0 : sb.st_size); +} + +#ifdef MMAP_SUPPORT +#include +#include + +/* + * Table for storing information on current open sections. + * (Should really be a linked list) + */ +#define MAX_MAPPED 100 +static int no_mapped = 0; +static struct { + char *base; + char *top; + unsigned short channel; +} map_table[MAX_MAPPED]; + +/* + * This routine maps a file into a private section. Note that this + * method of accessing a file is by far the fastest under VMS. + * The routine may fail (i.e. return 0) for several reasons, for + * example: + * - There is no more room for storing the info on sections. + * - The process is out of open file quota, channels, ... + * - fd does not describe an opened file. + * - The file is already opened for write access by this process + * or another process + * - There is no free "hole" in virtual memory that fits the + * size of the file + */ +static int +_tiffMapProc(thandle_t fd, tdata_t* pbase, toff_t* psize) +{ + char name[256]; + struct FAB fab; + unsigned short channel; + char *inadr[2], *retadr[2]; + unsigned long status; + long size; + + if (no_mapped >= MAX_MAPPED) + return(0); + /* + * We cannot use a file descriptor, we + * must open the file once more. + */ + if (getname(fd, name, 1) == NULL) + return(0); + /* prepare the FAB for a user file open */ + fab = cc$rms_fab; + fab.fab$v_ufo = 1; + fab.fab$b_fac = FAB$M_GET; + fab.fab$b_shr = FAB$M_SHRGET; + fab.fab$l_fna = name; + fab.fab$b_fns = strlen(name); + status = sys$open(&fab); /* open file & get channel number */ + if ((status&1) == 0) + return(0); + channel = (unsigned short)fab.fab$l_stv; + inadr[0] = inadr[1] = (char *)0; /* just an address in P0 space */ + /* + * Map the blocks of the file up to + * the EOF block into virtual memory. + */ + size = _tiffSizeProc(fd); + status = sys$crmpsc(inadr, retadr, 0, SEC$M_EXPREG, 0,0,0, channel, + howmany(size,512), 0,0,0); + if ((status&1) == 0){ + sys$dassgn(channel); + return(0); + } + *pbase = (tdata_t) retadr[0]; /* starting virtual address */ + /* + * Use the size of the file up to the + * EOF mark for UNIX compatibility. + */ + *psize = (toff_t) size; + /* Record the section in the table */ + map_table[no_mapped].base = retadr[0]; + map_table[no_mapped].top = retadr[1]; + map_table[no_mapped].channel = channel; + no_mapped++; + + return(1); +} + +/* + * This routine unmaps a section from the virtual address space of + * the process, but only if the base was the one returned from a + * call to TIFFMapFileContents. + */ +static void +_tiffUnmapProc(thandle_t fd, tdata_t base, toff_t size) +{ + char *inadr[2]; + int i, j; + + /* Find the section in the table */ + for (i = 0;i < no_mapped; i++) { + if (map_table[i].base == (char *) base) { + /* Unmap the section */ + inadr[0] = (char *) base; + inadr[1] = map_table[i].top; + sys$deltva(inadr, 0, 0); + sys$dassgn(map_table[i].channel); + /* Remove this section from the list */ + for (j = i+1; j < no_mapped; j++) + map_table[j-1] = map_table[j]; + no_mapped--; + return; + } + } +} +#else /* !MMAP_SUPPORT */ +static int +_tiffMapProc(thandle_t fd, tdata_t* pbase, toff_t* psize) +{ + return (0); +} + +static void +_tiffUnmapProc(thandle_t fd, tdata_t base, toff_t size) +{ +} +#endif /* !MMAP_SUPPORT */ + +/* + * Open a TIFF file descriptor for read/writing. + */ +TIFF* +TIFFFdOpen(int fd, const char* name, const char* mode) +{ + TIFF* tif; + + tif = TIFFClientOpen(name, mode, + (thandle_t) fd, + _tiffReadProc, _tiffWriteProc, _tiffSeekProc, _tiffCloseProc, + _tiffSizeProc, _tiffMapProc, _tiffUnmapProc); + if (tif) + tif->tif_fd = fd; + return (tif); +} + +/* + * Open a TIFF file for read/writing. + */ +TIFF* +TIFFOpen(const char* name, const char* mode) +{ + static const char module[] = "TIFFOpen"; + int m, fd; + + m = _TIFFgetMode(mode, module); + if (m == -1) + return ((TIFF*)0); + if (m&O_TRUNC){ + /* + * There is a bug in open in VAXC. If you use + * open w/ m=O_RDWR|O_CREAT|O_TRUNC the + * wrong thing happens. On the other hand + * creat does the right thing. + */ + fd = creat(name, 0666, + "alq = 128", "deq = 64", "mbc = 32", + "fop = tef", "ctx = stm"); + } else if (m&O_RDWR) { + fd = open(name, m, 0666, + "deq = 64", "mbc = 32", "fop = tef", "ctx = stm"); + } else + fd = open(name, m, 0666, "mbc = 32"); + if (fd < 0) { + TIFFError(module, "%s: Cannot open", name); + return ((TIFF*)0); + } + return (TIFFFdOpen(fd, name, mode)); +} + +void* +_TIFFmalloc(size_t s) +{ + return (malloc(s)); +} + +void +_TIFFfree(void* p) +{ + free(p); +} + +void* +_TIFFrealloc(void* p, size_t s) +{ + return (realloc(p, s)); +} diff --git a/panda/src/tiff/tif_warning.c b/panda/src/tiff/tif_warning.c new file mode 100644 index 0000000000..dc675897c6 --- /dev/null +++ b/panda/src/tiff/tif_warning.c @@ -0,0 +1,64 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + */ +#include "tiffiop.h" +#include + +static void +defaultHandler(const char* module, const char* fmt, va_list ap) +{ + if (module != NULL) + fprintf(stderr, "%s: ", module); + fprintf(stderr, "Warning, "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, ".\n"); +} + +static TIFFErrorHandler _warningHandler = defaultHandler; + +TIFFErrorHandler +TIFFSetWarningHandler(TIFFErrorHandler handler) +{ + TIFFErrorHandler prev = _warningHandler; + _warningHandler = handler; + return (prev); +} + +void +TIFFWarning(const char* module, const char* fmt, ...) +{ + if (_warningHandler) { + va_list ap; + va_start(ap, fmt); + (*_warningHandler)(module, fmt, ap); + va_end(ap); + } +} diff --git a/panda/src/tiff/tif_write.c b/panda/src/tiff/tif_write.c new file mode 100644 index 0000000000..1b79de0e38 --- /dev/null +++ b/panda/src/tiff/tif_write.c @@ -0,0 +1,567 @@ +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Scanline-oriented Write Support + */ +#include "tiffiop.h" +#include +#include + +#define STRIPINCR 20 /* expansion factor on strip array */ + +static int TIFFWriteCheck(TIFF*, int, const char*); +static int TIFFBufferSetup(TIFF*, const char*); +static int TIFFGrowStrips(TIFF*, int, const char*); +static int TIFFAppendToStrip(TIFF*, tstrip_t, tidata_t, tsize_t); +static int TIFFSetupStrips(TIFF*); + +int +TIFFWriteScanline(TIFF* tif, tdata_t buf, uint32 row, tsample_t sample) +{ + static const char module[] = "TIFFWriteScanline"; + register TIFFDirectory *td; + int status, imagegrew = 0; + tstrip_t strip; + + if (!TIFFWriteCheck(tif, 0, module)) + return (-1); + /* + * Handle delayed allocation of data buffer. This + * permits it to be sized more intelligently (using + * directory information). + */ + if ((tif->tif_flags & TIFF_BUFFERSETUP) == 0) { + if (!TIFFBufferSetup(tif, module)) + return (-1); + tif->tif_flags |= TIFF_BUFFERSETUP; + } + td = &tif->tif_dir; + /* + * Extend image length if needed + * (but only for PlanarConfig=1). + */ + if (row >= td->td_imagelength) { /* extend image */ + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) { + TIFFError(tif->tif_name, + "Can not change \"ImageLength\" when using separate planes"); + return (-1); + } + td->td_imagelength = row+1; + imagegrew = 1; + } + /* + * Calculate strip and check for crossings. + */ + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) { + if (sample >= td->td_samplesperpixel) { + TIFFError(tif->tif_name, + "%d: Sample out of range, max %d", + sample, td->td_samplesperpixel); + return (-1); + } + strip = sample*td->td_stripsperimage + row/td->td_rowsperstrip; + } else + strip = row / td->td_rowsperstrip; + if (strip != tif->tif_curstrip) { + /* + * Changing strips -- flush any data present. + */ + if (!TIFFFlushData(tif)) + return (-1); + tif->tif_curstrip = strip; + /* + * Watch out for a growing image. The value of + * strips/image will initially be 1 (since it + * can't be deduced until the imagelength is known). + */ + if (strip >= td->td_stripsperimage && imagegrew) + td->td_stripsperimage = + howmany(td->td_imagelength, td->td_rowsperstrip); + tif->tif_row = + (strip % td->td_stripsperimage) * td->td_rowsperstrip; + if (tif->tif_preencode && !(*tif->tif_preencode)(tif)) + return (-1); + tif->tif_flags |= TIFF_POSTENCODE; + } + /* + * Check strip array to make sure there's space. + * We don't support dynamically growing files that + * have data organized in separate bitplanes because + * it's too painful. In that case we require that + * the imagelength be set properly before the first + * write (so that the strips array will be fully + * allocated above). + */ + if (strip >= td->td_nstrips && !TIFFGrowStrips(tif, 1, module)) + return (-1); + /* + * Ensure the write is either sequential or at the + * beginning of a strip (or that we can randomly + * access the data -- i.e. no encoding). + */ + if (row != tif->tif_row) { + if (tif->tif_seek) { + if (row < tif->tif_row) { + /* + * Moving backwards within the same strip: + * backup to the start and then decode + * forward (below). + */ + tif->tif_row = (strip % td->td_stripsperimage) * + td->td_rowsperstrip; + tif->tif_rawcp = tif->tif_rawdata; + } + /* + * Seek forward to the desired row. + */ + if (!(*tif->tif_seek)(tif, row - tif->tif_row)) + return (-1); + tif->tif_row = row; + } else { + TIFFError(tif->tif_name, + "Compression algorithm does not support random access"); + return (-1); + } + } + status = (*tif->tif_encoderow)(tif, buf, tif->tif_scanlinesize, sample); + tif->tif_row++; + return (status); +} + +/* + * Encode the supplied data and write it to the + * specified strip. There must be space for the + * data; we don't check if strips overlap! + * + * NB: Image length must be setup before writing; this + * interface does not support automatically growing + * the image on each write (as TIFFWriteScanline does). + */ +tsize_t +TIFFWriteEncodedStrip(TIFF* tif, tstrip_t strip, tdata_t data, tsize_t cc) +{ + static const char module[] = "TIFFWriteEncodedStrip"; + TIFFDirectory *td = &tif->tif_dir; + + if (!TIFFWriteCheck(tif, 0, module)) + return ((tsize_t) -1); + if (strip >= td->td_nstrips) { + TIFFError(module, "%s: Strip %lu out of range, max %lu", + tif->tif_name, (u_long) strip, (u_long) td->td_nstrips); + return ((tsize_t) -1); + } + /* + * Handle delayed allocation of data buffer. This + * permits it to be sized according to the directory + * info. + */ + if ((tif->tif_flags & TIFF_BUFFERSETUP) == 0) { + if (!TIFFBufferSetup(tif, module)) + return ((tsize_t) -1); + tif->tif_flags |= TIFF_BUFFERSETUP; + } + tif->tif_curstrip = strip; + tif->tif_flags &= ~TIFF_POSTENCODE; + if (tif->tif_preencode && !(*tif->tif_preencode)(tif)) + return ((tsize_t) -1); + if (!(*tif->tif_encodestrip)(tif, + data, cc, strip / td->td_stripsperimage)) + return ((tsize_t) 0); + if (tif->tif_postencode && !(*tif->tif_postencode)(tif)) + return (-1); + if (td->td_fillorder != tif->tif_fillorder && + (tif->tif_flags & TIFF_NOBITREV) == 0) + TIFFReverseBits(tif->tif_rawdata, tif->tif_rawcc); + if (tif->tif_rawcc > 0 && + !TIFFAppendToStrip(tif, strip, tif->tif_rawdata, tif->tif_rawcc)) + return (-1); + tif->tif_rawcc = 0; + tif->tif_rawcp = tif->tif_rawdata; + return (cc); +} + +/* + * Write the supplied data to the specified strip. + * There must be space for the data; we don't check + * if strips overlap! + * + * NB: Image length must be setup before writing; this + * interface does not support automatically growing + * the image on each write (as TIFFWriteScanline does). + */ +tsize_t +TIFFWriteRawStrip(TIFF* tif, tstrip_t strip, tdata_t data, tsize_t cc) +{ + static const char module[] = "TIFFWriteRawStrip"; + + if (!TIFFWriteCheck(tif, 0, module)) + return ((tsize_t) -1); + if (strip >= tif->tif_dir.td_nstrips) { + TIFFError(module, "%s: Strip %lu out of range, max %lu", + tif->tif_name, (u_long) strip, + (u_long) tif->tif_dir.td_nstrips); + return ((tsize_t) -1); + } + return (TIFFAppendToStrip(tif, strip, data, cc) ? cc : (tsize_t) -1); +} + +/* + * Write and compress a tile of data. The + * tile is selected by the (x,y,z,s) coordinates. + */ +tsize_t +TIFFWriteTile(TIFF* tif, + tdata_t buf, uint32 x, uint32 y, uint32 z, tsample_t s) +{ + if (!TIFFCheckTile(tif, x, y, z, s)) + return (-1); + /* + * NB: A tile size of -1 is used instead of tif_tilesize knowing + * that TIFFWriteEncodedTile will clamp this to the tile size. + * This is done because the tile size may not be defined until + * after the output buffer is setup in TIFFBufferSetup. + */ + return (TIFFWriteEncodedTile(tif, + TIFFComputeTile(tif, x, y, z, s), buf, (tsize_t) -1)); +} + +/* + * Encode the supplied data and write it to the + * specified tile. There must be space for the + * data. The function clamps individual writes + * to a tile to the tile size, but does not (and + * can not) check that multiple writes to the same + * tile do not write more than tile size data. + * + * NB: Image length must be setup before writing; this + * interface does not support automatically growing + * the image on each write (as TIFFWriteScanline does). + */ +tsize_t +TIFFWriteEncodedTile(TIFF* tif, ttile_t tile, tdata_t data, tsize_t cc) +{ + static const char module[] = "TIFFWriteEncodedTile"; + TIFFDirectory *td; + + if (!TIFFWriteCheck(tif, 1, module)) + return ((tsize_t) -1); + td = &tif->tif_dir; + if (tile >= td->td_nstrips) { + TIFFError(module, "%s: Tile %lu out of range, max %lu", + tif->tif_name, (u_long) tile, (u_long) td->td_nstrips); + return ((tsize_t) -1); + } + /* + * Handle delayed allocation of data buffer. This + * permits it to be sized more intelligently (using + * directory information). + */ + if ((tif->tif_flags & TIFF_BUFFERSETUP) == 0) { + if (!TIFFBufferSetup(tif, module)) + return ((tsize_t) -1); + tif->tif_flags |= TIFF_BUFFERSETUP; + } + tif->tif_curtile = tile; + /* + * Compute tiles per row & per column to compute + * current row and column + */ + tif->tif_row = (tile % howmany(td->td_imagelength, td->td_tilelength)) + * td->td_tilelength; + tif->tif_col = (tile % howmany(td->td_imagewidth, td->td_tilewidth)) + * td->td_tilewidth; + + tif->tif_flags &= ~TIFF_POSTENCODE; + if (tif->tif_preencode && !(*tif->tif_preencode)(tif)) + return ((tsize_t) -1); + /* + * Clamp write amount to the tile size. This is mostly + * done so that callers can pass in some large number + * (e.g. -1) and have the tile size used instead. + */ + if (cc > tif->tif_tilesize) + cc = tif->tif_tilesize; + if (!(*tif->tif_encodetile)(tif, data, cc, tile/td->td_stripsperimage)) + return ((tsize_t) 0); + if (tif->tif_postencode && !(*tif->tif_postencode)(tif)) + return ((tsize_t) -1); + if (td->td_fillorder != tif->tif_fillorder && + (tif->tif_flags & TIFF_NOBITREV) == 0) + TIFFReverseBits((u_char *)tif->tif_rawdata, tif->tif_rawcc); + if (tif->tif_rawcc > 0 && !TIFFAppendToStrip(tif, tile, + tif->tif_rawdata, tif->tif_rawcc)) + return ((tsize_t) -1); + tif->tif_rawcc = 0; + tif->tif_rawcp = tif->tif_rawdata; + return (cc); +} + +/* + * Write the supplied data to the specified strip. + * There must be space for the data; we don't check + * if strips overlap! + * + * NB: Image length must be setup before writing; this + * interface does not support automatically growing + * the image on each write (as TIFFWriteScanline does). + */ +tsize_t +TIFFWriteRawTile(TIFF* tif, ttile_t tile, tdata_t data, tsize_t cc) +{ + static const char module[] = "TIFFWriteRawTile"; + + if (!TIFFWriteCheck(tif, 1, module)) + return ((tsize_t) -1); + if (tile >= tif->tif_dir.td_nstrips) { + TIFFError(module, "%s: Tile %lu out of range, max %lu", + tif->tif_name, (u_long) tile, + (u_long) tif->tif_dir.td_nstrips); + return ((tsize_t) -1); + } + return (TIFFAppendToStrip(tif, tile, data, cc) ? cc : (tsize_t) -1); +} + +static int +TIFFSetupStrips(TIFF* tif) +{ +#define isUnspecified(td, v) \ + (td->v == 0xffffffff || (td)->td_imagelength == 0) + register TIFFDirectory *td = &tif->tif_dir; + + if (!isTiled(tif)) + td->td_stripsperimage = isUnspecified(td, td_rowsperstrip) ? + 1 : howmany(td->td_imagelength, td->td_rowsperstrip); + else + td->td_stripsperimage = isUnspecified(td, td_tilelength) ? + 1 : TIFFNumberOfTiles(tif); + td->td_nstrips = td->td_stripsperimage; + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) + td->td_nstrips *= td->td_samplesperpixel; + td->td_stripoffset = (uint32 *) + _TIFFmalloc(td->td_nstrips * sizeof (uint32)); + td->td_stripbytecount = (uint32 *) + _TIFFmalloc(td->td_nstrips * sizeof (uint32)); + if (td->td_stripoffset == NULL || td->td_stripbytecount == NULL) + return (0); + /* + * Place data at the end-of-file + * (by setting offsets to zero). + */ + memset(td->td_stripoffset, 0, td->td_nstrips*sizeof (uint32)); + memset(td->td_stripbytecount, 0, td->td_nstrips*sizeof (uint32)); + TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS); + TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS); + return (1); +#undef isUnspecified +} + +/* + * Verify file is writable and that the directory + * information is setup properly. In doing the latter + * we also "freeze" the state of the directory so + * that important information is not changed. + */ +static int +TIFFWriteCheck(TIFF* tif, int tiles, const char* module) +{ + if (tif->tif_mode == O_RDONLY) { + TIFFError(module, "%s: File not open for writing", + tif->tif_name); + return (0); + } + if (tiles ^ isTiled(tif)) { + TIFFError(tif->tif_name, tiles ? + "Can not write tiles to a stripped image" : + "Can not write scanlines to a tiled image"); + return (0); + } + /* + * On the first write verify all the required information + * has been setup and initialize any data structures that + * had to wait until directory information was set. + * Note that a lot of our work is assumed to remain valid + * because we disallow any of the important parameters + * from changing after we start writing (i.e. once + * TIFF_BEENWRITING is set, TIFFSetField will only allow + * the image's length to be changed). + */ + if ((tif->tif_flags & TIFF_BEENWRITING) == 0) { + if (!TIFFFieldSet(tif, FIELD_IMAGEDIMENSIONS)) { + TIFFError(module, + "%s: Must set \"ImageWidth\" before writing data", + tif->tif_name); + return (0); + } + if (!TIFFFieldSet(tif, FIELD_PLANARCONFIG)) { + TIFFError(module, + "%s: Must set \"PlanarConfiguration\" before writing data", + tif->tif_name); + return (0); + } + if (tif->tif_dir.td_stripoffset == NULL && + !TIFFSetupStrips(tif)) { + tif->tif_dir.td_nstrips = 0; + TIFFError(module, "%s: No space for %s arrays", + tif->tif_name, isTiled(tif) ? "tile" : "strip"); + return (0); + } + tif->tif_flags |= TIFF_BEENWRITING; + } + return (1); +} + +/* + * Setup the raw data buffer used for encoding. + */ +static int +TIFFBufferSetup(TIFF* tif, const char* module) +{ + tsize_t size; + + if (isTiled(tif)) + tif->tif_tilesize = size = TIFFTileSize(tif); + else + tif->tif_scanlinesize = size = TIFFScanlineSize(tif); + /* + * Make raw data buffer at least 8K + */ + if (size < 8*1024) + size = 8*1024; + tif->tif_rawdata = _TIFFmalloc(size); + if (tif->tif_rawdata == NULL) { + TIFFError(module, "%s: No space for output buffer", + tif->tif_name); + return (0); + } + tif->tif_flags |= TIFF_MYBUFFER; + tif->tif_rawdatasize = size; + tif->tif_rawcc = 0; + tif->tif_rawcp = tif->tif_rawdata; + return (1); +} + +/* + * Grow the strip data structures by delta strips. + */ +static int +TIFFGrowStrips(TIFF* tif, int delta, const char* module) +{ + TIFFDirectory *td = &tif->tif_dir; + + assert(td->td_planarconfig == PLANARCONFIG_CONTIG); + td->td_stripoffset = (uint32*)_TIFFrealloc(td->td_stripoffset, + (td->td_nstrips + delta) * sizeof (uint32)); + td->td_stripbytecount = (uint32*)_TIFFrealloc(td->td_stripbytecount, + (td->td_nstrips + delta) * sizeof (uint32)); + if (td->td_stripoffset == NULL || td->td_stripbytecount == NULL) { + td->td_nstrips = 0; + TIFFError(module, "%s: No space to expand strip arrays", + tif->tif_name); + return (0); + } + memset(td->td_stripoffset+td->td_nstrips, 0, delta*sizeof (uint32)); + memset(td->td_stripbytecount+td->td_nstrips, 0, delta*sizeof (uint32)); + td->td_nstrips += delta; + return (1); +} + +/* + * Append the data to the specified strip. + * + * NB: We don't check that there's space in the + * file (i.e. that strips do not overlap). + */ +static int +TIFFAppendToStrip(TIFF* tif, tstrip_t strip, tidata_t data, tsize_t cc) +{ + TIFFDirectory *td = &tif->tif_dir; + static const char module[] = "TIFFAppendToStrip"; + + if (td->td_stripoffset[strip] == 0 || tif->tif_curoff == 0) { + /* + * No current offset, set the current strip. + */ + if (td->td_stripoffset[strip] != 0) { + if (!SeekOK(tif, td->td_stripoffset[strip])) { + TIFFError(module, + "%s: Seek error at scanline %lu", + tif->tif_name, (u_long) tif->tif_row); + return (0); + } + } else + td->td_stripoffset[strip] = + TIFFSeekFile(tif, 0L, L_XTND); + tif->tif_curoff = td->td_stripoffset[strip]; + } + if (!WriteOK(tif, data, cc)) { + TIFFError(module, "%s: Write error at scanline %lu", + tif->tif_name, (u_long) tif->tif_row); + return (0); + } + tif->tif_curoff += cc; + td->td_stripbytecount[strip] += cc; + return (1); +} + +/* + * Internal version of TIFFFlushData that can be + * called by ``encodestrip routines'' w/o concern + * for infinite recursion. + */ +int +TIFFFlushData1(TIFF* tif) +{ + if (tif->tif_rawcc > 0) { + if (tif->tif_dir.td_fillorder != tif->tif_fillorder && + (tif->tif_flags & TIFF_NOBITREV) == 0) + TIFFReverseBits((u_char *)tif->tif_rawdata, + tif->tif_rawcc); + if (!TIFFAppendToStrip(tif, + isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip, + tif->tif_rawdata, tif->tif_rawcc)) + return (0); + tif->tif_rawcc = 0; + tif->tif_rawcp = tif->tif_rawdata; + } + return (1); +} + +/* + * Set the current write offset. This should only be + * used to set the offset to a known previous location + * (very carefully), or to 0 so that the next write gets + * appended to the end of the file. + */ +void +TIFFSetWriteOffset(TIFF* tif, toff_t off) +{ + tif->tif_curoff = off; +} diff --git a/panda/src/tiff/tiff.h b/panda/src/tiff/tiff.h new file mode 100644 index 0000000000..56c24855e1 --- /dev/null +++ b/panda/src/tiff/tiff.h @@ -0,0 +1,276 @@ +/* $Header$ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _TIFF_ +#define _TIFF_ +/* + * Tag Image File Format (TIFF) + * + * Based on Rev 6.0 from: + * Developer's Desk + * Aldus Corporation + * 411 First Ave. South + * Suite 200 + * Seattle, WA 98104 + * 206-622-5500 + */ +#define TIFF_VERSION 42 + +#define TIFF_BIGENDIAN 0x4d4d +#define TIFF_LITTLEENDIAN 0x4949 + +/* + * Intrinsic data types required by the file format: + * + * 8-bit quantities char/unsigned char + * 16-bit quantities int16/uint16 + * 32-bit quantities int32/uint32 + * strings unsigned char* + */ +typedef short int16; +typedef unsigned short uint16; /* sizeof (uint16) must == 2 */ +#if defined(__alpha) +typedef int int32; +typedef unsigned int uint32; /* sizeof (uint32) must == 4 */ +#else +typedef long int32; +typedef unsigned long uint32; /* sizeof (uint32) must == 4 */ +#endif + +typedef struct { + uint16 tiff_magic; /* magic number (defines byte order) */ + uint16 tiff_version; /* TIFF version number */ + uint32 tiff_diroff; /* byte offset to first directory */ +} TIFFHeader; + +/* + * TIFF Image File Directories are comprised of + * a table of field descriptors of the form shown + * below. The table is sorted in ascending order + * by tag. The values associated with each entry + * are disjoint and may appear anywhere in the file + * (so long as they are placed on a word boundary). + * + * If the value is 4 bytes or less, then it is placed + * in the offset field to save space. If the value + * is less than 4 bytes, it is left-justified in the + * offset field. + */ +typedef struct { + uint16 tdir_tag; /* see below */ + uint16 tdir_type; /* data type; see below */ + uint32 tdir_count; /* number of items; length in spec */ + uint32 tdir_offset; /* byte offset to field data */ +} TIFFDirEntry; + +/* + * NB: In the comments below, + * - items marked with a + are obsoleted by revision 5.0, + * - items marked with a ! are introduced in revision 6.0. + * - items marked with a $ are obsoleted by revision 6.0. + */ + +/* + * Tag data type information. + * + * Note: RATIONALs are the ratio of two 32-bit integer values. + */ +typedef enum { + TIFF_NOTYPE = 0, /* placeholder */ + TIFF_BYTE = 1, /* 8-bit unsigned integer */ + TIFF_ASCII = 2, /* 8-bit bytes w/ last byte null */ + TIFF_SHORT = 3, /* 16-bit unsigned integer */ + TIFF_LONG = 4, /* 32-bit unsigned integer */ + TIFF_RATIONAL = 5, /* 64-bit unsigned fraction */ + TIFF_SBYTE = 6, /* !8-bit signed integer */ + TIFF_UNDEFINED = 7, /* !8-bit untyped data */ + TIFF_SSHORT = 8, /* !16-bit signed integer */ + TIFF_SLONG = 9, /* !32-bit signed integer */ + TIFF_SRATIONAL = 10, /* !64-bit signed fraction */ + TIFF_FLOAT = 11, /* !32-bit IEEE floating point */ + TIFF_DOUBLE = 12 /* !64-bit IEEE floating point */ +} TIFFDataType; + +/* + * TIFF Tag Definitions. + */ +#define TIFFTAG_SUBFILETYPE 254 /* subfile data descriptor */ +#define FILETYPE_REDUCEDIMAGE 0x1 /* reduced resolution version */ +#define FILETYPE_PAGE 0x2 /* one page of many */ +#define FILETYPE_MASK 0x4 /* transparency mask */ +#define TIFFTAG_OSUBFILETYPE 255 /* +kind of data in subfile */ +#define OFILETYPE_IMAGE 1 /* full resolution image data */ +#define OFILETYPE_REDUCEDIMAGE 2 /* reduced size image data */ +#define OFILETYPE_PAGE 3 /* one page of many */ +#define TIFFTAG_IMAGEWIDTH 256 /* image width in pixels */ +#define TIFFTAG_IMAGELENGTH 257 /* image height in pixels */ +#define TIFFTAG_BITSPERSAMPLE 258 /* bits per channel (sample) */ +#define TIFFTAG_COMPRESSION 259 /* data compression technique */ +#define COMPRESSION_NONE 1 /* dump mode */ +#define COMPRESSION_CCITTRLE 2 /* CCITT modified Huffman RLE */ +#define COMPRESSION_CCITTFAX3 3 /* CCITT Group 3 fax encoding */ +#define COMPRESSION_CCITTFAX4 4 /* CCITT Group 4 fax encoding */ +#define COMPRESSION_LZW 5 /* Lempel-Ziv & Welch */ +#define COMPRESSION_JPEG 6 /* !JPEG compression */ +#define COMPRESSION_NEXT 32766 /* NeXT 2-bit RLE */ +#define COMPRESSION_CCITTRLEW 32771 /* #1 w/ word alignment */ +#define COMPRESSION_PACKBITS 32773 /* Macintosh RLE */ +#define COMPRESSION_THUNDERSCAN 32809 /* ThunderScan RLE */ +#define TIFFTAG_PHOTOMETRIC 262 /* photometric interpretation */ +#define PHOTOMETRIC_MINISWHITE 0 /* min value is white */ +#define PHOTOMETRIC_MINISBLACK 1 /* min value is black */ +#define PHOTOMETRIC_RGB 2 /* RGB color model */ +#define PHOTOMETRIC_PALETTE 3 /* color map indexed */ +#define PHOTOMETRIC_MASK 4 /* $holdout mask */ +#define PHOTOMETRIC_SEPARATED 5 /* !color separations */ +#define PHOTOMETRIC_YCBCR 6 /* !CCIR 601 */ +#define PHOTOMETRIC_CIELAB 8 /* !1976 CIE L*a*b* */ +#define TIFFTAG_THRESHHOLDING 263 /* +thresholding used on data */ +#define THRESHHOLD_BILEVEL 1 /* b&w art scan */ +#define THRESHHOLD_HALFTONE 2 /* or dithered scan */ +#define THRESHHOLD_ERRORDIFFUSE 3 /* usually floyd-steinberg */ +#define TIFFTAG_CELLWIDTH 264 /* +dithering matrix width */ +#define TIFFTAG_CELLLENGTH 265 /* +dithering matrix height */ +#define TIFFTAG_FILLORDER 266 /* data order within a byte */ +#define FILLORDER_MSB2LSB 1 /* most significant -> least */ +#define FILLORDER_LSB2MSB 2 /* least significant -> most */ +#define TIFFTAG_DOCUMENTNAME 269 /* name of doc. image is from */ +#define TIFFTAG_IMAGEDESCRIPTION 270 /* info about image */ +#define TIFFTAG_MAKE 271 /* scanner manufacturer name */ +#define TIFFTAG_MODEL 272 /* scanner model name/number */ +#define TIFFTAG_STRIPOFFSETS 273 /* offsets to data strips */ +#define TIFFTAG_ORIENTATION 274 /* +image orientation */ +#define ORIENTATION_TOPLEFT 1 /* row 0 top, col 0 lhs */ +#define ORIENTATION_TOPRIGHT 2 /* row 0 top, col 0 rhs */ +#define ORIENTATION_BOTRIGHT 3 /* row 0 bottom, col 0 rhs */ +#define ORIENTATION_BOTLEFT 4 /* row 0 bottom, col 0 lhs */ +#define ORIENTATION_LEFTTOP 5 /* row 0 lhs, col 0 top */ +#define ORIENTATION_RIGHTTOP 6 /* row 0 rhs, col 0 top */ +#define ORIENTATION_RIGHTBOT 7 /* row 0 rhs, col 0 bottom */ +#define ORIENTATION_LEFTBOT 8 /* row 0 lhs, col 0 bottom */ +#define TIFFTAG_SAMPLESPERPIXEL 277 /* samples per pixel */ +#define TIFFTAG_ROWSPERSTRIP 278 /* rows per strip of data */ +#define TIFFTAG_STRIPBYTECOUNTS 279 /* bytes counts for strips */ +#define TIFFTAG_MINSAMPLEVALUE 280 /* +minimum sample value */ +#define TIFFTAG_MAXSAMPLEVALUE 281 /* +maximum sample value */ +#define TIFFTAG_XRESOLUTION 282 /* pixels/resolution in x */ +#define TIFFTAG_YRESOLUTION 283 /* pixels/resolution in y */ +#define TIFFTAG_PLANARCONFIG 284 /* storage organization */ +#define PLANARCONFIG_CONTIG 1 /* single image plane */ +#define PLANARCONFIG_SEPARATE 2 /* separate planes of data */ +#define TIFFTAG_PAGENAME 285 /* page name image is from */ +#define TIFFTAG_XPOSITION 286 /* x page offset of image lhs */ +#define TIFFTAG_YPOSITION 287 /* y page offset of image lhs */ +#define TIFFTAG_FREEOFFSETS 288 /* +byte offset to free block */ +#define TIFFTAG_FREEBYTECOUNTS 289 /* +sizes of free blocks */ +#define TIFFTAG_GRAYRESPONSEUNIT 290 /* $gray scale curve accuracy */ +#define GRAYRESPONSEUNIT_10S 1 /* tenths of a unit */ +#define GRAYRESPONSEUNIT_100S 2 /* hundredths of a unit */ +#define GRAYRESPONSEUNIT_1000S 3 /* thousandths of a unit */ +#define GRAYRESPONSEUNIT_10000S 4 /* ten-thousandths of a unit */ +#define GRAYRESPONSEUNIT_100000S 5 /* hundred-thousandths */ +#define TIFFTAG_GRAYRESPONSECURVE 291 /* $gray scale response curve */ +#define TIFFTAG_GROUP3OPTIONS 292 /* 32 flag bits */ +#define GROUP3OPT_2DENCODING 0x1 /* 2-dimensional coding */ +#define GROUP3OPT_UNCOMPRESSED 0x2 /* data not compressed */ +#define GROUP3OPT_FILLBITS 0x4 /* fill to byte boundary */ +#define TIFFTAG_GROUP4OPTIONS 293 /* 32 flag bits */ +#define GROUP4OPT_UNCOMPRESSED 0x2 /* data not compressed */ +#define TIFFTAG_RESOLUTIONUNIT 296 /* units of resolutions */ +#define RESUNIT_NONE 1 /* no meaningful units */ +#define RESUNIT_INCH 2 /* english */ +#define RESUNIT_CENTIMETER 3 /* metric */ +#define TIFFTAG_PAGENUMBER 297 /* page numbers of multi-page */ +#define TIFFTAG_COLORRESPONSEUNIT 300 /* $color curve accuracy */ +#define COLORRESPONSEUNIT_10S 1 /* tenths of a unit */ +#define COLORRESPONSEUNIT_100S 2 /* hundredths of a unit */ +#define COLORRESPONSEUNIT_1000S 3 /* thousandths of a unit */ +#define COLORRESPONSEUNIT_10000S 4 /* ten-thousandths of a unit */ +#define COLORRESPONSEUNIT_100000S 5 /* hundred-thousandths */ +#define TIFFTAG_TRANSFERFUNCTION 301 /* !colorimetry info */ +#define TIFFTAG_SOFTWARE 305 /* name & release */ +#define TIFFTAG_DATETIME 306 /* creation date and time */ +#define TIFFTAG_ARTIST 315 /* creator of image */ +#define TIFFTAG_HOSTCOMPUTER 316 /* machine where created */ +#define TIFFTAG_PREDICTOR 317 /* prediction scheme w/ LZW */ +#define TIFFTAG_WHITEPOINT 318 /* image white point */ +#define TIFFTAG_PRIMARYCHROMATICITIES 319 /* !primary chromaticities */ +#define TIFFTAG_COLORMAP 320 /* RGB map for pallette image */ +#define TIFFTAG_HALFTONEHINTS 321 /* !highlight+shadow info */ +#define TIFFTAG_TILEWIDTH 322 /* !rows/data tile */ +#define TIFFTAG_TILELENGTH 323 /* !cols/data tile */ +#define TIFFTAG_TILEOFFSETS 324 /* !offsets to data tiles */ +#define TIFFTAG_TILEBYTECOUNTS 325 /* !byte counts for tiles */ +#define TIFFTAG_BADFAXLINES 326 /* lines w/ wrong pixel count */ +#define TIFFTAG_CLEANFAXDATA 327 /* regenerated line info */ +#define CLEANFAXDATA_CLEAN 0 /* no errors detected */ +#define CLEANFAXDATA_REGENERATED 1 /* receiver regenerated lines */ +#define CLEANFAXDATA_UNCLEAN 2 /* uncorrected errors exist */ +#define TIFFTAG_CONSECUTIVEBADFAXLINES 328 /* max consecutive bad lines */ +#define TIFFTAG_INKSET 332 /* !inks in separated image */ +#define INKSET_CMYK 1 /* !cyan-magenta-yellow-black */ +#define TIFFTAG_INKNAMES 333 /* !ascii names of inks */ +#define TIFFTAG_DOTRANGE 336 /* !0% and 100% dot codes */ +#define TIFFTAG_TARGETPRINTER 337 /* !separation target */ +#define TIFFTAG_EXTRASAMPLES 338 /* !info about extra samples */ +#define EXTRASAMPLE_UNSPECIFIED 0 /* !unspecified data */ +#define EXTRASAMPLE_ASSOCALPHA 1 /* !associated alpha data */ +#define EXTRASAMPLE_UNASSALPHA 2 /* !unassociated alpha data */ +#define TIFFTAG_SAMPLEFORMAT 339 /* !data sample format */ +#define SAMPLEFORMAT_UINT 1 /* !unsigned integer data */ +#define SAMPLEFORMAT_INT 2 /* !signed integer data */ +#define SAMPLEFORMAT_IEEEFP 3 /* !IEEE floating point data */ +#define SAMPLEFORMAT_VOID 4 /* !untyped data */ +#define TIFFTAG_SMINSAMPLEVALUE 340 /* !variable MinSampleValue */ +#define TIFFTAG_SMAXSAMPLEVALUE 341 /* !variable MaxSampleValue */ +#define TIFFTAG_JPEGPROC 512 /* !JPEG processing algorithm */ +#define JPEGPROC_BASELINE 1 /* !baseline sequential */ +#define JPEGPROC_LOSSLESS 14 /* !Huffman coded lossless */ +#define TIFFTAG_JPEGIFOFFSET 513 /* !pointer to SOI marker */ +#define TIFFTAG_JPEGIFBYTECOUNT 514 /* !JFIF stream length */ +#define TIFFTAG_JPEGRESTARTINTERVAL 515 /* !restart interval length */ +#define TIFFTAG_JPEGLOSSLESSPREDICTORS 517 /* !lossless proc predictor */ +#define TIFFTAG_JPEGPOINTTRANSFORM 518 /* !lossless point transform */ +#define TIFFTAG_JPEGQTABLES 519 /* !Q matrice offsets */ +#define TIFFTAG_JPEGDCTABLES 520 /* !DCT table offsets */ +#define TIFFTAG_JPEGACTABLES 521 /* !AC coefficient offsets */ +#define TIFFTAG_YCBCRCOEFFICIENTS 529 /* !RGB -> YCbCr transform */ +#define TIFFTAG_YCBCRSUBSAMPLING 530 /* !YCbCr subsampling factors */ +#define TIFFTAG_YCBCRPOSITIONING 531 /* !subsample positioning */ +#define YCBCRPOSITION_CENTERED 1 /* !as in PostScript Level 2 */ +#define YCBCRPOSITION_COSITED 2 /* !as in CCIR 601-1 */ +#define TIFFTAG_REFERENCEBLACKWHITE 532 /* !colorimetry info */ +/* tags 32952-32956 are private tags registered to Island Graphics */ +#define TIFFTAG_REFPTS 32953 /* image reference points */ +#define TIFFTAG_REGIONTACKPOINT 32954 /* region-xform tack point */ +#define TIFFTAG_REGIONWARPCORNERS 32955 /* warp quadrilateral */ +#define TIFFTAG_REGIONAFFINE 32956 /* affine transformation mat */ +/* tags 32995-32999 are private tags registered to SGI */ +#define TIFFTAG_MATTEING 32995 /* $use ExtraSamples */ +#define TIFFTAG_DATATYPE 32996 /* $use SampleFormat */ +#define TIFFTAG_IMAGEDEPTH 32997 /* z depth of image */ +#define TIFFTAG_TILEDEPTH 32998 /* z depth/data tile */ +#endif /* _TIFF_ */ diff --git a/panda/src/tiff/tiffcomp.h b/panda/src/tiff/tiffcomp.h new file mode 100644 index 0000000000..bafa6cd39b --- /dev/null +++ b/panda/src/tiff/tiffcomp.h @@ -0,0 +1,111 @@ +/* $Header$ */ + +/* + * Copyright (c) 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _COMPAT_ +#define _COMPAT_ +/* + * This file contains a hodgepodge of definitions and + * declarations that are needed to provide compatibility + * between the native system and the base implementation + * that the library assumes. + * + * NB: This file is a mess. + */ + +/* + * Setup basic type definitions and function declaratations. + */ +#ifdef THINK_C +#include + +#include +#include +#endif +#include +#ifdef applec +#include +#else +#ifndef THINK_C +#include +#endif +#endif +#ifdef VMS +#include +#include +#else +#include +#endif +#if defined(THINK_C) || defined(applec) +#include +#define BSDTYPES +#endif + +/* + * Workarounds for BSD lseek definitions. + */ +#if defined(SYSV) || defined(VMS) +#if defined(SYSV) +#include +#endif +#define L_SET SEEK_SET +#define L_INCR SEEK_CUR +#define L_XTND SEEK_END +#endif /* defined(SYSV) || defined(VMS) */ +#ifndef L_SET +#define L_SET 0 +#define L_INCR 1 +#define L_XTND 2 +#endif + +/* + * The library uses memset, memcpy, and memcmp. + * ANSI C and System V define these in string.h. + */ +#include + +/* + * The BSD typedefs are used throughout the library. + * If your system doesn't have them in , + * then define BSDTYPES in your Makefile. + */ +#ifdef BSDTYPES +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; +#endif + +/* + * dblparam_t is the type that a double precision + * floating point value will have on the parameter + * stack (when coerced by the compiler). + */ +#ifdef applec +typedef extended dblparam_t; +#else +typedef double dblparam_t; +#endif +#endif /* _COMPAT_ */ diff --git a/panda/src/tiff/tiffconf.h b/panda/src/tiff/tiffconf.h new file mode 100644 index 0000000000..d911dbe2dd --- /dev/null +++ b/panda/src/tiff/tiffconf.h @@ -0,0 +1,94 @@ +/* $Header$ */ +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _TIFFCONF_ +#define _TIFFCONF_ +/* + * Library Configuration Definitions. + * + * This file defines the default configuration for the library. + * If the target system does not have make or a way to specify + * #defines on the command line, this file can be edited to + * configure the library. Otherwise, one can override portability + * and configuration-related definitions from a Makefile or command + * line by defining FEATURE_SUPPORT and COMPRESSION_SUPPORT (see below). + */ + +/* + * General portability-related defines: + * + * HAVE_IEEEFP define as 0 or 1 according to the floating point + * format suported by the machine + * BSDTYPES define this if your system does NOT define the + * usual 4BSD typedefs + */ +#ifndef HAVE_IEEEFP +#define HAVE_IEEEFP 1 +#endif + +#ifndef FEATURE_SUPPORT +/* + * Feature support definitions: + * + * MMAP_SUPPORT enable support for memory mapping read-only files + * COLORIMETRY_SUPPORT enable support for 6.0 colorimetry tags + * JPEG_SUPPORT enable support for 6.0 JPEG tags & JPEG algorithms + * YCBCR_SUPPORT enable support for 6.0 YCbCr tags + * CMYK_SUPPORT enable support for 6.0 CMYK tags + */ +#define COLORIMETRY_SUPPORT +#define JPEG_SUPPORT +#define YCBCR_SUPPORT +#define CMYK_SUPPORT +#endif + +#ifndef COMPRESSION_SUPPORT +/* + * Compression support defines: + * + * CCITT_SUPPORT enable support for CCITT Group 3 & 4 algorithms + * PACKBITS_SUPPORT enable support for Macintosh PackBits algorithm + * LZW_SUPPORT enable support for LZW algorithm + * THUNDER_SUPPORT enable support for ThunderScan 4-bit RLE algorithm + * NEXT_SUPPORT enable support for NeXT 2-bit RLE algorithm + * JPEG_SUPPORT enable support for JPEG DCT algorithm + */ +#define CCITT_SUPPORT +#define PACKBITS_SUPPORT +#define LZW_SUPPORT +#define THUNDER_SUPPORT +#define NEXT_SUPPORT +#endif + +/* + * ``Orthogonal Features'' + * + * STRIPCHOP_SUPPORT automatically convert single-strip uncompressed images + * to mutiple strips of ~8Kb (for reducing memory use) + */ +#ifndef STRIPCHOP_SUPPORT +#define STRIPCHOP_SUPPORT /* enable strip chopping */ +#endif +#endif /* _TIFFCONF_ */ diff --git a/panda/src/tiff/tiffio.h b/panda/src/tiff/tiffio.h new file mode 100644 index 0000000000..e4ef5adc90 --- /dev/null +++ b/panda/src/tiff/tiffio.h @@ -0,0 +1,196 @@ +/* $Header$ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _TIFFIO_ +#define _TIFFIO_ + +/* + * TIFF I/O Library Definitions. + */ +#include + +#include "tiff.h" + +/* + * TIFF is defined as an incomplete type to hide the + * library's internal data structures from clients. + */ +typedef struct tiff TIFF; + +/* + * The following typedefs define the intrinsic size of + * data types used in the *exported* interfaces. These + * definitions depend on the proper definition of types + * in tiff.h. Note also that the varargs interface used + * pass tag types and values uses the types defined in + * tiff.h directly. + * + * NB: ttag_t is unsigned int and not unsigned short because + * ANSI C requires that the type before the ellipsis be a + * promoted type (i.e. one of int, unsigned int, pointer, + * or double). + * NB: tsize_t is int32 and not uint32 because some functions + * return -1. + * NB: toff_t is not off_t for many reasons; TIFFs max out at + * 32-bit file offsets being the most important + */ +typedef unsigned int ttag_t; /* directory tag */ +typedef uint16 tdir_t; /* directory index */ +typedef uint16 tsample_t; /* sample number */ +typedef uint32 tstrip_t; /* strip number */ +typedef uint32 ttile_t; /* tile number */ +typedef int32 tsize_t; /* i/o size in bytes */ +typedef void* tdata_t; /* image data ref */ +typedef void* thandle_t; /* client data handle */ +typedef int32 toff_t; /* file offset */ + +#ifndef NULL +#define NULL 0 +#endif + +/* + * Flags to pass to TIFFPrintDirectory to control + * printing of data structures that are potentially + * very large. Bit-or these flags to enable printing + * multiple items. + */ +#define TIFFPRINT_NONE 0x0 /* no extra info */ +#define TIFFPRINT_STRIPS 0x1 /* strips/tiles info */ +#define TIFFPRINT_CURVES 0x2 /* color/gray response curves */ +#define TIFFPRINT_COLORMAP 0x4 /* colormap */ +#define TIFFPRINT_JPEGQTABLES 0x100 /* JPEG Q matrices */ +#define TIFFPRINT_JPEGACTABLES 0x200 /* JPEG AC tables */ +#define TIFFPRINT_JPEGDCTABLES 0x200 /* JPEG DC tables */ + +/* + * Macros for extracting components from the + * packed ABGR form returned by TIFFReadRGBAImage. + */ +#define TIFFGetR(abgr) ((abgr) & 0xff) +#define TIFFGetG(abgr) (((abgr) >> 8) & 0xff) +#define TIFFGetB(abgr) (((abgr) >> 16) & 0xff) +#define TIFFGetA(abgr) (((abgr) >> 24) & 0xff) + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif +typedef void (*TIFFErrorHandler)(const char* module, const char* fmt, va_list); +typedef tsize_t (*TIFFReadWriteProc)(thandle_t, tdata_t, tsize_t); +typedef toff_t (*TIFFSeekProc)(thandle_t, toff_t, int); +typedef int (*TIFFCloseProc)(thandle_t); +typedef toff_t (*TIFFSizeProc)(thandle_t); +typedef int (*TIFFMapFileProc)(thandle_t, tdata_t*, toff_t*); +typedef void (*TIFFUnmapFileProc)(thandle_t, tdata_t, toff_t); + +extern const char* TIFFGetVersion(void); + +extern void TIFFClose(TIFF*); +extern int TIFFFlush(TIFF*); +extern int TIFFFlushData(TIFF*); +extern int TIFFGetField(TIFF*, ttag_t, ...); +extern int TIFFVGetField(TIFF*, ttag_t, va_list); +extern int TIFFGetFieldDefaulted(TIFF*, ttag_t, ...); +extern int TIFFVGetFieldDefaulted(TIFF*, ttag_t, va_list); +extern int TIFFReadDirectory(TIFF*); +extern tsize_t TIFFScanlineSize(TIFF*); +extern tsize_t TIFFStripSize(TIFF*); +extern tsize_t TIFFVStripSize(TIFF*, uint32); +extern tsize_t TIFFTileRowSize(TIFF*); +extern tsize_t TIFFTileSize(TIFF*); +extern tsize_t TIFFVTileSize(TIFF*, uint32); +extern int TIFFFileno(TIFF*); +extern int TIFFGetMode(TIFF*); +extern int TIFFIsTiled(TIFF*); +extern int TIFFIsByteSwapped(TIFF*); +extern uint32 TIFFCurrentRow(TIFF*); +extern tdir_t TIFFCurrentDirectory(TIFF*); +extern tstrip_t TIFFCurrentStrip(TIFF*); +extern ttile_t TIFFCurrentTile(TIFF*); +extern int TIFFReadBufferSetup(TIFF*, tdata_t, tsize_t); +extern int TIFFLastDirectory(TIFF*); +extern int TIFFSetDirectory(TIFF*, tdir_t); +extern int TIFFSetField(TIFF*, ttag_t, ...); +extern int TIFFVSetField(TIFF*, ttag_t, va_list); +extern int TIFFWriteDirectory(TIFF *); +#if defined(c_plusplus) || defined(__cplusplus) +extern void TIFFPrintDirectory(TIFF*, FILE*, long = 0); +extern int TIFFReadScanline(TIFF*, tdata_t, uint32, tsample_t = 0); +extern int TIFFWriteScanline(TIFF*, tdata_t, uint32, tsample_t = 0); +extern int TIFFReadRGBAImage(TIFF*, + unsigned long, unsigned long, unsigned long*, int stop = 0); +#else +extern void TIFFPrintDirectory(TIFF*, FILE*, long); +extern int TIFFReadScanline(TIFF*, tdata_t, uint32, tsample_t); +extern int TIFFWriteScanline(TIFF*, tdata_t, uint32, tsample_t); +extern int TIFFReadRGBAImage(TIFF*, + unsigned long, unsigned long, unsigned long*, int stop); +#endif +extern TIFF* TIFFOpen(const char*, const char*); +extern TIFF* TIFFFdOpen(int, const char*, const char*); +extern TIFF* TIFFClientOpen(const char* name, const char* mode, + thandle_t clientdata, + TIFFReadWriteProc readproc, TIFFReadWriteProc writeproc, + TIFFSeekProc seekproc, TIFFCloseProc closeproc, + TIFFSizeProc sizeproc, + TIFFMapFileProc mapproc, TIFFUnmapFileProc unmapproc); +extern const char* TIFFFileName(TIFF*); +extern void TIFFError(const char*, const char*, ...); +extern void TIFFWarning(const char*, const char*, ...); +extern TIFFErrorHandler TIFFSetErrorHandler(TIFFErrorHandler handler); +extern TIFFErrorHandler TIFFSetWarningHandler(TIFFErrorHandler handler); +extern ttile_t TIFFComputeTile(TIFF*, uint32, uint32, uint32, tsample_t); +extern int TIFFCheckTile(TIFF*, uint32, uint32, uint32, tsample_t); +extern ttile_t TIFFNumberOfTiles(TIFF*); +extern tsize_t TIFFReadTile(TIFF*, + tdata_t, uint32, uint32, uint32, tsample_t); +extern tsize_t TIFFWriteTile(TIFF*, + tdata_t, uint32, uint32, uint32, tsample_t); +extern tstrip_t TIFFComputeStrip(TIFF*, uint32, tsample_t); +extern tstrip_t TIFFNumberOfStrips(TIFF*); +extern tsize_t TIFFReadEncodedStrip(TIFF*, tstrip_t, tdata_t, tsize_t); +extern tsize_t TIFFReadRawStrip(TIFF*, tstrip_t, tdata_t, tsize_t); +extern tsize_t TIFFReadEncodedTile(TIFF*, ttile_t, tdata_t, tsize_t); +extern tsize_t TIFFReadRawTile(TIFF*, ttile_t, tdata_t, tsize_t); +extern tsize_t TIFFWriteEncodedStrip(TIFF*, tstrip_t, tdata_t, tsize_t); +extern tsize_t TIFFWriteRawStrip(TIFF*, tstrip_t, tdata_t, tsize_t); +extern tsize_t TIFFWriteEncodedTile(TIFF*, ttile_t, tdata_t, tsize_t); +extern tsize_t TIFFWriteRawTile(TIFF*, ttile_t, tdata_t, tsize_t); +extern void TIFFSetWriteOffset(TIFF*, toff_t); +extern void TIFFSwabShort(uint16 *); +extern void TIFFSwabLong(uint32 *); +extern void TIFFSwabArrayOfShort(uint16 *, unsigned long); +extern void TIFFSwabArrayOfLong(uint32 *, unsigned long); +extern void TIFFReverseBits(unsigned char *, unsigned long); +extern const unsigned char* TIFFGetBitRevTable(int); + +extern void TIFFModeCCITTFax3(TIFF* tif, int isClassF); /* XXX */ +#if defined(__cplusplus) +} +#endif +#endif /* _TIFFIO_ */ diff --git a/panda/src/tiff/tiffiop.h b/panda/src/tiff/tiffiop.h new file mode 100644 index 0000000000..f49054a4a7 --- /dev/null +++ b/panda/src/tiff/tiffiop.h @@ -0,0 +1,406 @@ +/* $Header$ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler + * Copyright (c) 1991, 1992 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _TIFFIOP_ +#define _TIFFIOP_ +/* + * ``Library-private'' definitions. + */ +#include + +#include "tiffconf.h" +#include "tiffcomp.h" +#include "tiffio.h" + +/* + * Internal format of a TIFF directory entry. + */ +typedef struct { +#define FIELD_SETLONGS 2 + /* bit vector of fields that are set */ + u_long td_fieldsset[FIELD_SETLONGS]; + + uint32 td_imagewidth, td_imagelength, td_imagedepth; + uint32 td_tilewidth, td_tilelength, td_tiledepth; + uint16 td_subfiletype; + uint16 td_bitspersample; + uint16 td_sampleformat; + uint16 td_compression; + uint16 td_photometric; + uint16 td_threshholding; + uint16 td_fillorder; + uint16 td_orientation; + uint16 td_samplesperpixel; + uint16 td_predictor; + uint32 td_rowsperstrip; + u_long td_minsamplevalue, td_maxsamplevalue; /* XXX */ + float td_xresolution, td_yresolution; + uint16 td_resolutionunit; + uint16 td_planarconfig; + float td_xposition, td_yposition; + uint32 td_group3options; + uint32 td_group4options; + uint16 td_pagenumber[2]; + uint16 td_cleanfaxdata; + uint16 td_badfaxrun; + uint32 td_badfaxlines; + uint16 *td_colormap[3]; + uint16 td_halftonehints[2]; + uint16 td_extrasamples, *td_sampleinfo; + char *td_documentname; + char *td_artist; + char *td_datetime; + char *td_hostcomputer; + char *td_imagedescription; + char *td_make; + char *td_model; + char *td_software; + char *td_pagename; + tstrip_t td_stripsperimage; + tstrip_t td_nstrips; /* size of offset & bytecount arrays */ + uint32 *td_stripoffset; + uint32 *td_stripbytecount; +#ifdef YCBCR_SUPPORT + float *td_ycbcrcoeffs; + uint16 td_ycbcrsubsampling[2]; + uint16 td_ycbcrpositioning; +#endif +#ifdef JPEG_SUPPORT + uint16 td_jpegproc; + uint16 td_jpegrestartinterval; + u_char **td_qtab; + u_char **td_dctab; + u_char **td_actab; +#endif +#ifdef COLORIMETRY_SUPPORT + float *td_whitepoint; + float *td_primarychromas; + float *td_refblackwhite; + uint16 *td_transferfunction[3]; +#endif +#ifdef CMYK_SUPPORT + uint16 td_inkset; + uint16 td_dotrange[2]; + char *td_inknames; + char *td_targetprinter; +#endif +} TIFFDirectory; + +/* + * Field flags used to indicate fields that have + * been set in a directory, and to reference fields + * when manipulating a directory. + */ +/* multi-entry fields */ +#define FIELD_IMAGEDIMENSIONS 0 +#define FIELD_TILEDIMENSIONS 1 +#define FIELD_CELLDIMENSIONS 2 /* XXX */ +#define FIELD_RESOLUTION 3 +#define FIELD_POSITION 4 +/* single-entry fields */ +#define FIELD_SUBFILETYPE 5 +#define FIELD_BITSPERSAMPLE 6 +#define FIELD_COMPRESSION 7 +#define FIELD_PHOTOMETRIC 8 +#define FIELD_THRESHHOLDING 9 +#define FIELD_FILLORDER 10 +#define FIELD_DOCUMENTNAME 11 +#define FIELD_IMAGEDESCRIPTION 12 +#define FIELD_MAKE 13 +#define FIELD_MODEL 14 +#define FIELD_ORIENTATION 15 +#define FIELD_SAMPLESPERPIXEL 16 +#define FIELD_ROWSPERSTRIP 17 +#define FIELD_MINSAMPLEVALUE 18 +#define FIELD_MAXSAMPLEVALUE 19 +#define FIELD_PLANARCONFIG 20 +#define FIELD_PAGENAME 21 +#define FIELD_GROUP3OPTIONS 22 +#define FIELD_GROUP4OPTIONS 23 +#define FIELD_RESOLUTIONUNIT 24 +#define FIELD_PAGENUMBER 25 +#define FIELD_STRIPBYTECOUNTS 26 +#define FIELD_STRIPOFFSETS 27 +#define FIELD_COLORMAP 28 +#define FIELD_PREDICTOR 29 +#define FIELD_ARTIST 30 +#define FIELD_DATETIME 31 +#define FIELD_HOSTCOMPUTER 32 +#define FIELD_SOFTWARE 33 +#define FIELD_EXTRASAMPLES 34 +#define FIELD_BADFAXLINES 35 +#define FIELD_CLEANFAXDATA 36 +#define FIELD_BADFAXRUN 37 +#define FIELD_SAMPLEFORMAT 38 +#define FIELD_SMINSAMPLEVALUE 39 +#define FIELD_SMAXSAMPLEVALUE 40 +#define FIELD_IMAGEDEPTH 41 +#define FIELD_TILEDEPTH 42 +#define FIELD_HALFTONEHINTS 43 +#ifdef YCBCR_SUPPORT +#define FIELD_YCBCRCOEFFICIENTS 44 +#define FIELD_YCBCRSUBSAMPLING 45 +#define FIELD_YCBCRPOSITIONING 46 +#endif +#ifdef JPEG_SUPPORT +#define FIELD_JPEGPROC 47 +#define FIELD_JPEGRESTARTINTERVAL 48 +#define FIELD_JPEGQTABLES 49 +#define FIELD_JPEGDCTABLES 50 +#define FIELD_JPEGACTABLES 51 +#endif +#ifdef COLORIMETRY_SUPPORT +#define FIELD_REFBLACKWHITE 52 +#define FIELD_WHITEPOINT 53 +#define FIELD_PRIMARYCHROMAS 54 +#define FIELD_TRANSFERFUNCTION 55 +#endif +#ifdef CMYK_SUPPORT +#define FIELD_INKSET 56 +#define FIELD_INKNAMES 57 +#define FIELD_DOTRANGE 58 +#define FIELD_TARGETPRINTER 59 +#endif +#define FIELD_LAST 59 + +#define TIFFExtractData(tif, type, v) \ + ((tif)->tif_header.tiff_magic == TIFF_BIGENDIAN ? \ + ((v) >> (tif)->tif_typeshift[type]) & (tif)->tif_typemask[type] : \ + (v) & (tif)->tif_typemask[type]) +#define TIFFInsertData(tif, type, v) \ + ((tif)->tif_header.tiff_magic == TIFF_BIGENDIAN ? \ + ((v) & (tif)->tif_typemask[type]) << (tif)->tif_typeshift[type] : \ + (v) & (tif)->tif_typemask[type]) + +typedef struct { + ttag_t field_tag; /* field's tag */ + short field_readcount; /* read count (-1 for unknown) */ + short field_writecount; /* write count (-1 for unknown) */ + TIFFDataType field_type; /* type of associated data */ + u_short field_bit; /* bit in fieldsset bit vector */ + u_short field_oktochange; /* if true, can change while writing */ + char *field_name; /* ASCII name */ +} TIFFFieldInfo; + +#define FIELD_IGNORE ((u_short)-1) /* tags processed but ignored */ + +#define TIFF_ANY TIFF_NOTYPE /* for field descriptor searching */ +#define TIFF_VARIABLE -1 /* marker for variable length tags */ +#define TIFF_SPP -2 /* marker for SamplesPerPixel tags */ + +extern const TIFFFieldInfo tiffFieldInfo[];/* table of field descriptors */ +extern const int tiffDataWidth[]; /* table of tag datatype widths */ + +#define BITn(n) (((u_long)1L)<<((n)&0x1f)) +#define BITFIELDn(tif, n) ((tif)->tif_dir.td_fieldsset[(n)/32]) +#define TIFFFieldSet(tif, field) (BITFIELDn(tif, field) & BITn(field)) +#define TIFFSetFieldBit(tif, field) (BITFIELDn(tif, field) |= BITn(field)) +#define TIFFClrFieldBit(tif, field) (BITFIELDn(tif, field) &= ~BITn(field)) + +#define FieldSet(fields, f) (fields[(f)/32] & BITn(f)) +#define ResetFieldBit(fields, f) (fields[(f)/32] &= ~BITn(f)) + +/* + * Typedefs for ``method pointers'' used internally. + */ +typedef unsigned char* tidata_t; /* internal image data type */ + +typedef void (*TIFFVoidMethod)(TIFF*); +typedef int (*TIFFBoolMethod)(TIFF*); +typedef int (*TIFFCodeMethod)(TIFF*, tidata_t, tsize_t, tsample_t); +typedef int (*TIFFSeekMethod)(TIFF*, uint32); +typedef void (*TIFFPostMethod)(TIFF*, tidata_t, tsize_t); + +struct tiff { + char* tif_name; /* name of open file */ + short tif_fd; /* open file descriptor */ + short tif_mode; /* open mode (O_*) */ + char tif_fillorder; /* natural bit fill order for machine */ + char tif_options; /* compression-specific options */ + short tif_flags; +#define TIFF_DIRTYHEADER 0x1 /* header must be written on close */ +#define TIFF_DIRTYDIRECT 0x2 /* current directory must be written */ +#define TIFF_BUFFERSETUP 0x4 /* data buffers setup */ +#define TIFF_BEENWRITING 0x8 /* written 1+ scanlines to file */ +#define TIFF_SWAB 0x10 /* byte swap file information */ +#define TIFF_NOBITREV 0x20 /* inhibit bit reversal logic */ +#define TIFF_MYBUFFER 0x40 /* my raw data buffer; free on close */ +#define TIFF_ISTILED 0x80 /* file is tile, not strip- based */ +#define TIFF_MAPPED 0x100 /* file is mapped into memory */ +#define TIFF_POSTENCODE 0x200 /* need call to postencode routine */ + toff_t tif_diroff; /* file offset of current directory */ + toff_t tif_nextdiroff; /* file offset of following directory */ + TIFFDirectory tif_dir; /* internal rep of current directory */ + TIFFHeader tif_header; /* file's header block */ + const int* tif_typeshift; /* data type shift counts */ + const long* tif_typemask; /* data type masks */ + uint32 tif_row; /* current scanline */ + tdir_t tif_curdir; /* current directory (index) */ + tstrip_t tif_curstrip; /* current strip for read/write */ + toff_t tif_curoff; /* current offset for read/write */ + toff_t tif_dataoff; /* current offset for writing dir */ +/* tiling support */ + uint32 tif_col; /* current column (offset by row too) */ + ttile_t tif_curtile; /* current tile for read/write */ + tsize_t tif_tilesize; /* # of bytes in a tile */ +/* compression scheme hooks */ + TIFFBoolMethod tif_predecode; /* pre row/strip/tile decoding */ + TIFFBoolMethod tif_preencode; /* pre row/strip/tile encoding */ + TIFFBoolMethod tif_postencode; /* post row/strip/tile encoding */ + TIFFCodeMethod tif_decoderow; /* scanline decoding routine */ + TIFFCodeMethod tif_encoderow; /* scanline encoding routine */ + TIFFCodeMethod tif_decodestrip;/* strip decoding routine */ + TIFFCodeMethod tif_encodestrip;/* strip encoding routine */ + TIFFCodeMethod tif_decodetile; /* tile decoding routine */ + TIFFCodeMethod tif_encodetile; /* tile encoding routine */ + TIFFVoidMethod tif_close; /* cleanup-on-close routine */ + TIFFSeekMethod tif_seek; /* position within a strip routine */ + TIFFVoidMethod tif_cleanup; /* routine called to cleanup state */ + tidata_t tif_data; /* compression scheme private data */ +/* input/output buffering */ + tsize_t tif_scanlinesize;/* # of bytes in a scanline */ + tsize_t tif_scanlineskew;/* scanline skew for reading strips */ + tidata_t tif_rawdata; /* raw data buffer */ + tsize_t tif_rawdatasize;/* # of bytes in raw data buffer */ + tidata_t tif_rawcp; /* current spot in raw buffer */ + tsize_t tif_rawcc; /* bytes unread from raw buffer */ +/* memory-mapped file support */ + tidata_t tif_base; /* base of mapped file */ + toff_t tif_size; /* size of mapped file region (bytes) */ + TIFFMapFileProc tif_mapproc; /* map file method */ + TIFFUnmapFileProc tif_unmapproc;/* unmap file method */ +/* input/output callback methods */ + thandle_t tif_clientdata; /* callback parameter */ + TIFFReadWriteProc tif_readproc; /* read method */ + TIFFReadWriteProc tif_writeproc;/* write method */ + TIFFSeekProc tif_seekproc; /* lseek method */ + TIFFCloseProc tif_closeproc; /* close method */ + TIFFSizeProc tif_sizeproc; /* filesize method */ +/* post-decoding support */ + TIFFPostMethod tif_postdecode;/* post decoding routine */ +}; + +#define isTiled(tif) (((tif)->tif_flags & TIFF_ISTILED) != 0) +#define isMapped(tif) (((tif)->tif_flags & TIFF_MAPPED) != 0) +#define TIFFReadFile(tif, buf, size) \ + ((*(tif)->tif_readproc)((tif)->tif_clientdata,buf,size)) +#define TIFFWriteFile(tif, buf, size) \ + ((*(tif)->tif_writeproc)((tif)->tif_clientdata,buf,size)) +#define TIFFSeekFile(tif, off, whence) \ + ((*(tif)->tif_seekproc)((tif)->tif_clientdata,off,whence)) +#define TIFFCloseFile(tif) \ + ((*(tif)->tif_closeproc)((tif)->tif_clientdata)) +#define TIFFGetFileSize(tif) \ + ((*(tif)->tif_sizeproc)((tif)->tif_clientdata)) +#define TIFFMapFileContents(tif, paddr, psize) \ + ((*(tif)->tif_mapproc)((tif)->tif_clientdata,paddr,psize)) +#define TIFFUnmapFileContents(tif, addr, size) \ + ((*(tif)->tif_unmapproc)((tif)->tif_clientdata,addr,size)) + +/* + * Default Read/Seek/Write definitions. + */ +#ifndef ReadOK +#define ReadOK(tif, buf, size) \ + (TIFFReadFile(tif, (tdata_t) buf, (tsize_t) size) == size) +#endif +#ifndef SeekOK +#define SeekOK(tif, off) \ + (TIFFSeekFile(tif, (toff_t) off, L_SET) == (toff_t) off) +#endif +#ifndef WriteOK +#define WriteOK(tif, buf, size) \ + (TIFFWriteFile(tif, (tdata_t) buf, (tsize_t) size) == (tsize_t) size) +#endif + +/* generic option bit names */ +#define TIFF_OPT0 0x1 +#define TIFF_OPT1 0x2 +#define TIFF_OPT2 0x4 +#define TIFF_OPT3 0x8 +#define TIFF_OPT4 0x10 +#define TIFF_OPT5 0x20 +#define TIFF_OPT6 0x40 +#define TIFF_OPT7 0x80 + +/* NB: the u_long casts are to silence certain ANSI-C compilers */ +#ifdef howmany +#undef howmany +#endif +#define howmany(x, y) ((((u_long)(x))+(((u_long)(y))-1))/((u_long)(y))) +#ifdef roundup +#undef roundup +#endif +#define roundup(x, y) (howmany(x,y)*((u_long)(y))) + +#if defined(__cplusplus) +extern "C" { +#endif +extern void* _TIFFmalloc(size_t); +extern void* _TIFFrealloc(void*, size_t); +extern void _TIFFfree(void*); +extern int _TIFFgetMode(const char* mode, const char* module); +extern const TIFFFieldInfo *TIFFFindFieldInfo(ttag_t, TIFFDataType); +extern const TIFFFieldInfo *TIFFFieldWithTag(ttag_t); +extern void _TIFFgetfield(TIFFDirectory*, ttag_t, ...); +extern int TIFFNoRowEncode(TIFF*, tidata_t, tsize_t, tsample_t); +extern int TIFFNoStripEncode(TIFF*, tidata_t, tsize_t, tsample_t); +extern int TIFFNoTileEncode(TIFF*, tidata_t, tsize_t, tsample_t); +extern int TIFFNoRowDecode(TIFF*, tidata_t, tsize_t, tsample_t); +extern int TIFFNoStripDecode(TIFF*, tidata_t, tsize_t, tsample_t); +extern int TIFFNoTileDecode(TIFF*, tidata_t, tsize_t, tsample_t); +extern void TIFFNoPostDecode(TIFF*, tidata_t, tsize_t); +extern void TIFFSwab16BitData(TIFF*, tidata_t, tsize_t); +extern void TIFFSwab32BitData(TIFF*, tidata_t, tsize_t); +extern int TIFFFlushData1(TIFF*); +extern void TIFFFreeDirectory(TIFF*); +extern int TIFFDefaultDirectory(TIFF*); +extern int TIFFSetCompressionScheme(TIFF *, int); + +extern int TIFFInitDumpMode(TIFF*); +#ifdef PACKBITS_SUPPORT +extern int TIFFInitPackBits(TIFF*); +#endif +#ifdef CCITT_SUPPORT +extern int TIFFInitCCITTRLE(TIFF*), TIFFInitCCITTRLEW(TIFF*); +extern int TIFFInitCCITTFax3(TIFF*), TIFFInitCCITTFax4(TIFF*); +#endif +#ifdef THUNDER_SUPPORT +extern int TIFFInitThunderScan(TIFF*); +#endif +#ifdef NEXT_SUPPORT +extern int TIFFInitNeXT(TIFF*); +#endif +#ifdef LZW_SUPPORT +extern int TIFFInitLZW(TIFF*); +#endif +#ifdef JPEG_SUPPORT +extern int TIFFInitJPEG(TIFF*); +#endif +#if defined(__cplusplus) +} +#endif +#endif /* _TIFFIOP_ */ diff --git a/panda/src/vrpn/Sources.pp b/panda/src/vrpn/Sources.pp new file mode 100644 index 0000000000..41656a7807 --- /dev/null +++ b/panda/src/vrpn/Sources.pp @@ -0,0 +1,17 @@ +#define DIRECTORY_IF_VRPN yes + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET pandavrpn + #define LOCAL_LIBS \ + dgraph + + #define SOURCES \ + config_vrpn.cxx config_vrpn.h vrpnClient.I vrpnClient.cxx \ + vrpnClient.h + + #define INSTALL_HEADERS \ + config_vrpn.h vrpnClient.I vrpnClient.h + +#end lib_target diff --git a/panda/src/vrpn/config_vrpn.cxx b/panda/src/vrpn/config_vrpn.cxx new file mode 100644 index 0000000000..33c38e850f --- /dev/null +++ b/panda/src/vrpn/config_vrpn.cxx @@ -0,0 +1,17 @@ +// Filename: config_vrpn.cxx +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_vrpn.h" +#include "vrpnClient.h" + +#include + +Configure(config_vrpn); +NotifyCategoryDef(vrpn, ""); + + +ConfigureFn(config_vrpn) { + VrpnClient::init_type(); +} diff --git a/panda/src/vrpn/config_vrpn.h b/panda/src/vrpn/config_vrpn.h new file mode 100644 index 0000000000..2fd4b08eca --- /dev/null +++ b/panda/src/vrpn/config_vrpn.h @@ -0,0 +1,14 @@ +// Filename: config_vrpn.h +// Created by: jason (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_VRPN_H +#define CONFIG_VRPN_H + +#include +#include + +NotifyCategoryDecl(vrpn, EXPCL_PANDA, EXPTP_PANDA); + +#endif diff --git a/panda/src/vrpn/vrpnClient.I b/panda/src/vrpn/vrpnClient.I new file mode 100644 index 0000000000..620e084eda --- /dev/null +++ b/panda/src/vrpn/vrpnClient.I @@ -0,0 +1,118 @@ +// Filename: vrpnClient.I +// Created by: jason (04Aug00) +// +//////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE VrpnClient:: +VrpnClient(const string &server) : + ClientBase(server) +{ + _connection = vrpn_get_connection_by_name(server.c_str()); +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::tracker_position +// Access: Private +// Description: Stores the latest position information as sent by +// the tracker (for the particular sensor we have +// interest in) +//////////////////////////////////////////////////////////////////// +INLINE void VrpnClient:: +tracker_position(const string &tracker, const vrpn_TRACKERCB info) { + double ptime = convert_to_secs(info.msg_time); + LPoint3f pos(info.pos[0], info.pos[1], info.pos[2]); + LVector4f pquat(info.quat[0], info.quat[1], info.quat[2], info.quat[3]); + + push_tracker_position(tracker, info.sensor, ptime, pos, pquat); +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::tracker_velocity +// Access: Private +// Description: Stores the latest velocity information as sent by +// the tracker (for the particular sensor we have +// interest in) +//////////////////////////////////////////////////////////////////// +INLINE void VrpnClient:: +tracker_velocity(const string &tracker, const vrpn_TRACKERVELCB info) { + double vtime = convert_to_secs(info.msg_time); + LPoint3f vel(info.vel[0], info.vel[1], info.vel[2]); + LVector4f vquat(info.vel_quat[0], info.vel_quat[1], + info.vel_quat[2], info.vel_quat[3]); + float dt = info.vel_quat_dt; + + push_tracker_velocity(tracker, info.sensor, vtime, vel, vquat, dt); +} +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::tracker_acceleration +// Access: Private +// Description: Stores the latest acceleration information as sent by +// the tracker (for the particular sensor we have +// interest in) +//////////////////////////////////////////////////////////////////// +INLINE void VrpnClient:: +tracker_acceleration(const string &tracker, const vrpn_TRACKERACCCB info) { + double atime = convert_to_secs(info.msg_time); + LPoint3f acc(info.acc[0], info.acc[1], info.acc[2]); + LVector4f aquat(info.acc_quat[0], info.acc_quat[1], + info.acc_quat[2], info.acc_quat[3]); + float dt = info.acc_quat_dt; + + push_tracker_acceleration(tracker, info.sensor, atime, acc, aquat, dt); +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::analog +// Access: Private +// Description: Stores the latest information as sent by +// the analog device +//////////////////////////////////////////////////////////////////// +INLINE void VrpnClient:: +analog(const string &analog, const vrpn_ANALOGCB info) { + double atime = convert_to_secs(info.msg_time); + + push_analog(analog, atime, info.channel, info.num_channel); +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::button +// Access: Private +// Description: Stores the latest button pressed information as sent by +// the button +//////////////////////////////////////////////////////////////////// +INLINE void VrpnClient:: +button(const string &button, const vrpn_BUTTONCB info) { + double btime = convert_to_secs(info.msg_time); + + push_button(button, btime, info.button, info.state); +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::dial +// Access: Private +// Description: Stores the latest change information as sent by +// the dial +//////////////////////////////////////////////////////////////////// +INLINE void VrpnClient:: +dial(const string &dial, const vrpn_DIALCB info) { + double dtime = convert_to_secs(info.msg_time); + + push_dial(dial, dtime, info.dial, info.change); +} + +//////////////////////////////////////////////////////////////////// +// Function: convert_to_secs +// Access: Public +// Description: Little inline function to convert a struct timeval +// to only seconds +//////////////////////////////////////////////////////////////////// +INLINE double convert_to_secs(struct timeval msg_time) { + return (double)(msg_time.tv_sec) + (double)msg_time.tv_usec * 0.000001; +} diff --git a/panda/src/vrpn/vrpnClient.cxx b/panda/src/vrpn/vrpnClient.cxx new file mode 100644 index 0000000000..399c5b8490 --- /dev/null +++ b/panda/src/vrpn/vrpnClient.cxx @@ -0,0 +1,255 @@ +// Filename: vrpnClient.cxx +// Created by: jason (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "vrpnClient.h" + +TypeHandle VrpnClient::_type_handle; + +#include +#include + +typedef struct { + string device_name; + void *self; +} VrpnClientInfo; + + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::add_remote_tracker +// Access: Public, Virtual +// Description: Creates a new vrpn remote tracker object and registers +// a callback with it. +//////////////////////////////////////////////////////////////////// +bool VrpnClient:: +add_remote_tracker(const string &tracker, int sensor) { + + vrpn_Tracker_Remote *vrpn_tracker = new vrpn_Tracker_Remote(tracker.c_str(), _connection); + if (vrpn_tracker == (vrpn_Tracker_Remote *)NULL) { + return false; + } + + //Now package up the information that needs to be passed to the + //callback function to allow it to determine for which tracker we + //are receiving information for + VrpnClientInfo *data = new VrpnClientInfo; + data->device_name = tracker; + data->self = this; + + vrpn_tracker->register_change_handler((void*)data, st_tracker_position); + vrpn_tracker->register_change_handler((void*)data, st_tracker_velocity); + vrpn_tracker->register_change_handler((void*)data, st_tracker_acceleration); + + _vrpn_trackers[tracker] = vrpn_tracker; + _trackers.push_back(tracker); + _sensors[tracker].push_back(sensor); +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::add_remote_analog +// Access: Public, Virtual +// Description: Creates a new vrpn remote analog object and registers +// a callback with it. +//////////////////////////////////////////////////////////////////// +bool VrpnClient:: +add_remote_analog(const string &analog) { + + vrpn_Analog_Remote *vrpn_analog = new vrpn_Analog_Remote(analog.c_str(), _connection); + if (vrpn_analog == (vrpn_Analog_Remote *)NULL) { + return false; + } + + //Now package up the information that needs to be passed to the + //callback function to allow it to determine for which analog we + //are receiving information for + VrpnClientInfo *data = new VrpnClientInfo; + data->device_name = analog; + data->self = this; + + vrpn_analog->register_change_handler((void*)data, st_analog); + + _vrpn_analogs[analog] = vrpn_analog; + _analogs.push_back(analog); +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::add_remote_button +// Access: Public, Virtual +// Description: Creates a new vrpn remote button object and registers +// a callback with it. +//////////////////////////////////////////////////////////////////// +bool VrpnClient:: +add_remote_button(const string &button) { + + vrpn_Button_Remote *vrpn_button = new vrpn_Button_Remote(button.c_str(), _connection); + if (vrpn_button == (vrpn_Button_Remote *)NULL) { + return false; + } + + //Now package up the information that needs to be passed to the + //callback function to allow it to determine for which button we + //are receiving information for + VrpnClientInfo *data = new VrpnClientInfo; + data->device_name = button; + data->self = this; + + vrpn_button->register_change_handler((void*)data, st_button); + + _vrpn_buttons[button] = vrpn_button; + _buttons.push_back(button); +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::add_remote_dial +// Access: Public, Virtual +// Description: Creates a new vrpn remote dial object and registers +// a callback with it. +//////////////////////////////////////////////////////////////////// +bool VrpnClient:: +add_remote_dial(const string &dial) { + + vrpn_Dial_Remote *vrpn_dial = new vrpn_Dial_Remote(dial.c_str(), _connection); + if (vrpn_dial == (vrpn_Dial_Remote *)NULL) { + return false; + } + + //Now package up the information that needs to be passed to the + //callback function to allow it to determine for which dial we + //are receiving information for + VrpnClientInfo *data = new VrpnClientInfo; + data->device_name = dial; + data->self = this; + + vrpn_dial->register_change_handler((void*)data, st_dial); + + _vrpn_dials[dial] = vrpn_dial; + _dials.push_back(dial); +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::max_analog_channels +// Access: Public, Virtual +// Description: Max number of analog channels +//////////////////////////////////////////////////////////////////// +int VrpnClient:: +max_analog_channels() { + return vrpn_CHANNEL_MAX; +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::poll_trackers +// Access: Public, Virtual +// Description: Calls mainloop for the registered tracker object +// Note: In a non-threaded case, this may need to come up with +// some kind of cacheing scheme so we don't call mainloop +// multiple times when a user is just asking for the data +// of multiple sensors on 1 tracker (as that is the interface +// supported). This is a non-trivial problem as it is +// difficult to know when we should and shouldn't cache. +//////////////////////////////////////////////////////////////////// +void VrpnClient:: +poll_tracker(const string &tracker) { + _vrpn_trackers[tracker]->mainloop(); +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::poll_analog +// Access: Public, Virtual +// Description: Calls mainloop for the registered analog object +//////////////////////////////////////////////////////////////////// +void VrpnClient:: +poll_analog(const string &analog) { + _vrpn_analogs[analog]->mainloop(); +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::poll_button +// Access: Public, Virtual +// Description: Calls mainloop for the registered button object +//////////////////////////////////////////////////////////////////// +void VrpnClient:: +poll_button(const string &button) { + _vrpn_buttons[button]->mainloop(); +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::poll_dial +// Access: Public, Virtual +// Description: Calls mainloop for the registered dial object +//////////////////////////////////////////////////////////////////// +void VrpnClient:: +poll_dial(const string &dial) { + _vrpn_dials[dial]->mainloop(); +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::st_tracker_position +// Access: Private, Static +// Description: Callback function that merely passes the data down +// to the appropriate non-static function +//////////////////////////////////////////////////////////////////// +void VrpnClient:: +st_tracker_position(void *userdata, const vrpn_TRACKERCB info) { + VrpnClientInfo *data = (VrpnClientInfo *)userdata; + ((VrpnClient *)data->self)->tracker_position(data->device_name, info); +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::st_tracker_velocity +// Access: Private, Static +// Description: Callback function that merely passes the data down +// to the appropriate non-static function +//////////////////////////////////////////////////////////////////// +void VrpnClient:: +st_tracker_velocity(void *userdata, const vrpn_TRACKERVELCB info) { + VrpnClientInfo *data = (VrpnClientInfo *)userdata; + ((VrpnClient *)data->self)->tracker_velocity(data->device_name, info); +} +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::st_tracker_acceleration +// Access: Private, Static +// Description: Callback function that merely passes the data down +// to the appropriate non-static function +//////////////////////////////////////////////////////////////////// +void VrpnClient:: +st_tracker_acceleration(void *userdata, const vrpn_TRACKERACCCB info) { + VrpnClientInfo *data = (VrpnClientInfo *)userdata; + ((VrpnClient *)data->self)->tracker_acceleration(data->device_name, info); +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::st_analog +// Access: Private, Static +// Description: Callback function that merely passes the data down +// to the appropriate non-static function +//////////////////////////////////////////////////////////////////// +void VrpnClient:: +st_analog(void *userdata, const vrpn_ANALOGCB info) { + VrpnClientInfo *data = (VrpnClientInfo *)userdata; + ((VrpnClient *)data->self)->analog(data->device_name, info); +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::st_button +// Access: Private, Static +// Description: Callback function that merely passes the data down +// to the appropriate non-static function +//////////////////////////////////////////////////////////////////// +void VrpnClient:: +st_button(void *userdata, const vrpn_BUTTONCB info) { + VrpnClientInfo *data = (VrpnClientInfo *)userdata; + ((VrpnClient *)data->self)->button(data->device_name, info); +} + +//////////////////////////////////////////////////////////////////// +// Function: VrpnClient::st_dial +// Access: Private, Static +// Description: Callback function that merely passes the data down +// to the appropriate non-static function +//////////////////////////////////////////////////////////////////// +void VrpnClient:: +st_dial(void *userdata, const vrpn_DIALCB info) { + VrpnClientInfo *data = (VrpnClientInfo *)userdata; + ((VrpnClient *)data->self)->dial(data->device_name, info); +} diff --git a/panda/src/vrpn/vrpnClient.h b/panda/src/vrpn/vrpnClient.h new file mode 100644 index 0000000000..a511a44165 --- /dev/null +++ b/panda/src/vrpn/vrpnClient.h @@ -0,0 +1,102 @@ +// Filename: vrpnClient.h +// Created by: jason (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VRPN_CLIENT +#define VRPN_CLIENT + +#include +#include + +#include +#include +#include +#include +#include + +class EXPCL_PANDA VrpnClient : public ClientBase { +public: + INLINE VrpnClient(const string &server); + + //ADD FUNCTIONS + virtual bool add_remote_tracker(const string &tracker, int sensor); + virtual bool add_remote_analog(const string &analog); + virtual bool add_remote_button(const string &button); + virtual bool add_remote_dial(const string &dial); + +protected: + virtual int max_analog_channels(); + + //Device polling functions + virtual void poll_tracker(const string &tracker); + virtual void poll_analog(const string &analog); + virtual void poll_button(const string &button); + virtual void poll_dial(const string &dial); + +private: + //Private VRPN objects + typedef map VrpnTrackers; + typedef map VrpnAnalogs; + typedef map VrpnButtons; + typedef map VrpnDials; + + vrpn_Connection *_connection; + VrpnTrackers _vrpn_trackers; + VrpnAnalogs _vrpn_analogs; + VrpnButtons _vrpn_buttons; + VrpnDials _vrpn_dials; + + //VRPN Callback functions. Each callback actually needs to be a + //pair of two functions, one a static and one not. The static is + //needed because we can't set member functions as callbacks (due to + //the implicity this pointer) and the non-static function is needed + //so that we can set the non-static data storage variables + //appropriately +private: + static void st_tracker_position(void *userdata, const vrpn_TRACKERCB info); + INLINE void tracker_position(const string &tracker, const vrpn_TRACKERCB info); + + static void st_tracker_velocity(void *userdata, const vrpn_TRACKERVELCB info); + INLINE void tracker_velocity(const string &tracker, const vrpn_TRACKERVELCB info); + + static void st_tracker_acceleration(void *userdata, const vrpn_TRACKERACCCB info); + INLINE void tracker_acceleration(const string &tracker, const vrpn_TRACKERACCCB info); + + static void st_analog(void *userdata, const vrpn_ANALOGCB info); + INLINE void analog(const string &analog, const vrpn_ANALOGCB info); + + static void st_button(void *userdata, const vrpn_BUTTONCB info); + INLINE void button(const string &button, const vrpn_BUTTONCB info); + + static void st_dial(void *userdata, const vrpn_DIALCB info); + INLINE void dial(const string &dial, const vrpn_DIALCB info); + +public: + static TypeHandle get_class_type( void ) { + return _type_handle; + } + static void init_type( void ) { + ClientBase::init_type(); + register_type( _type_handle, "VrpnClient", + ClientBase::get_class_type() ); + } + virtual TypeHandle get_type( void ) const { + return get_class_type(); + } + virtual TypeHandle force_init_type() { + init_type(); + return get_class_type(); + } + +private: + static TypeHandle _type_handle; +}; + +INLINE double convert_to_secs(struct timeval msg_time); + +#include "vrpnClient.I" + +#endif + + diff --git a/panda/src/wdxdisplay/Sources.pp b/panda/src/wdxdisplay/Sources.pp new file mode 100644 index 0000000000..a89c221a09 --- /dev/null +++ b/panda/src/wdxdisplay/Sources.pp @@ -0,0 +1,16 @@ +#define DIRECTORY_IF_DX yes + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET wdxdisplay + + #define SOURCES \ + config_wdxdisplay.cxx config_wdxdisplay.h wdxGraphicsPipe.cxx \ + wdxGraphicsPipe.h wdxGraphicsWindow.cxx wdxGraphicsWindow.h + + #define INSTALL_HEADERS \ + wdxGraphicsPipe.h wdxGraphicsWindow.h + +#end lib_target + diff --git a/panda/src/wdxdisplay/config_wdxdisplay.cxx b/panda/src/wdxdisplay/config_wdxdisplay.cxx new file mode 100644 index 0000000000..b7ba1432f1 --- /dev/null +++ b/panda/src/wdxdisplay/config_wdxdisplay.cxx @@ -0,0 +1,24 @@ +// Filename: config_wdxdisplay.cxx +// Created by: mike (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_wdxdisplay.h" +#include "wdxGraphicsPipe.h" +#include "wdxGraphicsWindow.h" + +#include + +Configure(config_wdxdisplay); +NotifyCategoryDef(wdxdisplay, "display"); + +ConfigureFn(config_wdxdisplay) { + wdxGraphicsPipe::init_type(); + GraphicsPipe::_factory.register_factory( + wdxGraphicsPipe::get_class_type(), + wdxGraphicsPipe::make_wdxGraphicsPipe); + wdxGraphicsWindow::init_type(); + GraphicsWindow::_factory.register_factory( + wdxGraphicsWindow::get_class_type(), + wdxGraphicsWindow::make_wdxGraphicsWindow); +} diff --git a/panda/src/wdxdisplay/config_wdxdisplay.h b/panda/src/wdxdisplay/config_wdxdisplay.h new file mode 100644 index 0000000000..25b2a7670d --- /dev/null +++ b/panda/src/wdxdisplay/config_wdxdisplay.h @@ -0,0 +1,15 @@ +// Filename: config_wdxdisplay.h +// Created by: mike (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __CONFIG_WDXDISPLAY_H__ +#define __CONFIG_WDXDISPLAY_H__ + +#include +#include + +NotifyCategoryDecl(wdxdisplay, EXPCL_PANDADX, EXPTP_PANDADX); + + +#endif /* __CONFIG_WDXDISPLAY_H__ */ diff --git a/panda/src/wdxdisplay/wdxGraphicsPipe.cxx b/panda/src/wdxdisplay/wdxGraphicsPipe.cxx new file mode 100644 index 0000000000..3f46f86447 --- /dev/null +++ b/panda/src/wdxdisplay/wdxGraphicsPipe.cxx @@ -0,0 +1,1877 @@ +// Filename: wdxGraphicsPipe.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "wdxGraphicsPipe.h" +#include "config_wdxdisplay.h" +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle wdxGraphicsPipe::_type_handle; + +//wdxGraphicsPipe *global_pipe; + +#define MOUSE_ENTERED 0 +#define MOUSE_EXITED 1 + +wdxGraphicsPipe::wdxGraphicsPipe(const PipeSpecifier& spec) + : InteractiveGraphicsPipe(spec) +{ + /* + WNDCLASS wc; + HINSTANCE hinstance = GetModuleHandle(NULL); + + // Clear before filling in window structure! + memset(&wc, 0, sizeof(WNDCLASS)); + wc.style = CS_HREDRAW | CS_VREDRAW; //CS_OWNDC; + wc.lpfnWndProc = (WNDPROC)static_window_proc; + wc.hInstance = NULL; //hinstance; + wc.hIcon = LoadIcon(hinstance, IDI_WINLOGO); + wc.hCursor = LoadCursor(hinstance, IDC_CROSS); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = "wdxDisplay"; + +#if 0 + if (dx_full_screen) + { + WNDCLASS wndClass = { CS_HREDRAW | CS_VREDRAW, (WNDPROC)static_window_proc, 0, 0, NULL, + NULL, NULL, (HBRUSH)GetStockObject(WHITE_BRUSH), NULL, + TEXT("Fullscreen Window") }; + + if (!RegisterClass( &wndClass )) { + wdxdisplay_cat.fatal() + << "wdxGraphicsPipe::construct(): could not register full screen window " + << "class" << endl; + exit(0); + } + } + else +#endif + if (!RegisterClass(&wc)) { + wdxdisplay_cat.fatal() + << "wdxGraphicsPipe::construct(): could not register window " + << "class" << endl; + exit(0); + } + +*/ + _width = GetSystemMetrics(SM_CXSCREEN); + _height = GetSystemMetrics(SM_CYSCREEN); + _shift = false; +// global_pipe = this; +} + +//////////////////////////////////////////////////////////////////// +// Function: wdxGraphicsPipe::get_window_type +// Access: Public, Virtual +// Description: Returns the TypeHandle of the kind of window +// preferred by this kind of pipe. +//////////////////////////////////////////////////////////////////// +TypeHandle wdxGraphicsPipe:: +get_window_type() const { + return wdxGraphicsWindow::get_class_type(); +} + +GraphicsPipe *wdxGraphicsPipe:: +make_wdxGraphicsPipe(const FactoryParams ¶ms) { + GraphicsPipe::PipeSpec *pipe_param; + if (!get_param_into(pipe_param, params)) { + return new wdxGraphicsPipe(PipeSpecifier()); + } else { + return new wdxGraphicsPipe(pipe_param->get_specifier()); + } +} + + +TypeHandle wdxGraphicsPipe::get_class_type(void) { + return _type_handle; +} + +void wdxGraphicsPipe::init_type(void) { + InteractiveGraphicsPipe::init_type(); + register_type(_type_handle, "wdxGraphicsPipe", + InteractiveGraphicsPipe::get_class_type()); +} + +TypeHandle wdxGraphicsPipe::get_type(void) const { + return get_class_type(); +} + +wdxGraphicsPipe::wdxGraphicsPipe(void) { + wdxdisplay_cat.error() + << "wdxGraphicsPipes should not be created with the default constructor" + << endl; +} + +wdxGraphicsPipe::wdxGraphicsPipe(const wdxGraphicsPipe&) { + wdxdisplay_cat.error() + << "wdxGraphicsPipes should not be copied" << endl; +} + +wdxGraphicsPipe& wdxGraphicsPipe::operator=(const wdxGraphicsPipe&) { + wdxdisplay_cat.error() + << "wdxGraphicsPipes should not be assigned" << endl; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: find_window +// Access: +// Description: Find the window that has the xwindow "win" in the +// window list for the pipe (if it exists) +//////////////////////////////////////////////////////////////////// +wdxGraphicsWindow *wdxGraphicsPipe:: +find_window(HWND win) { + int num_windows = get_num_windows(); + for (int w = 0; w < num_windows; w++) { + wdxGraphicsWindow *window = DCAST(wdxGraphicsWindow, get_window(w)); + if (window->_mwindow == win) + return window; + } + return NULL; +} + +/* +//////////////////////////////////////////////////////////////////// +// Function: static_window_proc +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +LONG WINAPI wdxGraphicsPipe:: +static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + return global_pipe->window_proc(hwnd, msg, wparam, lparam); +} + +//////////////////////////////////////////////////////////////////// +// Function: window_proc +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +LONG wdxGraphicsPipe:: +window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + PAINTSTRUCT ps; + wdxGraphicsWindow *window; + int button = -1; + int x, y, width, height; + + switch (msg) { + case WM_CREATE: + return 0; + + case WM_CLOSE: + DestroyWindow(hwnd); + return 0; + + case WM_DESTROY: + // window = find_window(hwnd); + // if (window) + // window->dx_cleanup(); + PostQuitMessage(0); + return 0; + + + case WM_PAINT: + window = find_window(hwnd); + if (window) { +// BeginPaint(hwnd, &ps); + window->show_frame(); +// EndPaint(hwnd, &ps); + } + else DefWindowProc(hwnd, msg, wparam, lparam); + return 0; + + case WM_SYSCHAR: + case WM_CHAR: + return 0; + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + window = find_window(hwnd); + if (window) { + POINT point; +// window->make_current(); + GetCursorPos(&point); + ScreenToClient(hwnd, &point); + window->handle_keypress(lookup_key(wparam), point.x, point.y); + } + return 0; + case WM_SYSKEYUP: + case WM_KEYUP: + window = find_window(hwnd); + if (window) { + POINT point; +// window->make_current(); + GetCursorPos(&point); + ScreenToClient(hwnd, &point); + window->handle_keyrelease(lookup_key(wparam), point.x, point.y); + } + return 0; + + case WM_LBUTTONDOWN: + button = 0; + case WM_MBUTTONDOWN: + if (button < 0) + button = 1; + case WM_RBUTTONDOWN: + if (button < 0) + button = 2; + SetCapture(hwnd); + // Win32 doesn't return the same numbers as X does when the mouse + // goes beyond the upper or left side of the window + x = LOWORD(lparam); + y = HIWORD(lparam); + if (x & 1 << 15) x -= (1 << 16); + if (y & 1 << 15) y -= (1 << 16); + window = find_window(hwnd); + if (window) { +// window->make_current(); + window->handle_keypress(MouseButton::button(button), x, y); + } + return 0; + + case WM_LBUTTONUP: + button = 0; + case WM_MBUTTONUP: + if (button < 0) + button = 1; + case WM_RBUTTONUP: + if (button < 0) + button = 2; + ReleaseCapture(); + window = find_window(hwnd); + if (window) { + x = LOWORD(lparam); + y = HIWORD(lparam); + if (x & 1 << 15) x -= (1 << 16); + if (y & 1 << 15) y -= (1 << 16); +// window->make_current(); + window->handle_keyrelease(MouseButton::button(button), x, y); + } + return 0; + + case WM_MOUSEMOVE: + window = find_window(hwnd); + if (window) { + x = LOWORD(lparam); + y = HIWORD(lparam); + if (x & 1 << 15) x -= (1 << 16); + if (y & 1 << 15) y -= (1 << 16); + if (window->mouse_motion_enabled() + && wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) + { +// window->make_current(); + window->handle_mouse_motion(x, y); + } + else if (window->mouse_passive_motion_enabled() && + ((wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) == 0)) + { +// window->make_current(); + window->handle_mouse_motion(x, y); + } + } + return 0; + + case WM_MOVE: + window = find_window(hwnd); + if (window) { + window->handle_window_move(LOWORD(lparam), HIWORD(lparam) ); + } + return 0; + + case WM_SIZE: + window = find_window(hwnd); + if (window) { + width = LOWORD(lparam); + height = HIWORD(lparam); + window->handle_reshape(width, height); + } + return 0; + + case WM_SETCURSOR: + // We need to set the cursor every time the mouse moves on Win32! + if (LOWORD(lparam) != HTCLIENT) + return DefWindowProc(hwnd, msg, wparam, lparam); + window = find_window(hwnd); + if (window) { + SetCursor(LoadCursor(NULL, IDC_CROSS)); + } + return 1; + case WM_SETFOCUS: + window = find_window(hwnd); + if (window) + { + if (window->mouse_entry_enabled()) { +// window->make_current(); + window->handle_mouse_entry(MOUSE_ENTERED); + } + } + return 0; + case WM_KILLFOCUS: + window = find_window(hwnd); + if (window) + { + if (window->mouse_entry_enabled()) { +// window->make_current(); + window->handle_mouse_entry(MOUSE_EXITED); + } + } + return 0; + default: + return DefWindowProc(hwnd, msg, wparam, lparam); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: lookup_key +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +ButtonHandle +wdxGraphicsPipe::lookup_key(WPARAM wparam) const { + switch (wparam) { + case VK_BACK: return KeyboardButton::backspace(); + case VK_TAB: return KeyboardButton::tab(); + case VK_ESCAPE: return KeyboardButton::escape(); + case VK_SPACE: return KeyboardButton::space(); + case VK_UP: return KeyboardButton::up(); + case VK_DOWN: return KeyboardButton::down(); + case VK_LEFT: return KeyboardButton::left(); + case VK_RIGHT: return KeyboardButton::right(); + case VK_PRIOR: return KeyboardButton::page_up(); + case VK_NEXT: return KeyboardButton::page_down(); + case VK_HOME: return KeyboardButton::home(); + case VK_END: return KeyboardButton::end(); + case VK_F1: return KeyboardButton::f1(); + case VK_F2: return KeyboardButton::f2(); + case VK_F3: return KeyboardButton::f3(); + case VK_F4: return KeyboardButton::f4(); + case VK_F5: return KeyboardButton::f5(); + case VK_F6: return KeyboardButton::f6(); + case VK_F7: return KeyboardButton::f7(); + case VK_F8: return KeyboardButton::f8(); + case VK_F9: return KeyboardButton::f9(); + case VK_F10: return KeyboardButton::f10(); + case VK_F11: return KeyboardButton::f11(); + case VK_F12: return KeyboardButton::f12(); + case VK_INSERT: return KeyboardButton::insert(); + case VK_DELETE: return KeyboardButton::del(); + default: + int key = MapVirtualKey(wparam, 2); + if (isascii(key) && key != 0) { + if (GetKeyState(VK_SHIFT) >= 0) + key = tolower(key); + return KeyboardButton::ascii_key((uchar)key); + } + break; + } + return ButtonHandle::none(); +} +*/ + + +extern char * ConvD3DErrorToString(const HRESULT &error) { + switch(error) + { + case E_FAIL: + return "Unspecified error E_FAIL"; + + case DD_OK: + return "No error.\0"; + case D3DERR_BADMAJORVERSION : // (700) + return "D3DERR_BADMAJORVERSION";//: // (700) + case D3DERR_BADMINORVERSION : // (701) + return "D3DERR_BADMINORVERSION";//: // (701) + case D3DERR_INVALID_DEVICE : // (705) + return "D3DERR_INVALID_DEVICE";//: // (705) + case D3DERR_INITFAILED : // (706) + return "D3DERR_INITFAILED";//: // (706) + case D3DERR_DEVICEAGGREGATED : // (707) + return "D3DERR_DEVICEAGGREGATED";//: // (707) + case D3DERR_EXECUTE_CREATE_FAILED : // (710) + return "D3DERR_EXECUTE_CREATE_FAILED";//: // (710) + case D3DERR_EXECUTE_DESTROY_FAILED : // (711) + return "D3DERR_EXECUTE_DESTROY_FAILED";//: // (711) + case D3DERR_EXECUTE_LOCK_FAILED : // (712) + return "D3DERR_EXECUTE_LOCK_FAILED";//: // (712) + case D3DERR_EXECUTE_UNLOCK_FAILED : // (713) + return "D3DERR_EXECUTE_UNLOCK_FAILED";//: // (713) + case D3DERR_EXECUTE_LOCKED : // (714) + return "D3DERR_EXECUTE_LOCKED";//: // (714) + case D3DERR_EXECUTE_NOT_LOCKED : // (715) + return "D3DERR_EXECUTE_NOT_LOCKED";//: // (715) + case D3DERR_EXECUTE_FAILED : // (716) + return "D3DERR_EXECUTE_FAILED";//: // (716) + case D3DERR_EXECUTE_CLIPPED_FAILED : // (717) + return "D3DERR_EXECUTE_CLIPPED_FAILED";//: // (717) + case D3DERR_TEXTURE_NO_SUPPORT : // (720) + return "D3DERR_TEXTURE_NO_SUPPORT";//: // (720) + case D3DERR_TEXTURE_CREATE_FAILED : // (721) + return "D3DERR_TEXTURE_CREATE_FAILED";//: // (721) + case D3DERR_TEXTURE_DESTROY_FAILED : // (722) + return "D3DERR_TEXTURE_DESTROY_FAILED";//: // (722) + case D3DERR_TEXTURE_LOCK_FAILED : // (723) + return "D3DERR_TEXTURE_LOCK_FAILED";//: // (723) + case D3DERR_TEXTURE_UNLOCK_FAILED : // (724) + return "D3DERR_TEXTURE_UNLOCK_FAILED";//: // (724) + case D3DERR_TEXTURE_LOAD_FAILED : // (725) + return "D3DERR_TEXTURE_LOAD_FAILED";//: // (725) + case D3DERR_TEXTURE_SWAP_FAILED : // (726) + return "D3DERR_TEXTURE_SWAP_FAILED";//: // (726) + case D3DERR_TEXTURE_LOCKED : // (727) + return "D3DERR_TEXTURE_LOCKED";//: // (727) + case D3DERR_TEXTURE_NOT_LOCKED : // (728) + return "D3DERR_TEXTURE_NOT_LOCKED";//: // (728) + case D3DERR_TEXTURE_GETSURF_FAILED : // (729) + return "D3DERR_TEXTURE_GETSURF_FAILED";//: // (729) + case D3DERR_MATRIX_CREATE_FAILED : // (730) + return "D3DERR_MATRIX_CREATE_FAILED";//: // (730) + case D3DERR_MATRIX_DESTROY_FAILED : // (731) + return "D3DERR_MATRIX_DESTROY_FAILED";//: // (731) + case D3DERR_MATRIX_SETDATA_FAILED : // (732) + return "D3DERR_MATRIX_SETDATA_FAILED";//: // (732) + case D3DERR_MATRIX_GETDATA_FAILED : // (733) + return "D3DERR_MATRIX_GETDATA_FAILED";//: // (733) + case D3DERR_SETVIEWPORTDATA_FAILED : // (734) + return "D3DERR_SETVIEWPORTDATA_FAILED";//: // (734) + case D3DERR_INVALIDCURRENTVIEWPORT : // (735) + return "D3DERR_INVALIDCURRENTVIEWPORT";//: // (735) + case D3DERR_INVALIDPRIMITIVETYPE : // (736) + return "D3DERR_INVALIDPRIMITIVETYPE";//: // (736) + case D3DERR_INVALIDVERTEXTYPE : // (737) + return "D3DERR_INVALIDVERTEXTYPE";//: // (737) + case D3DERR_TEXTURE_BADSIZE : // (738) + return "D3DERR_TEXTURE_BADSIZE";//: // (738) + case D3DERR_INVALIDRAMPTEXTURE : // (739) + return "D3DERR_INVALIDRAMPTEXTURE";//: // (739) + case D3DERR_MATERIAL_CREATE_FAILED : // (740) + return "D3DERR_MATERIAL_CREATE_FAILED";//: // (740) + case D3DERR_MATERIAL_DESTROY_FAILED : // (741) + return "D3DERR_MATERIAL_DESTROY_FAILED";//: // (741) + case D3DERR_MATERIAL_SETDATA_FAILED : // (742) + return "D3DERR_MATERIAL_SETDATA_FAILED";//: // (742) + case D3DERR_MATERIAL_GETDATA_FAILED : // (743) + return "D3DERR_MATERIAL_GETDATA_FAILED";//: // (743) + case D3DERR_INVALIDPALETTE : // (744) + return "D3DERR_INVALIDPALETTE";//: // (744) + case D3DERR_ZBUFF_NEEDS_SYSTEMMEMORY : // (745) + return "D3DERR_ZBUFF_NEEDS_SYSTEMMEMORY";//: // (745) + case D3DERR_ZBUFF_NEEDS_VIDEOMEMORY : // (746) + return "D3DERR_ZBUFF_NEEDS_VIDEOMEMORY";//: // (746) + case D3DERR_SURFACENOTINVIDMEM : // (747) + return "D3DERR_SURFACENOTINVIDMEM";//: // (747) + case D3DERR_LIGHT_SET_FAILED : // (750) + return "D3DERR_LIGHT_SET_FAILED";//: // (750) + case D3DERR_LIGHTHASVIEWPORT : // (751) + return "D3DERR_LIGHTHASVIEWPORT";//: // (751) + case D3DERR_LIGHTNOTINTHISVIEWPORT : // (752) + return "D3DERR_LIGHTNOTINTHISVIEWPORT";//: // (752) + case D3DERR_SCENE_IN_SCENE : // (760) + return "D3DERR_SCENE_IN_SCENE";//: // (760) + case D3DERR_SCENE_NOT_IN_SCENE : // (761) + return "D3DERR_SCENE_NOT_IN_SCENE";//: // (761) + case D3DERR_SCENE_BEGIN_FAILED : // (762) + return "D3DERR_SCENE_BEGIN_FAILED";//: // (762) + case D3DERR_SCENE_END_FAILED : // (763) + return "D3DERR_SCENE_END_FAILED";//: // (763) + case D3DERR_INBEGIN : // (770) + return "D3DERR_INBEGIN";//: // (770) + case D3DERR_NOTINBEGIN : // (771) + return "D3DERR_NOTINBEGIN";//: // (771) + case D3DERR_NOVIEWPORTS : // (772) + return "D3DERR_NOVIEWPORTS";//: // (772) + case D3DERR_VIEWPORTDATANOTSET : // (773) + return "D3DERR_VIEWPORTDATANOTSET";//: // (773) + case D3DERR_VIEWPORTHASNODEVICE : // (774) + return "D3DERR_VIEWPORTHASNODEVICE";//: // (774) + case D3DERR_NOCURRENTVIEWPORT : // (775) + return "D3DERR_NOCURRENTVIEWPORT";//: // (775) + case D3DERR_INVALIDVERTEXFORMAT : // (2048) + return "D3DERR_INVALIDVERTEXFORMAT";//: // (2048) + case D3DERR_COLORKEYATTACHED : // (2050) + return "D3DERR_COLORKEYATTACHED";//: // (2050) + case D3DERR_VERTEXBUFFEROPTIMIZED : // (2060) + return "D3DERR_VERTEXBUFFEROPTIMIZED";//: // (2060) + case D3DERR_VBUF_CREATE_FAILED : // (2061) + return "D3DERR_VBUF_CREATE_FAILED";//: // (2061) + case D3DERR_VERTEXBUFFERLOCKED : // (2062) + return "D3DERR_VERTEXBUFFERLOCKED";//: // (2062) + case D3DERR_ZBUFFER_NOTPRESENT : // (2070) + return "D3DERR_ZBUFFER_NOTPRESENT";//: // (2070) + case D3DERR_STENCILBUFFER_NOTPRESENT : // (2071) + return "D3DERR_STENCILBUFFER_NOTPRESENT";//: // (2071) + case D3DERR_WRONGTEXTUREFORMAT : // (2072) + return "D3DERR_WRONGTEXTUREFORMAT";//: // (2072) + case D3DERR_UNSUPPORTEDCOLOROPERATION : // (2073) + return "D3DERR_UNSUPPORTEDCOLOROPERATION";//: // (2073) + case D3DERR_UNSUPPORTEDCOLORARG : // (2074) + return "D3DERR_UNSUPPORTEDCOLORARG";//: // (2074) + case D3DERR_UNSUPPORTEDALPHAOPERATION : // (2075) + return "D3DERR_UNSUPPORTEDALPHAOPERATION";//: // (2075) + case D3DERR_UNSUPPORTEDALPHAARG : // (2076) + return "D3DERR_UNSUPPORTEDALPHAARG";//: // (2076) + case D3DERR_TOOMANYOPERATIONS : // (2077) + return "D3DERR_TOOMANYOPERATIONS";//: // (2077) + case D3DERR_CONFLICTINGTEXTUREFILTER : // (2078) + return "D3DERR_CONFLICTINGTEXTUREFILTER";//: // (2078) + case D3DERR_UNSUPPORTEDFACTORVALUE : // (2079) + return "D3DERR_UNSUPPORTEDFACTORVALUE";//: // (2079) + case D3DERR_CONFLICTINGRENDERSTATE : // (2081) + return "D3DERR_CONFLICTINGRENDERSTATE";//: // (2081) + case D3DERR_UNSUPPORTEDTEXTUREFILTER : // (2082) + return "D3DERR_UNSUPPORTEDTEXTUREFILTER";//: // (2082) + case D3DERR_TOOMANYPRIMITIVES : // (2083) + return "D3DERR_TOOMANYPRIMITIVES";//: // (2083) + case D3DERR_INVALIDMATRIX : // (2084) + return "D3DERR_INVALIDMATRIX";//: // (2084) + case D3DERR_TOOMANYVERTICES : // (2085) + return "D3DERR_TOOMANYVERTICES";//: // (2085) + case D3DERR_CONFLICTINGTEXTUREPALETTE : // (2086) + return "D3DERR_CONFLICTINGTEXTUREPALETTE";//: // (2086) +//#if DX7 + case D3DERR_VERTEXBUFFERUNLOCKFAILED : // (2063) + return "D3DERR_VERTEXBUFFERUNLOCKFAILED";//: // (2063) + case D3DERR_INVALIDSTATEBLOCK : // (2100) + return "D3DERR_INVALIDSTATEBLOCK";//: // (2100) + case D3DERR_INBEGINSTATEBLOCK : // (2101) + return "D3DERR_INBEGINSTATEBLOCK";//: // (2101) + case D3DERR_NOTINBEGINSTATEBLOCK : // (2102) + return "D3DERR_NOTINBEGINSTATEBLOCK";//: // (2102) + //case D3DERR_INOVERLAYSTATEBLOCK : // (2103) + // return "D3DERR_INOVERLAYSTATEBLOCK";//: // (2103) + case DDERR_NOSTEREOHARDWARE : // ( 181 ) + return "DDERR_NOSTEREOHARDWARE ";//: // ( 181 ) + case DDERR_NOSURFACELEFT : // ( 182 ) + return "DDERR_NOSURFACELEFT ";//: // ( 182 ) + case DDERR_DDSCAPSCOMPLEXREQUIRED : // ( 542 ) + return "DDERR_DDSCAPSCOMPLEXREQUIRED";//: // ( 542 ) + case DDERR_NOTONMIPMAPSUBLEVEL : // ( 603 ) + return "DDERR_NOTONMIPMAPSUBLEVEL";//: // ( 603 ) + case DDERR_TESTFINISHED : // ( 692 ) + return "DDERR_TESTFINISHED";//: // ( 692 ) + case DDERR_NEWMODE : // ( 693 ) + return "DDERR_NEWMODE";//: // ( 693 ) +//#endif + //case D3DERR_COMMAND_UNPARSED : // (3000) + /// return "case";//D3DERR_COMMAND_UNPARSED : // (3000) + + case DDERR_ALREADYINITIALIZED : // ( 5 ) + return "DDERR_ALREADYINITIALIZED ";//: // ( 5 ) + case DDERR_CANNOTATTACHSURFACE : // ( 10 ) + return "DDERR_CANNOTATTACHSURFACE ";//: // ( 10 ) + case DDERR_CANNOTDETACHSURFACE : // ( 20 ) + return "DDERR_CANNOTDETACHSURFACE ";//: // ( 20 ) + case DDERR_CURRENTLYNOTAVAIL : // ( 40 ) + return "DDERR_CURRENTLYNOTAVAIL ";//: // ( 40 ) + case DDERR_EXCEPTION : // ( 55 ) + return "DDERR_EXCEPTION ";//: // ( 55 ) + case DDERR_HEIGHTALIGN : // ( 90 ) + return "DDERR_HEIGHTALIGN ";//: // ( 90 ) + case DDERR_INCOMPATIBLEPRIMARY : // ( 95 ) + return "DDERR_INCOMPATIBLEPRIMARY ";//: // ( 95 ) + case DDERR_INVALIDCAPS : // ( 100 ) + return "DDERR_INVALIDCAPS ";//: // ( 100 ) + case DDERR_INVALIDCLIPLIST : // ( 110 ) + return "DDERR_INVALIDCLIPLIST ";//: // ( 110 ) + case DDERR_INVALIDMODE : // ( 120 ) + return "DDERR_INVALIDMODE ";//: // ( 120 ) + case DDERR_INVALIDOBJECT : // ( 130 ) + return "DDERR_INVALIDOBJECT ";//: // ( 130 ) + case DDERR_INVALIDPIXELFORMAT : // ( 145 ) + return "DDERR_INVALIDPIXELFORMAT ";//: // ( 145 ) + case DDERR_INVALIDRECT : // ( 150 ) + return "DDERR_INVALIDRECT ";//: // ( 150 ) + case DDERR_LOCKEDSURFACES : // ( 160 ) + return "DDERR_LOCKEDSURFACES ";//: // ( 160 ) + case DDERR_NO3D : // ( 170 ) + return "DDERR_NO3D ";//: // ( 170 ) + case DDERR_NOALPHAHW : // ( 180 ) + return "DDERR_NOALPHAHW ";//: // ( 180 ) + case DDERR_NOCLIPLIST : // ( 205 ) + return "DDERR_NOCLIPLIST ";//: // ( 205 ) + case DDERR_NOCOLORCONVHW : // ( 210 ) + return "DDERR_NOCOLORCONVHW ";//: // ( 210 ) + case DDERR_NOCOOPERATIVELEVELSET : // ( 212 ) + return "DDERR_NOCOOPERATIVELEVELSET ";//: // ( 212 ) + case DDERR_NOCOLORKEY : // ( 215 ) + return "DDERR_NOCOLORKEY ";//: // ( 215 ) + case DDERR_NOCOLORKEYHW : // ( 220 ) + return "DDERR_NOCOLORKEYHW ";//: // ( 220 ) + case DDERR_NODIRECTDRAWSUPPORT : // ( 222 ) + return "DDERR_NODIRECTDRAWSUPPORT ";//: // ( 222 ) + case DDERR_NOEXCLUSIVEMODE : // ( 225 ) + return "DDERR_NOEXCLUSIVEMODE ";//: // ( 225 ) + case DDERR_NOFLIPHW : // ( 230 ) + return "DDERR_NOFLIPHW ";//: // ( 230 ) + case DDERR_NOGDI : // ( 240 ) + return "DDERR_NOGDI ";//: // ( 240 ) + case DDERR_NOMIRRORHW : // ( 250 ) + return "DDERR_NOMIRRORHW ";//: // ( 250 ) + case DDERR_NOTFOUND : // ( 255 ) + return "DDERR_NOTFOUND ";//: // ( 255 ) + case DDERR_NOOVERLAYHW : // ( 260 ) + return "DDERR_NOOVERLAYHW ";//: // ( 260 ) + case DDERR_OVERLAPPINGRECTS : // ( 270 ) + return "DDERR_OVERLAPPINGRECTS ";//: // ( 270 ) + case DDERR_NORASTEROPHW : // ( 280 ) + return "DDERR_NORASTEROPHW ";//: // ( 280 ) + case DDERR_NOROTATIONHW : // ( 290 ) + return "DDERR_NOROTATIONHW ";//: // ( 290 ) + case DDERR_NOSTRETCHHW : // ( 310 ) + return "DDERR_NOSTRETCHHW ";//: // ( 310 ) + case DDERR_NOT4BITCOLOR : // ( 316 ) + return "DDERR_NOT4BITCOLOR ";//: // ( 316 ) + case DDERR_NOT4BITCOLORINDEX : // ( 317 ) + return "DDERR_NOT4BITCOLORINDEX ";//: // ( 317 ) + case DDERR_NOT8BITCOLOR : // ( 320 ) + return "DDERR_NOT8BITCOLOR ";//: // ( 320 ) + case DDERR_NOTEXTUREHW : // ( 330 ) + return "DDERR_NOTEXTUREHW ";//: // ( 330 ) + case DDERR_NOVSYNCHW : // ( 335 ) + return "DDERR_NOVSYNCHW ";//: // ( 335 ) + case DDERR_NOZBUFFERHW : // ( 340 ) + return "DDERR_NOZBUFFERHW ";//: // ( 340 ) + case DDERR_NOZOVERLAYHW : // ( 350 ) + return "DDERR_NOZOVERLAYHW ";//: // ( 350 ) + case DDERR_OUTOFCAPS : // ( 360 ) + return "DDERR_OUTOFCAPS ";//: // ( 360 ) + case DDERR_OUTOFVIDEOMEMORY : // ( 380 ) + return "DDERR_OUTOFVIDEOMEMORY ";//: // ( 380 ) + case DDERR_OVERLAYCANTCLIP : // ( 382 ) + return "DDERR_OVERLAYCANTCLIP ";//: // ( 382 ) + case DDERR_OVERLAYCOLORKEYONLYONEACTIVE : // ( 384 ) + return "DDERR_OVERLAYCOLORKEYONLYONEACTIVE ";//: // ( 384 ) + case DDERR_PALETTEBUSY : // ( 387 ) + return "DDERR_PALETTEBUSY ";//: // ( 387 ) + case DDERR_COLORKEYNOTSET : // ( 400 ) + return "DDERR_COLORKEYNOTSET ";//: // ( 400 ) + case DDERR_SURFACEALREADYATTACHED : // ( 410 ) + return "DDERR_SURFACEALREADYATTACHED ";//: // ( 410 ) + case DDERR_SURFACEALREADYDEPENDENT : // ( 420 ) + return "DDERR_SURFACEALREADYDEPENDENT ";//: // ( 420 ) + case DDERR_SURFACEBUSY : // ( 430 ) + return "DDERR_SURFACEBUSY ";//: // ( 430 ) + case DDERR_CANTLOCKSURFACE : // ( 435 ) + return "DDERR_CANTLOCKSURFACE";//: // ( 435 ) + case DDERR_SURFACEISOBSCURED : // ( 440 ) + return "DDERR_SURFACEISOBSCURED ";//: // ( 440 ) + case DDERR_SURFACELOST : // ( 450 ) + return "DDERR_SURFACELOST ";//: // ( 450 ) + case DDERR_SURFACENOTATTACHED : // ( 460 ) + return "DDERR_SURFACENOTATTACHED ";//: // ( 460 ) + case DDERR_TOOBIGHEIGHT : // ( 470 ) + return "DDERR_TOOBIGHEIGHT ";//: // ( 470 ) + case DDERR_TOOBIGSIZE : // ( 480 ) + return "DDERR_TOOBIGSIZE ";//: // ( 480 ) + case DDERR_TOOBIGWIDTH : // ( 490 ) + return "DDERR_TOOBIGWIDTH ";//: // ( 490 ) + case DDERR_UNSUPPORTEDFORMAT : // ( 510 ) + return "DDERR_UNSUPPORTEDFORMAT ";//: // ( 510 ) + case DDERR_UNSUPPORTEDMASK : // ( 520 ) + return "DDERR_UNSUPPORTEDMASK ";//: // ( 520 ) + case DDERR_INVALIDSTREAM : // ( 521 ) + return "DDERR_INVALIDSTREAM";//: // ( 521 ) + case DDERR_VERTICALBLANKINPROGRESS : // ( 537 ) + return "DDERR_VERTICALBLANKINPROGRESS ";//: // ( 537 ) + case DDERR_WASSTILLDRAWING : // ( 540 ) + return "DDERR_WASSTILLDRAWING ";//: // ( 540 ) + case DDERR_XALIGN : // ( 560 ) + return "DDERR_XALIGN ";//: // ( 560 ) + case DDERR_INVALIDDIRECTDRAWGUID : // ( 561 ) + return "DDERR_INVALIDDIRECTDRAWGUID ";//: // ( 561 ) + case DDERR_DIRECTDRAWALREADYCREATED : // ( 562 ) + return "DDERR_DIRECTDRAWALREADYCREATED ";//: // ( 562 ) + case DDERR_NODIRECTDRAWHW : // ( 563 ) + return "DDERR_NODIRECTDRAWHW ";//: // ( 563 ) + case DDERR_PRIMARYSURFACEALREADYEXISTS : // ( 564 ) + return "DDERR_PRIMARYSURFACEALREADYEXISTS ";//: // ( 564 ) + case DDERR_NOEMULATION : // ( 565 ) + return "DDERR_NOEMULATION ";//: // ( 565 ) + case DDERR_REGIONTOOSMALL : // ( 566 ) + return "DDERR_REGIONTOOSMALL ";//: // ( 566 ) + case DDERR_CLIPPERISUSINGHWND : // ( 567 ) + return "DDERR_CLIPPERISUSINGHWND ";//: // ( 567 ) + case DDERR_NOCLIPPERATTACHED : // ( 568 ) + return "DDERR_NOCLIPPERATTACHED ";//: // ( 568 ) + case DDERR_NOHWND : // ( 569 ) + return "DDERR_NOHWND ";//: // ( 569 ) + case DDERR_HWNDSUBCLASSED : // ( 570 ) + return "DDERR_HWNDSUBCLASSED ";//: // ( 570 ) + case DDERR_HWNDALREADYSET : // ( 571 ) + return "DDERR_HWNDALREADYSET ";//: // ( 571 ) + case DDERR_NOPALETTEATTACHED : // ( 572 ) + return "DDERR_NOPALETTEATTACHED ";//: // ( 572 ) + case DDERR_NOPALETTEHW : // ( 573 ) + return "DDERR_NOPALETTEHW ";//: // ( 573 ) + case DDERR_BLTFASTCANTCLIP : // ( 574 ) + return "DDERR_BLTFASTCANTCLIP ";//: // ( 574 ) + case DDERR_NOBLTHW : // ( 575 ) + return "DDERR_NOBLTHW ";//: // ( 575 ) + case DDERR_NODDROPSHW : // ( 576 ) + return "DDERR_NODDROPSHW ";//: // ( 576 ) + case DDERR_OVERLAYNOTVISIBLE : // ( 577 ) + return "DDERR_OVERLAYNOTVISIBLE ";//: // ( 577 ) + case DDERR_NOOVERLAYDEST : // ( 578 ) + return "DDERR_NOOVERLAYDEST ";//: // ( 578 ) + case DDERR_INVALIDPOSITION : // ( 579 ) + return "DDERR_INVALIDPOSITION ";//: // ( 579 ) + case DDERR_NOTAOVERLAYSURFACE : // ( 580 ) + return "DDERR_NOTAOVERLAYSURFACE ";//: // ( 580 ) + case DDERR_EXCLUSIVEMODEALREADYSET : // ( 581 ) + return "DDERR_EXCLUSIVEMODEALREADYSET ";//: // ( 581 ) + case DDERR_NOTFLIPPABLE : // ( 582 ) + return "DDERR_NOTFLIPPABLE ";//: // ( 582 ) + case DDERR_CANTDUPLICATE : // ( 583 ) + return "DDERR_CANTDUPLICATE ";//: // ( 583 ) + case DDERR_NOTLOCKED : // ( 584 ) + return "DDERR_NOTLOCKED ";//: // ( 584 ) + case DDERR_CANTCREATEDC : // ( 585 ) + return "DDERR_CANTCREATEDC ";//: // ( 585 ) + case DDERR_NODC : // ( 586 ) + return "DDERR_NODC ";//: // ( 586 ) + case DDERR_WRONGMODE : // ( 587 ) + return "DDERR_WRONGMODE ";//: // ( 587 ) + case DDERR_IMPLICITLYCREATED : // ( 588 ) + return "DDERR_IMPLICITLYCREATED ";//: // ( 588 ) + case DDERR_NOTPALETTIZED : // ( 589 ) + return "DDERR_NOTPALETTIZED ";//: // ( 589 ) + case DDERR_UNSUPPORTEDMODE : // ( 590 ) + return "DDERR_UNSUPPORTEDMODE ";//: // ( 590 ) + case DDERR_NOMIPMAPHW : // ( 591 ) + return "DDERR_NOMIPMAPHW ";//: // ( 591 ) + case DDERR_INVALIDSURFACETYPE : // ( 592 ) + return "DDERR_INVALIDSURFACETYPE";//: // ( 592 ) + case DDERR_NOOPTIMIZEHW : // ( 600 ) + return "DDERR_NOOPTIMIZEHW";//: // ( 600 ) + case DDERR_NOTLOADED : // ( 601 ) + return "DDERR_NOTLOADED";//: // ( 601 ) + case DDERR_NOFOCUSWINDOW : // ( 602 ) + return "DDERR_NOFOCUSWINDOW";//: // ( 602 ) + case DDERR_DCALREADYCREATED : // ( 620 ) + return "DDERR_DCALREADYCREATED ";//: // ( 620 ) + case DDERR_NONONLOCALVIDMEM : // ( 630 ) + return "DDERR_NONONLOCALVIDMEM";//: // ( 630 ) + case DDERR_CANTPAGELOCK : // ( 640 ) + return "DDERR_CANTPAGELOCK ";//: // ( 640 ) + case DDERR_CANTPAGEUNLOCK : // ( 660 ) + return "DDERR_CANTPAGEUNLOCK ";//: // ( 660 ) + case DDERR_NOTPAGELOCKED : // ( 680 ) + return "DDERR_NOTPAGELOCKED ";//: // ( 680 ) + case DDERR_MOREDATA : // ( 690 ) + return "DDERR_MOREDATA ";//: // ( 690 ) + case DDERR_EXPIRED : // ( 691 ) + return "DDERR_EXPIRED";//: // ( 691 ) + case DDERR_VIDEONOTACTIVE : // ( 695 ) + return "DDERR_VIDEONOTACTIVE ";//: // ( 695 ) + case DDERR_DEVICEDOESNTOWNSURFACE : // ( 699 ) + return "DDERR_DEVICEDOESNTOWNSURFACE ";//: // ( 699 ) + case DXFILEERR_BADOBJECT : // (850) + return "DXFILEERR_BADOBJECT";//: // (850) + case DXFILEERR_BADVALUE : // (851) + return "DXFILEERR_BADVALUE";//: // (851) + case DXFILEERR_BADTYPE : // (852) + return "DXFILEERR_BADTYPE";//: // (852) + case DXFILEERR_BADSTREAMHANDLE : // (853) + return "DXFILEERR_BADSTREAMHANDLE";//: // (853) + case DXFILEERR_BADALLOC : // (854) + return "DXFILEERR_BADALLOC";//: // (854) + case DXFILEERR_NOTFOUND : // (855) + return "DXFILEERR_NOTFOUND";//: // (855) + case DXFILEERR_NOTDONEYET : // (856) + return "DXFILEERR_NOTDONEYET";//: // (856) + case DXFILEERR_FILENOTFOUND : // (857) + return "DXFILEERR_FILENOTFOUND";//: // (857) + case DXFILEERR_RESOURCENOTFOUND : // (858) + return "DXFILEERR_RESOURCENOTFOUND";//: // (858) + case DXFILEERR_URLNOTFOUND : // (859) + return "DXFILEERR_URLNOTFOUND";//: // (859) + case DXFILEERR_BADRESOURCE : // (860) + return "DXFILEERR_BADRESOURCE";//: // (860) + case DXFILEERR_BADFILETYPE : // (861) + return "DXFILEERR_BADFILETYPE";//: // (861) + case DXFILEERR_BADFILEVERSION : // (862) + return "DXFILEERR_BADFILEVERSION";//: // (862) + case DXFILEERR_BADFILEFLOATSIZE : // (863) + return "DXFILEERR_BADFILEFLOATSIZE";//: // (863) + case DXFILEERR_BADFILECOMPRESSIONTYPE : // (864) + return "DXFILEERR_BADFILECOMPRESSIONTYPE";//: // (864) + case DXFILEERR_BADFILE : // (865) + return "DXFILEERR_BADFILE";//: // (865) + case DXFILEERR_PARSEERROR : // (866) + return "DXFILEERR_PARSEERROR";//: // (866) + case DXFILEERR_NOTEMPLATE : // (867) + return "DXFILEERR_NOTEMPLATE";//: // (867) + case DXFILEERR_BADARRAYSIZE : // (868) + return "DXFILEERR_BADARRAYSIZE";//: // (868) + case DXFILEERR_BADDATAREFERENCE : // (869) + return "DXFILEERR_BADDATAREFERENCE";//: // (869) + case DXFILEERR_INTERNALERROR : // (870) + return "DXFILEERR_INTERNALERROR";//: // (870) + case DXFILEERR_NOMOREOBJECTS : // (871) + return "DXFILEERR_NOMOREOBJECTS";//: // (871) + case DXFILEERR_BADINTRINSICS : // (872) + return "DXFILEERR_BADINTRINSICS";//: // (872) + case DXFILEERR_NOMORESTREAMHANDLES : // (873) + return "DXFILEERR_NOMORESTREAMHANDLES";//: // (873) + case DXFILEERR_NOMOREDATA : // (874) + return "DXFILEERR_NOMOREDATA";//: // (874) + case DXFILEERR_BADCACHEFILE : // (875) + return "DXFILEERR_BADCACHEFILE";//: // (875) + case DXFILEERR_NOINTERNET : // (876) + return "DXFILEERR_NOINTERNET";//: // (876) + + case E_UNEXPECTED : + return "E_UNEXPECTED "; + case E_NOTIMPL : + return "E_NOTIMPL "; + case E_OUTOFMEMORY : + return "E_OUTOFMEMORY "; + case E_INVALIDARG : + return "E_INVALIDARG or DDERR_INVALIDPARAMS"; + case E_NOINTERFACE : + return "E_NOINTERFACE "; + case E_POINTER : + return "E_POINTER "; + case E_HANDLE : + return "E_HANDLE "; + case E_ABORT : + return "E_ABORT "; +// case E_FAIL : +// return "E_FAIL "; + case E_ACCESSDENIED : + return "E_ACCESSDENIED "; + case E_PENDING : + return "E_PENDING "; + case CO_E_INIT_TLS : + return "CO_E_INIT_TLS "; + case CO_E_INIT_SHARED_ALLOCATOR : + return "CO_E_INIT_SHARED_ALLOCATOR "; + case CO_E_INIT_MEMORY_ALLOCATOR : + return "CO_E_INIT_MEMORY_ALLOCATOR "; + case CO_E_INIT_CLASS_CACHE : + return "CO_E_INIT_CLASS_CACHE "; + case CO_E_INIT_RPC_CHANNEL : + return "CO_E_INIT_RPC_CHANNEL "; + case CO_E_INIT_TLS_SET_CHANNEL_CONTROL : + return "CO_E_INIT_TLS_SET_CHANNEL_CONTROL "; + case CO_E_INIT_TLS_CHANNEL_CONTROL : + return "CO_E_INIT_TLS_CHANNEL_CONTROL "; + case CO_E_INIT_UNACCEPTED_USER_ALLOCATOR : + return "CO_E_INIT_UNACCEPTED_USER_ALLOCATOR "; + case CO_E_INIT_SCM_MUTEX_EXISTS : + return "CO_E_INIT_SCM_MUTEX_EXISTS "; + case CO_E_INIT_SCM_FILE_MAPPING_EXISTS : + return "CO_E_INIT_SCM_FILE_MAPPING_EXISTS "; + case CO_E_INIT_SCM_MAP_VIEW_OF_FILE : + return "CO_E_INIT_SCM_MAP_VIEW_OF_FILE "; + case CO_E_INIT_SCM_EXEC_FAILURE : + return "CO_E_INIT_SCM_EXEC_FAILURE "; + case CO_E_INIT_ONLY_SINGLE_THREADED : + return "CO_E_INIT_ONLY_SINGLE_THREADED "; + case CO_E_CANT_REMOTE : + return "CO_E_CANT_REMOTE "; + case CO_E_BAD_SERVER_NAME : + return "CO_E_BAD_SERVER_NAME "; + case CO_E_WRONG_SERVER_IDENTITY : + return "CO_E_WRONG_SERVER_IDENTITY "; + case CO_E_OLE1DDE_DISABLED : + return "CO_E_OLE1DDE_DISABLED "; + case CO_E_RUNAS_SYNTAX : + return "CO_E_RUNAS_SYNTAX "; + case CO_E_CREATEPROCESS_FAILURE : + return "CO_E_CREATEPROCESS_FAILURE "; + case CO_E_RUNAS_CREATEPROCESS_FAILURE : + return "CO_E_RUNAS_CREATEPROCESS_FAILURE "; + case CO_E_RUNAS_LOGON_FAILURE : + return "CO_E_RUNAS_LOGON_FAILURE "; + case CO_E_LAUNCH_PERMSSION_DENIED : + return "CO_E_LAUNCH_PERMSSION_DENIED "; + case CO_E_START_SERVICE_FAILURE : + return "CO_E_START_SERVICE_FAILURE "; + case CO_E_REMOTE_COMMUNICATION_FAILURE : + return "CO_E_REMOTE_COMMUNICATION_FAILURE "; + case CO_E_SERVER_START_TIMEOUT : + return "CO_E_SERVER_START_TIMEOUT "; + case CO_E_CLSREG_INCONSISTENT : + return "CO_E_CLSREG_INCONSISTENT "; + case CO_E_IIDREG_INCONSISTENT : + return "CO_E_IIDREG_INCONSISTENT "; + case CO_E_NOT_SUPPORTED : + return "CO_E_NOT_SUPPORTED "; + case CO_E_RELOAD_DLL : + return "CO_E_RELOAD_DLL "; + case CO_E_MSI_ERROR : + return "CO_E_MSI_ERROR "; + case OLE_E_OLEVERB : + return "OLE_E_OLEVERB "; + case OLE_E_ADVF : + return "OLE_E_ADVF "; + case OLE_E_ENUM_NOMORE : + return "OLE_E_ENUM_NOMORE "; + case OLE_E_ADVISENOTSUPPORTED : + return "OLE_E_ADVISENOTSUPPORTED "; + case OLE_E_NOCONNECTION : + return "OLE_E_NOCONNECTION "; + case OLE_E_NOTRUNNING : + return "OLE_E_NOTRUNNING "; + case OLE_E_NOCACHE : + return "OLE_E_NOCACHE "; + case OLE_E_BLANK : + return "OLE_E_BLANK "; + case OLE_E_CLASSDIFF : + return "OLE_E_CLASSDIFF "; + case OLE_E_CANT_GETMONIKER : + return "OLE_E_CANT_GETMONIKER "; + case OLE_E_CANT_BINDTOSOURCE : + return "OLE_E_CANT_BINDTOSOURCE "; + case OLE_E_STATIC : + return "OLE_E_STATIC "; + case OLE_E_PROMPTSAVECANCELLED : + return "OLE_E_PROMPTSAVECANCELLED "; + case OLE_E_INVALIDRECT : + return "OLE_E_INVALIDRECT "; + case OLE_E_WRONGCOMPOBJ : + return "OLE_E_WRONGCOMPOBJ "; + case OLE_E_INVALIDHWND : + return "OLE_E_INVALIDHWND "; + case OLE_E_NOT_INPLACEACTIVE : + return "OLE_E_NOT_INPLACEACTIVE "; + case OLE_E_CANTCONVERT : + return "OLE_E_CANTCONVERT "; + case OLE_E_NOSTORAGE : + return "OLE_E_NOSTORAGE "; + case DV_E_FORMATETC : + return "DV_E_FORMATETC "; + case DV_E_DVTARGETDEVICE : + return "DV_E_DVTARGETDEVICE "; + case DV_E_STGMEDIUM : + return "DV_E_STGMEDIUM "; + case DV_E_STATDATA : + return "DV_E_STATDATA "; + case DV_E_LINDEX : + return "DV_E_LINDEX "; + case DV_E_TYMED : + return "DV_E_TYMED "; + case DV_E_CLIPFORMAT : + return "DV_E_CLIPFORMAT "; + case DV_E_DVASPECT : + return "DV_E_DVASPECT "; + case DV_E_DVTARGETDEVICE_SIZE : + return "DV_E_DVTARGETDEVICE_SIZE "; + case DV_E_NOIVIEWOBJECT : + return "DV_E_NOIVIEWOBJECT "; + case DRAGDROP_E_NOTREGISTERED : + return "DRAGDROP_E_NOTREGISTERED "; + case DRAGDROP_E_ALREADYREGISTERED : + return "DRAGDROP_E_ALREADYREGISTERED "; + case DRAGDROP_E_INVALIDHWND : + return "DRAGDROP_E_INVALIDHWND "; + case CLASS_E_NOAGGREGATION : + return "CLASS_E_NOAGGREGATION "; + case CLASS_E_CLASSNOTAVAILABLE : + return "CLASS_E_CLASSNOTAVAILABLE "; + case CLASS_E_NOTLICENSED : + return "CLASS_E_NOTLICENSED "; + case VIEW_E_DRAW : + return "VIEW_E_DRAW "; + case REGDB_E_READREGDB : + return "REGDB_E_READREGDB "; + case REGDB_E_WRITEREGDB : + return "REGDB_E_WRITEREGDB "; + case REGDB_E_KEYMISSING : + return "REGDB_E_KEYMISSING "; + case REGDB_E_INVALIDVALUE : + return "REGDB_E_INVALIDVALUE "; + case REGDB_E_CLASSNOTREG : + return "REGDB_E_CLASSNOTREG "; + case REGDB_E_IIDNOTREG : + return "REGDB_E_IIDNOTREG "; + case CAT_E_CATIDNOEXIST : + return "CAT_E_CATIDNOEXIST "; + case CAT_E_NODESCRIPTION : + return "CAT_E_NODESCRIPTION "; + case CS_E_PACKAGE_NOTFOUND : + return "CS_E_PACKAGE_NOTFOUND "; + case CS_E_NOT_DELETABLE : + return "CS_E_NOT_DELETABLE "; + case CS_E_CLASS_NOTFOUND : + return "CS_E_CLASS_NOTFOUND "; + case CS_E_INVALID_VERSION : + return "CS_E_INVALID_VERSION "; + case CS_E_NO_CLASSSTORE : + return "CS_E_NO_CLASSSTORE "; + case CACHE_E_NOCACHE_UPDATED : + return "CACHE_E_NOCACHE_UPDATED "; + case OLEOBJ_E_NOVERBS : + return "OLEOBJ_E_NOVERBS "; + case OLEOBJ_E_INVALIDVERB : + return "OLEOBJ_E_INVALIDVERB "; + case INPLACE_E_NOTUNDOABLE : + return "INPLACE_E_NOTUNDOABLE "; + case INPLACE_E_NOTOOLSPACE : + return "INPLACE_E_NOTOOLSPACE "; + case CONVERT10_E_OLESTREAM_GET : + return "CONVERT10_E_OLESTREAM_GET "; + case CONVERT10_E_OLESTREAM_PUT : + return "CONVERT10_E_OLESTREAM_PUT "; + case CONVERT10_E_OLESTREAM_FMT : + return "CONVERT10_E_OLESTREAM_FMT "; + case CONVERT10_E_OLESTREAM_BITMAP_TO_DIB : + return "CONVERT10_E_OLESTREAM_BITMAP_TO_DIB "; + case CONVERT10_E_STG_FMT : + return "CONVERT10_E_STG_FMT "; + case CONVERT10_E_STG_NO_STD_STREAM : + return "CONVERT10_E_STG_NO_STD_STREAM "; + case CONVERT10_E_STG_DIB_TO_BITMAP : + return "CONVERT10_E_STG_DIB_TO_BITMAP "; + case CLIPBRD_E_CANT_OPEN : + return "CLIPBRD_E_CANT_OPEN "; + case CLIPBRD_E_CANT_EMPTY : + return "CLIPBRD_E_CANT_EMPTY "; + case CLIPBRD_E_CANT_SET : + return "CLIPBRD_E_CANT_SET "; + case CLIPBRD_E_BAD_DATA : + return "CLIPBRD_E_BAD_DATA "; + case CLIPBRD_E_CANT_CLOSE : + return "CLIPBRD_E_CANT_CLOSE "; + case MK_E_CONNECTMANUALLY : + return "MK_E_CONNECTMANUALLY "; + case MK_E_EXCEEDEDDEADLINE : + return "MK_E_EXCEEDEDDEADLINE "; + case MK_E_NEEDGENERIC : + return "MK_E_NEEDGENERIC "; + case MK_E_UNAVAILABLE : + return "MK_E_UNAVAILABLE "; + case MK_E_SYNTAX : + return "MK_E_SYNTAX "; + case MK_E_NOOBJECT : + return "MK_E_NOOBJECT "; + case MK_E_INVALIDEXTENSION : + return "MK_E_INVALIDEXTENSION "; + case MK_E_INTERMEDIATEINTERFACENOTSUPPORTED : + return "MK_E_INTERMEDIATEINTERFACENOTSUPPORTED "; + case MK_E_NOTBINDABLE : + return "MK_E_NOTBINDABLE "; + case MK_E_NOTBOUND : + return "MK_E_NOTBOUND "; + case MK_E_CANTOPENFILE : + return "MK_E_CANTOPENFILE "; + case MK_E_MUSTBOTHERUSER : + return "MK_E_MUSTBOTHERUSER "; + case MK_E_NOINVERSE : + return "MK_E_NOINVERSE "; + case MK_E_NOSTORAGE : + return "MK_E_NOSTORAGE "; + case MK_E_NOPREFIX : + return "MK_E_NOPREFIX "; + case MK_E_ENUMERATION_FAILED : + return "MK_E_ENUMERATION_FAILED "; + case CO_E_NOTINITIALIZED : + return "CO_E_NOTINITIALIZED "; + case CO_E_ALREADYINITIALIZED : + return "CO_E_ALREADYINITIALIZED "; + case CO_E_CANTDETERMINECLASS : + return "CO_E_CANTDETERMINECLASS "; + case CO_E_CLASSSTRING : + return "CO_E_CLASSSTRING "; + case CO_E_IIDSTRING : + return "CO_E_IIDSTRING "; + case CO_E_APPNOTFOUND : + return "CO_E_APPNOTFOUND "; + case CO_E_APPSINGLEUSE : + return "CO_E_APPSINGLEUSE "; + case CO_E_ERRORINAPP : + return "CO_E_ERRORINAPP "; + case CO_E_DLLNOTFOUND : + return "CO_E_DLLNOTFOUND "; + case CO_E_ERRORINDLL : + return "CO_E_ERRORINDLL "; + case CO_E_WRONGOSFORAPP : + return "CO_E_WRONGOSFORAPP "; + case CO_E_OBJNOTREG : + return "CO_E_OBJNOTREG "; + case CO_E_OBJISREG : + return "CO_E_OBJISREG "; + case CO_E_OBJNOTCONNECTED : + return "CO_E_OBJNOTCONNECTED "; + case CO_E_APPDIDNTREG : + return "CO_E_APPDIDNTREG "; + case CO_E_RELEASED : + return "CO_E_RELEASED "; + case CO_E_FAILEDTOIMPERSONATE : + return "CO_E_FAILEDTOIMPERSONATE "; + case CO_E_FAILEDTOGETSECCTX : + return "CO_E_FAILEDTOGETSECCTX "; + case CO_E_FAILEDTOOPENTHREADTOKEN : + return "CO_E_FAILEDTOOPENTHREADTOKEN "; + case CO_E_FAILEDTOGETTOKENINFO : + return "CO_E_FAILEDTOGETTOKENINFO "; + case CO_E_TRUSTEEDOESNTMATCHCLIENT : + return "CO_E_TRUSTEEDOESNTMATCHCLIENT "; + case CO_E_FAILEDTOQUERYCLIENTBLANKET : + return "CO_E_FAILEDTOQUERYCLIENTBLANKET "; + case CO_E_FAILEDTOSETDACL : + return "CO_E_FAILEDTOSETDACL "; + case CO_E_ACCESSCHECKFAILED : + return "CO_E_ACCESSCHECKFAILED "; + case CO_E_NETACCESSAPIFAILED : + return "CO_E_NETACCESSAPIFAILED "; + case CO_E_WRONGTRUSTEENAMESYNTAX : + return "CO_E_WRONGTRUSTEENAMESYNTAX "; + case CO_E_INVALIDSID : + return "CO_E_INVALIDSID "; + case CO_E_CONVERSIONFAILED : + return "CO_E_CONVERSIONFAILED "; + case CO_E_NOMATCHINGSIDFOUND : + return "CO_E_NOMATCHINGSIDFOUND "; + case CO_E_LOOKUPACCSIDFAILED : + return "CO_E_LOOKUPACCSIDFAILED "; + case CO_E_NOMATCHINGNAMEFOUND : + return "CO_E_NOMATCHINGNAMEFOUND "; + case CO_E_LOOKUPACCNAMEFAILED : + return "CO_E_LOOKUPACCNAMEFAILED "; + case CO_E_SETSERLHNDLFAILED : + return "CO_E_SETSERLHNDLFAILED "; + case CO_E_FAILEDTOGETWINDIR : + return "CO_E_FAILEDTOGETWINDIR "; + case CO_E_PATHTOOLONG : + return "CO_E_PATHTOOLONG "; + case CO_E_FAILEDTOGENUUID : + return "CO_E_FAILEDTOGENUUID "; + case CO_E_FAILEDTOCREATEFILE : + return "CO_E_FAILEDTOCREATEFILE "; + case CO_E_FAILEDTOCLOSEHANDLE : + return "CO_E_FAILEDTOCLOSEHANDLE "; + case CO_E_EXCEEDSYSACLLIMIT : + return "CO_E_EXCEEDSYSACLLIMIT "; + case CO_E_ACESINWRONGORDER : + return "CO_E_ACESINWRONGORDER "; + case CO_E_INCOMPATIBLESTREAMVERSION : + return "CO_E_INCOMPATIBLESTREAMVERSION "; + case CO_E_FAILEDTOOPENPROCESSTOKEN : + return "CO_E_FAILEDTOOPENPROCESSTOKEN "; + case CO_E_DECODEFAILED : + return "CO_E_DECODEFAILED "; + case CO_E_ACNOTINITIALIZED : + return "CO_E_ACNOTINITIALIZED "; + case OLE_S_USEREG : + return "OLE_S_USEREG "; + case OLE_S_STATIC : + return "OLE_S_STATIC "; + case OLE_S_MAC_CLIPFORMAT : + return "OLE_S_MAC_CLIPFORMAT "; + case DRAGDROP_S_DROP : + return "DRAGDROP_S_DROP "; + case DRAGDROP_S_CANCEL : + return "DRAGDROP_S_CANCEL "; + case DRAGDROP_S_USEDEFAULTCURSORS : + return "DRAGDROP_S_USEDEFAULTCURSORS "; + case DATA_S_SAMEFORMATETC : + return "DATA_S_SAMEFORMATETC "; + case VIEW_S_ALREADY_FROZEN : + return "VIEW_S_ALREADY_FROZEN "; + case CACHE_S_FORMATETC_NOTSUPPORTED : + return "CACHE_S_FORMATETC_NOTSUPPORTED "; + case CACHE_S_SAMECACHE : + return "CACHE_S_SAMECACHE "; + case CACHE_S_SOMECACHES_NOTUPDATED : + return "CACHE_S_SOMECACHES_NOTUPDATED "; + case OLEOBJ_S_INVALIDVERB : + return "OLEOBJ_S_INVALIDVERB "; + case OLEOBJ_S_CANNOT_DOVERB_NOW : + return "OLEOBJ_S_CANNOT_DOVERB_NOW "; + case OLEOBJ_S_INVALIDHWND : + return "OLEOBJ_S_INVALIDHWND "; + case INPLACE_S_TRUNCATED : + return "INPLACE_S_TRUNCATED "; + case CONVERT10_S_NO_PRESENTATION : + return "CONVERT10_S_NO_PRESENTATION "; + case MK_S_REDUCED_TO_SELF : + return "MK_S_REDUCED_TO_SELF "; + case MK_S_ME : + return "MK_S_ME "; + case MK_S_HIM : + return "MK_S_HIM "; + case MK_S_US : + return "MK_S_US "; + case MK_S_MONIKERALREADYREGISTERED : + return "MK_S_MONIKERALREADYREGISTERED "; + case CO_E_CLASS_CREATE_FAILED : + return "CO_E_CLASS_CREATE_FAILED "; + case CO_E_SCM_ERROR : + return "CO_E_SCM_ERROR "; + case CO_E_SCM_RPC_FAILURE : + return "CO_E_SCM_RPC_FAILURE "; + case CO_E_BAD_PATH : + return "CO_E_BAD_PATH "; + case CO_E_SERVER_EXEC_FAILURE : + return "CO_E_SERVER_EXEC_FAILURE "; + case CO_E_OBJSRV_RPC_FAILURE : + return "CO_E_OBJSRV_RPC_FAILURE "; + case MK_E_NO_NORMALIZED : + return "MK_E_NO_NORMALIZED "; + case CO_E_SERVER_STOPPING : + return "CO_E_SERVER_STOPPING "; + case MEM_E_INVALID_ROOT : + return "MEM_E_INVALID_ROOT "; + case MEM_E_INVALID_LINK : + return "MEM_E_INVALID_LINK "; + case MEM_E_INVALID_SIZE : + return "MEM_E_INVALID_SIZE "; + case CO_S_NOTALLINTERFACES : + return "CO_S_NOTALLINTERFACES "; + case DISP_E_UNKNOWNINTERFACE : + return "DISP_E_UNKNOWNINTERFACE "; + case DISP_E_MEMBERNOTFOUND : + return "DISP_E_MEMBERNOTFOUND "; + case DISP_E_PARAMNOTFOUND : + return "DISP_E_PARAMNOTFOUND "; + case DISP_E_TYPEMISMATCH : + return "DISP_E_TYPEMISMATCH "; + case DISP_E_UNKNOWNNAME : + return "DISP_E_UNKNOWNNAME "; + case DISP_E_NONAMEDARGS : + return "DISP_E_NONAMEDARGS "; + case DISP_E_BADVARTYPE : + return "DISP_E_BADVARTYPE "; + case DISP_E_EXCEPTION : + return "DISP_E_EXCEPTION "; + case DISP_E_OVERFLOW : + return "DISP_E_OVERFLOW "; + case DISP_E_BADINDEX : + return "DISP_E_BADINDEX "; + case DISP_E_UNKNOWNLCID : + return "DISP_E_UNKNOWNLCID "; + case DISP_E_ARRAYISLOCKED : + return "DISP_E_ARRAYISLOCKED "; + case DISP_E_BADPARAMCOUNT : + return "DISP_E_BADPARAMCOUNT "; + case DISP_E_PARAMNOTOPTIONAL : + return "DISP_E_PARAMNOTOPTIONAL "; + case DISP_E_BADCALLEE : + return "DISP_E_BADCALLEE "; + case DISP_E_NOTACOLLECTION : + return "DISP_E_NOTACOLLECTION "; + case DISP_E_DIVBYZERO : + return "DISP_E_DIVBYZERO "; + case TYPE_E_BUFFERTOOSMALL : + return "TYPE_E_BUFFERTOOSMALL "; + case TYPE_E_FIELDNOTFOUND : + return "TYPE_E_FIELDNOTFOUND "; + case TYPE_E_INVDATAREAD : + return "TYPE_E_INVDATAREAD "; + case TYPE_E_UNSUPFORMAT : + return "TYPE_E_UNSUPFORMAT "; + case TYPE_E_REGISTRYACCESS : + return "TYPE_E_REGISTRYACCESS "; + case TYPE_E_LIBNOTREGISTERED : + return "TYPE_E_LIBNOTREGISTERED "; + case TYPE_E_UNDEFINEDTYPE : + return "TYPE_E_UNDEFINEDTYPE "; + case TYPE_E_QUALIFIEDNAMEDISALLOWED : + return "TYPE_E_QUALIFIEDNAMEDISALLOWED "; + case TYPE_E_INVALIDSTATE : + return "TYPE_E_INVALIDSTATE "; + case TYPE_E_WRONGTYPEKIND : + return "TYPE_E_WRONGTYPEKIND "; + case TYPE_E_ELEMENTNOTFOUND : + return "TYPE_E_ELEMENTNOTFOUND "; + case TYPE_E_AMBIGUOUSNAME : + return "TYPE_E_AMBIGUOUSNAME "; + case TYPE_E_NAMECONFLICT : + return "TYPE_E_NAMECONFLICT "; + case TYPE_E_UNKNOWNLCID : + return "TYPE_E_UNKNOWNLCID "; + case TYPE_E_DLLFUNCTIONNOTFOUND : + return "TYPE_E_DLLFUNCTIONNOTFOUND "; + case TYPE_E_BADMODULEKIND : + return "TYPE_E_BADMODULEKIND "; + case TYPE_E_SIZETOOBIG : + return "TYPE_E_SIZETOOBIG "; + case TYPE_E_DUPLICATEID : + return "TYPE_E_DUPLICATEID "; + case TYPE_E_INVALIDID : + return "TYPE_E_INVALIDID "; + case TYPE_E_TYPEMISMATCH : + return "TYPE_E_TYPEMISMATCH "; + case TYPE_E_OUTOFBOUNDS : + return "TYPE_E_OUTOFBOUNDS "; + case TYPE_E_IOERROR : + return "TYPE_E_IOERROR "; + case TYPE_E_CANTCREATETMPFILE : + return "TYPE_E_CANTCREATETMPFILE "; + case TYPE_E_CANTLOADLIBRARY : + return "TYPE_E_CANTLOADLIBRARY "; + case TYPE_E_INCONSISTENTPROPFUNCS : + return "TYPE_E_INCONSISTENTPROPFUNCS "; + case TYPE_E_CIRCULARTYPE : + return "TYPE_E_CIRCULARTYPE "; + case STG_E_INVALIDFUNCTION : + return "STG_E_INVALIDFUNCTION "; + case STG_E_FILENOTFOUND : + return "STG_E_FILENOTFOUND "; + case STG_E_PATHNOTFOUND : + return "STG_E_PATHNOTFOUND "; + case STG_E_TOOMANYOPENFILES : + return "STG_E_TOOMANYOPENFILES "; + case STG_E_ACCESSDENIED : + return "STG_E_ACCESSDENIED "; + case STG_E_INVALIDHANDLE : + return "STG_E_INVALIDHANDLE "; + case STG_E_INSUFFICIENTMEMORY : + return "STG_E_INSUFFICIENTMEMORY "; + case STG_E_INVALIDPOINTER : + return "STG_E_INVALIDPOINTER "; + case STG_E_NOMOREFILES : + return "STG_E_NOMOREFILES "; + case STG_E_DISKISWRITEPROTECTED : + return "STG_E_DISKISWRITEPROTECTED "; + case STG_E_SEEKERROR : + return "STG_E_SEEKERROR "; + case STG_E_WRITEFAULT : + return "STG_E_WRITEFAULT "; + case STG_E_READFAULT : + return "STG_E_READFAULT "; + case STG_E_SHAREVIOLATION : + return "STG_E_SHAREVIOLATION "; + case STG_E_LOCKVIOLATION : + return "STG_E_LOCKVIOLATION "; + case STG_E_FILEALREADYEXISTS : + return "STG_E_FILEALREADYEXISTS "; + case STG_E_INVALIDPARAMETER : + return "STG_E_INVALIDPARAMETER "; + case STG_E_MEDIUMFULL : + return "STG_E_MEDIUMFULL "; + case STG_E_PROPSETMISMATCHED : + return "STG_E_PROPSETMISMATCHED "; + case STG_E_ABNORMALAPIEXIT : + return "STG_E_ABNORMALAPIEXIT "; + case STG_E_INVALIDHEADER : + return "STG_E_INVALIDHEADER "; + case STG_E_INVALIDNAME : + return "STG_E_INVALIDNAME "; + case STG_E_UNKNOWN : + return "STG_E_UNKNOWN "; + case STG_E_UNIMPLEMENTEDFUNCTION : + return "STG_E_UNIMPLEMENTEDFUNCTION "; + case STG_E_INVALIDFLAG : + return "STG_E_INVALIDFLAG "; + case STG_E_INUSE : + return "STG_E_INUSE "; + case STG_E_NOTCURRENT : + return "STG_E_NOTCURRENT "; + case STG_E_REVERTED : + return "STG_E_REVERTED "; + case STG_E_CANTSAVE : + return "STG_E_CANTSAVE "; + case STG_E_OLDFORMAT : + return "STG_E_OLDFORMAT "; + case STG_E_OLDDLL : + return "STG_E_OLDDLL "; + case STG_E_SHAREREQUIRED : + return "STG_E_SHAREREQUIRED "; + case STG_E_NOTFILEBASEDSTORAGE : + return "STG_E_NOTFILEBASEDSTORAGE "; + case STG_E_EXTANTMARSHALLINGS : + return "STG_E_EXTANTMARSHALLINGS "; + case STG_E_DOCFILECORRUPT : + return "STG_E_DOCFILECORRUPT "; + case STG_E_BADBASEADDRESS : + return "STG_E_BADBASEADDRESS "; + case STG_E_INCOMPLETE : + return "STG_E_INCOMPLETE "; + case STG_E_TERMINATED : + return "STG_E_TERMINATED "; + case STG_S_CONVERTED : + return "STG_S_CONVERTED "; + case STG_S_BLOCK : + return "STG_S_BLOCK "; + case STG_S_RETRYNOW : + return "STG_S_RETRYNOW "; + case STG_S_MONITORING : + return "STG_S_MONITORING "; + case STG_S_MULTIPLEOPENS : + return "STG_S_MULTIPLEOPENS "; + case STG_S_CONSOLIDATIONFAILED : + return "STG_S_CONSOLIDATIONFAILED "; + case STG_S_CANNOTCONSOLIDATE : + return "STG_S_CANNOTCONSOLIDATE "; + case RPC_E_CALL_REJECTED : + return "RPC_E_CALL_REJECTED "; + case RPC_E_CALL_CANCELED : + return "RPC_E_CALL_CANCELED "; + case RPC_E_CANTPOST_INSENDCALL : + return "RPC_E_CANTPOST_INSENDCALL "; + case RPC_E_CANTCALLOUT_INASYNCCALL : + return "RPC_E_CANTCALLOUT_INASYNCCALL "; + case RPC_E_CANTCALLOUT_INEXTERNALCALL : + return "RPC_E_CANTCALLOUT_INEXTERNALCALL "; + case RPC_E_CONNECTION_TERMINATED : + return "RPC_E_CONNECTION_TERMINATED "; + case RPC_E_SERVER_DIED : + return "RPC_E_SERVER_DIED "; + case RPC_E_CLIENT_DIED : + return "RPC_E_CLIENT_DIED "; + case RPC_E_INVALID_DATAPACKET : + return "RPC_E_INVALID_DATAPACKET "; + case RPC_E_CANTTRANSMIT_CALL : + return "RPC_E_CANTTRANSMIT_CALL "; + case RPC_E_CLIENT_CANTMARSHAL_DATA : + return "RPC_E_CLIENT_CANTMARSHAL_DATA "; + case RPC_E_CLIENT_CANTUNMARSHAL_DATA : + return "RPC_E_CLIENT_CANTUNMARSHAL_DATA "; + case RPC_E_SERVER_CANTMARSHAL_DATA : + return "RPC_E_SERVER_CANTMARSHAL_DATA "; + case RPC_E_SERVER_CANTUNMARSHAL_DATA : + return "RPC_E_SERVER_CANTUNMARSHAL_DATA "; + case RPC_E_INVALID_DATA : + return "RPC_E_INVALID_DATA "; + case RPC_E_INVALID_PARAMETER : + return "RPC_E_INVALID_PARAMETER "; + case RPC_E_CANTCALLOUT_AGAIN : + return "RPC_E_CANTCALLOUT_AGAIN "; + case RPC_E_SERVER_DIED_DNE : + return "RPC_E_SERVER_DIED_DNE "; + case RPC_E_SYS_CALL_FAILED : + return "RPC_E_SYS_CALL_FAILED "; + case RPC_E_OUT_OF_RESOURCES : + return "RPC_E_OUT_OF_RESOURCES "; + case RPC_E_ATTEMPTED_MULTITHREAD : + return "RPC_E_ATTEMPTED_MULTITHREAD "; + case RPC_E_NOT_REGISTERED : + return "RPC_E_NOT_REGISTERED "; + case RPC_E_FAULT : + return "RPC_E_FAULT "; + case RPC_E_SERVERFAULT : + return "RPC_E_SERVERFAULT "; + case RPC_E_CHANGED_MODE : + return "RPC_E_CHANGED_MODE "; + case RPC_E_INVALIDMETHOD : + return "RPC_E_INVALIDMETHOD "; + case RPC_E_DISCONNECTED : + return "RPC_E_DISCONNECTED "; + case RPC_E_RETRY : + return "RPC_E_RETRY "; + case RPC_E_SERVERCALL_RETRYLATER : + return "RPC_E_SERVERCALL_RETRYLATER "; + case RPC_E_SERVERCALL_REJECTED : + return "RPC_E_SERVERCALL_REJECTED "; + case RPC_E_INVALID_CALLDATA : + return "RPC_E_INVALID_CALLDATA "; + case RPC_E_CANTCALLOUT_ININPUTSYNCCALL : + return "RPC_E_CANTCALLOUT_ININPUTSYNCCALL "; + case RPC_E_WRONG_THREAD : + return "RPC_E_WRONG_THREAD "; + case RPC_E_THREAD_NOT_INIT : + return "RPC_E_THREAD_NOT_INIT "; + case RPC_E_VERSION_MISMATCH : + return "RPC_E_VERSION_MISMATCH "; + case RPC_E_INVALID_HEADER : + return "RPC_E_INVALID_HEADER "; + case RPC_E_INVALID_EXTENSION : + return "RPC_E_INVALID_EXTENSION "; + case RPC_E_INVALID_IPID : + return "RPC_E_INVALID_IPID "; + case RPC_E_INVALID_OBJECT : + return "RPC_E_INVALID_OBJECT "; + case RPC_S_CALLPENDING : + return "RPC_S_CALLPENDING "; + case RPC_S_WAITONTIMER : + return "RPC_S_WAITONTIMER "; + case RPC_E_CALL_COMPLETE : + return "RPC_E_CALL_COMPLETE "; + case RPC_E_UNSECURE_CALL : + return "RPC_E_UNSECURE_CALL "; + case RPC_E_TOO_LATE : + return "RPC_E_TOO_LATE "; + case RPC_E_NO_GOOD_SECURITY_PACKAGES : + return "RPC_E_NO_GOOD_SECURITY_PACKAGES "; + case RPC_E_ACCESS_DENIED : + return "RPC_E_ACCESS_DENIED "; + case RPC_E_REMOTE_DISABLED : + return "RPC_E_REMOTE_DISABLED "; + case RPC_E_INVALID_OBJREF : + return "RPC_E_INVALID_OBJREF "; + case RPC_E_NO_CONTEXT : + return "RPC_E_NO_CONTEXT "; + case RPC_E_TIMEOUT : + return "RPC_E_TIMEOUT "; + case RPC_E_NO_SYNC : + return "RPC_E_NO_SYNC "; + case RPC_E_UNEXPECTED : + return "RPC_E_UNEXPECTED "; + case NTE_BAD_UID : + return "NTE_BAD_UID "; + case NTE_BAD_HASH : + return "NTE_BAD_HASH "; + //case NTE_BAD_HASH : + //return "NTE_BAD_HASH "; + case NTE_BAD_KEY : + return "NTE_BAD_KEY "; + case NTE_BAD_LEN : + return "NTE_BAD_LEN "; + case NTE_BAD_DATA : + return "NTE_BAD_DATA "; + case NTE_BAD_SIGNATURE : + return "NTE_BAD_SIGNATURE "; + case NTE_BAD_VER : + return "NTE_BAD_VER "; + case NTE_BAD_ALGID : + return "NTE_BAD_ALGID "; + case NTE_BAD_FLAGS : + return "NTE_BAD_FLAGS "; + case NTE_BAD_TYPE : + return "NTE_BAD_TYPE "; + case NTE_BAD_KEY_STATE : + return "NTE_BAD_KEY_STATE "; + case NTE_BAD_HASH_STATE : + return "NTE_BAD_HASH_STATE "; + case NTE_NO_KEY : + return "NTE_NO_KEY "; + case NTE_NO_MEMORY : + return "NTE_NO_MEMORY "; + case NTE_EXISTS : + return "NTE_EXISTS "; + case NTE_PERM : + return "NTE_PERM "; + case NTE_NOT_FOUND : + return "NTE_NOT_FOUND "; + case NTE_DOUBLE_ENCRYPT : + return "NTE_DOUBLE_ENCRYPT "; + case NTE_BAD_PROVIDER : + return "NTE_BAD_PROVIDER "; + case NTE_BAD_PROV_TYPE : + return "NTE_BAD_PROV_TYPE "; + case NTE_BAD_PUBLIC_KEY : + return "NTE_BAD_PUBLIC_KEY "; + case NTE_BAD_KEYSET : + return "NTE_BAD_KEYSET "; + case NTE_PROV_TYPE_NOT_DEF : + return "NTE_PROV_TYPE_NOT_DEF "; + case NTE_PROV_TYPE_ENTRY_BAD : + return "NTE_PROV_TYPE_ENTRY_BAD "; + case NTE_KEYSET_NOT_DEF : + return "NTE_KEYSET_NOT_DEF "; + case NTE_KEYSET_ENTRY_BAD : + return "NTE_KEYSET_ENTRY_BAD "; + case NTE_PROV_TYPE_NO_MATCH : + return "NTE_PROV_TYPE_NO_MATCH "; + case NTE_SIGNATURE_FILE_BAD : + return "NTE_SIGNATURE_FILE_BAD "; + case NTE_PROVIDER_DLL_FAIL : + return "NTE_PROVIDER_DLL_FAIL "; + case NTE_PROV_DLL_NOT_FOUND : + return "NTE_PROV_DLL_NOT_FOUND "; + case NTE_BAD_KEYSET_PARAM : + return "NTE_BAD_KEYSET_PARAM "; + case NTE_FAIL : + return "NTE_FAIL "; + case NTE_SYS_ERR : + return "NTE_SYS_ERR "; + case CRYPT_E_MSG_ERROR : + return "CRYPT_E_MSG_ERROR "; + case CRYPT_E_UNKNOWN_ALGO : + return "CRYPT_E_UNKNOWN_ALGO "; + case CRYPT_E_OID_FORMAT : + return "CRYPT_E_OID_FORMAT "; + case CRYPT_E_INVALID_MSG_TYPE : + return "CRYPT_E_INVALID_MSG_TYPE "; + case CRYPT_E_UNEXPECTED_ENCODING : + return "CRYPT_E_UNEXPECTED_ENCODING "; + case CRYPT_E_AUTH_ATTR_MISSING : + return "CRYPT_E_AUTH_ATTR_MISSING "; + case CRYPT_E_HASH_VALUE : + return "CRYPT_E_HASH_VALUE "; + case CRYPT_E_INVALID_INDEX : + return "CRYPT_E_INVALID_INDEX "; + case CRYPT_E_ALREADY_DECRYPTED : + return "CRYPT_E_ALREADY_DECRYPTED "; + case CRYPT_E_NOT_DECRYPTED : + return "CRYPT_E_NOT_DECRYPTED "; + case CRYPT_E_RECIPIENT_NOT_FOUND : + return "CRYPT_E_RECIPIENT_NOT_FOUND "; + case CRYPT_E_CONTROL_TYPE : + return "CRYPT_E_CONTROL_TYPE "; + case CRYPT_E_ISSUER_SERIALNUMBER : + return "CRYPT_E_ISSUER_SERIALNUMBER "; + case CRYPT_E_SIGNER_NOT_FOUND : + return "CRYPT_E_SIGNER_NOT_FOUND "; + case CRYPT_E_ATTRIBUTES_MISSING : + return "CRYPT_E_ATTRIBUTES_MISSING "; + case CRYPT_E_STREAM_MSG_NOT_READY : + return "CRYPT_E_STREAM_MSG_NOT_READY "; + case CRYPT_E_STREAM_INSUFFICIENT_DATA : + return "CRYPT_E_STREAM_INSUFFICIENT_DATA "; + case CRYPT_E_BAD_LEN : + return "CRYPT_E_BAD_LEN "; + case CRYPT_E_BAD_ENCODE : + return "CRYPT_E_BAD_ENCODE "; + case CRYPT_E_FILE_ERROR : + return "CRYPT_E_FILE_ERROR "; + case CRYPT_E_NOT_FOUND : + return "CRYPT_E_NOT_FOUND "; + case CRYPT_E_EXISTS : + return "CRYPT_E_EXISTS "; + case CRYPT_E_NO_PROVIDER : + return "CRYPT_E_NO_PROVIDER "; + case CRYPT_E_SELF_SIGNED : + return "CRYPT_E_SELF_SIGNED "; + case CRYPT_E_DELETED_PREV : + return "CRYPT_E_DELETED_PREV "; + case CRYPT_E_NO_MATCH : + return "CRYPT_E_NO_MATCH "; + case CRYPT_E_UNEXPECTED_MSG_TYPE : + return "CRYPT_E_UNEXPECTED_MSG_TYPE "; + case CRYPT_E_NO_KEY_PROPERTY : + return "CRYPT_E_NO_KEY_PROPERTY "; + case CRYPT_E_NO_DECRYPT_CERT : + return "CRYPT_E_NO_DECRYPT_CERT "; + case CRYPT_E_BAD_MSG : + return "CRYPT_E_BAD_MSG "; + case CRYPT_E_NO_SIGNER : + return "CRYPT_E_NO_SIGNER "; + case CRYPT_E_PENDING_CLOSE : + return "CRYPT_E_PENDING_CLOSE "; + case CRYPT_E_REVOKED : + return "CRYPT_E_REVOKED "; + case CRYPT_E_NO_REVOCATION_DLL : + return "CRYPT_E_NO_REVOCATION_DLL "; + case CRYPT_E_NO_REVOCATION_CHECK : + return "CRYPT_E_NO_REVOCATION_CHECK "; + case CRYPT_E_REVOCATION_OFFLINE : + return "CRYPT_E_REVOCATION_OFFLINE "; + case CRYPT_E_NOT_IN_REVOCATION_DATABASE : + return "CRYPT_E_NOT_IN_REVOCATION_DATABASE "; + case CRYPT_E_INVALID_NUMERIC_STRING : + return "CRYPT_E_INVALID_NUMERIC_STRING "; + case CRYPT_E_INVALID_PRINTABLE_STRING : + return "CRYPT_E_INVALID_PRINTABLE_STRING "; + case CRYPT_E_INVALID_IA5_STRING : + return "CRYPT_E_INVALID_IA5_STRING "; + case CRYPT_E_INVALID_X500_STRING : + return "CRYPT_E_INVALID_X500_STRING "; + case CRYPT_E_NOT_CHAR_STRING : + return "CRYPT_E_NOT_CHAR_STRING "; + case CRYPT_E_FILERESIZED : + return "CRYPT_E_FILERESIZED "; + case CRYPT_E_SECURITY_SETTINGS : + return "CRYPT_E_SECURITY_SETTINGS "; + case CRYPT_E_NO_VERIFY_USAGE_DLL : + return "CRYPT_E_NO_VERIFY_USAGE_DLL "; + case CRYPT_E_NO_VERIFY_USAGE_CHECK : + return "CRYPT_E_NO_VERIFY_USAGE_CHECK "; + case CRYPT_E_VERIFY_USAGE_OFFLINE : + return "CRYPT_E_VERIFY_USAGE_OFFLINE "; + case CRYPT_E_NOT_IN_CTL : + return "CRYPT_E_NOT_IN_CTL "; + case CRYPT_E_NO_TRUSTED_SIGNER : + return "CRYPT_E_NO_TRUSTED_SIGNER "; + case CRYPT_E_OSS_ERROR : + return "CRYPT_E_OSS_ERROR "; + case CERTSRV_E_BAD_REQUESTSUBJECT : + return "CERTSRV_E_BAD_REQUESTSUBJECT "; + case CERTSRV_E_NO_REQUEST : + return "CERTSRV_E_NO_REQUEST "; + case CERTSRV_E_BAD_REQUESTSTATUS : + return "CERTSRV_E_BAD_REQUESTSTATUS "; + case CERTSRV_E_PROPERTY_EMPTY : + return "CERTSRV_E_PROPERTY_EMPTY "; + //case CERTDB_E_JET_ERROR : + //return "CERTDB_E_JET_ERROR "; + case TRUST_E_SYSTEM_ERROR : + return "TRUST_E_SYSTEM_ERROR "; + case TRUST_E_NO_SIGNER_CERT : + return "TRUST_E_NO_SIGNER_CERT "; + case TRUST_E_COUNTER_SIGNER : + return "TRUST_E_COUNTER_SIGNER "; + case TRUST_E_CERT_SIGNATURE : + return "TRUST_E_CERT_SIGNATURE "; + case TRUST_E_TIME_STAMP : + return "TRUST_E_TIME_STAMP "; + case TRUST_E_BAD_DIGEST : + return "TRUST_E_BAD_DIGEST "; + case TRUST_E_BASIC_CONSTRAINTS : + return "TRUST_E_BASIC_CONSTRAINTS "; + case TRUST_E_FINANCIAL_CRITERIA : + return "TRUST_E_FINANCIAL_CRITERIA "; + case TRUST_E_PROVIDER_UNKNOWN : + return "TRUST_E_PROVIDER_UNKNOWN "; + case TRUST_E_ACTION_UNKNOWN : + return "TRUST_E_ACTION_UNKNOWN "; + case TRUST_E_SUBJECT_FORM_UNKNOWN : + return "TRUST_E_SUBJECT_FORM_UNKNOWN "; + case TRUST_E_SUBJECT_NOT_TRUSTED : + return "TRUST_E_SUBJECT_NOT_TRUSTED "; + case DIGSIG_E_ENCODE : + return "DIGSIG_E_ENCODE "; + case DIGSIG_E_DECODE : + return "DIGSIG_E_DECODE "; + case DIGSIG_E_EXTENSIBILITY : + return "DIGSIG_E_EXTENSIBILITY "; + case DIGSIG_E_CRYPTO : + return "DIGSIG_E_CRYPTO "; + case PERSIST_E_SIZEDEFINITE : + return "PERSIST_E_SIZEDEFINITE "; + case PERSIST_E_SIZEINDEFINITE : + return "PERSIST_E_SIZEINDEFINITE "; + case PERSIST_E_NOTSELFSIZING : + return "PERSIST_E_NOTSELFSIZING "; + case TRUST_E_NOSIGNATURE : + return "TRUST_E_NOSIGNATURE "; + case CERT_E_EXPIRED : + return "CERT_E_EXPIRED "; + case CERT_E_VALIDITYPERIODNESTING : + return "CERT_E_VALIDITYPERIODNESTING "; + case CERT_E_ROLE : + return "CERT_E_ROLE "; + case CERT_E_PATHLENCONST : + return "CERT_E_PATHLENCONST "; + case CERT_E_CRITICAL : + return "CERT_E_CRITICAL "; + case CERT_E_PURPOSE : + return "CERT_E_PURPOSE "; + case CERT_E_ISSUERCHAINING : + return "CERT_E_ISSUERCHAINING "; + case CERT_E_MALFORMED : + return "CERT_E_MALFORMED "; + case CERT_E_UNTRUSTEDROOT : + return "CERT_E_UNTRUSTEDROOT "; + case CERT_E_CHAINING : + return "CERT_E_CHAINING "; + case TRUST_E_FAIL : + return "TRUST_E_FAIL "; + case CERT_E_REVOKED : + return "CERT_E_REVOKED "; + case CERT_E_UNTRUSTEDTESTROOT : + return "CERT_E_UNTRUSTEDTESTROOT "; + case CERT_E_REVOCATION_FAILURE : + return "CERT_E_REVOCATION_FAILURE "; + case CERT_E_CN_NO_MATCH : + return "CERT_E_CN_NO_MATCH "; + case CERT_E_WRONG_USAGE : + return "CERT_E_WRONG_USAGE "; + case SPAPI_E_EXPECTED_SECTION_NAME : + return "SPAPI_E_EXPECTED_SECTION_NAME "; + case SPAPI_E_BAD_SECTION_NAME_LINE : + return "SPAPI_E_BAD_SECTION_NAME_LINE "; + case SPAPI_E_SECTION_NAME_TOO_LONG : + return "SPAPI_E_SECTION_NAME_TOO_LONG "; + case SPAPI_E_GENERAL_SYNTAX : + return "SPAPI_E_GENERAL_SYNTAX "; + case SPAPI_E_WRONG_INF_STYLE : + return "SPAPI_E_WRONG_INF_STYLE "; + case SPAPI_E_SECTION_NOT_FOUND : + return "SPAPI_E_SECTION_NOT_FOUND "; + case SPAPI_E_LINE_NOT_FOUND : + return "SPAPI_E_LINE_NOT_FOUND "; + case SPAPI_E_NO_ASSOCIATED_CLASS : + return "SPAPI_E_NO_ASSOCIATED_CLASS "; + case SPAPI_E_CLASS_MISMATCH : + return "SPAPI_E_CLASS_MISMATCH "; + case SPAPI_E_DUPLICATE_FOUND : + return "SPAPI_E_DUPLICATE_FOUND "; + case SPAPI_E_NO_DRIVER_SELECTED : + return "SPAPI_E_NO_DRIVER_SELECTED "; + case SPAPI_E_KEY_DOES_NOT_EXIST : + return "SPAPI_E_KEY_DOES_NOT_EXIST "; + case SPAPI_E_INVALID_DEVINST_NAME : + return "SPAPI_E_INVALID_DEVINST_NAME "; + case SPAPI_E_INVALID_CLASS : + return "SPAPI_E_INVALID_CLASS "; + case SPAPI_E_DEVINST_ALREADY_EXISTS : + return "SPAPI_E_DEVINST_ALREADY_EXISTS "; + case SPAPI_E_DEVINFO_NOT_REGISTERED : + return "SPAPI_E_DEVINFO_NOT_REGISTERED "; + case SPAPI_E_INVALID_REG_PROPERTY : + return "SPAPI_E_INVALID_REG_PROPERTY "; + case SPAPI_E_NO_INF : + return "SPAPI_E_NO_INF "; + case SPAPI_E_NO_SUCH_DEVINST : + return "SPAPI_E_NO_SUCH_DEVINST "; + case SPAPI_E_CANT_LOAD_CLASS_ICON : + return "SPAPI_E_CANT_LOAD_CLASS_ICON "; + case SPAPI_E_INVALID_CLASS_INSTALLER : + return "SPAPI_E_INVALID_CLASS_INSTALLER "; + case SPAPI_E_DI_DO_DEFAULT : + return "SPAPI_E_DI_DO_DEFAULT "; + case SPAPI_E_DI_NOFILECOPY : + return "SPAPI_E_DI_NOFILECOPY "; + case SPAPI_E_INVALID_HWPROFILE : + return "SPAPI_E_INVALID_HWPROFILE "; + case SPAPI_E_NO_DEVICE_SELECTED : + return "SPAPI_E_NO_DEVICE_SELECTED "; + case SPAPI_E_DEVINFO_LIST_LOCKED : + return "SPAPI_E_DEVINFO_LIST_LOCKED "; + case SPAPI_E_DEVINFO_DATA_LOCKED : + return "SPAPI_E_DEVINFO_DATA_LOCKED "; + case SPAPI_E_DI_BAD_PATH : + return "SPAPI_E_DI_BAD_PATH "; + case SPAPI_E_NO_CLASSINSTALL_PARAMS : + return "SPAPI_E_NO_CLASSINSTALL_PARAMS "; + case SPAPI_E_FILEQUEUE_LOCKED : + return "SPAPI_E_FILEQUEUE_LOCKED "; + case SPAPI_E_BAD_SERVICE_INSTALLSECT : + return "SPAPI_E_BAD_SERVICE_INSTALLSECT "; + case SPAPI_E_NO_CLASS_DRIVER_LIST : + return "SPAPI_E_NO_CLASS_DRIVER_LIST "; + case SPAPI_E_NO_ASSOCIATED_SERVICE : + return "SPAPI_E_NO_ASSOCIATED_SERVICE "; + case SPAPI_E_NO_DEFAULT_DEVICE_INTERFACE : + return "SPAPI_E_NO_DEFAULT_DEVICE_INTERFACE "; + case SPAPI_E_DEVICE_INTERFACE_ACTIVE : + return "SPAPI_E_DEVICE_INTERFACE_ACTIVE "; + case SPAPI_E_DEVICE_INTERFACE_REMOVED : + return "SPAPI_E_DEVICE_INTERFACE_REMOVED "; + case SPAPI_E_BAD_INTERFACE_INSTALLSECT : + return "SPAPI_E_BAD_INTERFACE_INSTALLSECT "; + case SPAPI_E_NO_SUCH_INTERFACE_CLASS : + return "SPAPI_E_NO_SUCH_INTERFACE_CLASS "; + case SPAPI_E_INVALID_REFERENCE_STRING : + return "SPAPI_E_INVALID_REFERENCE_STRING "; + case SPAPI_E_INVALID_MACHINENAME : + return "SPAPI_E_INVALID_MACHINENAME "; + case SPAPI_E_REMOTE_COMM_FAILURE : + return "SPAPI_E_REMOTE_COMM_FAILURE "; + case SPAPI_E_MACHINE_UNAVAILABLE : + return "SPAPI_E_MACHINE_UNAVAILABLE "; + case SPAPI_E_NO_CONFIGMGR_SERVICES : + return "SPAPI_E_NO_CONFIGMGR_SERVICES "; + case SPAPI_E_INVALID_PROPPAGE_PROVIDER : + return "SPAPI_E_INVALID_PROPPAGE_PROVIDER "; + case SPAPI_E_NO_SUCH_DEVICE_INTERFACE : + return "SPAPI_E_NO_SUCH_DEVICE_INTERFACE "; + case SPAPI_E_DI_POSTPROCESSING_REQUIRED : + return "SPAPI_E_DI_POSTPROCESSING_REQUIRED "; + case SPAPI_E_INVALID_COINSTALLER : + return "SPAPI_E_INVALID_COINSTALLER "; + case SPAPI_E_NO_COMPAT_DRIVERS : + return "SPAPI_E_NO_COMPAT_DRIVERS "; + case SPAPI_E_NO_DEVICE_ICON : + return "SPAPI_E_NO_DEVICE_ICON "; + case SPAPI_E_INVALID_INF_LOGCONFIG : + return "SPAPI_E_INVALID_INF_LOGCONFIG "; + case SPAPI_E_DI_DONT_INSTALL : + return "SPAPI_E_DI_DONT_INSTALL "; + case SPAPI_E_INVALID_FILTER_DRIVER : + return "SPAPI_E_INVALID_FILTER_DRIVER "; + case SPAPI_E_ERROR_NOT_INSTALLED : + return "SPAPI_E_ERROR_NOT_INSTALLED "; + + + + default: + static char buff[1000]; + sprintf(buff, "Unrecognized error value: %08X\0", error); + + return buff; + } +} + diff --git a/panda/src/wdxdisplay/wdxGraphicsPipe.h b/panda/src/wdxdisplay/wdxGraphicsPipe.h new file mode 100644 index 0000000000..cf3562f5f7 --- /dev/null +++ b/panda/src/wdxdisplay/wdxGraphicsPipe.h @@ -0,0 +1,71 @@ +// Filename: wdxGraphicsPipe.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef WDXGRAPHICSPIPE_H +#define WDXGRAPHICSPIPE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include "wdxGraphicsWindow.h" +#define WINDOWS_LEAN_AND_MEAN +#include +#undef WINDOWS_LEAN_AND_MEAN + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +class Xclass; + +extern char * ConvD3DErrorToString(const HRESULT &error); + +//////////////////////////////////////////////////////////////////// +// Class : wdxGraphicsPipe +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDADX wdxGraphicsPipe : public InteractiveGraphicsPipe { +public: + wdxGraphicsPipe(const PipeSpecifier&); + + wdxGraphicsWindow* find_window(HWND win); +// ButtonHandle lookup_key(WPARAM wparam) const; + + virtual TypeHandle get_window_type() const; + +public: + + static GraphicsPipe* make_wdxGraphicsPipe(const FactoryParams ¶ms); + + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + + static TypeHandle _type_handle; + + int _width; + int _height; + bool _shift; + +protected: + + wdxGraphicsPipe(void); + wdxGraphicsPipe(const wdxGraphicsPipe&); + wdxGraphicsPipe& operator=(const wdxGraphicsPipe&); + + /* + static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, + LPARAM lparam); + LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + */ +}; + +#endif diff --git a/panda/src/wdxdisplay/wdxGraphicsWindow.cxx b/panda/src/wdxdisplay/wdxGraphicsWindow.cxx new file mode 100644 index 0000000000..0f2af4d7af --- /dev/null +++ b/panda/src/wdxdisplay/wdxGraphicsWindow.cxx @@ -0,0 +1,2016 @@ +// Filename: wdxGraphicsWindow.cxx +// Created by: mike (09Jan00) +// +//////////////////////////////////////////////////////////////////// +// Copyright (C) 1999-2000 +// Walt Disney Imagineering, Inc. +// +// These coded instructions, statements, data structures and +// computer programs contain unpublished proprietary information of +// Walt Disney Imagineering and are protected by Federal copyright +// law. They may not be disclosed to third parties or copied or +// duplicated in any form, in whole or in part, without the prior +// written consent of Walt Disney Imagineering Inc. +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#ifndef STRICT +#define STRICT +#endif +#define D3D_OVERLOADS +#define INITGUID +#include +#include "wdxGraphicsWindow.h" +#include "wdxGraphicsPipe.h" +#include "config_wdxdisplay.h" + +#include +#include +#include +#include "dxGraphicsStateGuardian.h" + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle wdxGraphicsWindow::_type_handle; + +// kluge +wdxGraphicsWindow* global_dxwin = NULL; + +#define MOUSE_ENTERED 0 +#define MOUSE_EXITED 1 + +static DWORD BitDepth_2_DDBDMask(DWORD iBitDepth) { + switch(iBitDepth) { + case 16: return DDBD_16; + case 32: return DDBD_32; + case 24: return DDBD_24; + case 8: return DDBD_8; + case 1: return DDBD_1; + case 2: return DDBD_2; + case 4: return DDBD_4; + } + return 0x0; +} + +#ifdef _DEBUG +static void DebugPrintPixFmt(DDPIXELFORMAT* pddpf) { + static int iddpfnum=0; + ostream *dbgout = &dxgsg_cat.debug(); + + *dbgout << "ZBuf DDPF[" << iddpfnum << "]: RGBBitCount:" << pddpf->dwRGBBitCount + << " Flags:" << (void *)pddpf->dwFlags ; + + if(pddpf->dwFlags & DDPF_STENCILBUFFER) { + *dbgout << " StencilBits:" << (void *) pddpf->dwStencilBitDepth; + } + + *dbgout << endl; + + iddpfnum++; +} +#endif + +#define MAX_DX_ZBUF_FMTS 20 + +static int cNumZBufFmts=0; + +HRESULT CALLBACK EnumZBufFmtsCallback( LPDDPIXELFORMAT pddpf, VOID* param ) +{ + DDPIXELFORMAT *ZBufFmtsArr = (DDPIXELFORMAT *) param; + assert(cNumZBufFmts < MAX_DX_ZBUF_FMTS); + memcpy( &(ZBufFmtsArr[cNumZBufFmts]), pddpf, sizeof(DDPIXELFORMAT) ); + cNumZBufFmts++; + return DDENUMRET_OK; +} + +//////////////////////////////////////////////////////////////////// +// Function: lookup_key +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +ButtonHandle wdxGraphicsWindow:: +lookup_key(WPARAM wparam) const { + switch (wparam) { + case VK_BACK: return KeyboardButton::backspace(); + case VK_TAB: return KeyboardButton::tab(); + case VK_ESCAPE: return KeyboardButton::escape(); + case VK_SPACE: return KeyboardButton::space(); + case VK_UP: return KeyboardButton::up(); + case VK_DOWN: return KeyboardButton::down(); + case VK_LEFT: return KeyboardButton::left(); + case VK_RIGHT: return KeyboardButton::right(); + case VK_PRIOR: return KeyboardButton::page_up(); + case VK_NEXT: return KeyboardButton::page_down(); + case VK_HOME: return KeyboardButton::home(); + case VK_END: return KeyboardButton::end(); + case VK_F1: return KeyboardButton::f1(); + case VK_F2: return KeyboardButton::f2(); + case VK_F3: return KeyboardButton::f3(); + case VK_F4: return KeyboardButton::f4(); + case VK_F5: return KeyboardButton::f5(); + case VK_F6: return KeyboardButton::f6(); + case VK_F7: return KeyboardButton::f7(); + case VK_F8: return KeyboardButton::f8(); + case VK_F9: return KeyboardButton::f9(); + case VK_F10: return KeyboardButton::f10(); + case VK_F11: return KeyboardButton::f11(); + case VK_F12: return KeyboardButton::f12(); + case VK_INSERT: return KeyboardButton::insert(); + case VK_DELETE: return KeyboardButton::del(); + default: + int key = MapVirtualKey(wparam, 2); + if (isascii(key) && key != 0) { + if (GetKeyState(VK_SHIFT) >= 0) + key = tolower(key); + return KeyboardButton::ascii_key((uchar)key); + } + break; + } + return ButtonHandle::none(); +} + +DXGraphicsStateGuardian *wdxGraphicsWindow::get_dxgsg(void) { + return (_gsg==NULL) ? NULL : DCAST(DXGraphicsStateGuardian, _gsg); +} + +void AtExitFn() { +#ifdef _DEBUG + wdxdisplay_cat.debug() << "AtExitFn called\n"; + OutputDebugString("AtExitFn called\n"); +#endif + if(global_dxwin==NULL) + return; + if(global_dxwin->_hParentWindow!=NULL) { + SetForegroundWindow(global_dxwin->_hParentWindow); + } + DXGraphicsStateGuardian* dxgsg = global_dxwin->get_dxgsg(); + if(dxgsg!=NULL) + dxgsg->dx_cleanup(); + +// global_dxwin->_dx_ready = false; +// delete dxgsg; does panda do this itself? +} + +//////////////////////////////////////////////////////////////////// +// Function: static_window_proc +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +LONG WINAPI wdxGraphicsWindow::static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + return global_dxwin->window_proc(hwnd, msg, wparam, lparam); +} + +#ifdef _DEBUG +extern void dbgPrintVidMem(LPDIRECTDRAW7 pDD, LPDDSCAPS2 lpddsCaps,const char *pMsg) { + DWORD dwTotal,dwFree; + char tmpstr[100],tmpstr2[100]; + HRESULT hr; + +/* + * These Caps bits arent allowed to be specified when calling GetAvailVidMem. + * They don't affect surface allocation in a vram heap. + */ + +#define AVAILVIDMEM_BADCAPS (DDSCAPS_BACKBUFFER | \ + DDSCAPS_FRONTBUFFER | \ + DDSCAPS_COMPLEX | \ + DDSCAPS_FLIP | \ + DDSCAPS_OWNDC | \ + DDSCAPS_PALETTE | \ + DDSCAPS_SYSTEMMEMORY | \ + DDSCAPS_VISIBLE | \ + DDSCAPS_WRITEONLY) + + DDSCAPS2 ddsCaps = *lpddsCaps; + ddsCaps.dwCaps &= ~(AVAILVIDMEM_BADCAPS); // turn off the bad caps +// ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; done internally by DX anyway + + if( FAILED( hr = pDD->GetAvailableVidMem(&ddsCaps,&dwTotal,&dwFree))) { + wdxdisplay_cat.debug() << "wdxGraphicsWindow::GetAvailableVidMem failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + sprintf(tmpstr,"%.4g",dwTotal/1000000.0); + sprintf(tmpstr2,"%.4g",dwFree/1000000.0); + wdxdisplay_cat.debug() << "AvailableVidMem before creating "<< pMsg << ",(megs) total: " << tmpstr << " free:" << tmpstr2 <SetDXStatus(false); // disable rendering whilst we much with surfs + + // Want to change rendertarget size without destroying d3d device. To save vid memory + // (and make resizing work on memory-starved 4MB cards), we need to construct + // a temporary mini-sized render target for the d3d device (it cannot point to a + // NULL rendertarget) before creating the fully resized buffers. The old + // rendertargets will be freed when these temp targets are set, and that will give + // us the memory to create the resized target + + LPDIRECTDRAWSURFACE7 pddsDummy = NULL,pddsDummyZ = NULL; + HRESULT hr; + + DX_DECLARE_CLEAN( DDSURFACEDESC2, ddsd ); + + if(dxgsg->GetBackBuffer()==NULL) // bugbug why is this ever true?? + return DefWindowProc(hwnd, msg, wparam, lparam); + + dxgsg->GetBackBuffer()->GetSurfaceDesc(&ddsd); + LPDIRECTDRAW7 pDD = dxgsg->GetDDInterface(); + + ddsd.dwFlags &= ~DDSD_PITCH; + ddsd.dwWidth = 1; ddsd.dwHeight = 1; + ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_COMPLEX | DDSCAPS_FLIP | DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER); + + PRINTVIDMEM(pDD,&ddsd.ddsCaps,"dummy backbuf"); + + if( FAILED( hr = pDD->CreateSurface( &ddsd, &pddsDummy, NULL ) ) ) + { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::Resize CreateSurface for temp backbuf failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + DX_DECLARE_CLEAN( DDSURFACEDESC2, ddsdZ ); + dxgsg->GetZBuffer()->GetSurfaceDesc(&ddsdZ); + ddsdZ.dwFlags &= ~DDSD_PITCH; + ddsdZ.dwWidth = 1; ddsdZ.dwHeight = 1; + + PRINTVIDMEM(pDD,&ddsdZ.ddsCaps,"dummy zbuf"); + + if( FAILED( hr = pDD->CreateSurface( &ddsdZ, &pddsDummyZ, NULL ) ) ) { + wdxdisplay_cat.fatal() << "wdxGraphicsWindow::Resize CreateSurface for temp zbuf failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + if( FAILED( hr = pddsDummy->AddAttachedSurface( pddsDummyZ ) ) ) { + wdxdisplay_cat.fatal() << "wdxGraphicsWindow::Resize AddAttachedSurf for temp zbuf failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + if( FAILED( hr = dxgsg->GetD3DDevice()->SetRenderTarget( pddsDummy, 0x0 )) ) + { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::Resize failed to set render target to temporary surface, result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + RELEASE(pddsDummyZ); + RELEASE(pddsDummy); + + RECT view_rect; + GetClientRect( _mwindow, &view_rect ); + ClientToScreen( _mwindow, (POINT*)&view_rect.left ); + ClientToScreen( _mwindow, (POINT*)&view_rect.right ); + + dxgsg->dx_setup_after_resize(view_rect,_mwindow); // create the new resized rendertargets + + _dx_ready = true; + dxgsg->SetDXStatus(true); + } + bWindowAdjustingType = NotAdjusting; + return 0; + + case WM_ENTERSIZEMOVE: { + DXGraphicsStateGuardian* dxgsg = DCAST(DXGraphicsStateGuardian, _gsg); + _dx_ready = true; + dxgsg->SetDXStatus(true); // dont disable here because I want to see pic as I resize + bWindowAdjustingType = MovingOrResizing; + } + return DefWindowProc(hwnd, msg, wparam, lparam); + + case WM_DISPLAYCHANGE: + if(!dx_full_screen) { + wdxdisplay_cat.fatal() << "WM_DISPLAYCHANGE received: fatal error, desktop bitdepth change not handled\n"; + exit(1); + } + + case WM_SIZE: { + if(_mwindow==NULL) + return DefWindowProc(hwnd, msg, wparam, lparam); + + width = LOWORD(lparam); height = HIWORD(lparam); +#ifdef _DEBUG + wdxdisplay_cat.spam() << "WM_SIZE received with width:" << width << " height: " << height << endl; +#endif + if (_props._xsize != width || _props._ysize != height) { + DXGraphicsStateGuardian* dxgsg = DCAST(DXGraphicsStateGuardian, _gsg); + bWindowAdjustingType = Resizing; + assert(_mwindow!=NULL); + + RECT view_rect; + GetClientRect( _mwindow, &view_rect ); + ClientToScreen( _mwindow, (POINT*)&view_rect.left ); + ClientToScreen( _mwindow, (POINT*)&view_rect.right ); + assert(width == (view_rect.right - view_rect.left)); + assert(height == (view_rect.bottom - view_rect.top)); + + resized(width, height); // change _props xsize,ysize + _props._xorg = view_rect.left; // _props should reflect view rectangle + _props._yorg = view_rect.top; + } + + return DefWindowProc(hwnd, msg, wparam, lparam); + } + + case WM_SETFOCUS: + if (_dx_ready && _mouse_entry_enabled) + handle_mouse_entry(MOUSE_ENTERED,hMouseCrossIcon); + return 0; + +#if 0 + case WM_WINDOWPOSCHANGING: { + LPWINDOWPOS pWindPos = (LPWINDOWPOS) lparam; + wdxdisplay_cat.spam() << "WM_WINDOWPOSCHANGING received, flags 0x" << pWindPos->flags << endl; + return DefWindowProc(hwnd, msg, wparam, lparam); + } + + case WM_GETMINMAXINFO: + wdxdisplay_cat.spam() << "WM_GETMINMAXINFO received\n" << endl; + return DefWindowProc(hwnd, msg, wparam, lparam); +#endif + + case WM_ERASEBKGND: + if(bWindowAdjustingType) + return DefWindowProc(hwnd, msg, wparam, lparam); + else return 0; // dont let GDI waste time redrawing the deflt background + + case WM_KILLFOCUS: + if (_dx_ready && _mouse_entry_enabled) + handle_mouse_entry(MOUSE_EXITED,hMouseCrossIcon); + return 0; + + default: + return DefWindowProc(hwnd, msg, wparam, lparam); + } +// IDirectDraw7::TestCooperativeLevel + +} + + + +//////////////////////////////////////////////////////////////////// +// Function: Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +wdxGraphicsWindow:: +wdxGraphicsWindow(GraphicsPipe* pipe) : GraphicsWindow(pipe) { + config(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +wdxGraphicsWindow:: +wdxGraphicsWindow(GraphicsPipe* pipe, const + GraphicsWindow::Properties& props) : GraphicsWindow(pipe, props) { + config(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Destructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +wdxGraphicsWindow::~wdxGraphicsWindow(void) { +#ifdef WBD_GL_MODE + free(_visual); +#endif //WBD_GL_MODE +} + + + +#ifdef WBD_GL_MODE +//////////////////////////////////////////////////////////////////// +// Function: get_config +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow:: +get_config(PIXELFORMATDESCRIPTOR *visual, int attrib, int *value) { + if (visual == NULL) + return; + + switch (attrib) { + case GLX_USE_GL: + if (visual->dwFlags & (PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW)) { + if (visual->iPixelType == PFD_TYPE_COLORINDEX && + visual->cColorBits >= 24) { + *value = 0; + } else { + *value = 1; + } + } else { + *value = 0; + } + break; + case GLX_BUFFER_SIZE: + if (visual->iPixelType == PFD_TYPE_RGBA) + *value = visual->cColorBits; + else + *value = 8; + break; + case GLX_LEVEL: + *value = visual->bReserved; + break; + case GLX_RGBA: + *value = visual->iPixelType == PFD_TYPE_RGBA; + break; + case GLX_DOUBLEBUFFER: + *value = visual->dwFlags & PFD_DOUBLEBUFFER; + break; + case GLX_STEREO: + *value = visual->dwFlags & PFD_STEREO; + break; + case GLX_AUX_BUFFERS: + *value = visual->cAuxBuffers; + break; + case GLX_RED_SIZE: + *value = visual->cRedBits; + break; + case GLX_GREEN_SIZE: + *value = visual->cGreenBits; + break; + case GLX_BLUE_SIZE: + *value = visual->cBlueBits; + break; + case GLX_ALPHA_SIZE: + *value = visual->cAlphaBits; + break; + case GLX_DEPTH_SIZE: + *value = visual->cDepthBits; + break; + case GLX_STENCIL_SIZE: + *value = visual->cStencilBits; + break; + case GLX_ACCUM_RED_SIZE: + *value = visual->cAccumRedBits; + break; + case GLX_ACCUM_GREEN_SIZE: + *value = visual->cAccumGreenBits; + break; + case GLX_ACCUM_BLUE_SIZE: + *value = visual->cAccumBlueBits; + break; + case GLX_ACCUM_ALPHA_SIZE: + *value = visual->cAccumAlphaBits; + break; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: try_for_visual +// Description: This is a static function that attempts to get the +// requested visual, if it is available. It's just a +// wrapper around glXChooseVisual(). It returns the +// visual information if possible, or NULL if it is not. +//////////////////////////////////////////////////////////////////// +PIXELFORMATDESCRIPTOR* wdxGraphicsWindow:: +try_for_visual(/*wdxGraphicsPipe *pipe,*/ int mask, + int want_depth_bits, int want_color_bits) { + static const int max_attrib_list = 32; + int attrib_list[max_attrib_list]; + int n=0; + + wdxdisplay_cat.debug() + << "Trying for visual with: RGB(" << want_color_bits << ")"; + + int want_color_component_bits; + if (mask & W_ALPHA) { + want_color_component_bits = max(want_color_bits / 4, 1); + } else { + want_color_component_bits = max(want_color_bits / 3, 1); + } + + attrib_list[n++] = GLX_RGBA; + attrib_list[n++] = GLX_RED_SIZE; + attrib_list[n++] = want_color_component_bits; + attrib_list[n++] = GLX_GREEN_SIZE; + attrib_list[n++] = want_color_component_bits; + attrib_list[n++] = GLX_BLUE_SIZE; + attrib_list[n++] = want_color_component_bits; + + if (mask & W_ALPHA) { + wdxdisplay_cat.debug(false) << " ALPHA"; + attrib_list[n++] = GLX_ALPHA_SIZE; + attrib_list[n++] = want_color_component_bits; + } + if (mask & W_DOUBLE) { + wdxdisplay_cat.debug(false) << " DOUBLEBUFFER"; + attrib_list[n++] = GLX_DOUBLEBUFFER; + } + if (mask & W_STEREO) { + wdxdisplay_cat.debug(false) << " STEREO"; + attrib_list[n++] = GLX_STEREO; + } + if (mask & W_DEPTH) { + wdxdisplay_cat.debug(false) << " DEPTH(" << want_depth_bits << ")"; + attrib_list[n++] = GLX_DEPTH_SIZE; + attrib_list[n++] = want_depth_bits; + } + if (mask & W_STENCIL) { + wdxdisplay_cat.debug(false) << " STENCIL"; + attrib_list[n++] = GLX_STENCIL_SIZE; + attrib_list[n++] = 1; + } + if (mask & W_ACCUM) { + wdxdisplay_cat.debug(false) << " ACCUM"; + attrib_list[n++] = GLX_ACCUM_RED_SIZE; + attrib_list[n++] = want_color_component_bits; + attrib_list[n++] = GLX_ACCUM_GREEN_SIZE; + attrib_list[n++] = want_color_component_bits; + attrib_list[n++] = GLX_ACCUM_BLUE_SIZE; + attrib_list[n++] = want_color_component_bits; + if (mask & W_ALPHA) { + attrib_list[n++] = GLX_ACCUM_ALPHA_SIZE; + attrib_list[n++] = want_color_component_bits; + } + } + + // Terminate the list + nassertr(n < max_attrib_list, NULL); + attrib_list[n] = 0L; + + PIXELFORMATDESCRIPTOR pfd; + PIXELFORMATDESCRIPTOR *match = NULL; + bool stereo = false; + + memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); + pfd.nSize = (sizeof(PIXELFORMATDESCRIPTOR)); + pfd.nVersion = 1; + + // Defaults + pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW; + pfd.iPixelType = PFD_TYPE_COLORINDEX; + pfd.cColorBits = 32; + pfd.cDepthBits = 0; + + int *p = attrib_list; + while (*p) { + switch (*p) { + case GLX_USE_GL: + pfd.dwFlags |= PFD_SUPPORT_OPENGL; + break; + case GLX_LEVEL: + pfd.bReserved = *(++p); + break; + case GLX_RGBA: + pfd.iPixelType = PFD_TYPE_RGBA; + break; + case GLX_DOUBLEBUFFER: + pfd.dwFlags |= PFD_DOUBLEBUFFER; + break; + case GLX_STEREO: + stereo = true; + pfd.dwFlags |= PFD_STEREO; + break; + case GLX_AUX_BUFFERS: + pfd.cAuxBuffers = *(++p); + break; + case GLX_RED_SIZE: + pfd.cRedBits = 8; // Try to get the maximum + ++p; + break; + case GLX_GREEN_SIZE: + pfd.cGreenBits = 8; // Try to get the maximum + ++p; + break; + case GLX_BLUE_SIZE: + pfd.cBlueBits = 8; // Try to get the maximum + ++p; + break; + case GLX_ALPHA_SIZE: + pfd.cAlphaBits = 8; // Try to get the maximum + ++p; + break; + case GLX_DEPTH_SIZE: + pfd.cDepthBits = 32; // Try to get the maximum + ++p; + break; + case GLX_STENCIL_SIZE: + pfd.cStencilBits = *(++p); + break; + case GLX_ACCUM_RED_SIZE: + case GLX_ACCUM_GREEN_SIZE: + case GLX_ACCUM_BLUE_SIZE: + case GLX_ACCUM_ALPHA_SIZE: + // Only cAccumBits is used for requesting accum buffer + pfd.cAccumBits = 1; + ++p; + break; + } + ++p; + } + + int pf = ChoosePixelFormat(_hdc, &pfd); + if (pf > 0) { + match = (PIXELFORMATDESCRIPTOR *)malloc(sizeof(PIXELFORMATDESCRIPTOR)); + DescribePixelFormat(_hdc, pf, sizeof(PIXELFORMATDESCRIPTOR), match); + + // ChoosePixelFormat is dumb about stereo + if (stereo) { + if (!(match->dwFlags & PFD_STEREO)) { + wdxdisplay_cat.info() + << "wdxGraphicsWindow::try_for_visual() - request for " + << "stereo failed" << endl; + } + } + } + + return match; +} + + +//////////////////////////////////////////////////////////////////// +// Function: choose visual +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow::choose_visual(void) { + // don't need pipe... wdxGraphicsPipe* pipe = DCAST(wdxGraphicsPipe, _pipe); + + int mask = _props._mask; + int want_depth_bits = _props._want_depth_bits; + int want_color_bits = _props._want_color_bits; + + if (mask & W_MULTISAMPLE) { + wdxdisplay_cat.info() + << "wdxGraphicsWindow::choose_visual() - multisample not supported" + << endl; + mask &= ~W_MULTISAMPLE; + } + + _visual = try_for_visual(/*pipe,*/ mask, want_depth_bits, want_color_bits); + + // This is the severity level at which we'll report the details of + // the visual we actually do find. Normally, it's debug-level + // information: we don't care about that much detail. + NotifySeverity show_visual_severity = NS_debug; + + if (_visual == NULL) { + wdxdisplay_cat.info() + << "wdxGraphicsWindow::choose_visual() - visual with requested\n" + << " capabilities not found; trying for lesser visual.\n"; + + // If we're unable to get the visual we asked for, however, we + // probably *do* care to know the details about what we actually + // got, even if we don't have debug mode set. So we'll report the + // visual at a higher level. + show_visual_severity = NS_info; + + bool special_size_request = + (want_depth_bits != 1 || want_color_bits != 1); + + // We try to be smart about choosing a close match for the visual. + // First, we'll eliminate some of the more esoteric options one at + // a time, then two at a time, and finally we'll try just the bare + // minimum. + + if (special_size_request) { + // Actually, first we'll eliminate all of the minimum sizes, to + // try to open a window with all of the requested options, but + // maybe not as many bits in some options as we'd like. + _visual = try_for_visual(/*pipe,*/ mask); + } + + if (_visual == NULL) { + // Ok, not good enough. Now try to eliminate options, but keep + // as many bits as we asked for. + + // This array keeps the bitmasks of options that we pull out of + // the requested mask, in order. + + static const int strip_properties[] = { + // One esoteric option removed. + W_MULTISAMPLE, + W_STENCIL, + W_ACCUM, + W_ALPHA, + W_STEREO, + + // Two esoteric options removed. + W_STENCIL | W_MULTISAMPLE, + W_ACCUM | W_MULTISAMPLE, + W_ALPHA | W_MULTISAMPLE, + W_STEREO | W_MULTISAMPLE, + W_STENCIL | W_ACCUM, + W_ALPHA | W_STEREO, + W_STENCIL | W_ACCUM | W_MULTISAMPLE, + W_ALPHA | W_STEREO | W_MULTISAMPLE, + + // All esoteric options removed. + W_STENCIL | W_ACCUM | W_ALPHA | W_STEREO | W_MULTISAMPLE, + + // All esoteric options, plus some we'd really really prefer, + // removed. + W_STENCIL | W_ACCUM | W_ALPHA | W_STEREO | W_MULTISAMPLE | W_DOUBLE, + + // A zero marks the end of the array. + 0 + }; + + set tried_masks; + tried_masks.insert(mask); + + int i; + for (i = 0; _visual == NULL && strip_properties[i] != 0; i++) { + int new_mask = mask & ~strip_properties[i]; + if (tried_masks.insert(new_mask).second) { + _visual = try_for_visual(/*pipe,*/ new_mask, want_depth_bits, + want_color_bits); + } + } + + if (special_size_request) { + tried_masks.clear(); + tried_masks.insert(mask); + + if (_visual == NULL) { + // Try once more, this time eliminating all of the size + // requests. + for (i = 0; _visual == NULL && strip_properties[i] != 0; i++) { + int new_mask = mask & ~strip_properties[i]; + if (tried_masks.insert(new_mask).second) { + _visual = try_for_visual(/*pipe,*/ new_mask); + } + } + } + } + + if (_visual == NULL) { + // Here's our last-ditch desparation attempt: give us any GLX + // visual at all! + _visual = try_for_visual(/*pipe,*/ 0); + } + + if (_visual == NULL) { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::choose_visual() - could not get any " + "visual." << endl; + exit(1); + } + } + } + + if (wdxdisplay_cat.is_on(show_visual_severity)) { + int render_mode, double_buffer, stereo, red_size, green_size, blue_size, + alpha_size, ared_size, agreen_size, ablue_size, aalpha_size, + depth_size, stencil_size; + + get_config(_visual, GLX_RGBA, &render_mode); + get_config(_visual, GLX_DOUBLEBUFFER, &double_buffer); + get_config(_visual, GLX_STEREO, &stereo); + get_config(_visual, GLX_RED_SIZE, &red_size); + get_config(_visual, GLX_GREEN_SIZE, &green_size); + get_config(_visual, GLX_BLUE_SIZE, &blue_size); + get_config(_visual, GLX_ALPHA_SIZE, &alpha_size); + get_config(_visual, GLX_ACCUM_RED_SIZE, &ared_size); + get_config(_visual, GLX_ACCUM_GREEN_SIZE, &agreen_size); + get_config(_visual, GLX_ACCUM_BLUE_SIZE, &ablue_size); + get_config(_visual, GLX_ACCUM_ALPHA_SIZE, &aalpha_size); + get_config(_visual, GLX_DEPTH_SIZE, &depth_size); + get_config(_visual, GLX_STENCIL_SIZE, &stencil_size); + + wdxdisplay_cat.out(show_visual_severity) + << "GLX Visual Info (# bits of each):" << endl + << " RGBA: " << red_size << " " << green_size << " " << blue_size + << " " << alpha_size << endl + << " Accum RGBA: " << ared_size << " " << agreen_size << " " + << ablue_size << " " << aalpha_size << endl + << " Depth: " << depth_size << endl + << " Stencil: " << stencil_size << endl + << " DoubleBuffer? " << double_buffer << endl + << " Stereo? " << stereo << endl; + } +} + + +/* +#else // DX Version of try_for_visual called from the Callback function to EnumDisplayModes +bool wdxGraphicsWindow:: +try_for_visual(GraphicsWindow::Properties *props) +{ + static const int max_attrib_list = 32; + int attrib_list[max_attrib_list]; + int n=0; + + wdxdisplay_cat.debug() + << "Trying for visual with: RGB(" << want_color_bits << ")"; + + int want_color_component_bits; + if (mask & W_ALPHA) { + want_color_component_bits = max(props->want_color_bits / 4, 1); + } else { + want_color_component_bits = max(props->want_color_bits / 3, 1); + } + + attrib_list[n++] = GLX_RGBA; + attrib_list[n++] = GLX_RED_SIZE; + attrib_list[n++] = want_color_component_bits; + attrib_list[n++] = GLX_GREEN_SIZE; + attrib_list[n++] = want_color_component_bits; + attrib_list[n++] = GLX_BLUE_SIZE; + attrib_list[n++] = want_color_component_bits; + + if (mask & W_ALPHA) { + wdxdisplay_cat.debug(false) << " ALPHA"; + attrib_list[n++] = GLX_ALPHA_SIZE; + attrib_list[n++] = want_color_component_bits; + } + if (mask & W_DOUBLE) { + wdxdisplay_cat.debug(false) << " DOUBLEBUFFER"; + attrib_list[n++] = GLX_DOUBLEBUFFER; + } + if (mask & W_STEREO) { + wdxdisplay_cat.debug(false) << " STEREO"; + attrib_list[n++] = GLX_STEREO; + } + if (mask & W_DEPTH) { + wdxdisplay_cat.debug(false) << " DEPTH(" << want_depth_bits << ")"; + attrib_list[n++] = GLX_DEPTH_SIZE; + attrib_list[n++] = want_depth_bits; + } + if (mask & W_STENCIL) { + wdxdisplay_cat.debug(false) << " STENCIL"; + attrib_list[n++] = GLX_STENCIL_SIZE; + attrib_list[n++] = 1; + } + if (mask & W_ACCUM) { + wdxdisplay_cat.debug(false) << " ACCUM"; + attrib_list[n++] = GLX_ACCUM_RED_SIZE; + attrib_list[n++] = want_color_component_bits; + attrib_list[n++] = GLX_ACCUM_GREEN_SIZE; + attrib_list[n++] = want_color_component_bits; + attrib_list[n++] = GLX_ACCUM_BLUE_SIZE; + attrib_list[n++] = want_color_component_bits; + if (mask & W_ALPHA) { + attrib_list[n++] = GLX_ACCUM_ALPHA_SIZE; + attrib_list[n++] = want_color_component_bits; + } + } + + // Terminate the list + nassertr(n < max_attrib_list, NULL); + attrib_list[n] = 0L; + + bool stereo = false; + + int *p = attrib_list; + while (*p) { + switch (*p) { + case GLX_LEVEL: + break; + case GLX_RGBA: + break; + case GLX_DOUBLEBUFFER: + break; + case GLX_STEREO: + stereo = true; + break; + case GLX_AUX_BUFFERS: + break; + case GLX_RED_SIZE: + ++p; + break; + case GLX_GREEN_SIZE: + ++p; + break; + case GLX_BLUE_SIZE: + ++p; + break; + case GLX_ALPHA_SIZE: + ++p; + break; + case GLX_DEPTH_SIZE: + ++p; + break; + case GLX_STENCIL_SIZE: + ++p; + break; + case GLX_ACCUM_RED_SIZE: + case GLX_ACCUM_GREEN_SIZE: + case GLX_ACCUM_BLUE_SIZE: + case GLX_ACCUM_ALPHA_SIZE: + ++p; + break; + } + ++p; + } + + return TRUE; +} +*/ +#endif //WBD_GL_MODE + + + +//////////////////////////////////////////////////////////////////// +// Function: config +// Access: +// Description: Set up Direct X +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow::config(void) { + GraphicsWindow::config(); + + global_dxwin = this; // kludge to get window proc working + + _dx_ready = false; + _mwindow = NULL; + _gsg = NULL; + _hParentWindow = NULL; + + // Create a GSG to manage the graphics + make_gsg(); + + WNDCLASS wc; + HINSTANCE hinstance = GetModuleHandle(NULL); + + // Clear before filling in window structure! + ZeroMemory(&wc, sizeof(WNDCLASS)); + wc.style = CS_HREDRAW | CS_VREDRAW; //CS_OWNDC; + wc.lpfnWndProc = (WNDPROC) static_window_proc; + wc.hInstance = hinstance; + wc.hIcon = LoadIcon(hinstance, IDI_WINLOGO); + wc.hCursor = LoadCursor(NULL/*hinstance*/, IDC_CROSS); + wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = "wdxDisplay"; + + if (!RegisterClass(&wc)) { + wdxdisplay_cat.fatal() << "wdxGraphicsWindow:: could not register window class" << endl; + exit(1); + } + + wdxGraphicsPipe* pipe = DCAST(wdxGraphicsPipe, _pipe); + + int style; + RECT win_rect; + SetRect(&win_rect, _props._xorg, _props._yorg, _props._xorg + _props._xsize, + _props._yorg + _props._ysize); + + // rect now contains the coords for the entire window, not the client + if (dx_full_screen) { + _mwindow = CreateWindow("wdxDisplay", _props._title.c_str(), + WS_POPUP, 0, 0, _props._xsize,_props._ysize, + NULL, NULL, hinstance, 0); + } else { + if (_props._border) + style = WS_OVERLAPPEDWINDOW; + else + style = WS_POPUP | WS_MAXIMIZE; + + AdjustWindowRect(&win_rect, style, FALSE); //compute window size based on desired client area size + + // make sure origin is on screen + if (win_rect.left < 0) { win_rect.right -= win_rect.left; win_rect.left = 0; } + if (win_rect.top < 0) { win_rect.bottom -= win_rect.top; win_rect.top = 0; } + + _mwindow = CreateWindow("wdxDisplay", _props._title.c_str(), + style, win_rect.left, win_rect.top, win_rect.right-win_rect.left, + win_rect.bottom-win_rect.top, + NULL, NULL, hinstance, 0); + } + + if (!_mwindow) { + wdxdisplay_cat.fatal() << "wdxGraphicsWindow::config() - failed to create Mwindow" << endl; + exit(1); + } + + _hParentWindow=GetForegroundWindow(); + + // move window to top of zorder + SetWindowPos(_mwindow, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE); + + _hdc = GetDC(_mwindow); + + dx_setup(); + + _mouse_input_enabled = false; + _mouse_motion_enabled = false; + _mouse_passive_motion_enabled = false; + _mouse_entry_enabled = false; + _entry_state = -1; + + // Enable detection of mouse input + enable_mouse_input(true); + enable_mouse_motion(true); + enable_mouse_passive_motion(true); + + // Now indicate that we have our keyboard/mouse device ready. + GraphicsWindowInputDevice device = + GraphicsWindowInputDevice::pointer_and_keyboard("keyboard/mouse"); + _input_devices.push_back(device); + + ShowWindow(_mwindow, SW_SHOWNORMAL); +// UpdateWindow( _mwindow ); +} + +HRESULT CALLBACK EnumDevicesCallback(LPSTR pDeviceDescription, LPSTR pDeviceName, + LPD3DDEVICEDESC7 pD3DDeviceDesc,LPVOID pContext) { + D3DDEVICEDESC7 *pd3ddevs = (D3DDEVICEDESC7 *)pContext; +#ifdef _DEBUG + wdxdisplay_cat.spam() << "wdxGraphicsWindow: Enumerating Device " << pDeviceName << " : " << pDeviceDescription << endl; +#endif + + // only saves hal and tnl devs + + if(IsEqualGUID(pD3DDeviceDesc->deviceGUID,IID_IDirect3DHALDevice)) { + CopyMemory(pd3ddevs,pD3DDeviceDesc,sizeof(D3DDEVICEDESC7)); + } else if(IsEqualGUID(pD3DDeviceDesc->deviceGUID,IID_IDirect3DTnLHalDevice)) { + CopyMemory(&pd3ddevs[1],pD3DDeviceDesc,sizeof(D3DDEVICEDESC7)); + } + return DDENUMRET_OK; +} + +HRESULT WINAPI EnumDisplayModesCallBack(LPDDSURFACEDESC2 lpDDSurfaceDesc,LPVOID lpContext) { + DWORD *pDDBDMask = (DWORD*)lpContext; + + *pDDBDMask |= BitDepth_2_DDBDMask(lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount); + return DDENUMRET_OK; +} + +//////////////////////////////////////////////////////////////////// +// Function: dx_setup +// Description: Set up the DirectX environment. The size of the +// rendered area will be computed from the Client area +// of the window (if in windowed mode) and the _props +// structure will be set accordingly. +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow:: +dx_setup() +{ + LPDIRECTDRAWSURFACE7 pBackDDSurf; + LPDIRECTDRAWSURFACE7 pZDDSurf; + LPDIRECT3D7 pD3DI; + LPDIRECT3DDEVICE7 pD3DDevice; + LPDIRECTDRAWSURFACE7 pPrimaryDDSurf; + LPDIRECTDRAW7 pDD; + RECT view_rect; + int i; + DX_DECLARE_CLEAN( DDSURFACEDESC2, SurfaceDesc ); + + // Check for DirectX 7 by looking for DirectDrawCreateEx + + HINSTANCE DDHinst = LoadLibrary( "DDRAW.DLL" ); + if( DDHinst == 0 ) { + wdxdisplay_cat.fatal() << "wdxGraphicsWindow::config() - DDRAW.DLL doesn't exist!" << endl; + exit(1); + } + + if(NULL == GetProcAddress( DDHinst, "DirectDrawCreateEx" )) { + wdxdisplay_cat.fatal() << "wdxGraphicsWindow::config() - Panda currently requires DirectX 7.0!" << endl; + exit(1); + } + + // Create the Direct Draw Object + HRESULT hr = DirectDrawCreateEx(NULL, (void **)&pDD, IID_IDirectDraw7, NULL); + if (hr != DD_OK) { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::config() - DirectDrawCreateEx failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + FreeLibrary(DDHinst); //undo LoadLib above, decrement ddrawl.dll refcnt (after DDrawCreate, since dont want to unload/reload) + +#ifdef _DEBUG + DDDEVICEIDENTIFIER2 dddi; + pDD->GetDeviceIdentifier(&dddi,0x0); + wdxdisplay_cat.debug() + << " GfxCard: " << dddi.szDescription << "; DriverVer: " << HIWORD(dddi.liDriverVersion.HighPart) << "." << LOWORD(dddi.liDriverVersion.HighPart) << "." << HIWORD(dddi.liDriverVersion.LowPart) << "." << LOWORD(dddi.liDriverVersion.LowPart) << endl; +#endif + + // Query DirectDraw for access to Direct3D + + hr = pDD->QueryInterface( IID_IDirect3D7, (VOID**)&pD3DI ); + if (hr != DD_OK) { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::config() - QI for D3D failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + // just look for HAL and TnL devices right now. I dont think + // we have any interest in the sw rasts at this point + + D3DDEVICEDESC7 d3ddevs[2]; // put HAL in 0, TnL in 1 + ZeroMemory(d3ddevs,2*sizeof(D3DDEVICEDESC7)); + + hr = pD3DI->EnumDevices(EnumDevicesCallback,d3ddevs); + if (hr != DD_OK) { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::config() - EnumDevices failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + if(!(d3ddevs[0].dwDevCaps & D3DDEVCAPS_HWRASTERIZATION )) { + wdxdisplay_cat.fatal() << "wdxGraphicsWindow:: No 3D HW present, exiting..." << endl; + exit(1); + } + + DX_DECLARE_CLEAN(DDCAPS,DDCaps); + pDD->GetCaps(&DDCaps,NULL); + if((!(DDCaps.dwCaps2 & DDCAPS2_CANRENDERWINDOWED)) && !dx_full_screen) { + wdxdisplay_cat.fatal() << "wdxGraphicsWindow:: 3D HW cannot render windowed, exiting..." << endl; + exit(1); + } + + if (dx_full_screen) { + DWORD dwRenderWidth = _props._xsize; + DWORD dwRenderHeight = _props._ysize; + _props._xorg = _props._yorg = 0; + SetRect(&view_rect, _props._xorg, _props._yorg, _props._xsize, _props._ysize); + + // CREATE FULL SCREEN BUFFERS + // Store the rectangle which contains the renderer + + DWORD dwSupportedBitDepthMask=0x0; // uses DDBD_... + DX_DECLARE_CLEAN(DDSURFACEDESC2,ddsd_search); + ddsd_search.dwFlags = DDSD_HEIGHT | DDSD_WIDTH; + ddsd_search.dwWidth=dwRenderWidth; ddsd_search.dwHeight=dwRenderHeight; + + if(FAILED(hr= pDD->EnumDisplayModes(0x0,&ddsd_search,&dwSupportedBitDepthMask,EnumDisplayModesCallBack))) { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::EnumDisplayModes failed, result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + DWORD dwFullScreenBitDepth; + + // Now we try to figure out if we can use requested screen resolution and best + // rendertarget bpp and still have at least 2 meg of texture vidmem + + // Get Current VidMem avail. Note this is only an estimate, when we switch to fullscreen + // mode from desktop, more vidmem will be available (typically 1.2 meg). I dont want + // to switch to fullscreen more than once due to the annoying monitor flicker, so try + // to figure out optimal mode using this estimate + DDSCAPS2 ddsCaps; + DWORD dwTotal,dwFree; + ZeroMemory(&ddsCaps,sizeof(DDSCAPS2)); + ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY; //set internally by DX anyway, dont think this any different than 0x0 + + if( FAILED( hr = pDD->GetAvailableVidMem(&ddsCaps,&dwTotal,&dwFree))) { + wdxdisplay_cat.debug() << "wdxGraphicsWindow::GetAvailableVidMem failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + +#ifdef _DEBUG + wdxdisplay_cat.debug() << "before FullScreen switch: GetAvailableVidMem returns Total: " << dwTotal/1000000.0 << " Free: " << dwFree/1000000.0 << endl; +#endif + + // note: this chooses 32bpp, which may not be preferred over 16 for memory & speed reasons + if((dwSupportedBitDepthMask & DDBD_32) && (d3ddevs[0].dwDeviceRenderBitDepth & DDBD_32)) { + dwFullScreenBitDepth=32; // go for 32bpp if its avail + } else if((dwSupportedBitDepthMask & DDBD_24) && (d3ddevs[0].dwDeviceRenderBitDepth & DDBD_24)) { + dwFullScreenBitDepth=24; // go for 24bpp if its avail + } else if((dwSupportedBitDepthMask & DDBD_16) && (d3ddevs[0].dwDeviceRenderBitDepth & DDBD_16)) { + dwFullScreenBitDepth=16; // do 16bpp + } else { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::config() - No Supported FullScreen resolutions at " << dwRenderWidth << "x" << dwRenderHeight << endl; + exit(1); + } + + #define THREE_MEG 3000000 + if(dwFree< THREE_MEG) { + // we're going to need 800x600 or 640x480 at 16 bit to save enough tex vidmem + dwFullScreenBitDepth=16; // do 16bpp + wdxdisplay_cat.debug() << "wdxGraphicsWindow:: using 16bpp rendertargets to save tex vidmem. XXX BUGBUG: auto-reduce-scrn-res not implemented yet XXX\n"; + // BUGBUG: need to insert code to reduce dimensions here. need to modify + // _props scrn dimension values as well + } + +#if 0 +// cant do this without more accurate way to estimate mem used before actually switching +// to that fullscrn mode. simply computing memsize based on GetDisplayMode doesnt seem +// to be accurate within more than 1 meg + + // we think we need to reserve at least 2 megs of vidmem for textures. + // to do this, reduce buffer bitdepth if possible + #define RESERVEDTEXVIDMEM 2000000 + + int rendertargetmem=dwRenderWidth*dwRenderHeight*(dwFullScreenBitDepth>>3); + int memleft = dwFree-rendertargetmem*2; //*2 to handle backbuf/zbuf + + if(memleft < RESERVEDTEXVIDMEM) { + dwFullScreenBitDepth=16; + wdxdisplay_cat.debug() << "wdxGraphicsWindow:: using 16bpp rendertargets to save tex vidmem\n"; + assert((dwSupportedBitDepthMask & DDBD_16) && (d3ddevs[0].dwDeviceRenderBitDepth & DDBD_16)); // probably a safe assumption + rendertargetmem=dwRenderWidth*dwRenderHeight*(dwFullScreenBitDepth>>3); + memleft = dwFree-rendertargetmem*2; + + // BUGBUG: if we still cant reserve 2 megs of vidmem, need to auto-reduce the scrn res + if(memleft < RESERVEDTEXVIDMEM) + wdxdisplay_cat.debug() << "wdxGraphicsWindow:: XXXXXX WARNING: cant reserve 2MB of tex vidmem. only " << memleft << " bytes available. Need to rewrite wdxdisplay to try lower resolutions XXXXXXXXXXXXXXXXXXXX\n"; + } +#endif + + if(FAILED(hr = pDD->SetCooperativeLevel(_mwindow, DDSCL_FPUSETUP | DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT ))) { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::config() - SetCooperativeLevel failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + if( FAILED( hr = pDD->SetDisplayMode( dwRenderWidth, dwRenderHeight, + dwFullScreenBitDepth, 0L, 0L ))) { + wdxdisplay_cat.fatal() << "wdxGraphicsWindow::CreateFullscreenBuffers() - Can't set display mode : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + +#ifdef _DEBUG + wdxdisplay_cat.debug() << "wdxGraphicsWindow::setting displaymode to " << dwRenderWidth << "x" << dwRenderHeight << " at "<< dwFullScreenBitDepth << "bpp" <GetAvailableVidMem(&ddsCaps,&dwTotal,&dwFree))) { + wdxdisplay_cat.debug() << "wdxGraphicsWindow::GetAvailableVidMem failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + wdxdisplay_cat.debug() << "after FullScreen switch: GetAvailableVidMem returns Total: " << dwTotal/1000000.0 << " Free: " << dwFree/1000000.0 << endl; +#endif + + // Setup to create the primary surface w/backbuffer + DDSURFACEDESC2 ddsd; + ZeroMemory( &ddsd, sizeof(ddsd) ); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS|DDSD_BACKBUFFERCOUNT; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE | + DDSCAPS_FLIP | DDSCAPS_COMPLEX; + ddsd.dwBackBufferCount = 1; + + PRINTVIDMEM(pDD,&ddsd.ddsCaps,"initial primary & backbuf"); + + // Create the primary surface + if( FAILED( hr = pDD->CreateSurface( &ddsd, &pPrimaryDDSurf, NULL ) ) ) + { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::CreateFullscreenBuffers() - CreateSurface failed for primary surface: result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + // Get the backbuffer, which was created along with the primary. + DDSCAPS2 ddscaps = { DDSCAPS_BACKBUFFER, 0, 0, 0 }; + if( FAILED( hr = pPrimaryDDSurf->GetAttachedSurface( &ddscaps, &pBackDDSurf ) ) ) + { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::CreateFullscreenBuffers() - Can't get the backbuffer: result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + } // end create full screen buffers + + else { // CREATE WINDOWED BUFFERS + if(FAILED(hr = pDD->GetDisplayMode( &SurfaceDesc ))) { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::GetDisplayMode failed result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + if( SurfaceDesc.ddpfPixelFormat.dwRGBBitCount <= 8 ) { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::config() - Can't run windowed in an 8-bit or less display mode" << endl; + exit(1); + } + + if(!(BitDepth_2_DDBDMask(SurfaceDesc.ddpfPixelFormat.dwRGBBitCount) & d3ddevs[0].dwDeviceRenderBitDepth)) { + wdxdisplay_cat.fatal() << "wdxGraphicsWindow:: - 3D Device doesnt support rendering at " << SurfaceDesc.ddpfPixelFormat.dwRGBBitCount << "bpp (current desktop bitdepth)" << endl; + exit(1); + } + + if(FAILED(hr = pDD->SetCooperativeLevel(_mwindow, DDSCL_FPUSETUP | DDSCL_NORMAL))) { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::config() - SetCooperativeLevel failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + // Get the dimensions of the viewport and screen bounds + + GetClientRect( _mwindow, &view_rect ); + ClientToScreen( _mwindow, (POINT*)&view_rect.left ); + ClientToScreen( _mwindow, (POINT*)&view_rect.right ); + DWORD dwRenderWidth = view_rect.right - view_rect.left; + DWORD dwRenderHeight = view_rect.bottom - view_rect.top; + _props._xorg = view_rect.left; // _props should reflect view rectangle + _props._yorg = view_rect.top; + _props._xsize = dwRenderWidth; + _props._ysize = dwRenderHeight; + + // Initialize the description of the primary surface + ZeroMemory( &SurfaceDesc, sizeof(DDSURFACEDESC2) ); + SurfaceDesc.dwSize = sizeof(DDSURFACEDESC2); + SurfaceDesc.dwFlags = DDSD_CAPS ; + SurfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + PRINTVIDMEM(pDD,&SurfaceDesc.ddsCaps,"initial primary surface"); + + // Create the primary surface. This includes all of the visible + // window, so no need to specify height/width + if(FAILED(hr = pDD->CreateSurface( &SurfaceDesc, &pPrimaryDDSurf, NULL ))) { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow:: CreateSurface failed for primary surface: result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + // Create a clipper object which handles all our clipping for cases when + // our window is partially obscured by other windows. + LPDIRECTDRAWCLIPPER Clipper; + if(FAILED(hr = pDD->CreateClipper( 0, &Clipper, NULL ))) { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow:: CreateClipper failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + // Associate the clipper with our window. Note that, afterwards, the + // clipper is internally referenced by the primary surface, so it is safe + // to release our local reference to it. + Clipper->SetHWnd( 0, _mwindow ); + pPrimaryDDSurf->SetClipper( Clipper ); + Clipper->Release(); + + // Setup a surface description to create a backbuffer. + SurfaceDesc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; + SurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY; + SurfaceDesc.dwWidth = dwRenderWidth; + SurfaceDesc.dwHeight = dwRenderHeight; + + PRINTVIDMEM(pDD,&SurfaceDesc.ddsCaps,"initial backbuf"); + + // Create the backbuffer. (might want to handle failure due to running out of video memory. + if(FAILED(hr = pDD->CreateSurface( &SurfaceDesc, &pBackDDSurf, NULL ))) { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow:: CreateSurface failed for backbuffer : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + } // end create windowed buffers + +// ======================================================== + + // Check if the device supports z-bufferless hidden surface removal. If so, + // we don't really need a z-buffer + if(!(d3ddevs[0].dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_ZBUFFERLESSHSR )) { + + // Get z-buffer dimensions from the render target + DX_DECLARE_CLEAN(DDSURFACEDESC2,ddsd); + pBackDDSurf->GetSurfaceDesc( &ddsd ); + + // Setup the surface desc for the z-buffer. + ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_PIXELFORMAT; + ddsd.ddsCaps.dwCaps = DDSCAPS_ZBUFFER | DDSCAPS_VIDEOMEMORY; + + DDPIXELFORMAT ZBufPixFmts[MAX_DX_ZBUF_FMTS]; + + // Get an appropiate pixel format from enumeration of the formats. On the + // first pass, we look for a zbuffer dpeth which is equal to the frame + // buffer depth (as some cards unfornately require this). + if( FAILED(pD3DI->EnumZBufferFormats( IID_IDirect3DHALDevice, EnumZBufFmtsCallback, + (VOID*)&ZBufPixFmts ))) { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::config() - EnumZBufferFormats failed " << endl; + exit(1); + } + + #ifdef _DEBUG + { static BOOL bPrinted=FALSE; + if(!bPrinted) { + for(i=0;idwRGBBitCount) { + case 16: + if(!(pCurPixFmt->dwFlags & DDPF_STENCILBUFFER)) + pz16=pCurPixFmt; + break; + case 24: + assert(!(pCurPixFmt->dwFlags & DDPF_STENCILBUFFER)); // shouldnt be stencil at 24bpp + pz24=pCurPixFmt; + break; + case 32: + if(!(pCurPixFmt->dwFlags & DDPF_STENCILBUFFER)) + pz32=pCurPixFmt; + break; + } + } + + if(pz32!=NULL) { + ddsd.ddpfPixelFormat = *pz32; + } else if(pz24!=NULL) { + ddsd.ddpfPixelFormat = *pz24; + } else if(pz16!=NULL){ + ddsd.ddpfPixelFormat = *pz16; + } else { + wdxdisplay_cat.fatal() << "wdxGraphicsWindow::config() - could find a valid zbuffer format!\n"; + exit(1); + } + } + + PRINTVIDMEM(pDD,&ddsd.ddsCaps,"initial zbuf"); + + // Create and attach a z-buffer + if( FAILED( hr = pDD->CreateSurface( &ddsd, &pZDDSurf, NULL ) ) ) { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::config() - CreateSurface failed for Z buffer: result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + if( FAILED( hr = pBackDDSurf->AddAttachedSurface( pZDDSurf ) ) ) { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::config() - AddAttachedSurface failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + +#ifdef _DEBUG + wdxdisplay_cat.debug() << "Creating " << ddsd.ddpfPixelFormat.dwRGBBitCount << "bpp zbuffer\n"; +#endif + } + + // Create the device. The device is created off of our back buffer, which + // becomes the render target for the newly created device. + hr = pD3DI->CreateDevice( IID_IDirect3DHALDevice, pBackDDSurf, &pD3DDevice ); + if (hr != DD_OK) { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::config() - CreateDevice failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + // Create the viewport + D3DVIEWPORT7 vp = { 0, 0, _props._xsize, _props._ysize, 0.0f, 1.0f }; + hr = pD3DDevice->SetViewport( &vp ); + if (hr != DD_OK) { + wdxdisplay_cat.fatal() + << "wdxGraphicsWindow::config() - SetViewport failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + DXGraphicsStateGuardian* dxgsg = DCAST(DXGraphicsStateGuardian, _gsg); + dxgsg->Set_HDC(_hdc); + dxgsg->init_dx(pDD, pPrimaryDDSurf, pBackDDSurf, pZDDSurf, pD3DI, pD3DDevice, view_rect); + + _dx_ready = true; + dxgsg->SetDXStatus(true); + +} + +//////////////////////////////////////////////////////////////////// +// Function: setup_colormap +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow::setup_colormap(void) { + wdxGraphicsPipe* pipe = DCAST(wdxGraphicsPipe, _pipe); + + PIXELFORMATDESCRIPTOR pfd; + LOGPALETTE *logical; + int n; + + /* grab the pixel format */ + memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); + DescribePixelFormat(_hdc, GetPixelFormat(_hdc), + sizeof(PIXELFORMATDESCRIPTOR), &pfd); + + if (!(pfd.dwFlags & PFD_NEED_PALETTE || + pfd.iPixelType == PFD_TYPE_COLORINDEX)) + return; + + n = 1 << pfd.cColorBits; + + /* allocate a bunch of memory for the logical palette (assume 256 + colors in a Win32 palette */ + logical = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) + + sizeof(PALETTEENTRY) * n); + memset(logical, 0, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * n); + + /* set the entries in the logical palette */ + logical->palVersion = 0x300; + logical->palNumEntries = n; + + /* start with a copy of the current system palette */ + GetSystemPaletteEntries(_hdc, 0, 256, &logical->palPalEntry[0]); + + if (pfd.iPixelType == PFD_TYPE_RGBA) { + int redMask = (1 << pfd.cRedBits) - 1; + int greenMask = (1 << pfd.cGreenBits) - 1; + int blueMask = (1 << pfd.cBlueBits) - 1; + int i; + + /* fill in an RGBA color palette */ + for (i = 0; i < n; ++i) { + logical->palPalEntry[i].peRed = + (((i >> pfd.cRedShift) & redMask) * 255) / redMask; + logical->palPalEntry[i].peGreen = + (((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask; + logical->palPalEntry[i].peBlue = + (((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask; + logical->palPalEntry[i].peFlags = 0; + } + } + + _colormap = CreatePalette(logical); + free(logical); + + SelectPalette(_hdc, _colormap, FALSE); + RealizePalette(_hdc); +} + + +//////////////////////////////////////////////////////////////////// +// Function: end_frame +// Access: +// Description: timer info, incs frame # +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow::end_frame(void) { + PStatTimer timer(_swap_pcollector); // added to mimic wglGraphicsWindow.cxx +// SwapBuffers(_hdc); + GraphicsWindow::end_frame(); +} + + +//----------------------------------------------------------------------------- +// Name: SetViewMatrix() +// Desc: Given an eye point, a lookat point, and an up vector, this +// function builds a 4x4 view matrix. +//----------------------------------------------------------------------------- +HRESULT SetViewMatrix( D3DMATRIX& mat, D3DVECTOR& vFrom, D3DVECTOR& vAt, + D3DVECTOR& vWorldUp ) +{ + // Get the z basis vector, which points straight ahead. This is the + // difference from the eyepoint to the lookat point. + D3DVECTOR vView = vAt - vFrom; + + FLOAT fLength = Magnitude( vView ); + if( fLength < 1e-6f ) + return E_INVALIDARG; + + // Normalize the z basis vector + vView /= fLength; + + // Get the dot product, and calculate the projection of the z basis + // vector onto the up vector. The projection is the y basis vector. + FLOAT fDotProduct = DotProduct( vWorldUp, vView ); + + D3DVECTOR vUp = vWorldUp - fDotProduct * vView; + + // If this vector has near-zero length because the input specified a + // bogus up vector, let's try a default up vector + if( 1e-6f > ( fLength = Magnitude( vUp ) ) ) + { + vUp = D3DVECTOR( 0.0f, 1.0f, 0.0f ) - vView.y * vView; + + // If we still have near-zero length, resort to a different axis. + if( 1e-6f > ( fLength = Magnitude( vUp ) ) ) + { + vUp = D3DVECTOR( 0.0f, 0.0f, 1.0f ) - vView.z * vView; + + if( 1e-6f > ( fLength = Magnitude( vUp ) ) ) + return E_INVALIDARG; + } + } + + // Normalize the y basis vector + vUp /= fLength; + + // The x basis vector is found simply with the cross product of the y + // and z basis vectors + D3DVECTOR vRight = CrossProduct( vUp, vView ); + + // Start building the matrix. The first three rows contains the basis + // vectors used to rotate the view to point at the lookat point + mat._11 = vRight.x; mat._12 = vUp.x; mat._13 = vView.x; mat._14 = 0.0f; + mat._21 = vRight.y; mat._22 = vUp.y; mat._23 = vView.y; mat._24 = 0.0f; + mat._31 = vRight.z; mat._32 = vUp.z; mat._33 = vView.z; mat._34 = 0.0f; + + // Do the translation values (rotations are still about the eyepoint) + mat._41 = - DotProduct( vFrom, vRight ); + mat._42 = - DotProduct( vFrom, vUp ); + mat._43 = - DotProduct( vFrom, vView ); + mat._44 = 1.0f; + + return S_OK; +} + + + + +//////////////////////////////////////////////////////////////////// +// Function: begin_frame +// Access: +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow::begin_frame(void) { + GraphicsWindow::begin_frame(); +} + + +void wdxGraphicsWindow::show_frame(void) +{ + DXGraphicsStateGuardian* dxgsg = DCAST(DXGraphicsStateGuardian, _gsg); + dxgsg->show_frame(); +} + +/** +//////////////////////////////////////////////////////////////////// +// Function: end_frame +// Access: +// Description: Swaps the front and back buffers. +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow::end_frame(void) { + + BUGBUG: do we want this here (as it is in GL)?? + { + PStatTimer timer(_swap_pcollector); + SwapBuffers(_hdc); + } + + GraphicsWindow::end_frame(); +} +**/ + + + +//////////////////////////////////////////////////////////////////// +// Function: handle_window_move +// Access: +// Description: we receive the new x and y position of the client +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow::handle_window_move(int x, int y) { + DXGraphicsStateGuardian* dxgsg = DCAST(DXGraphicsStateGuardian, _gsg); + dxgsg->adjust_view_rect(x,y); + _props._xorg = x; + _props._yorg = y; +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_mouse_motion +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow::handle_mouse_motion(int x, int y) { + _input_devices[0].set_pointer_in_window(x, y); +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_mouse_entry +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow::handle_mouse_entry(int state,HCURSOR hCrossIcon) { + if (state == MOUSE_EXITED) { + _input_devices[0].set_pointer_out_of_window(); + }else { + SetCursor(hCrossIcon); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_keypress +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow:: +handle_keypress(ButtonHandle key, int x, int y) { + _input_devices[0].set_pointer_in_window(x, y); + if (key != ButtonHandle::none()) { + _input_devices[0].button_down(key); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_keyrelease +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow:: +handle_keyrelease(ButtonHandle key, int, int) { + if (key != ButtonHandle::none()) { + _input_devices[0].button_up(key); + } +} + +#if 0 +//////////////////////////////////////////////////////////////////// +// Function: handle_changes +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow::handle_changes(void) { + // As an optimization, check to see if change is anything other than a + // redisplay + if (_change_mask & WDXWIN_CONFIGURE) { + RECT changes; + POINT point; + GetClientRect(_mwindow, &changes); + point.x = 0; + point.y = 0; + ClientToScreen(_mwindow, &point); + changes.left = point.x; + changes.top = point.y; + // Don't do this in full-screen mode + AdjustWindowRect(&changes, WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | + WS_CLIPCHILDREN, FALSE); + SetWindowPos(_mwindow, HWND_TOP, changes.left, changes.top, + changes.right - changes.left, changes.bottom - changes.top, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | + SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER); + } + _change_mask = 0; +} +#endif + +//////////////////////////////////////////////////////////////////// +// Function: process_events +// Access:// Description: +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow::process_events(void) { +#if 0 + MSG msg; + + if (!GetMessage(&msg, NULL, 0, 0)) + exit(0); + do { + // Translate virtual key messages + TranslateMessage(&msg); + // Call window_proc + DispatchMessage(&msg); + } while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { +#endif +} + +/* +//////////////////////////////////////////////////////////////////// +// Function: idle_wait +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow::idle_wait(void) { + MSG msg; + if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) + process_events(); + + call_idle_callback(); +} +*/ + +//////////////////////////////////////////////////////////////////// +// Function: wdxGraphicsWindow::supports_update +// Access: Public, Virtual +// Description: Returns true if this particular kind of +// GraphicsWindow supports use of the update() function +// to update the graphics one frame at a time, so that +// the window does not need to be the program's main +// loop. Returns false if the only way to update the +// window is to call main_loop(). +//////////////////////////////////////////////////////////////////// +bool wdxGraphicsWindow:: +supports_update() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: update +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow::update(void) { + _show_code_pcollector.stop(); + PStatClient::main_tick(); + + // Always ask for a redisplay for now + + MSG msg; +#if 0 + if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) + process_events(); + else call_draw_callback(true); +#else + + while(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { + if (!GetMessage(&msg, NULL, 0, 0)) { + // WM_QUIT received + AtExitFn(); + exit(0); + } + + // Translate virtual key messages + TranslateMessage(&msg); + // Call window_proc + DispatchMessage(&msg); + } + + call_draw_callback(true); +#endif + + call_idle_callback(); +/* + if (_idle_callback) + idle_wait(); + else if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) + process_events(); +*/ + _show_code_pcollector.start(); +} + +//////////////////////////////////////////////////////////////////// +// Function: enable_mouse_input +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow::enable_mouse_input(bool val) { + _mouse_input_enabled = val; +} + +//////////////////////////////////////////////////////////////////// +// Function: enable_mouse_motion +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow::enable_mouse_motion(bool val) { + _mouse_motion_enabled = val; +} + +//////////////////////////////////////////////////////////////////// +// Function: enable_mouse_passive_motion +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow::enable_mouse_passive_motion(bool val) { + _mouse_passive_motion_enabled = val; +} + + +//////////////////////////////////////////////////////////////////// +// Function: wdxGraphicsWindow::get_gsg_type +// Access: Public, Virtual +// Description: Returns the TypeHandle of the kind of GSG preferred +// by this kind of window. +//////////////////////////////////////////////////////////////////// +TypeHandle wdxGraphicsWindow:: +get_gsg_type() const { + return DXGraphicsStateGuardian::get_class_type(); +} + +GraphicsWindow *wdxGraphicsWindow:: +make_wdxGraphicsWindow(const FactoryParams ¶ms) { + GraphicsWindow::WindowPipe *pipe_param; + if (!get_param_into(pipe_param, params)) { + wdxdisplay_cat.error() + << "No pipe specified for window creation!" << endl; + return NULL; + } + + GraphicsPipe *pipe = pipe_param->get_pipe(); + + GraphicsWindow::WindowProps *props_param; + if (!get_param_into(props_param, params)) { + return new wdxGraphicsWindow(pipe); + } else { + return new wdxGraphicsWindow(pipe, props_param->get_properties()); + } +} + +TypeHandle wdxGraphicsWindow::get_class_type(void) { + return _type_handle; +} + +void wdxGraphicsWindow::init_type(void) { + GraphicsWindow::init_type(); + register_type(_type_handle, "wdxGraphicsWindow", + GraphicsWindow::get_class_type()); +} + +TypeHandle wdxGraphicsWindow::get_type(void) const { + return get_class_type(); +} + + diff --git a/panda/src/wdxdisplay/wdxGraphicsWindow.h b/panda/src/wdxdisplay/wdxGraphicsWindow.h new file mode 100644 index 0000000000..4a786269fc --- /dev/null +++ b/panda/src/wdxdisplay/wdxGraphicsWindow.h @@ -0,0 +1,122 @@ +// Filename: wdxGraphicsWindow.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef WDXGRAPHICSWINDOW_H +#define WDXGRAPHICSWINDOW_H +//#define WBD_GL_MODE 1 // if setting this, do it in wdxGraphicsStateGuardian.h too +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#define WINDOWS_LEAN_AND_MEAN +#include +#undef WINDOWS_LEAN_AND_MEAN +#include + + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +class wdxGraphicsPipe; + +const int WDXWIN_CONFIGURE = 4; +const int WDXWIN_EVENT = 8; + +//////////////////////////////////////////////////////////////////// +// Class : wdxGraphicsWindow +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDADX wdxGraphicsWindow : public GraphicsWindow { + +friend class DXGraphicsStateGuardian; + +public: + wdxGraphicsWindow(GraphicsPipe* pipe); + wdxGraphicsWindow(GraphicsPipe* pipe, + const GraphicsWindow::Properties& props); + virtual ~wdxGraphicsWindow(void); + + virtual bool supports_update() const; + virtual void update(void); + virtual void end_frame( void ); + + virtual TypeHandle get_gsg_type() const; + static GraphicsWindow* make_wdxGraphicsWindow(const FactoryParams ¶ms); + + static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, + LPARAM lparam); + LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + + INLINE bool mouse_entry_enabled(void) { return _mouse_entry_enabled; } + INLINE bool mouse_motion_enabled(void) { return _mouse_motion_enabled; } + INLINE bool mouse_passive_motion_enabled(void) { + return _mouse_passive_motion_enabled; + } + void handle_window_move( int x, int y ); + void handle_mouse_motion( int x, int y ); + void handle_mouse_entry( int state, HCURSOR hMouseCursor ); + void handle_keypress( ButtonHandle key, int x, int y ); + void handle_keyrelease( ButtonHandle key, int x, int y ); + void dx_setup(); + virtual void begin_frame( void ); + void show_frame(); + DXGraphicsStateGuardian *get_dxgsg(void); + +protected: +#if WBD_GL_MODE + PIXELFORMATDESCRIPTOR* try_for_visual(/*wdxGraphicsPipe *pipe,*/ + int mask, int want_depth_bits = 1, int want_color_bits = 1); + void choose_visual(void); + static void get_config(PIXELFORMATDESCRIPTOR* visual, int attrib, int *value); +#else + ButtonHandle lookup_key(WPARAM wparam) const; + +#endif + virtual void config( void ); + void setup_colormap(void); + + void enable_mouse_input(bool val); + void enable_mouse_motion(bool val); + void enable_mouse_passive_motion(bool val); + void enable_mouse_entry(bool val); + + void process_events(void); + //void idle_wait(void); + + +public: + HWND _mwindow; + HWND _hParentWindow; + +private: + HDC _hdc; + HPALETTE _colormap; + + + bool _mouse_input_enabled; + bool _mouse_motion_enabled; + bool _mouse_passive_motion_enabled; + bool _mouse_entry_enabled; + int _entry_state; + bool _ignore_key_repeat; + bool _dx_ready; + +public: + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} +// virtual void make_current(void); +// virtual void unmake_current(void); + + +private: + static TypeHandle _type_handle; +}; + +#endif diff --git a/panda/src/wgldisplay/Sources.pp b/panda/src/wgldisplay/Sources.pp new file mode 100644 index 0000000000..d60a14c200 --- /dev/null +++ b/panda/src/wgldisplay/Sources.pp @@ -0,0 +1,28 @@ +#define DIRECTORY_IF_WGL yes + +#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET wgldisplay + #define LOCAL_LIBS \ + glgsg display putil + + #define SOURCES \ + config_wgldisplay.cxx config_wgldisplay.h wglGraphicsPipe.cxx \ + wglGraphicsPipe.h wglGraphicsWindow.cxx wglGraphicsWindow.h + + #define INSTALL_HEADERS \ + wglGraphicsPipe.h wglGraphicsWindow.h + +#end lib_target + +#begin test_bin_target + #define TARGET test_wgl + #define LOCAL_LIBS \ + putil graph display mathutil gobj sgraph wgldisplay glgsg + + #define SOURCES \ + test_wgl.cxx + +#end test_bin_target + diff --git a/panda/src/wgldisplay/config_wgldisplay.cxx b/panda/src/wgldisplay/config_wgldisplay.cxx new file mode 100644 index 0000000000..67103c2356 --- /dev/null +++ b/panda/src/wgldisplay/config_wgldisplay.cxx @@ -0,0 +1,24 @@ +// Filename: config_wgldisplay.cxx +// Created by: mike (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "config_wgldisplay.h" +#include "wglGraphicsPipe.h" +#include "wglGraphicsWindow.h" + +#include + +Configure(config_wgldisplay); +NotifyCategoryDef(wgldisplay, "display"); + +ConfigureFn(config_wgldisplay) { + wglGraphicsPipe::init_type(); + GraphicsPipe::_factory.register_factory( + wglGraphicsPipe::get_class_type(), + wglGraphicsPipe::make_wglGraphicsPipe); + wglGraphicsWindow::init_type(); + GraphicsWindow::_factory.register_factory( + wglGraphicsWindow::get_class_type(), + wglGraphicsWindow::make_wglGraphicsWindow); +} diff --git a/panda/src/wgldisplay/config_wgldisplay.h b/panda/src/wgldisplay/config_wgldisplay.h new file mode 100644 index 0000000000..38707e3b53 --- /dev/null +++ b/panda/src/wgldisplay/config_wgldisplay.h @@ -0,0 +1,15 @@ +// Filename: config_wgldisplay.h +// Created by: mike (07Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __CONFIG_WGLDISPLAY_H__ +#define __CONFIG_WGLDISPLAY_H__ + +#include +#include + +NotifyCategoryDecl(wgldisplay, EXPCL_PANDAGL, EXPTP_PANDAGL); + + +#endif /* __CONFIG_WGLDISPLAY_H__ */ diff --git a/panda/src/wgldisplay/test_wgl.cxx b/panda/src/wgldisplay/test_wgl.cxx new file mode 100644 index 0000000000..2f622340bf --- /dev/null +++ b/panda/src/wgldisplay/test_wgl.cxx @@ -0,0 +1,67 @@ +// Filename: test_wgl.cxx +// Created by: mike (02Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#include "wglGraphicsWindow.h" + +PT(GraphicsPipe) main_pipe; +PT(GraphicsWindow) main_win; +PT_NamedNode render; +NodeAttributes initial_state; + +void render_frame(GraphicsPipe *pipe) { + GraphicsPipe::wins_iterator wi; + for (wi = pipe->get_win_begin(); + wi != pipe->get_win_end(); + ++wi) { + (*wi)->get_gsg()->render_frame(initial_state); + } +} + +void display_func(void) { + render_frame(main_pipe); +} + +int main() { + + GraphicsPipe::resolve_modules(); + + cout << "Known pipe types:" << endl; + GraphicsPipe::PipeFactory::FactoryTypesIter pti; + for (pti = GraphicsPipe::_factory.get_types_begin(); + pti != GraphicsPipe::_factory.get_types_end(); ++pti) { + cout << " " << (*pti) << endl; + } + cout << "after Known pipe types" << endl; + + GraphicsPipe::PipeFactory& factory = GraphicsPipe::_factory; + GraphicsPipe::PipePrioritiesIter pribegin = + GraphicsPipe::get_priorities_begin(); + GraphicsPipe::PipePrioritiesIter priend = GraphicsPipe::get_priorities_end(); + + GraphicsPipe::PipeParams ps1; + if (pribegin == priend) + main_pipe = factory.instance(InteractiveGraphicsPipe::get_class_type(), + ps1.begin(), ps1.end()); + else + main_pipe = factory.instance(InteractiveGraphicsPipe::get_class_type(), + ps1.begin(), ps1.end(), pribegin, priend); + nassertr(main_pipe != (GraphicsPipe*)0L, 0); + cout << "Opened a '" << main_pipe->get_type().get_name() + << "' interactive graphics pipe." << endl; + + main_win = new wglGraphicsWindow(main_pipe); +#if 0 + main_win->register_draw_function(display_func); + main_win->main_loop(); +#endif + + return 0; +} diff --git a/panda/src/wgldisplay/wglGraphicsPipe.cxx b/panda/src/wgldisplay/wglGraphicsPipe.cxx new file mode 100644 index 0000000000..eb9484f1c7 --- /dev/null +++ b/panda/src/wgldisplay/wglGraphicsPipe.cxx @@ -0,0 +1,374 @@ +// Filename: wglGraphicsPipe.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "wglGraphicsPipe.h" +#include "config_wgldisplay.h" +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle wglGraphicsPipe::_type_handle; + +wglGraphicsPipe *global_pipe; + +#define MOUSE_ENTERED 0 +#define MOUSE_EXITED 1 + +wglGraphicsPipe::wglGraphicsPipe(const PipeSpecifier& spec) + : InteractiveGraphicsPipe(spec) +{ + // Register a standard window class + WNDCLASS stdwc; + HINSTANCE hinstance = GetModuleHandle(NULL); + + // Clear before filling in window structure! + memset(&stdwc, 0, sizeof(WNDCLASS)); + stdwc.style = CS_OWNDC; + stdwc.lpfnWndProc = (WNDPROC)static_window_proc; + stdwc.hInstance = hinstance; + stdwc.hIcon = LoadIcon(hinstance, IDI_WINLOGO); + stdwc.hCursor = LoadCursor(hinstance, IDC_CROSS); + stdwc.hbrBackground = NULL; + stdwc.lpszMenuName = NULL; + stdwc.lpszClassName = "wglStandard"; + + if (!RegisterClass(&stdwc)) { + wgldisplay_cat.fatal() + << "wglGraphicsPipe::construct(): could not register standard window " + << "class" << endl; + exit(0); + } + + // Register a fullscreen window class + WNDCLASS fswc; + + // Clear before filling in window structure! + memset(&fswc, 0, sizeof(WNDCLASS)); + fswc.style = CS_HREDRAW | CS_VREDRAW; + fswc.lpfnWndProc = (WNDPROC)static_window_proc; + fswc.hInstance = hinstance; + fswc.hIcon = NULL; + fswc.hCursor = LoadCursor(hinstance, IDC_CROSS); + fswc.hbrBackground = NULL; + fswc.lpszMenuName = NULL; + fswc.lpszClassName = "wglFullscreen"; + + if (!RegisterClass(&fswc)) { + wgldisplay_cat.fatal() + << "wglGraphicsPipe::construct(): could not register fullscreen window " + << "class" << endl; + exit(0); + } + + _width = GetSystemMetrics(SM_CXSCREEN); + _height = GetSystemMetrics(SM_CYSCREEN); + _shift = false; + global_pipe = this; +} + +//////////////////////////////////////////////////////////////////// +// Function: wglGraphicsPipe::get_window_type +// Access: Public, Virtual +// Description: Returns the TypeHandle of the kind of window +// preferred by this kind of pipe. +//////////////////////////////////////////////////////////////////// +TypeHandle wglGraphicsPipe:: +get_window_type() const { + return wglGraphicsWindow::get_class_type(); +} + +GraphicsPipe *wglGraphicsPipe:: +make_wglGraphicsPipe(const FactoryParams ¶ms) { + GraphicsPipe::PipeSpec *pipe_param; + if (!get_param_into(pipe_param, params)) { + return new wglGraphicsPipe(PipeSpecifier()); + } else { + return new wglGraphicsPipe(pipe_param->get_specifier()); + } +} + + +TypeHandle wglGraphicsPipe::get_class_type(void) { + return _type_handle; +} + +void wglGraphicsPipe::init_type(void) { + InteractiveGraphicsPipe::init_type(); + register_type(_type_handle, "wglGraphicsPipe", + InteractiveGraphicsPipe::get_class_type()); +} + +TypeHandle wglGraphicsPipe::get_type(void) const { + return get_class_type(); +} + +wglGraphicsPipe::wglGraphicsPipe(void) { + wgldisplay_cat.error() + << "wglGraphicsPipes should not be created with the default constructor" + << endl; +} + +wglGraphicsPipe::wglGraphicsPipe(const wglGraphicsPipe&) { + wgldisplay_cat.error() + << "wglGraphicsPipes should not be copied" << endl; +} + +wglGraphicsPipe& wglGraphicsPipe::operator=(const wglGraphicsPipe&) { + wgldisplay_cat.error() + << "wglGraphicsPipes should not be assigned" << endl; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: find_window +// Access: +// Description: Find the window that has the xwindow "win" in the +// window list for the pipe (if it exists) +//////////////////////////////////////////////////////////////////// +wglGraphicsWindow *wglGraphicsPipe:: +find_window(HWND win) { + int num_windows = get_num_windows(); + for (int w = 0; w < num_windows; w++) { + wglGraphicsWindow *window = DCAST(wglGraphicsWindow, get_window(w)); + if (window->_mwindow == win) + return window; + } + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: static_window_proc +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +LONG WINAPI wglGraphicsPipe:: +static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + return global_pipe->window_proc(hwnd, msg, wparam, lparam); +} + +//////////////////////////////////////////////////////////////////// +// Function: window_proc +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +LONG wglGraphicsPipe:: +window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + PAINTSTRUCT ps; + wglGraphicsWindow *window; + int button = -1; + int x, y, width, height; + static HCURSOR hMouseCrossIcon = NULL; + + switch (msg) { + case WM_CREATE: + hMouseCrossIcon = LoadCursor(NULL, IDC_CROSS); + SetCursor(hMouseCrossIcon); + return 0; + case WM_CLOSE: + PostQuitMessage(0); + return 0; + case WM_PAINT: + window = find_window(hwnd); + if (window) { + BeginPaint(hwnd, &ps); + EndPaint(hwnd, &ps); + } + return 0; + case WM_SYSCHAR: + case WM_CHAR: + return 0; + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + window = find_window(hwnd); + if (window) { + POINT point; + window->make_current(); + GetCursorPos(&point); + ScreenToClient(hwnd, &point); + window->handle_keypress(lookup_key(wparam), point.x, point.y); + } + return 0; + case WM_SYSKEYUP: + case WM_KEYUP: + window = find_window(hwnd); + if (window) { + POINT point; + window->make_current(); + GetCursorPos(&point); + ScreenToClient(hwnd, &point); + window->handle_keyrelease(lookup_key(wparam), point.x, point.y); + } + return 0; + case WM_LBUTTONDOWN: + button = 0; + case WM_MBUTTONDOWN: + if (button < 0) + button = 1; + case WM_RBUTTONDOWN: + if (button < 0) + button = 2; + SetCapture(hwnd); + // Win32 doesn't return the same numbers as X does when the mouse + // goes beyond the upper or left side of the window + x = LOWORD(lparam); + y = HIWORD(lparam); + if (x & 1 << 15) x -= (1 << 16); + if (y & 1 << 15) y -= (1 << 16); + window = find_window(hwnd); + if (window) { + window->make_current(); + window->handle_keypress(MouseButton::button(button), x, y); + } + return 0; + case WM_LBUTTONUP: + button = 0; + case WM_MBUTTONUP: + if (button < 0) + button = 1; + case WM_RBUTTONUP: + if (button < 0) + button = 2; + ReleaseCapture(); + window = find_window(hwnd); + if (window) { + x = LOWORD(lparam); + y = HIWORD(lparam); + if (x & 1 << 15) x -= (1 << 16); + if (y & 1 << 15) y -= (1 << 16); + window->make_current(); + window->handle_keyrelease(MouseButton::button(button), x, y); + } + return 0; + case WM_MOUSEMOVE: + window = find_window(hwnd); + if (window) { + x = LOWORD(lparam); + y = HIWORD(lparam); + if (x & 1 << 15) x -= (1 << 16); + if (y & 1 << 15) y -= (1 << 16); + if (window->mouse_motion_enabled() + && wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) { + window->make_current(); + window->handle_mouse_motion(x, y); + } else if (window->mouse_passive_motion_enabled() && + ((wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) == 0)) { + window->make_current(); + window->handle_mouse_motion(x, y); + } + } + return 0; + case WM_SIZE: + window = find_window(hwnd); + if (window) { + width = LOWORD(lparam); + height = HIWORD(lparam); + window->handle_reshape(width, height); + } + return 0; + +#if 0 +//this is unnecessary, just handle mouse entry + case WM_SETCURSOR: + // We need to set the cursor every time the mouse moves on Win32! + if (LOWORD(lparam) != HTCLIENT) + return DefWindowProc(hwnd, msg, wparam, lparam); + window = find_window(hwnd); + if (window) { + SetCursor(LoadCursor(NULL, IDC_CROSS)); + } + return 1; +#endif + case WM_SETFOCUS: + SetCursor(hMouseCrossIcon); + window = find_window(hwnd); + if (window) { + if (window->mouse_entry_enabled()) { + window->make_current(); + window->handle_mouse_entry(MOUSE_ENTERED); + } + } + return 0; + case WM_KILLFOCUS: + window = find_window(hwnd); + if (window) { + if (window->mouse_entry_enabled()) { + window->make_current(); + window->handle_mouse_entry(MOUSE_EXITED); + } + } + return 0; + default: + return DefWindowProc(hwnd, msg, wparam, lparam); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: lookup_key +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +ButtonHandle +wglGraphicsPipe::lookup_key(WPARAM wparam) const { + switch (wparam) { + case VK_BACK: return KeyboardButton::backspace(); + case VK_TAB: return KeyboardButton::tab(); + case VK_ESCAPE: return KeyboardButton::escape(); + case VK_SPACE: return KeyboardButton::space(); + case VK_UP: return KeyboardButton::up(); + case VK_DOWN: return KeyboardButton::down(); + case VK_LEFT: return KeyboardButton::left(); + case VK_RIGHT: return KeyboardButton::right(); + case VK_PRIOR: return KeyboardButton::page_up(); + case VK_NEXT: return KeyboardButton::page_down(); + case VK_HOME: return KeyboardButton::home(); + case VK_END: return KeyboardButton::end(); + case VK_F1: return KeyboardButton::f1(); + case VK_F2: return KeyboardButton::f2(); + case VK_F3: return KeyboardButton::f3(); + case VK_F4: return KeyboardButton::f4(); + case VK_F5: return KeyboardButton::f5(); + case VK_F6: return KeyboardButton::f6(); + case VK_F7: return KeyboardButton::f7(); + case VK_F8: return KeyboardButton::f8(); + case VK_F9: return KeyboardButton::f9(); + case VK_F10: return KeyboardButton::f10(); + case VK_F11: return KeyboardButton::f11(); + case VK_F12: return KeyboardButton::f12(); + case VK_INSERT: return KeyboardButton::insert(); + case VK_DELETE: return KeyboardButton::del(); + default: + int key = MapVirtualKey(wparam, 2); + if (isascii(key) && key != 0) { + if (GetKeyState(VK_SHIFT) >= 0) + key = tolower(key); + else { + switch (key) { + case '1': key = '!'; break; + case '2': key = '@'; break; + case '3': key = '#'; break; + case '4': key = '$'; break; + case '5': key = '%'; break; + case '6': key = '^'; break; + case '7': key = '&'; break; + case '8': key = '*'; break; + case '9': key = '('; break; + case '0': key = ')'; break; + case '=': key = '+'; break; + case '/': key = '?'; break; + case ';': key = ':'; break; + case '`': key = '~'; break; + } + } + return KeyboardButton::ascii_key((uchar)key); + } + break; + } + return ButtonHandle::none(); +} diff --git a/panda/src/wgldisplay/wglGraphicsPipe.h b/panda/src/wgldisplay/wglGraphicsPipe.h new file mode 100644 index 0000000000..be9f51f180 --- /dev/null +++ b/panda/src/wgldisplay/wglGraphicsPipe.h @@ -0,0 +1,67 @@ +// Filename: wglGraphicsPipe.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef WGLGRAPHICSPIPE_H +#define WGLGRAPHICSPIPE_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#include +#include "wglGraphicsWindow.h" +#define WINDOWS_LEAN_AND_MEAN +#include +#undef WINDOWS_LEAN_AND_MEAN + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +class Xclass; + +//////////////////////////////////////////////////////////////////// +// Class : wglGraphicsPipe +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAGL wglGraphicsPipe : public InteractiveGraphicsPipe { +public: + wglGraphicsPipe(const PipeSpecifier&); + + wglGraphicsWindow* find_window(HWND win); + ButtonHandle lookup_key(WPARAM wparam) const; + + virtual TypeHandle get_window_type() const; + +public: + + static GraphicsPipe* make_wglGraphicsPipe(const FactoryParams ¶ms); + + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + + static TypeHandle _type_handle; + + int _width; + int _height; + bool _shift; + +protected: + + wglGraphicsPipe(void); + wglGraphicsPipe(const wglGraphicsPipe&); + wglGraphicsPipe& operator=(const wglGraphicsPipe&); + + static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, + LPARAM lparam); + LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); +}; + +#endif diff --git a/panda/src/wgldisplay/wglGraphicsWindow.cxx b/panda/src/wgldisplay/wglGraphicsWindow.cxx new file mode 100644 index 0000000000..4bbb35d25a --- /dev/null +++ b/panda/src/wgldisplay/wglGraphicsWindow.cxx @@ -0,0 +1,917 @@ +// Filename: wglGraphicsWindow.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include "wglGraphicsWindow.h" +#include "wglGraphicsPipe.h" +#include "config_wgldisplay.h" + +#include +#include +#include +#include + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Static variables +//////////////////////////////////////////////////////////////////// +TypeHandle wglGraphicsWindow::_type_handle; + +#define MOUSE_ENTERED 0 +#define MOUSE_EXITED 1 + +//////////////////////////////////////////////////////////////////// +// Function: Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +wglGraphicsWindow:: +wglGraphicsWindow(GraphicsPipe* pipe) : GraphicsWindow(pipe) { + config(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Constructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +wglGraphicsWindow:: +wglGraphicsWindow(GraphicsPipe* pipe, const + GraphicsWindow::Properties& props) : GraphicsWindow(pipe, props) { + config(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Destructor +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +wglGraphicsWindow::~wglGraphicsWindow(void) { +// DestroyWindow(_mwindow); + free(_visual); +} + +//////////////////////////////////////////////////////////////////// +// Function: try_for_visual +// Description: This is a static function that attempts to get the +// requested visual, if it is available. It's just a +// wrapper around glXChooseVisual(). It returns the +// visual information if possible, or NULL if it is not. +//////////////////////////////////////////////////////////////////// +PIXELFORMATDESCRIPTOR* wglGraphicsWindow:: +try_for_visual(wglGraphicsPipe *pipe, int mask, + int want_depth_bits, int want_color_bits) { + static const int max_attrib_list = 32; + int attrib_list[max_attrib_list]; + int n=0; + + wgldisplay_cat.debug() + << "Trying for visual with: RGB(" << want_color_bits << ")"; + + int want_color_component_bits; + if (mask & W_ALPHA) { + want_color_component_bits = max(want_color_bits / 4, 1); + } else { + want_color_component_bits = max(want_color_bits / 3, 1); + } + + attrib_list[n++] = GLX_RGBA; + attrib_list[n++] = GLX_RED_SIZE; + attrib_list[n++] = want_color_component_bits; + attrib_list[n++] = GLX_GREEN_SIZE; + attrib_list[n++] = want_color_component_bits; + attrib_list[n++] = GLX_BLUE_SIZE; + attrib_list[n++] = want_color_component_bits; + + if (mask & W_ALPHA) { + wgldisplay_cat.debug(false) << " ALPHA"; + attrib_list[n++] = GLX_ALPHA_SIZE; + attrib_list[n++] = want_color_component_bits; + } + if (mask & W_DOUBLE) { + wgldisplay_cat.debug(false) << " DOUBLEBUFFER"; + attrib_list[n++] = GLX_DOUBLEBUFFER; + } + if (mask & W_STEREO) { + wgldisplay_cat.debug(false) << " STEREO"; + attrib_list[n++] = GLX_STEREO; + } + if (mask & W_DEPTH) { + wgldisplay_cat.debug(false) << " DEPTH(" << want_depth_bits << ")"; + attrib_list[n++] = GLX_DEPTH_SIZE; + attrib_list[n++] = want_depth_bits; + } + if (mask & W_STENCIL) { + wgldisplay_cat.debug(false) << " STENCIL"; + attrib_list[n++] = GLX_STENCIL_SIZE; + attrib_list[n++] = 1; + } + if (mask & W_ACCUM) { + wgldisplay_cat.debug(false) << " ACCUM"; + attrib_list[n++] = GLX_ACCUM_RED_SIZE; + attrib_list[n++] = want_color_component_bits; + attrib_list[n++] = GLX_ACCUM_GREEN_SIZE; + attrib_list[n++] = want_color_component_bits; + attrib_list[n++] = GLX_ACCUM_BLUE_SIZE; + attrib_list[n++] = want_color_component_bits; + if (mask & W_ALPHA) { + attrib_list[n++] = GLX_ACCUM_ALPHA_SIZE; + attrib_list[n++] = want_color_component_bits; + } + } + + // Terminate the list + nassertr(n < max_attrib_list, NULL); + attrib_list[n] = 0L; + + PIXELFORMATDESCRIPTOR pfd; + PIXELFORMATDESCRIPTOR *match = NULL; + bool stereo = false; + + memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); + pfd.nSize = (sizeof(PIXELFORMATDESCRIPTOR)); + pfd.nVersion = 1; + + // Defaults + pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW; + pfd.iPixelType = PFD_TYPE_COLORINDEX; + pfd.cColorBits = 32; + pfd.cDepthBits = 0; + + int *p = attrib_list; + while (*p) { + switch (*p) { + case GLX_USE_GL: + pfd.dwFlags |= PFD_SUPPORT_OPENGL; + break; + case GLX_LEVEL: + pfd.bReserved = *(++p); + break; + case GLX_RGBA: + pfd.iPixelType = PFD_TYPE_RGBA; + break; + case GLX_DOUBLEBUFFER: + pfd.dwFlags |= PFD_DOUBLEBUFFER; + break; + case GLX_STEREO: + stereo = true; + pfd.dwFlags |= PFD_STEREO; + break; + case GLX_AUX_BUFFERS: + pfd.cAuxBuffers = *(++p); + break; + case GLX_RED_SIZE: + pfd.cRedBits = 8; // Try to get the maximum + ++p; + break; + case GLX_GREEN_SIZE: + pfd.cGreenBits = 8; // Try to get the maximum + ++p; + break; + case GLX_BLUE_SIZE: + pfd.cBlueBits = 8; // Try to get the maximum + ++p; + break; + case GLX_ALPHA_SIZE: + pfd.cAlphaBits = 8; // Try to get the maximum + ++p; + break; + case GLX_DEPTH_SIZE: + pfd.cDepthBits = 32; // Try to get the maximum + ++p; + break; + case GLX_STENCIL_SIZE: + pfd.cStencilBits = *(++p); + break; + case GLX_ACCUM_RED_SIZE: + case GLX_ACCUM_GREEN_SIZE: + case GLX_ACCUM_BLUE_SIZE: + case GLX_ACCUM_ALPHA_SIZE: + // Only cAccumBits is used for requesting accum buffer + pfd.cAccumBits = 1; + ++p; + break; + } + ++p; + } + + int pf = ChoosePixelFormat(_hdc, &pfd); + if (pf > 0) { + match = (PIXELFORMATDESCRIPTOR *)malloc(sizeof(PIXELFORMATDESCRIPTOR)); + DescribePixelFormat(_hdc, pf, sizeof(PIXELFORMATDESCRIPTOR), match); + + // ChoosePixelFormat is dumb about stereo + if (stereo) { + if (!(match->dwFlags & PFD_STEREO)) { + wgldisplay_cat.info() + << "wglGraphicsWindow::try_for_visual() - request for " + << "stereo failed" << endl; + } + } + } + + return match; +} + +//////////////////////////////////////////////////////////////////// +// Function: get_config +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow:: +get_config(PIXELFORMATDESCRIPTOR *visual, int attrib, int *value) { + if (visual == NULL) + return; + + switch (attrib) { + case GLX_USE_GL: + if (visual->dwFlags & (PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW)) { + if (visual->iPixelType == PFD_TYPE_COLORINDEX && + visual->cColorBits >= 24) { + *value = 0; + } else { + *value = 1; + } + } else { + *value = 0; + } + break; + case GLX_BUFFER_SIZE: + if (visual->iPixelType == PFD_TYPE_RGBA) + *value = visual->cColorBits; + else + *value = 8; + break; + case GLX_LEVEL: + *value = visual->bReserved; + break; + case GLX_RGBA: + *value = visual->iPixelType == PFD_TYPE_RGBA; + break; + case GLX_DOUBLEBUFFER: + *value = visual->dwFlags & PFD_DOUBLEBUFFER; + break; + case GLX_STEREO: + *value = visual->dwFlags & PFD_STEREO; + break; + case GLX_AUX_BUFFERS: + *value = visual->cAuxBuffers; + break; + case GLX_RED_SIZE: + *value = visual->cRedBits; + break; + case GLX_GREEN_SIZE: + *value = visual->cGreenBits; + break; + case GLX_BLUE_SIZE: + *value = visual->cBlueBits; + break; + case GLX_ALPHA_SIZE: + *value = visual->cAlphaBits; + break; + case GLX_DEPTH_SIZE: + *value = visual->cDepthBits; + break; + case GLX_STENCIL_SIZE: + *value = visual->cStencilBits; + break; + case GLX_ACCUM_RED_SIZE: + *value = visual->cAccumRedBits; + break; + case GLX_ACCUM_GREEN_SIZE: + *value = visual->cAccumGreenBits; + break; + case GLX_ACCUM_BLUE_SIZE: + *value = visual->cAccumBlueBits; + break; + case GLX_ACCUM_ALPHA_SIZE: + *value = visual->cAccumAlphaBits; + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: choose visual +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow::choose_visual(void) { + wglGraphicsPipe* pipe = DCAST(wglGraphicsPipe, _pipe); + + int mask = _props._mask; + int want_depth_bits = _props._want_depth_bits; + int want_color_bits = _props._want_color_bits; + + if (mask & W_MULTISAMPLE) { + wgldisplay_cat.info() + << "wglGraphicsWindow::config() - multisample not supported" + << endl; + mask &= ~W_MULTISAMPLE; + } + + _visual = try_for_visual(pipe, mask, want_depth_bits, want_color_bits); + + // This is the severity level at which we'll report the details of + // the visual we actually do find. Normally, it's debug-level + // information: we don't care about that much detail. + NotifySeverity show_visual_severity = NS_debug; + + if (_visual == NULL) { + wgldisplay_cat.info() + << "wglGraphicsWindow::choose_visual() - visual with requested\n" + << " capabilities not found; trying for lesser visual.\n"; + + // If we're unable to get the visual we asked for, however, we + // probably *do* care to know the details about what we actually + // got, even if we don't have debug mode set. So we'll report the + // visual at a higher level. + show_visual_severity = NS_info; + + bool special_size_request = + (want_depth_bits != 1 || want_color_bits != 1); + + // We try to be smart about choosing a close match for the visual. + // First, we'll eliminate some of the more esoteric options one at + // a time, then two at a time, and finally we'll try just the bare + // minimum. + + if (special_size_request) { + // Actually, first we'll eliminate all of the minimum sizes, to + // try to open a window with all of the requested options, but + // maybe not as many bits in some options as we'd like. + _visual = try_for_visual(pipe, mask); + } + + if (_visual == NULL) { + // Ok, not good enough. Now try to eliminate options, but keep + // as many bits as we asked for. + + // This array keeps the bitmasks of options that we pull out of + // the requested mask, in order. + + static const int strip_properties[] = { + // One esoteric option removed. + W_MULTISAMPLE, + W_STENCIL, + W_ACCUM, + W_ALPHA, + W_STEREO, + + // Two esoteric options removed. + W_STENCIL | W_MULTISAMPLE, + W_ACCUM | W_MULTISAMPLE, + W_ALPHA | W_MULTISAMPLE, + W_STEREO | W_MULTISAMPLE, + W_STENCIL | W_ACCUM, + W_ALPHA | W_STEREO, + W_STENCIL | W_ACCUM | W_MULTISAMPLE, + W_ALPHA | W_STEREO | W_MULTISAMPLE, + + // All esoteric options removed. + W_STENCIL | W_ACCUM | W_ALPHA | W_STEREO | W_MULTISAMPLE, + + // All esoteric options, plus some we'd really really prefer, + // removed. + W_STENCIL | W_ACCUM | W_ALPHA | W_STEREO | W_MULTISAMPLE | W_DOUBLE, + + // A zero marks the end of the array. + 0 + }; + + set tried_masks; + tried_masks.insert(mask); + + int i; + for (i = 0; _visual == NULL && strip_properties[i] != 0; i++) { + int new_mask = mask & ~strip_properties[i]; + if (tried_masks.insert(new_mask).second) { + _visual = try_for_visual(pipe, new_mask, want_depth_bits, + want_color_bits); + } + } + + if (special_size_request) { + tried_masks.clear(); + tried_masks.insert(mask); + + if (_visual == NULL) { + // Try once more, this time eliminating all of the size + // requests. + for (i = 0; _visual == NULL && strip_properties[i] != 0; i++) { + int new_mask = mask & ~strip_properties[i]; + if (tried_masks.insert(new_mask).second) { + _visual = try_for_visual(pipe, new_mask); + } + } + } + } + + if (_visual == NULL) { + // Here's our last-ditch desparation attempt: give us any GLX + // visual at all! + _visual = try_for_visual(pipe, 0); + } + + if (_visual == NULL) { + wgldisplay_cat.fatal() + << "wglGraphicsWindow::choose_visual() - could not get any " + "visual." << endl; + exit(1); + } + } + } + + if (wgldisplay_cat.is_on(show_visual_severity)) { + int render_mode, double_buffer, stereo, red_size, green_size, blue_size, + alpha_size, ared_size, agreen_size, ablue_size, aalpha_size, + depth_size, stencil_size; + + get_config(_visual, GLX_RGBA, &render_mode); + get_config(_visual, GLX_DOUBLEBUFFER, &double_buffer); + get_config(_visual, GLX_STEREO, &stereo); + get_config(_visual, GLX_RED_SIZE, &red_size); + get_config(_visual, GLX_GREEN_SIZE, &green_size); + get_config(_visual, GLX_BLUE_SIZE, &blue_size); + get_config(_visual, GLX_ALPHA_SIZE, &alpha_size); + get_config(_visual, GLX_ACCUM_RED_SIZE, &ared_size); + get_config(_visual, GLX_ACCUM_GREEN_SIZE, &agreen_size); + get_config(_visual, GLX_ACCUM_BLUE_SIZE, &ablue_size); + get_config(_visual, GLX_ACCUM_ALPHA_SIZE, &aalpha_size); + get_config(_visual, GLX_DEPTH_SIZE, &depth_size); + get_config(_visual, GLX_STENCIL_SIZE, &stencil_size); + + wgldisplay_cat.out(show_visual_severity) + << "GLX Visual Info (# bits of each):" << endl + << " RGBA: " << red_size << " " << green_size << " " << blue_size + << " " << alpha_size << endl + << " Accum RGBA: " << ared_size << " " << agreen_size << " " + << ablue_size << " " << aalpha_size << endl + << " Depth: " << depth_size << endl + << " Stencil: " << stencil_size << endl + << " DoubleBuffer? " << double_buffer << endl + << " Stereo? " << stereo << endl; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: adjust_coords +// Access: +// Description: Adjust the window rectangle because Win32 thinks +// that the x, y, width, and height are the *entire* +// window, including decorations, whereas we assume +// the size is the *client* area of the window. +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow:: +adjust_coords(int &xorg, int &yorg, int &xsize, int &ysize) { + RECT rect; + rect.left = xorg; rect.top = yorg; + rect.right = xorg + xsize; + rect.bottom = yorg + ysize; + + // Style determines whether there are borders or not + // False in third parameter indicates window has no menu bar + AdjustWindowRect(&rect, WS_CLIPSIBLINGS | WS_CLIPCHILDREN | + WS_OVERLAPPEDWINDOW, false); + + // Readjust if the x and y are offscreen + if (rect.left < 0) + xorg = 0; + else + xorg = rect.left; + + if (rect.top < 0) + yorg = 0; + else + yorg = rect.top; + + xsize = rect.right - rect.left; + ysize = rect.bottom - rect.top; +} + +//////////////////////////////////////////////////////////////////// +// Function: config +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow::config(void) { + GraphicsWindow::config(); + + wglGraphicsPipe* pipe = DCAST(wglGraphicsPipe, _pipe); + HINSTANCE hinstance = GetModuleHandle(NULL); + + if (_props._fullscreen) { + _props._xorg = 0; + _props._yorg = 0; + _props._xsize = GetSystemMetrics(SM_CXSCREEN); + _props._ysize = GetSystemMetrics(SM_CYSCREEN); + _mwindow = CreateWindow("wglFullscreen", _props._title.c_str(), + WS_POPUP | WS_MAXIMIZE, + _props._xorg, _props._yorg, _props._xsize, _props._ysize, + NULL, NULL, hinstance, 0); + } else { + + int xorg = _props._xorg; + int yorg = _props._yorg; + int xsize = _props._xsize; + int ysize = _props._ysize; + + int style = WS_POPUP | WS_MAXIMIZE; + if (_props._border == true) { + style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW; + adjust_coords(xorg, yorg, xsize, ysize); + } + + _mwindow = CreateWindow("wglStandard", _props._title.c_str(), + style, xorg, yorg, xsize, ysize, + NULL, NULL, hinstance, 0); + } + + if (!_mwindow) { + wgldisplay_cat.fatal() + << "wglGraphicsWindow::config() - failed to create Mwindow" << endl; + exit(0); + } + + _hdc = GetDC(_mwindow); + + // Configure the framebuffer according to parameters specified in _props + // Initializes _visual + choose_visual(); + + if (!SetPixelFormat(_hdc, ChoosePixelFormat(_hdc, _visual), _visual)) { + wgldisplay_cat.fatal() + << "wglGraphicsWindow::config() - SetPixelFormat failed during " + << "window create" << endl; + exit(0); + } + + // Initializes _colormap + setup_colormap(); + + _context = wglCreateContext(_hdc); + if (!_context) { + wgldisplay_cat.fatal() + << "wglGraphicsWindow::config() - failed to create Win32 rendering " + << "context" << endl; + exit(0); + } + + _change_mask = 0; + + _mouse_input_enabled = false; + _mouse_motion_enabled = false; + _mouse_passive_motion_enabled = false; + _mouse_entry_enabled = false; + _entry_state = -1; + + ShowWindow(_mwindow, SW_SHOWNORMAL); + + // Enable detection of mouse input + enable_mouse_input(true); + enable_mouse_motion(true); + enable_mouse_passive_motion(true); + + // Now indicate that we have our keyboard/mouse device ready. + GraphicsWindowInputDevice device = + GraphicsWindowInputDevice::pointer_and_keyboard("keyboard/mouse"); + _input_devices.push_back(device); + + // Create a GSG to manage the graphics + // First make the new context and window the current one so GL knows how + // to configure itself in the gsg + make_current(); + make_gsg(); +} + +//////////////////////////////////////////////////////////////////// +// Function: setup_colormap +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow::setup_colormap(void) { + wglGraphicsPipe* pipe = DCAST(wglGraphicsPipe, _pipe); + + PIXELFORMATDESCRIPTOR pfd; + LOGPALETTE *logical; + int n; + + /* grab the pixel format */ + memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); + DescribePixelFormat(_hdc, GetPixelFormat(_hdc), + sizeof(PIXELFORMATDESCRIPTOR), &pfd); + + if (!(pfd.dwFlags & PFD_NEED_PALETTE || + pfd.iPixelType == PFD_TYPE_COLORINDEX)) + return; + + n = 1 << pfd.cColorBits; + + /* allocate a bunch of memory for the logical palette (assume 256 + colors in a Win32 palette */ + logical = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) + + sizeof(PALETTEENTRY) * n); + memset(logical, 0, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * n); + + /* set the entries in the logical palette */ + logical->palVersion = 0x300; + logical->palNumEntries = n; + + /* start with a copy of the current system palette */ + GetSystemPaletteEntries(_hdc, 0, 256, &logical->palPalEntry[0]); + + if (pfd.iPixelType == PFD_TYPE_RGBA) { + int redMask = (1 << pfd.cRedBits) - 1; + int greenMask = (1 << pfd.cGreenBits) - 1; + int blueMask = (1 << pfd.cBlueBits) - 1; + int i; + + /* fill in an RGBA color palette */ + for (i = 0; i < n; ++i) { + logical->palPalEntry[i].peRed = + (((i >> pfd.cRedShift) & redMask) * 255) / redMask; + logical->palPalEntry[i].peGreen = + (((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask; + logical->palPalEntry[i].peBlue = + (((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask; + logical->palPalEntry[i].peFlags = 0; + } + } + + _colormap = CreatePalette(logical); + free(logical); + + SelectPalette(_hdc, _colormap, FALSE); + RealizePalette(_hdc); +} + +//////////////////////////////////////////////////////////////////// +// Function: end_frame +// Access: +// Description: Swaps the front and back buffers. +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow::end_frame(void) { + { + PStatTimer timer(_swap_pcollector); + SwapBuffers(_hdc); + } + GraphicsWindow::end_frame(); +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_reshape +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow::handle_reshape(int w, int h) { + if (_props._xsize != w || _props._ysize != h) { + _props._xsize = w; + _props._ysize = h; + make_current(); + GdiFlush(); + resized(w, h); + _change_mask |= WGLWIN_CONFIGURE; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_mouse_motion +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow::handle_mouse_motion(int x, int y) { + _input_devices[0].set_pointer_in_window(x, y); +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_mouse_entry +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow::handle_mouse_entry(int state) { + if (state == MOUSE_EXITED) { + _input_devices[0].set_pointer_out_of_window(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_keypress +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow:: +handle_keypress(ButtonHandle key, int x, int y) { + _input_devices[0].set_pointer_in_window(x, y); + if (key != ButtonHandle::none()) { + _input_devices[0].button_down(key); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_keyrelease +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow:: +handle_keyrelease(ButtonHandle key, int, int) { + if (key != ButtonHandle::none()) { + _input_devices[0].button_up(key); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: handle_changes +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow::handle_changes(void) { + // As an optimization, check to see if change is anything other than a + // redisplay + if (_change_mask & WGLWIN_CONFIGURE) { + RECT changes; + POINT point; + GetClientRect(_mwindow, &changes); + point.x = 0; + point.y = 0; + ClientToScreen(_mwindow, &point); + changes.left = point.x; + changes.top = point.y; + // Don't do this in full-screen mode + AdjustWindowRect(&changes, WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | + WS_CLIPCHILDREN, FALSE); + SetWindowPos(_mwindow, HWND_TOP, changes.left, changes.top, + changes.right - changes.left, changes.bottom - changes.top, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | + SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER); + } + _change_mask = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: process_events +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow::process_events(void) { + MSG event, msg; + + do { + if (!GetMessage(&event, NULL, 0, 0)) + exit(0); + // Translate virtual key messages + TranslateMessage(&event); + // Call window_proc + DispatchMessage(&event); + } + while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)); +} + +//////////////////////////////////////////////////////////////////// +// Function: idle_wait +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow::idle_wait(void) { + MSG msg; + if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) + process_events(); + + call_idle_callback(); +} + +//////////////////////////////////////////////////////////////////// +// Function: wglGraphicsWindow::supports_update +// Access: Public, Virtual +// Description: Returns true if this particular kind of +// GraphicsWindow supports use of the update() function +// to update the graphics one frame at a time, so that +// the window does not need to be the program's main +// loop. Returns false if the only way to update the +// window is to call main_loop(). +//////////////////////////////////////////////////////////////////// +bool wglGraphicsWindow:: +supports_update() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: update +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow::update(void) { + _show_code_pcollector.stop(); + PStatClient::main_tick(); + + if (_change_mask) + handle_changes(); + + make_current(); + // Always ask for a redisplay for now + call_draw_callback(true); + + if (_idle_callback) + idle_wait(); + else + process_events(); + + _show_code_pcollector.start(); +} + +//////////////////////////////////////////////////////////////////// +// Function: enable_mouse_input +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow::enable_mouse_input(bool val) { + _mouse_input_enabled = val; +} + +//////////////////////////////////////////////////////////////////// +// Function: enable_mouse_motion +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow::enable_mouse_motion(bool val) { + _mouse_motion_enabled = val; +} + +//////////////////////////////////////////////////////////////////// +// Function: enable_mouse_passive_motion +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow::enable_mouse_passive_motion(bool val) { + _mouse_passive_motion_enabled = val; +} + +//////////////////////////////////////////////////////////////////// +// Function: make_current +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow::make_current(void) { + PStatTimer timer(_make_current_pcollector); + HGLRC current_context = wglGetCurrentContext(); + HDC current_dc = wglGetCurrentDC(); + if (current_context != _context || current_dc != _hdc) + wglMakeCurrent(_hdc, _context); +} + +//////////////////////////////////////////////////////////////////// +// Function: unmake_current +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow::unmake_current(void) { + wglMakeCurrent(NULL, NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: wglGraphicsWindow::get_gsg_type +// Access: Public, Virtual +// Description: Returns the TypeHandle of the kind of GSG preferred +// by this kind of window. +//////////////////////////////////////////////////////////////////// +TypeHandle wglGraphicsWindow:: +get_gsg_type() const { + return GLGraphicsStateGuardian::get_class_type(); +} + +GraphicsWindow *wglGraphicsWindow:: +make_wglGraphicsWindow(const FactoryParams ¶ms) { + GraphicsWindow::WindowPipe *pipe_param; + if (!get_param_into(pipe_param, params)) { + wgldisplay_cat.error() + << "No pipe specified for window creation!" << endl; + return NULL; + } + + GraphicsPipe *pipe = pipe_param->get_pipe(); + + GraphicsWindow::WindowProps *props_param; + if (!get_param_into(props_param, params)) { + return new wglGraphicsWindow(pipe); + } else { + return new wglGraphicsWindow(pipe, props_param->get_properties()); + } +} + +TypeHandle wglGraphicsWindow::get_class_type(void) { + return _type_handle; +} + +void wglGraphicsWindow::init_type(void) { + GraphicsWindow::init_type(); + register_type(_type_handle, "wglGraphicsWindow", + GraphicsWindow::get_class_type()); +} + +TypeHandle wglGraphicsWindow::get_type(void) const { + return get_class_type(); +} diff --git a/panda/src/wgldisplay/wglGraphicsWindow.h b/panda/src/wgldisplay/wglGraphicsWindow.h new file mode 100644 index 0000000000..df6b885484 --- /dev/null +++ b/panda/src/wgldisplay/wglGraphicsWindow.h @@ -0,0 +1,126 @@ +// Filename: wglGraphicsWindow.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +#ifndef WGLGRAPHICSWINDOW_H +#define WGLGRAPHICSWINDOW_H +// +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// +#include + +#include +#define WINDOWS_LEAN_AND_MEAN +#include +#undef WINDOWS_LEAN_AND_MEAN + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// +class wglGraphicsPipe; + +const int WGLWIN_CONFIGURE = 4; +const int WGLWIN_EVENT = 8; + +#define GLX_USE_GL 1 /* support GLX rendering */ +#define GLX_BUFFER_SIZE 2 /* depth of the color buffer */ +#define GLX_LEVEL 3 /* level in plane stacking */ +#define GLX_RGBA 4 /* true if RGBA mode */ +#define GLX_DOUBLEBUFFER 5 /* double buffering supported */ +#define GLX_STEREO 6 /* stereo buffering supported */ +#define GLX_AUX_BUFFERS 7 /* number of aux buffers */ +#define GLX_RED_SIZE 8 /* number of red component bits */ +#define GLX_GREEN_SIZE 9 /* number of green component bits */ +#define GLX_BLUE_SIZE 10 /* number of blue component bits */ +#define GLX_ALPHA_SIZE 11 /* number of alpha component bits */ +#define GLX_DEPTH_SIZE 12 /* number of depth bits */ +#define GLX_STENCIL_SIZE 13 /* number of stencil bits */ +#define GLX_ACCUM_RED_SIZE 14 /* number of red accum bits */ +#define GLX_ACCUM_GREEN_SIZE 15 /* number of green accum bits */ +#define GLX_ACCUM_BLUE_SIZE 16 /* number of blue accum bits */ +#define GLX_ACCUM_ALPHA_SIZE 17 /* number of alpha accum bits */ + + +//////////////////////////////////////////////////////////////////// +// Class : wglGraphicsWindow +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAGL wglGraphicsWindow : public GraphicsWindow { +public: + wglGraphicsWindow(GraphicsPipe* pipe); + wglGraphicsWindow(GraphicsPipe* pipe, + const GraphicsWindow::Properties& props); + virtual ~wglGraphicsWindow(void); + + virtual bool supports_update() const; + virtual void update(void); + virtual void end_frame( void ); + + virtual TypeHandle get_gsg_type() const; + static GraphicsWindow* make_wglGraphicsWindow(const FactoryParams ¶ms); + +public: + virtual void make_current(void); + virtual void unmake_current(void); + + INLINE bool mouse_entry_enabled(void) { return _mouse_entry_enabled; } + INLINE bool mouse_motion_enabled(void) { return _mouse_motion_enabled; } + INLINE bool mouse_passive_motion_enabled(void) { + return _mouse_passive_motion_enabled; + } + void handle_reshape( int w, int h ); + void handle_mouse_motion( int x, int y ); + void handle_mouse_entry( int state ); + void handle_keypress( ButtonHandle key, int x, int y ); + void handle_keyrelease( ButtonHandle key, int x, int y ); + +protected: + PIXELFORMATDESCRIPTOR* try_for_visual(wglGraphicsPipe *pipe, + int mask, int want_depth_bits = 1, int want_color_bits = 1); + void choose_visual(void); + static void get_config(PIXELFORMATDESCRIPTOR* visual, int attrib, int *value); + virtual void config( void ); + void setup_colormap(void); + + void enable_mouse_input(bool val); + void enable_mouse_motion(bool val); + void enable_mouse_passive_motion(bool val); + void enable_mouse_entry(bool val); + + void handle_changes(void); + void process_events(void); + void idle_wait(void); + + void adjust_coords(int &xorg, int &yorg, int &xsize, int &ysize); + +public: + uint _change_mask; + HWND _mwindow; + +private: + HGLRC _context; + HDC _hdc; + PIXELFORMATDESCRIPTOR* _visual; + HPALETTE _colormap; + + bool _mouse_input_enabled; + bool _mouse_motion_enabled; + bool _mouse_passive_motion_enabled; + bool _mouse_entry_enabled; + int _entry_state; + bool _ignore_key_repeat; + int _full_height, _full_width; + +public: + static TypeHandle get_class_type(void); + static void init_type(void); + virtual TypeHandle get_type(void) const; + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif